#include #include "DAESceneEncoder.h" #include "DAEOptimizer.h" //#define ENCODER_PRINT_TIME 1 using namespace gameplay; DAESceneEncoder::DAESceneEncoder() : _collada(NULL), _dom(NULL), file(NULL), _vertexBlendWeights(NULL), _vertexBlendIndices(NULL) { } DAESceneEncoder::~DAESceneEncoder() { } unsigned int getMaxOffset(domInputLocalOffset_Array& inputArray) { unsigned int maxOffset = 0; for (unsigned int i = 0; i < (int)inputArray.getCount(); ++i) { if ( inputArray[i]->getOffset() > maxOffset ) { maxOffset = (unsigned int)inputArray[i]->getOffset(); } } return maxOffset; } void DAESceneEncoder::optimizeCOLLADA(const EncoderArguments& arguments, domCOLLADA* dom) { DAEOptimizer optimizer(dom); const std::vector& groupAnimatioNodeIds = arguments.getGroupAnimationNodeId(); const std::vector& groupAnimatioIds = arguments.getGroupAnimationAnimationId(); assert(groupAnimatioNodeIds.size() == groupAnimatioIds.size()); size_t size = groupAnimatioNodeIds.size(); if (size > 0) { begin(); for (size_t i = 0; i < size; ++i) { optimizer.combineAnimations(groupAnimatioNodeIds[i], groupAnimatioIds[i]); } end("groupAnimation"); } if (arguments.DAEOutputEnabled()) { if (!_collada->writeTo(arguments.getFilePath(), arguments.getDAEOutputPath())) { fprintf(stderr,"Error: COLLADA failed to write the dom for file:%s\n", arguments.getDAEOutputPath().c_str()); } } } void DAESceneEncoder::triangulate(DAE* dae) { daeDatabase* dataBase = dae->getDatabase(); int geometryCount = (int)(dataBase->getElementCount(0, "geometry")); for (int i = 0; i < geometryCount; ++i) { // Find the next geometry element. domGeometry* domGeometry; dataBase->getElement((daeElement**)&domGeometry, i, 0, "geometry"); // Get the mesh out of the geometry. const domMeshRef domMesh = domGeometry->getMesh(); if (!domMesh) { continue; } // Loop over all the polygons elements. int polygonsCount = (int)(domMesh->getPolygons_array().getCount()); for (int j = 0; j < polygonsCount; ++j) { // Get the polygons out of the mesh. domPolygons* domPolygons = domMesh->getPolygons_array()[j]; // Create the triangles from the polygons createTrianglesFromPolygons(domMesh, domPolygons); } while (domMesh->getPolygons_array().getCount() > 0) { domPolygons* domPolygons = domMesh->getPolygons_array().get(0); // Remove the polygons from the mesh. domMesh->removeChildElement(domPolygons); } // Loop over all the polylist elements. int polylistCount = (int)(domMesh->getPolylist_array().getCount()); for (int j = 0; j < polylistCount; ++j) { // Get the polylist out of the mesh. domPolylist* domPolylist = domMesh->getPolylist_array()[j]; // Create the triangles from the polygon list createTrianglesFromPolylist(domMesh, domPolylist); } while (domMesh->getPolylist_array().getCount() > 0) { domPolylist* domPolylist = domMesh->getPolylist_array().get(0); // Remove the polylist from the mesh. domMesh->removeChildElement(domPolylist); } } } void DAESceneEncoder::createTrianglesFromPolygons(domMesh* domMesh, domPolygons* domPolygons) { // Create a new inside the mesh that has the same material as the . domTriangles* triangles = (domTriangles*)domMesh->createAndPlace("triangles"); triangles->setCount(0); triangles->setMaterial(domPolygons->getMaterial()); domP* domTrianglesP = (domP*)triangles->createAndPlace("p"); // Give the new the same <_dae> and as the old . for (unsigned int i = 0; i < domPolygons->getInput_array().getCount(); ++i) { triangles->placeElement(domPolygons->getInput_array()[i]->clone()); } // Get the number of inputs and primitives for the polygons array. unsigned int inputCount = getMaxOffset(domPolygons->getInput_array()) + 1; unsigned int primitiveCount = domPolygons->getP_array().getCount(); // Triangulate all the primitives, this generates all the triangles in a single

element. for (unsigned int j = 0; j < primitiveCount; ++j) { // Check the polygons for consistancy (some exported files have had the wrong number of indices). domP* domCurrentP = domPolygons->getP_array()[j]; int elementCount = (int)(domCurrentP->getValue().getCount()); if ( (elementCount % inputCount) != 0 ) { // Skip this case. } else { unsigned int triangleCount = (elementCount / inputCount) - 2; // Write out the primitives as triangles, just fan using the first element as the base. unsigned int index = inputCount; for (unsigned int k = 0; k < triangleCount; ++k) { // First vertex. for (unsigned int l = 0; l < inputCount; ++l) { domTrianglesP->getValue().append(domCurrentP->getValue()[l]); } // Second vertex. for (unsigned int l = 0; l < inputCount; ++l) { domTrianglesP->getValue().append(domCurrentP->getValue()[index + l]); } // Third vertex. index += inputCount; for (unsigned int l = 0; l < inputCount; ++l) { domTrianglesP->getValue().append(domCurrentP->getValue()[index + l]); } triangles->setCount(triangles->getCount() + 1); } } } } void DAESceneEncoder::createTrianglesFromPolylist(domMesh* domMesh, domPolylist* domPolylist) { // Create a new inside the mesh that has the same material as the . domTriangles* triangles = (domTriangles*)domMesh->createAndPlace("triangles"); triangles->setMaterial(domPolylist->getMaterial()); domP* domTrianglesP = (domP*)triangles->createAndPlace("p"); // Give the new the same <_dae> and as the old . for (int i = 0; i < (int)(domPolylist->getInput_array().getCount()); ++i) { triangles->placeElement(domPolylist->getInput_array()[i]->clone()); } // Get the number of inputs and primitives for the polygons array. unsigned int inputCount = getMaxOffset(domPolylist->getInput_array()) + 1; unsigned int primitiveCount = domPolylist->getVcount()->getValue().getCount(); unsigned int offset = 0; unsigned int trianglesProcessed = 0; // Triangulate all the primitives, this generates all the triangles in a single

element. for (unsigned int j = 0; j < primitiveCount; ++j) { unsigned int triangleCount = (unsigned int)domPolylist->getVcount()->getValue()[j] - 2; // Write out the primitives as triangles, just fan using the first element as the base. int index = inputCount; for (unsigned int k = 0; k < triangleCount; ++k) { // First vertex. for (unsigned int l = 0; l < inputCount; ++l) { domTrianglesP->getValue().append(domPolylist->getP()->getValue()[offset + l]); } // Second vertex. for (unsigned int l = 0; l < inputCount; ++l) { domTrianglesP->getValue().append(domPolylist->getP()->getValue()[offset + index + l]); } // Third vertex. index += inputCount; for (unsigned int l = 0; l < inputCount; ++l) { domTrianglesP->getValue().append(domPolylist->getP()->getValue()[offset + index + l]); } trianglesProcessed++; } offset += (unsigned int)domPolylist->getVcount()->getValue()[j] * inputCount; } triangles->setCount(trianglesProcessed); } void DAESceneEncoder::write(const std::string& filepath, const EncoderArguments& arguments) { _begin = std::clock(); const char* nodeId = arguments.getNodeId(); bool text = arguments.textOutputEnabled(); std::string filenameOnly = getFilenameFromFilePath(filepath); std::string dstPath = filepath.substr(0, filepath.find_last_of('/')); // Load the collada document _collada = new DAE(); begin(); _dom = _collada->open(filepath); end("Open file"); if (!_dom) { fprintf(stderr,"Error: COLLADA failed to open file:%s\n", filepath.c_str()); if (_collada) { delete _collada; _collada = NULL; } return; } // Run collada conditioners begin(); triangulate(_collada); end("triangulate"); // Optimize the dom before encoding optimizeCOLLADA(arguments, _dom); // Find the element within the const domCOLLADA::domSceneRef& domScene = _dom->getScene(); daeElement* scene = NULL; if (domScene && domScene->getInstance_visual_scene()) { scene = domScene->getInstance_visual_scene()->getUrl().getElement(); if (scene->getElementType() != COLLADA_TYPE::VISUAL_SCENE) { // This occured once where Maya exported a Node and Scene element with the same ID. fprintf(stderr,"Error: instance_visual_scene does not reference visual_scene for file:%s\n", filepath.c_str()); return; } if (scene) { if (nodeId == NULL) { // If the -n parameter was not passed then write out the entire scene. begin(); loadScene((domVisual_scene*)scene); end("load scene"); } else { // Resolve/Search for the node the user specified with the -n parameter. daeSIDResolver resolver(scene, nodeId); const domNode* node = daeSafeCast(resolver.getElement()); if (node) { //createNode(node, NULL); } else { fprintf(stderr,"COLLADA File loaded to the dom, but node was not found with -n%s.\n", nodeId); } } } else { fprintf(stderr,"COLLADA File loaded to the dom, but query for the dom assets failed.\n"); } } else { fprintf(stderr, "COLLADA File loaded to the dom, but missing .\n"); } // The animations should be loaded last begin(); loadAnimations(_dom); end("loadAnimations"); std::string dstFilename = dstPath; dstFilename.append(1, '/'); dstFilename.append(getFilenameNoExt(filenameOnly)); _gamePlayFile.adjust(); if (text) { std::string outFile = dstFilename + ".xml"; fprintf(stderr, "Saving debug file: %s\n", outFile.c_str()); _gamePlayFile.saveText(outFile); } else { std::string outFile = dstFilename + ".gpb"; fprintf(stderr, "Saving binary file: %s\n", outFile.c_str()); begin(); _gamePlayFile.saveBinary(outFile); end("save binary"); } // Cleanup if (file) { fclose(file); } if (_collada) { delete _collada; _collada = NULL; } } void DAESceneEncoder::loadAnimations(const domCOLLADA* dom) { // Call loadAnimation on all elements in all const domLibrary_animations_Array& animationLibrarys = dom->getLibrary_animations_array(); size_t animationLibrarysCount = animationLibrarys.getCount(); for (size_t i = 0; i < animationLibrarysCount; ++i) { const domLibrary_animationsRef& libraryAnimation = animationLibrarys.get(i); const domAnimation_Array& animationArray = libraryAnimation->getAnimation_array(); size_t animationCount = animationArray.getCount(); for (size_t j = 0; j < animationCount; ++j) { const domAnimationRef& animationRef = animationArray.get(j); loadAnimation(animationRef); } } } void DAESceneEncoder::loadAnimation(const domAnimationRef animationRef) { // points to one // points to multiple elements Animation* animation = new Animation(); const char* str = animationRef->getId(); if (str) { animation->setId(str); } // const domChannel_Array& channelArray = animationRef->getChannel_array(); size_t channelArrayCount = channelArray.getCount(); for (size_t i = 0; i < channelArrayCount; ++i) { AnimationChannel* animationChannel = new AnimationChannel(); const domChannelRef& channelRef = channelArray.get(i); // const domSamplerRef sampler = getSampler(channelRef); assert(sampler); // const domInputLocal_Array& inputArray = sampler->getInput_array(); size_t inputArrayCount = inputArray.getCount(); for (size_t j = 0; j < inputArrayCount; ++j) { const domInputLocalRef& inputLocal = inputArray.get(j); // const domSourceRef source = getSource(inputLocal, animationRef); std::string semantic = inputLocal->getSemantic(); if (equals(semantic, "INTERPOLATION")) { // Interpolation source is a list of strings loadInterpolation(source, animationChannel); } else { // The other sources are lists of floats. std::vector floats; copyFloats(source->getFloat_array(), &floats); if (equals(semantic, "INPUT")) { // TODO: Ensure param name is TIME? for (std::vector::iterator k = floats.begin(); k != floats.end(); ++k) { // Convert seconds to milliseconds *k = *k * 1000.0f; } animationChannel->setKeyTimes(floats); } else if (equals(semantic, "OUTPUT")) { animationChannel->setKeyValues(floats); } else if (equals(semantic, "IN_TANGENT")) { animationChannel->setTangentsIn(floats); } else if (equals(semantic, "OUT_TANGENT")) { animationChannel->setTangentsOut(floats); } } } // get target attribute enum value if (loadTarget(channelRef, animationChannel)) { animation->add(animationChannel); } } if (animation->getAnimationChannelCount() > 0) { _gamePlayFile.addAnimation(animation); } else { delete animation; } } void DAESceneEncoder::loadInterpolation(const domSourceRef source, AnimationChannel* animationChannel) { // COLLADA stores the interpolations as a list of strings while GBP uses unsigned int std::vector values; const domName_arrayRef nameArray = getSourceNameArray(source); assert(nameArray); const domListOfNames& names = nameArray->getValue(); size_t count = (size_t)names.getCount(); values.resize(count); if (count > 0) { for (size_t i = 0; i < count; ++i) { values[i] = AnimationChannel::getInterpolationType(names.get(i)); } // If all of the interpolation types are the same then only store the interpolation once // instead of storing the same type for each key frame. unsigned int firstType = values[0]; bool allEqual = true; for (size_t i = 1; i < count; ++i) { if (firstType != values[i]) { allEqual = false; break; } } if (allEqual) { values.resize(1); } } animationChannel->setInterpolations(values); } bool DAESceneEncoder::loadTarget(const domChannelRef& channelRef, AnimationChannel* animationChannel) { // GamePlay requires that animations are baked. Use "Bake Transforms" in your 3D modeling tool. // If the target of an animation is not a matrix then an error will be printed. const static char* TRANSFORM_WARNING_FORMAT = "Warning: Node \"%s\":\n %s %s\n"; const static char* TRANSFORM_MESSAGE = "transform found but not supported.\n Use \"Bake Transforms\" option when exporting."; unsigned int targetProperty = 0; DAEChannelTarget channelTarget(channelRef); const char* targetId = channelTarget.getTargetId().c_str(); // TODO: Do we want to support more than one? If yes then this needs to be fixed. for (size_t i = 0; i < channelTarget.getTargetAttributeCount(); ++i) { std::string prop; channelTarget.getPropertyName(i, &prop); daeElement* attributeElement = channelTarget.getTargetAttribute(i); if (attributeElement) { daeInt type = attributeElement->typeID(); if (type == domRotate::ID()) { printf(TRANSFORM_WARNING_FORMAT, targetId, "Rotate", TRANSFORM_MESSAGE); return false; /* // const domRotate* rotate = daeSafeCast(attributeElement); if (prop.size() > 0) { if (equalsIgnoreCase(prop, "ANGLE")) { targetProperty = Transform::ANIMATE_ROTATE; // get the rotation axis const domFloat4& f = rotate->getValue(); float x = (float)f.get(0); float y = (float)f.get(1); float z = (float)f.get(2); // Get the angle values that were already read const std::vector& keyValues = animationChannel->getKeyValues(); size_t size = keyValues.size(); assert(size > 0); // COLLADA only targeted a single prop but GBP requires all 4 rotate values. // Convert (ANGLE ANGLE ANGLE) to (X Y Z ANGLE X Y Z ANGLE X Y Z ANGLE) std::vector floats(size * 4); // Duplicate rotation axis. We will replace only the angle that COLLADA is targeting. for (size_t j = 0; j < size; ++j) { size_t k = j * 4; floats[k+0] = x; floats[k+1] = y; floats[k+2] = z; floats[k+3] = keyValues[j]; // angle } animationChannel->setKeyValues(floats); } } */ } else if (type == domScale::ID()) { printf(TRANSFORM_WARNING_FORMAT, targetId, "Scale", TRANSFORM_MESSAGE); return false; /* // //const domScale* scale = daeSafeCast(attributeElement); if (equalsIgnoreCase(prop, "X")) { targetProperty = Transform::ANIMATE_SCALE_X; } else if (equalsIgnoreCase(prop, "Y")) { targetProperty = Transform::ANIMATE_SCALE_Y; } else if (equalsIgnoreCase(prop, "Z")) { targetProperty = Transform::ANIMATE_SCALE_Z; } else { targetProperty = Transform::ANIMATE_SCALE; } */ } else if (type == domTranslate::ID()) { printf(TRANSFORM_WARNING_FORMAT, targetId, "Translate", TRANSFORM_MESSAGE); return false; /* // //const domTranslate* translate = daeSafeCast(attributeElement); if (equalsIgnoreCase(prop, "X")) { targetProperty = Transform::ANIMATE_TRANSLATE_X; } else if (equalsIgnoreCase(prop, "Y")) { targetProperty = Transform::ANIMATE_TRANSLATE_Y; } else if (equalsIgnoreCase(prop, "Z")) { targetProperty = Transform::ANIMATE_TRANSLATE_Z; } else { targetProperty = Transform::ANIMATE_TRANSLATE; } */ } else if (type == domMatrix::ID()) { // If the animation is targetting a matrix then convert it into // a scale, rotate, translate animation by decomposing the matrix. targetProperty = Transform::ANIMATE_SCALE_ROTATE_TRANSLATE; const std::vector& keyValues = animationChannel->getKeyValues(); assert(keyValues.size() % 16 == 0); // The matrix was 16 floats and the new values will be 10 floats size_t newSize = keyValues.size() / 16 * 10; std::vector floats(newSize); size_t matrixCount = keyValues.size() / 16; for (size_t i = 0; i < matrixCount; ++i) { size_t j = i * 16; // COLLADA used row-major but the Matrix class uses column-major Matrix matrix( keyValues[j+0], keyValues[j+4], keyValues[j+8], keyValues[j+12], keyValues[j+1], keyValues[j+5], keyValues[j+9], keyValues[j+13], keyValues[j+2], keyValues[j+6], keyValues[j+10], keyValues[j+14], keyValues[j+3], keyValues[j+7], keyValues[j+11], keyValues[j+15]); Vector3 scale; Quaternion rotation; Vector3 translation; matrix.decompose(&scale, &rotation, &translation); size_t k = i * 10; floats[k+0] = scale.x; floats[k+1] = scale.y; floats[k+2] = scale.z; floats[k+3] = rotation.x; floats[k+4] = rotation.y; floats[k+5] = rotation.z; floats[k+6] = rotation.w; floats[k+7] = translation.x; floats[k+8] = translation.y; floats[k+9] = translation.z; } animationChannel->setKeyValues(floats); } } } animationChannel->setTargetAttribute(targetProperty); animationChannel->setTargetId(channelTarget.getTargetId()); //animationChannel->removeDuplicates(); return true; } void DAESceneEncoder::begin() { #ifdef ENCODER_PRINT_TIME _begin = std::clock(); #endif } void DAESceneEncoder::end(const char* str) { #ifdef ENCODER_PRINT_TIME clock_t time = std::clock() - _begin; fprintf(stderr,"%5d %s\n", time, str); #endif } void DAESceneEncoder::copyFloats(const domFloat_array* source, std::vector* target) { std::vector& t = *target; size_t count = (size_t)source->getCount(); t.resize(count); const domListOfFloats& listOfFloats = source->getValue(); for (size_t i = 0; i < count; ++i) { t[i] = (float)listOfFloats.get(i); } } void DAESceneEncoder::loadScene(const domVisual_scene* visualScene) { Scene* scene = new Scene(); const domNode_Array& nodes = visualScene->getNode_array(); scene->setId(visualScene->getId()); size_t childCount = nodes.getCount(); for (size_t i = 0; i < childCount; ++i) { scene->add(loadNode(nodes[i], NULL)); } Node* activeCameraNode = findSceneActiveCameraNode(visualScene, scene); if (activeCameraNode) { scene->setActiveCameraNode(activeCameraNode); } _gamePlayFile.addScene(scene); } Node* DAESceneEncoder::findSceneActiveCameraNode(const domVisual_scene* visualScene, Scene* scene) { // Loops through each evaluate_scene's render until an active camera node is found. // Returns the first one found. // Find the active camera const domVisual_scene::domEvaluate_scene_Array& evaluateScenes = visualScene->getEvaluate_scene_array(); size_t evaluateSceneCount = evaluateScenes.getCount(); for (size_t i = 0; i < evaluateSceneCount; ++i) { const domVisual_scene::domEvaluate_scene::domRender_Array& renders = evaluateScenes[i]->getRender_array(); size_t renderCount = renders.getCount(); for (size_t j = 0; j < renderCount; ++j) { xsAnyURI cameraNodeURI = renders[i]->getCamera_node(); domNode* nodeRef = daeSafeCast(cameraNodeURI.getElement()); if (nodeRef) { std::string id = nodeRef->getId(); Node* node = _gamePlayFile.getNode(id.c_str()); if (node) { return node; } } } } // Find the first node in the scene that contains a camera. return scene->getFirstCameraNode(); } Node* DAESceneEncoder::loadNode(domNode* n, Node* parent) { Node* node = NULL; // Check if this node has already been loaded const char* id = n->getID(); if (id && strlen(id) > 0) { node = _gamePlayFile.getNode(n->getID()); if (node) { return node; } } // Load the node node = new Node(); if (parent) { parent->addChild(node); } if (n->getType() == NODETYPE_JOINT) { node->setIsJoint(true); } // Set node id node->setId(n->getId()); // If this node has an id then add it to the ref table _gamePlayFile.addNode(node); transformNode(n, node); loadControllerInstance(n, node); loadCameraInstance(n, node); loadLightInstance(n, node); loadGeometryInstance(n, node); // Load child nodes const domNode_Array& childNodes = n->getNode_array(); size_t childCount = childNodes.getCount(); for (size_t i = 0; i < childCount; ++i) { loadNode(childNodes.get(i), node); } return node; } void DAESceneEncoder::transformNode(domNode* domNode, Node* node) { // Apply the transform. // Note that we only honor the first matrix transform specified for the DOM node. const domMatrix_Array& matrixArray = domNode->getMatrix_array(); if (matrixArray.getCount() > 0) { const domMatrixRef& matrix = matrixArray.get(0); if (!matrix) { return; } const domFloat4x4& tx = matrix->getValue(); float transform[] = {(float)tx.get(0), (float)tx.get(4), (float)tx.get(8), (float)tx.get(12), (float)tx.get(1), (float)tx.get(5), (float)tx.get(9), (float)tx.get(13), (float)tx.get(2), (float)tx.get(6), (float)tx.get(10), (float)tx.get(14), (float)tx.get(3), (float)tx.get(7), (float)tx.get(11), (float)tx.get(15)}; node->setTransformMatrix(transform); } else { Matrix transform; calcTransform(domNode, transform); node->setTransformMatrix(transform.m); } // TODO: Handle transforming by other types (SRT, etc) (see "Node" child elements spec) /*Vector3 scale; Quaternion rotation; Vector3 translation; localTransform.Decompose(&scale, &rotation, &translation); node->SetScale(scale); node->SetRotation(rotation); node->SetTranslation(translation);*/ } void DAESceneEncoder::calcTransform(domNode* domNode, Matrix& dstTransform) { daeTArray > children; domNode->getChildren(children); size_t childCount = children.getCount(); for (size_t i = 0; i < childCount; ++i) { daeElementRef childElement = children[i]; switch (childElement->getElementType()) { case COLLADA_TYPE::TRANSLATE: { domTranslateRef translateNode = daeSafeCast(childElement); float x = (float)translateNode->getValue().get(0); float y = (float)translateNode->getValue().get(1); float z = (float)translateNode->getValue().get(2); dstTransform.translate(x, y, z); break; } case COLLADA_TYPE::ROTATE: { domRotateRef rotateNode = daeSafeCast(childElement); float x = (float)rotateNode->getValue().get(0); float y = (float)rotateNode->getValue().get(1); float z = (float)rotateNode->getValue().get(2); float angle = MATH_DEG_TO_RAD((float)rotateNode->getValue().get(3)); // COLLADA uses degrees, gameplay uses radians if (x == 1.0f && y == 0.0f && z == 0.0f) { dstTransform.rotateX(angle); } else if (x == 0.0f && y == 1.0f && z == 0.0f) { dstTransform.rotateY(angle); } else if (x == 0.0f && y == 0.0f && z == 1.0f) { dstTransform.rotateZ(angle); } else { dstTransform.rotate(x, y, z, angle); } break; } case COLLADA_TYPE::SCALE: { domScaleRef scaleNode = daeSafeCast(childElement); float x = (float)scaleNode->getValue().get(0); float y = (float)scaleNode->getValue().get(1); float z = (float)scaleNode->getValue().get(2); dstTransform.scale(x, y, z); break; } case COLLADA_TYPE::SKEW: warning("Skew transform found but not supported."); break; case COLLADA_TYPE::LOOKAT: warning("Lookat transform found but not supported."); break; default: break; } } } void DAESceneEncoder::loadCameraInstance(const domNode* n, Node* node) { // Does this node have any camera instances? const domInstance_camera_Array& instanceCameras = n->getInstance_camera_array(); size_t instanceCameraCount = instanceCameras.getCount(); for (size_t i = 0; i < instanceCameraCount; ++i) { // Get the camrea object const domInstance_camera* cameraInstanceRef = instanceCameras.get(i); xsAnyURI cameraURI = cameraInstanceRef->getUrl(); domCamera* cameraRef = daeSafeCast(cameraURI.getElement()); if (cameraRef) { CameraInstance* cameraInstance = loadCamera(cameraRef); if (cameraInstance) { node->setCameraInstance(cameraInstance); } } else { // warning } } } void DAESceneEncoder::loadLightInstance(const domNode* n, Node* node) { // Does this node have any light instances? const domInstance_light_Array& instanceLights = n->getInstance_light_array(); size_t instanceLightCount = instanceLights.getCount(); for (size_t i = 0; i < instanceLightCount; ++i) { // Get the camrea object const domInstance_light* lightInstanceRef = instanceLights.get(i); xsAnyURI lightURI = lightInstanceRef->getUrl(); domLight* lightRef = daeSafeCast(lightURI.getElement()); if (lightRef) { LightInstance* lightInstance = loadLight(lightRef); if (lightInstance) { node->setLightInstance(lightInstance); } } else { // warning } } } void DAESceneEncoder::loadGeometryInstance(const domNode* n, Node* node) { // Does this node have any geometry instances? const domInstance_geometry_Array& instanceGeometries = n->getInstance_geometry_array(); size_t instanceGeometryCount = instanceGeometries.getCount(); for (size_t i = 0; i < instanceGeometryCount; ++i) { // Get the geometry object const domInstance_geometryRef geometryInstanceRef = instanceGeometries.get(i); xsAnyURI geometryURI = geometryInstanceRef->getUrl(); domGeometry* geometry = daeSafeCast(geometryURI.getElement()); // Load the model from this geometry if (geometry) { Model* model = loadGeometry(geometry, geometryInstanceRef->getBind_material()); if (model) { node->setModel(model); } } else { warning(std::string("Failed to resolve geometry url: ") + geometryURI.getURI()); } } } void DAESceneEncoder::loadControllerInstance(const domNode* n, Node* node) { // Does this node have any controller instances? const domInstance_controller_Array& instanceControllers = n->getInstance_controller_array(); size_t instanceControllerCount = instanceControllers.getCount(); for (size_t i = 0; i < instanceControllerCount; ++i) { const domInstance_controllerRef instanceControllerRef = instanceControllers.get(i); xsAnyURI controllerURI = instanceControllerRef->getUrl(); domController* controllerRef = daeSafeCast(controllerURI.getElement()); if (controllerRef) { const domSkin* skinElement = controllerRef->getSkin(); if (skinElement) { Model* model = loadSkin(skinElement); if (model) { domInstance_controller::domSkeleton_Array& skeletons = instanceControllerRef->getSkeleton_array(); if (skeletons.getCount() == 0) { warning("No skeletons found for instance controller: "); delete model; continue; } // Load the skeleton for this skin domInstance_controller::domSkeletonRef skeleton = getSkeleton(instanceControllerRef); assert(skeleton); loadSkeleton(skeleton, model->getSkin()); node->setModel(model); } } } else { // warning } _jointLookupTable.clear(); _jointInverseBindPoseMatrices.clear(); } } CameraInstance* DAESceneEncoder::loadCamera(const domCamera* cameraRef) { ///////////////////////////// CAMERA // check if camera is already added to gamePlayFile const char* id = cameraRef->getId(); Camera* camera = _gamePlayFile.getCamera(id); if (camera == NULL) { camera = new Camera(); camera->setId(id); // Optics const domCamera::domOpticsRef opticsRef = cameraRef->getOptics(); if (opticsRef.cast()) { const domCamera::domOptics::domTechnique_commonRef techRef = opticsRef->getTechnique_common(); // Orthographics const domCamera::domOptics::domTechnique_common::domOrthographicRef orthographicRef = techRef->getOrthographic(); if (orthographicRef.cast()) { camera->setOrthographic(); camera->setAspectRatio((float)orthographicRef->getAspect_ratio()->getValue()); camera->setNearPlane((float)orthographicRef->getZnear()->getValue()); camera->setFarPlane((float)orthographicRef->getZfar()->getValue()); const domTargetableFloatRef xmag = orthographicRef->getXmag(); const domTargetableFloatRef ymag = orthographicRef->getYmag(); // Viewport width if (xmag.cast()) { camera->setViewportWidth((float)xmag->getValue()); } // Viewport height if (ymag.cast()) { camera->setViewportHeight((float)ymag->getValue()); } // TODO: Viewport x and y? } // Perspective const domCamera::domOptics::domTechnique_common::domPerspectiveRef perspectiveRef = techRef->getPerspective(); if (perspectiveRef.cast()) { camera->setPerspective(); camera->setNearPlane((float)perspectiveRef->getZnear()->getValue()); camera->setFarPlane((float)perspectiveRef->getZfar()->getValue()); float aspectRatio = -1.0f; if (perspectiveRef->getAspect_ratio().cast()) { aspectRatio = (float)perspectiveRef->getAspect_ratio()->getValue(); camera->setAspectRatio(aspectRatio); } if (perspectiveRef->getYfov().cast()) { camera->setFieldOfView((float)perspectiveRef->getYfov()->getValue()); } else if (perspectiveRef->getXfov().cast() && aspectRatio > 0.0f) { // The gameplaybinary stores the yfov but collada might have specified // an xfov and an aspect ratio. So use those to calculate the yfov. float xfov = (float)perspectiveRef->getXfov()->getValue(); float yfov = xfov / aspectRatio; camera->setFieldOfView(yfov); } } } _gamePlayFile.addCamera(camera); } CameraInstance* cameraInstance = new CameraInstance(); cameraInstance->setCamera(camera); return cameraInstance; } LightInstance* DAESceneEncoder::loadLight(const domLight* lightRef) { ///////////////////////////// LIGHT // check if light is already added to gamePlayFile const char* id = lightRef->getId(); Light* light = _gamePlayFile.getLight(id); if (light == NULL) { light = new Light(); light->setId(lightRef->getId()); const domLight::domTechnique_commonRef techRef = lightRef->getTechnique_common(); // Ambient light { const domLight::domTechnique_common::domAmbientRef ambientRef = techRef->getAmbient(); if (ambientRef.cast()) { light->setAmbientLight(); // color const domTargetableFloat3Ref float3Ref = ambientRef->getColor(); const domFloat3& color3 = float3Ref->getValue(); light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2)); } } // Directional light { const domLight::domTechnique_common::domDirectionalRef direcitonalRef = techRef->getDirectional(); if (direcitonalRef.cast()) { light->setDirectionalLight(); // color const domTargetableFloat3Ref float3Ref = direcitonalRef->getColor(); const domFloat3& color3 = float3Ref->getValue(); light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2)); } } // Spot light { const domLight::domTechnique_common::domSpotRef spotRef = techRef->getSpot(); if (spotRef.cast()) { light->setSpotLight(); // color const domTargetableFloat3Ref float3Ref = spotRef->getColor(); const domFloat3& color3 = float3Ref->getValue(); light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2)); const domTargetableFloatRef& constAtt = spotRef->getConstant_attenuation(); if (constAtt.cast()) { light->setConstantAttenuation((float)constAtt->getValue()); } const domTargetableFloatRef& linearAtt = spotRef->getLinear_attenuation(); if (linearAtt.cast()) { light->setLinearAttenuation((float)linearAtt->getValue()); } const domTargetableFloatRef& quadAtt = spotRef->getQuadratic_attenuation(); if (quadAtt.cast()) { light->setQuadraticAttenuation((float)quadAtt->getValue()); } const domTargetableFloatRef& falloffAngle = spotRef->getFalloff_angle(); if (falloffAngle.cast()) { light->setFalloffAngle((float)falloffAngle->getValue()); } const domTargetableFloatRef& falloffExp = spotRef->getFalloff_exponent(); if (falloffExp.cast()) { light->setFalloffExponent((float)falloffExp->getValue()); } } } // Point light { const domLight::domTechnique_common::domPointRef pointRef = techRef->getPoint(); if (pointRef.cast()) { light->setPointLight(); // color const domTargetableFloat3Ref float3Ref = pointRef->getColor(); const domFloat3& color3 = float3Ref->getValue(); light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2)); const domTargetableFloatRef& constAtt = pointRef->getConstant_attenuation(); if (constAtt.cast()) { light->setConstantAttenuation((float)constAtt->getValue()); } const domTargetableFloatRef& linearAtt = pointRef->getLinear_attenuation(); if (linearAtt.cast()) { light->setLinearAttenuation((float)linearAtt->getValue()); } const domTargetableFloatRef& quadAtt = pointRef->getQuadratic_attenuation(); if (quadAtt.cast()) { light->setQuadraticAttenuation((float)quadAtt->getValue()); } } } _gamePlayFile.addLight(light); } LightInstance* lightInstance = new LightInstance(); lightInstance->setLight(light); return lightInstance; } void DAESceneEncoder::loadSkeleton(domInstance_controller::domSkeleton* skeletonElement, MeshSkin* skin) { xsAnyURI skeletonUri = skeletonElement->getValue(); daeString skeletonId = skeletonUri.getID(); daeSIDResolver resolver(skeletonUri.getElement(), skeletonId); domNode* rootNode = daeSafeCast(resolver.getElement()); // Get the lookup scene id (sid) and joint index. std::string id = std::string(skeletonId); // Has the skeleton (root joint) been loaded yet? Node* skeleton = (Node*)_gamePlayFile.getFromRefTable(id); // The skeleton node is not loaded yet, so let's load it now if (skeleton == NULL) { // Find the top most parent of rootNode that has not yet been loaded domNode* topLevelParent = rootNode; while ( topLevelParent->getParent() && topLevelParent->getParent()->getElementType() == COLLADA_TYPE::NODE && _gamePlayFile.getFromRefTable(topLevelParent->getParent()->getID()) == NULL) { topLevelParent = (domNode*)topLevelParent->getParent(); } // Is the parent of this node loaded yet? Node* parentNode = NULL; if (topLevelParent->getParent() && topLevelParent->getParent()->getElementType() == COLLADA_TYPE::NODE && _gamePlayFile.getFromRefTable(topLevelParent->getParent()->getID()) != NULL) { parentNode = (Node*)_gamePlayFile.getFromRefTable(topLevelParent->getParent()->getID()); } // Finally, load the node hierarchy that includes the skeleton skeleton = loadNode(topLevelParent, parentNode); } if (skeleton == NULL) { // This shouldn't really happen.. skeleton = new Node(); skeleton->setId(id); _gamePlayFile.addNode(skeleton); } // Resolve and set joints array for skin std::list _joints; const std::list& jointNames = skin->getJointNames(); for (std::list::const_iterator i = jointNames.begin(); i != jointNames.end(); ++i) { Object* obj = _gamePlayFile.getFromRefTable(*i); if (obj) { Node* node = (Node*)obj; _joints.push_back(node); } } skin->setJoints(_joints); } Model* DAESceneEncoder::loadSkin(const domSkin* skinElement) { ///////////////////////////// SKIN Model* model = new Model(); MeshSkin* skin = new MeshSkin(); // Bind Shape Matrix const domSkin::domBind_shape_matrix* bindShapeMatrix = skinElement->getBind_shape_matrix(); if (bindShapeMatrix) { const domFloat4x4& m = bindShapeMatrix->getValue(); float transform[] = {(float)m.get(0), (float)m.get(4), (float)m.get(8), (float)m.get(12), (float)m.get(1), (float)m.get(5), (float)m.get(9), (float)m.get(13), (float)m.get(2), (float)m.get(6), (float)m.get(10), (float)m.get(14), (float)m.get(3), (float)m.get(7), (float)m.get(11), (float)m.get(15)}; skin->setBindShape(transform); } // Read and set our joints domSkin::domJointsRef _joints = skinElement->getJoints(); domInputLocal_Array& jointInputs = _joints->getInput_array(); // Process "JOINT" input semantic first (we need to do this to set the joint count) unsigned int jointCount = 0; for (unsigned int i = 0; i < jointInputs.getCount(); ++i) { domInputLocalRef input = jointInputs.get(i); std::string inputSemantic = std::string(input->getSemantic()); domURIFragmentType* sourceURI = &input->getSource(); sourceURI->resolveElement(); const domSourceRef source = (domSource*)(daeElement*)sourceURI->getElement(); if (equals(inputSemantic, "JOINT")) { // Get the joint Ids's std::list list; getJointNames(source, list); // Go through the joint list and conver them from sid to id because the sid information is // lost when converting to the gameplay binary format. for (std::list::iterator i = list.begin(); i != list.end(); ++i) { daeSIDResolver resolver(source->getDocument()->getDomRoot(), i->c_str()); daeElement* element = resolver.getElement(); if (element && element->getElementType() == COLLADA_TYPE::NODE) { domNodeRef node = daeSafeCast(element); const char* nodeId = node->getId(); if (nodeId && !equals(*i, nodeId)) { *i = nodeId; } } } // Get the joint count and set the capacities for both the jointCount = list.size(); _jointInverseBindPoseMatrices.reserve(jointCount); unsigned int j = 0; for (std::list::const_iterator i = list.begin(); i != list.end(); ++i) { _jointLookupTable[*i] = j++; } skin->setJointNames(list); } } // Make sure we have some joints if (jointCount == 0) { warning("No joints found for skin: "); return NULL; } // Process "INV_BIND_MATRIX" next for (unsigned int i = 0; i < jointInputs.getCount(); ++i) { domInputLocalRef input = jointInputs.get(i); std::string inputSemantic = std::string(input->getSemantic()); domURIFragmentType* sourceURI = &input->getSource(); sourceURI->resolveElement(); domSource* source = (domSource*)(daeElement*)sourceURI->getElement(); if (equals(inputSemantic, "INV_BIND_MATRIX")) { domListOfFloats& matrixFloats = source->getFloat_array()->getValue(); //unsigned int matrixFloatsCount = (unsigned int)source->getFloat_array()->getCount(); unsigned int jointIndex = 0; for (unsigned int j = 0; j < jointCount; ++j) { Matrix matrix((float)matrixFloats.get(jointIndex + 0), (float)matrixFloats.get(jointIndex + 4), (float)matrixFloats.get(jointIndex + 8), (float)matrixFloats.get(jointIndex + 12), (float)matrixFloats.get(jointIndex + 1), (float)matrixFloats.get(jointIndex + 5), (float)matrixFloats.get(jointIndex + 9), (float)matrixFloats.get(jointIndex + 13), (float)matrixFloats.get(jointIndex + 2), (float)matrixFloats.get(jointIndex + 6), (float)matrixFloats.get(jointIndex + 10), (float)matrixFloats.get(jointIndex + 14), (float)matrixFloats.get(jointIndex + 3), (float)matrixFloats.get(jointIndex + 7), (float)matrixFloats.get(jointIndex + 11), (float)matrixFloats.get(jointIndex + 15)); _jointInverseBindPoseMatrices.push_back(matrix); jointIndex += 16; } } } skin->setBindPoses(_jointInverseBindPoseMatrices); // Get the vertex weights inputs domSkin::domVertex_weights* vertexWeights = skinElement->getVertex_weights(); domInputLocalOffset_Array& vertexWeightsInputs = vertexWeights->getInput_array(); unsigned int vertexWeightsCount = (unsigned int)vertexWeights->getCount(); domListOfFloats jointWeights; for (unsigned int i = 0; i < jointInputs.getCount(); ++i) { domInputLocalOffsetRef input = vertexWeightsInputs.get(i); std::string inputSemantic = std::string(input->getSemantic()); domURIFragmentType* sourceURI = &input->getSource(); sourceURI->resolveElement(); domSource* source = (domSource*)(daeElement*)sourceURI->getElement(); if (equals(inputSemantic, "WEIGHT")) { domFloat_array* weights = source->getFloat_array(); if (weights) { jointWeights = weights->getValue(); } } } // Get the number of joint influences per vertex domSkin::domVertex_weights::domVcount* vCountElement = vertexWeights->getVcount(); domListOfUInts skinVertexInfluenceCounts = vCountElement->getValue(); // Get the joint/weight pair data. domSkin::domVertex_weights::domV* vElement = vertexWeights->getV(); domListOfInts skinVertexJointWeightPairIndices = vElement->getValue(); // Get the vertex influence count for any given vertex (up to max of 4) unsigned int maxVertexInfluencesCount = SCENE_SKIN_VERTEXINFLUENCES_MAX; skin->setVertexInfluenceCount(maxVertexInfluencesCount); // Get the vertex blend weights and joint indices and // allocate our vertex blend weights and blend indices arrays. // These will be used and cleaned up later in LoadMesh int skinVertexInfluenceCountTotal = skinVertexInfluenceCounts.getCount(); int totalVertexInfluencesCount = vertexWeightsCount * maxVertexInfluencesCount; _vertexBlendWeights = new float[totalVertexInfluencesCount]; _vertexBlendIndices = new unsigned int[totalVertexInfluencesCount]; // Preset the default blend weights to 0.0f (no effect) and blend indices to 0 (uses the first which when multiplied // will have no effect anyhow. memset(_vertexBlendWeights, 0, totalVertexInfluencesCount * sizeof(float)); memset(_vertexBlendIndices , 0, totalVertexInfluencesCount * sizeof(unsigned int)); int vOffset = 0; int weightOffset = 0; // Go through all the skin vertex influence weights from the indexed data. for (int i = 0; i < skinVertexInfluenceCountTotal; ++i) { // Get the influence count and directly get the vertext blend weights and indices. unsigned int vertexInfluenceCount = (unsigned int)skinVertexInfluenceCounts.get(i); float vertexInfluencesTotalWeights = 0.0f; std::vector vertexInfluences; //vertexInfluences.SetCapacity(vertexInfluenceCount); // Get the index/weight pairs and some the weight totals while at it. for (unsigned int j = 0; j < vertexInfluenceCount; ++j) { float weight = (float)jointWeights.get((unsigned int)skinVertexJointWeightPairIndices[vOffset + 1]); int index = (int)skinVertexJointWeightPairIndices[vOffset]; // Set invalid index corresponding weights to zero if (index < 0 || index > (int)vertexWeightsCount) { weight = 0.0f; index = 0; } SkinnedVertexWeightPair pair(weight, index); vertexInfluences.push_back(pair); vertexInfluencesTotalWeights += weight; vOffset+=2; } // Get up the the maximum vertex weight influence count. for (unsigned int j = 0; j < maxVertexInfluencesCount; ++j) { if (j < vertexInfluenceCount) { SkinnedVertexWeightPair pair = vertexInfluences[j]; _vertexBlendIndices[weightOffset] = pair.BlendIndex; if (vertexInfluencesTotalWeights > 0.0f) { _vertexBlendWeights[weightOffset] = pair.BlendWeight; } else { if (j == 0) { _vertexBlendWeights[weightOffset] = 1.0f; } else { _vertexBlendWeights[weightOffset] = 0.0f; } } } weightOffset++; } } model->setSkin(skin); /////////////////////////////////////////////////////////// // get geometry xsAnyURI geometryURI = skinElement->getSource(); domGeometry* geometry = daeSafeCast(geometryURI.getElement()); if (geometry) { const domMesh* meshElement = geometry->getMesh(); if (meshElement) { Mesh* mesh = loadMesh(meshElement, geometry->getId()); if (mesh) { model->setMesh(mesh); } } } /////////////////////////////////////////////////////////// return model; } Model* DAESceneEncoder::loadGeometry(const domGeometry* geometry, const domBind_materialRef bindMaterial) { // Does this geometry have a valid mesh? // Get the mesh for the geometry (if it has one) const domMesh* meshElement = geometry->getMesh(); if (meshElement == NULL) { warning(std::string("No mesh found for geometry: ") + geometry->getId()); return NULL; } ///////////////////////////// GEOMETRY // Load the mesh for this model Mesh* mesh = loadMesh(meshElement, geometry->getId()); if (mesh == NULL) { return NULL; } // Mesh instance Model* model = new Model(); model->setMesh(mesh); return model; } Mesh* DAESceneEncoder::loadMesh(const domMesh* meshElement, const std::string& geometryId) { const domTriangles_Array& trianglesArray = meshElement->getTriangles_array(); unsigned int trianglesArrayCount = (unsigned int)trianglesArray.getCount(); // Ensure the data is exported as triangles. if (trianglesArrayCount == 0) { warning(std::string("Geometry mesh has no triangles: ") + geometryId); return NULL; } // Check if this mesh already exists Mesh* mesh = _gamePlayFile.getMesh(geometryId.c_str()); if (mesh) { return mesh; } mesh = new Mesh(); mesh->setId(geometryId.c_str()); std::vector polygonInputs; // Quickly just go through each triangles array and make sure they have the same number of inputs // with the same layout. // const domSource_Array& sourceArray = meshElement->getSource_array(); const domInputLocal_Array& vertexArray = meshElement->getVertices()->getInput_array(); unsigned int inputCount = (unsigned int)-1; // Loop through our set of triangle lists (each list of triangles corresponds to a single MeshPart) for (unsigned int i = 0; i < trianglesArrayCount; ++i) { const domTrianglesRef& triangles = trianglesArray.get(i); const domInputLocalOffset_Array& inputArray = triangles->getInput_array(); // If not set then determine the number of input for all the triangles. if (inputCount == -1) { inputCount = (unsigned int)inputArray.getCount(); int texCoordCount = 0; for (unsigned int j = 0; j < inputCount; ++j) { const domInputLocalOffsetRef& input = inputArray.get(j); std::string inputSemantic = input->getSemantic(); // If its a vertex first do an extra lookup for the inclusive inputs if (equals(inputSemantic, "VERTEX")) { unsigned int vertexArrayCount = (unsigned int)vertexArray.getCount(); for (unsigned int k = 0; k < vertexArrayCount; ++k) { const domInputLocalRef& vertexInput = vertexArray.get(k); std::string semantic = std::string(vertexInput->getSemantic()); int type = getVertexUsageType(semantic); if (type == -1) { warning(std::string("Vertex semantic (") + semantic + ") is invalid/unsupported for geometry mesh: " + geometryId); } DAEPolygonInput* polygonInput = new DAEPolygonInput(); domURIFragmentType& sourceURI = vertexInput->getSource(); sourceURI.resolveElement(); domSource* source = (domSource*)(daeElement*)sourceURI.getElement(); polygonInput->offset = 0; polygonInput->sourceValues = source->getFloat_array()->getValue(); polygonInput->type = type; polygonInputs.push_back(polygonInput); } } else { std::string semantic = input->getSemantic(); int type = getVertexUsageType(semantic); if (type == -1) { warning(std::string("Semantic (") + semantic + ") is invalid/unsupported for geometry mesh: " + geometryId); break; } if (type == TEXCOORD0) { // Some meshes have multiple texture coordinates assert(texCoordCount <= 7); type += texCoordCount; ++texCoordCount; } DAEPolygonInput* polygonInput = new DAEPolygonInput(); domURIFragmentType& sourceURI = input->getSource(); sourceURI.resolveElement(); domSource* source = (domSource*)(daeElement*)sourceURI.getElement(); polygonInput->offset = (unsigned int)input->getOffset(); polygonInput->sourceValues = source->getFloat_array()->getValue(); polygonInput->type = type; // Get the accessor info const domSource::domTechnique_commonRef& technique = source->getTechnique_common(); if (technique.cast()) { const domAccessorRef& accessor = technique->getAccessor(); polygonInput->accessor = accessor; } polygonInputs.push_back(polygonInput); } } } else { // If there is a triangle array with a different number of inputs, this is not supported. if (inputCount != (unsigned int)inputArray.getCount()) { for (size_t j = 0; j < polygonInputs.size(); ++j) { delete polygonInputs[j]; } warning(std::string("Triangles do not all have the same number of input sources for geometry mesh: ") + geometryId); return NULL; } else { // TODO: Check if they are in the same order... } } } // Now we have validated that all input in all triangles are the same and in the same input layout. // Lets start to read them and build our subsets. for (unsigned int i = 0; i < trianglesArrayCount; ++i) { // Subset to be built. MeshPart* subset = new MeshPart(); // All of the information about the triangles and the sources to access the data from. domTriangles* triangles = daeSafeCast(trianglesArray.get(i)); // Parse the material for this subset //std::string materialName = triangles->getMaterial() == NULL ? _T("") : triangles->getMaterial(); //if (materialName.size() > 0) /// subset->material = ParseMaterial(bindMaterial, materialName); //const domInputLocalOffset_Array& inputArray = triangles->getInput_array(); const domListOfUInts& polyInts = triangles->getP()->getValue(); unsigned int polyIntsCount = (unsigned int)polyInts.getCount(); unsigned int poly = 0; unsigned int inputSourceCount = (unsigned int)polygonInputs.size(); unsigned int maxOffset = 0; // Go through the polygon indices for each input source retrieve the values // and iterate by its offset. Vertex vertex; for (unsigned int k = 0; k < inputSourceCount && poly < polyIntsCount;) { const domListOfFloats& source = polygonInputs[k]->sourceValues; unsigned int offset = polygonInputs[k]->offset; if (offset > maxOffset) { maxOffset = offset; } int type = polygonInputs[k]->type; unsigned int polyIndex = (unsigned int) polyInts.get(poly + offset); switch (type) { case POSITION: vertex = Vertex(); // TODO if (_vertexBlendWeights && _vertexBlendIndices) { vertex.hasWeights = true; vertex.blendWeights.x = _vertexBlendWeights[polyIndex * 4]; vertex.blendWeights.y = _vertexBlendWeights[polyIndex * 4 + 1]; vertex.blendWeights.z = _vertexBlendWeights[polyIndex * 4 + 2]; vertex.blendWeights.w = _vertexBlendWeights[polyIndex * 4 + 3]; vertex.blendIndices.x = (float)_vertexBlendIndices[polyIndex * 4]; vertex.blendIndices.y = (float)_vertexBlendIndices[polyIndex * 4 + 1]; vertex.blendIndices.z = (float)_vertexBlendIndices[polyIndex * 4 + 2]; vertex.blendIndices.w = (float)_vertexBlendIndices[polyIndex * 4 + 3]; } vertex.position.x = (float)source.get(polyIndex * 3); vertex.position.y = (float)source.get(polyIndex * 3 + 1); vertex.position.z = (float)source.get(polyIndex * 3 + 2); break; case NORMAL: vertex.hasNormal = true; vertex.normal.x = (float)source.get(polyIndex * 3); vertex.normal.y = (float)source.get(polyIndex * 3 + 1); vertex.normal.z = (float)source.get(polyIndex * 3 + 2); break; // TODO: Handle reading of per-vertex colors. // HOW do we know how many color components to read? // We must examine the Collada input accessor and read the stride/count to verify this - not ONLY for Color, but we should be doing this for ALL components (i.e. Position, Normal, etc). // case Color: // vertex.hasColor = true; // vertex.Diffuse.R = (float)source.get(polyIndex * 3); // vertex.Diffuse.G = (float)source.get(polyIndex * 3 + 1); // vertex.Diffuse.B = (float)source.get(polyIndex * 3 + 2); // vertex.Diffuse.A = (float)source.get(polyIndex * 3 + 3); // break; case TANGENT: vertex.hasTangent = true; vertex.tangent.x = (float)source.get(polyIndex * 3); vertex.tangent.y = (float)source.get(polyIndex * 3 + 1); vertex.tangent.z = (float)source.get(polyIndex * 3 + 2); break; case BINORMAL: vertex.hasBinormal = true; vertex.binormal.x = (float)source.get(polyIndex * 3); vertex.binormal.y = (float)source.get(polyIndex * 3 + 1); vertex.binormal.z = (float)source.get(polyIndex * 3 + 2); break; case TEXCOORD0: vertex.hasTexCoord = true; if (polygonInputs[k]->accessor) { // TODO: This assumes (s, t) are first unsigned int stride = (unsigned int)polygonInputs[k]->accessor->getStride(); vertex.texCoord.x = (float)source.get(polyIndex * stride); vertex.texCoord.y = (float)source.get(polyIndex * stride + 1); } else { vertex.texCoord.x = (float)source.get(polyIndex * 2); vertex.texCoord.y = (float)source.get(polyIndex * 2 + 1); } break; case TEXCOORD1: // TODO break; default: break; } // On the last input source attempt to add the vertex or index an existing one. if (k == (inputSourceCount - 1)) { // Only add unique vertices, use a hashtable and compare the hash functions of the // vertices. If they exist simply lookup the index of the existing ones. // otherwise add and new one and index it. unsigned int index; if (mesh->contains(vertex)) { index = mesh->getVertexIndex(vertex); } else { index = mesh->addVertex(vertex); } subset->addIndex(index); poly += (maxOffset+1); k = 0; } else { k++; } } // Add our new subset for the mesh. mesh->addMeshPart(subset); } bool hasNormals = mesh->vertices[0].hasNormal; bool hasColors = mesh->vertices[0].hasColor; bool hasTangents = mesh->vertices[0].hasTangent; bool hasBinormals = mesh->vertices[0].hasBinormal; bool hasTexCoords = mesh->vertices[0].hasTexCoord; bool hasWeights = mesh->vertices[0].hasWeights; // The order that the vertex elements are add to the list matters. // It should be the same order as how the Vertex data is written. // Position mesh->addVetexAttribute(POSITION, 3); // Normals if (hasNormals) { mesh->addVetexAttribute(NORMAL, 3); } // Tangents if (hasTangents) { mesh->addVetexAttribute(TANGENT, 3); } // Binormals if (hasBinormals) { mesh->addVetexAttribute(BINORMAL, 3); } // Texture Coordinates if (hasTexCoords) { mesh->addVetexAttribute(TEXCOORD0, 2); } // Diffuse Color if (hasColors) { mesh->addVetexAttribute(COLOR, 3); } // Skinning BlendWeights BlendIndices if (hasWeights /*_vertexBlendWeights && _vertexBlendIndices*/) { mesh->addVetexAttribute(BLENDWEIGHTS, 4); mesh->addVetexAttribute(BLENDINDICES, 4); } _gamePlayFile.addMesh(mesh); return mesh; } void DAESceneEncoder::warning(const std::string& message) { printf("Warning: %s\n", message.c_str()); } void DAESceneEncoder::warning(const char* message) { printf("Warning: %s\n", message); } int DAESceneEncoder::getVertexUsageType(const std::string& semantic) { int type = -1; if (semantic.length() > 0) { switch (semantic[0]) { case 'P': if (equals(semantic, "POSITION")) { type = POSITION; } break; case 'N': if (equals(semantic, "NORMAL")) { type = NORMAL; } case 'C': if (equals(semantic, "COLOR")) { type = COLOR; } case 'T': if (equals(semantic, "TANGENT")) { type = TANGENT; } else if (equals(semantic, "TEXCOORD")) { type = TEXCOORD0; } else if (equals(semantic, "TEXTANGENT")) { // Treat TEXTANGENT as TANGENT type = TANGENT; } else if (equals(semantic, "TEXBINORMAL")) { // Treat TEXBINORMAL as BINORMAL type = BINORMAL; } case 'B': if (equals(semantic, "BINORMAL")) { type = BINORMAL; } default: break; } } return type; } DAESceneEncoder::DAEPolygonInput::DAEPolygonInput(void) : offset(0), type(0), accessor(NULL) { } DAESceneEncoder::DAEPolygonInput::~DAEPolygonInput(void) { }