|
|
@@ -0,0 +1,1934 @@
|
|
|
+/*
|
|
|
+ * DAESceneEncoder.h
|
|
|
+ */
|
|
|
+
|
|
|
+#include <algorithm>
|
|
|
+
|
|
|
+#include "DAESceneEncoder.h"
|
|
|
+
|
|
|
+using namespace gameplay;
|
|
|
+
|
|
|
+DAESceneEncoder::DAESceneEncoder()
|
|
|
+ : _collada(NULL), file(NULL), vertexBlendWeights(NULL), vertexBlendIndices(NULL)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+DAESceneEncoder::~DAESceneEncoder()
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+std::string getFilenameFromFilePath(const std::string& filepath)
|
|
|
+{
|
|
|
+ if (filepath.find_last_of("/") != std::string::npos)
|
|
|
+ {
|
|
|
+ return filepath.substr(filepath.find_last_of("/")+1);
|
|
|
+ }
|
|
|
+ return "";
|
|
|
+}
|
|
|
+
|
|
|
+std::string getFilenameNoExt(const std::string& filename)
|
|
|
+{
|
|
|
+ if (filename.find_last_of(".") != std::string::npos)
|
|
|
+ {
|
|
|
+ return filename.substr(0, filename.find_last_of("."));
|
|
|
+ }
|
|
|
+ return filename;
|
|
|
+}
|
|
|
+
|
|
|
+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::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 <triangles> inside the mesh that has the same material as the <polygons>.
|
|
|
+ domTriangles* triangles = (domTriangles*)domMesh->createAndPlace("triangles");
|
|
|
+ triangles->setCount(0);
|
|
|
+ triangles->setMaterial(domPolygons->getMaterial());
|
|
|
+ domP* domTrianglesP = (domP*)triangles->createAndPlace("p");
|
|
|
+
|
|
|
+ // Give the new <triangles> the same <_dae> and <parameters> as the old <polygons>.
|
|
|
+ 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 <p> 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 <triangles> inside the mesh that has the same material as the <polylist>.
|
|
|
+ domTriangles* triangles = (domTriangles*)domMesh->createAndPlace("triangles");
|
|
|
+ triangles->setMaterial(domPolylist->getMaterial());
|
|
|
+ domP* domTrianglesP = (domP*)triangles->createAndPlace("p");
|
|
|
+
|
|
|
+ // Give the new <triangles> the same <_dae> and <parameters> as the old <polylist>.
|
|
|
+ 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 <p> 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 char* nodeId, bool text)
|
|
|
+{
|
|
|
+ std::string filenameOnly = getFilenameFromFilePath(filepath);
|
|
|
+ std::string dstPath = filepath.substr(0, filepath.find_last_of('/'));
|
|
|
+
|
|
|
+ // Load the collada document
|
|
|
+ _collada = new DAE();
|
|
|
+ if (_collada->load(filepath.c_str()) != DAE_OK)
|
|
|
+ {
|
|
|
+ fprintf(stderr,"Error: COLLADA failed to open file:%s\n", filepath.c_str());
|
|
|
+ if (_collada)
|
|
|
+ {
|
|
|
+ delete _collada;
|
|
|
+ _collada = NULL;
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get the dom tree
|
|
|
+ domCOLLADA* dom = _collada->getDom(filepath.c_str());
|
|
|
+ if (!dom)
|
|
|
+ {
|
|
|
+ fprintf(stderr,"Error: COLLADA failed to the dom for file:%s\n", filepath.c_str());
|
|
|
+ if (_collada)
|
|
|
+ {
|
|
|
+ delete _collada;
|
|
|
+ _collada = NULL;
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Run collada conditioners
|
|
|
+ triangulate(_collada);
|
|
|
+
|
|
|
+ // Find the <visual_scene> element within the <scene>
|
|
|
+ const domCOLLADA::domSceneRef& domScene = dom->getScene();
|
|
|
+ daeElement* scene = NULL;
|
|
|
+ if (domScene && domScene->getInstance_visual_scene())
|
|
|
+ {
|
|
|
+ scene = domScene->getInstance_visual_scene()->getUrl().getElement();
|
|
|
+ if (scene)
|
|
|
+ {
|
|
|
+ if (nodeId == NULL)
|
|
|
+ {
|
|
|
+ // If the -n <node_id> parameter was not passed then write out the entire scene.
|
|
|
+ loadScene((domVisual_scene*)scene);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Resolve/Search for the node the user specified with the -n <node_id> parameter.
|
|
|
+ daeSIDResolver resolver(scene, nodeId);
|
|
|
+ const domNode* node = daeSafeCast<domNode>(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 <visual_scene>.\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ // The animations should be loaded last
|
|
|
+ loadAnimations(dom);
|
|
|
+
|
|
|
+ std::string dstFilename = dstPath;
|
|
|
+ dstFilename.append(1, '/');
|
|
|
+ dstFilename.append(getFilenameNoExt(filenameOnly));
|
|
|
+
|
|
|
+ _gamePlayFile.adjust();
|
|
|
+
|
|
|
+ if (text)
|
|
|
+ {
|
|
|
+ _gamePlayFile.saveText(dstFilename + ".xml");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _gamePlayFile.saveBinary(dstFilename + ".gpb");
|
|
|
+ }
|
|
|
+
|
|
|
+ // Cleanup
|
|
|
+ if (file)
|
|
|
+ {
|
|
|
+ fclose(file);
|
|
|
+ }
|
|
|
+ if (_collada)
|
|
|
+ {
|
|
|
+ delete _collada;
|
|
|
+ _collada = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DAESceneEncoder::loadAnimations(const domCOLLADA* dom)
|
|
|
+{
|
|
|
+ // Call loadAnimation on all <animation> elements in all <library_animations>
|
|
|
+ const domLibrary_animations_Array& animationLibrary = dom->getLibrary_animations_array();
|
|
|
+ size_t animationLibraryCount = animationLibrary.getCount();
|
|
|
+ for (size_t i = 0; i < animationLibraryCount; i++)
|
|
|
+ {
|
|
|
+ const domLibrary_animationsRef& animationsRef = animationLibrary.get(i);
|
|
|
+ const domAnimation_Array& animationArray = animationsRef->getAnimation_array();
|
|
|
+ size_t animationCount = animationArray.getCount();
|
|
|
+ for (size_t i = 0; i < animationCount; i++)
|
|
|
+ {
|
|
|
+ const domAnimationRef& animationRef = animationArray.get(i);
|
|
|
+ loadAnimation(animationRef);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DAESceneEncoder::loadAnimation(const domAnimation* animationRef)
|
|
|
+{
|
|
|
+ // <channel> points to one <sampler>
|
|
|
+ // <sampler> points to multiple <input> elements
|
|
|
+
|
|
|
+ Animation* animation = new Animation();
|
|
|
+ animation->setId(findAnimationId(animationRef));
|
|
|
+
|
|
|
+ // <channel>
|
|
|
+ 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);
|
|
|
+
|
|
|
+ // <sampler>
|
|
|
+ daeElement* samplerElement = channelRef->getSource().getElement();
|
|
|
+ assert(samplerElement);
|
|
|
+ const domSampler* sampler = static_cast<domSampler*>(samplerElement);
|
|
|
+
|
|
|
+ // <input>
|
|
|
+ const domInputLocal_Array& inputArray = sampler->getInput_array();
|
|
|
+ size_t inputArrayCount = inputArray.getCount();
|
|
|
+ for (size_t i = 0; i < inputArrayCount; i++)
|
|
|
+ {
|
|
|
+ const domInputLocalRef& inputLocal = inputArray.get(i);
|
|
|
+
|
|
|
+ // <source>
|
|
|
+ daeElement* sourceElement = inputLocal->getSource().getElement();
|
|
|
+ const domSource* source = static_cast<domSource*>(sourceElement);
|
|
|
+
|
|
|
+ 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<float> floats;
|
|
|
+ copyFloats(source->getFloat_array(), &floats);
|
|
|
+ if (equals(semantic, "INPUT"))
|
|
|
+ {
|
|
|
+ // TODO: Ensure param name is TIME?
|
|
|
+ for (std::vector<float>::iterator i = floats.begin(); i != floats.end(); i++)
|
|
|
+ {
|
|
|
+ // Convert seconds to milliseconds
|
|
|
+ *i = *i * 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
|
|
|
+ loadTarget(channelRef, animationChannel);
|
|
|
+
|
|
|
+ // Set the ID of the AnimationChannel
|
|
|
+ std::string channelId = channelRef->getTarget();
|
|
|
+ replace(channelId.begin(), channelId.end(), '/', '_');
|
|
|
+ // ensure no duplicate IDs
|
|
|
+ while (_gamePlayFile.idExists(channelId))
|
|
|
+ {
|
|
|
+ channelId.append("_");
|
|
|
+ }
|
|
|
+ animationChannel->setId(channelId);
|
|
|
+
|
|
|
+ // Animations that target a joint should be grouped with the other animations for that skin.
|
|
|
+ // If this animation channel targets a joint then add the animation channel to the skin's animation instead.
|
|
|
+ DAEChannelTarget channelTarget(channelRef);
|
|
|
+ daeElement* element = channelTarget.getTargetElement();
|
|
|
+ daeInt type = element->typeID();
|
|
|
+ if (type == domNode::ID())
|
|
|
+ {
|
|
|
+ const domNode* node = daeSafeCast<domNode>(element);
|
|
|
+ if (node->getType() == NODETYPE_JOINT)
|
|
|
+ {
|
|
|
+ const char* id = node->getId();
|
|
|
+ if (id)
|
|
|
+ {
|
|
|
+ Animation* skinAnimation = _gamePlayFile.findAnimationForJoint(id);
|
|
|
+ if (skinAnimation)
|
|
|
+ {
|
|
|
+ // TODO: Assert will fail if <controller> doesn't have an ID.
|
|
|
+ assert(skinAnimation->getId().length() > 0);
|
|
|
+ skinAnimation->add(animationChannel);
|
|
|
+ _gamePlayFile.addAnimation(skinAnimation);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ animation->add(animationChannel);
|
|
|
+ }
|
|
|
+ if (animation->getAnimationChannelCount() > 0)
|
|
|
+ {
|
|
|
+ _gamePlayFile.addAnimation(animation);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ delete animation;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DAESceneEncoder::loadInterpolation(const domSource* source, AnimationChannel* animationChannel)
|
|
|
+{
|
|
|
+ // COLLADA stores the interpolations as a list of strings while GBP uses unsigned int
|
|
|
+ std::vector<unsigned int> values;
|
|
|
+ const domListOfNames& names = source->getName_array()->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);
|
|
|
+}
|
|
|
+
|
|
|
+void DAESceneEncoder::loadTarget(const domChannel* channelRef, AnimationChannel* animationChannel)
|
|
|
+{
|
|
|
+ unsigned int targetProperty = 0;
|
|
|
+ DAEChannelTarget channelTarget(channelRef);
|
|
|
+
|
|
|
+ // 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())
|
|
|
+ {
|
|
|
+ // <rotate>
|
|
|
+ const domRotate* rotate = daeSafeCast<domRotate>(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<float>& 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<float> 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())
|
|
|
+ {
|
|
|
+ // <scale>
|
|
|
+ //const domScale* scale = daeSafeCast<domScale>(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())
|
|
|
+ {
|
|
|
+ // <translate>
|
|
|
+ //const domTranslate* translate = daeSafeCast<domTranslate>(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<float>& 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<float> 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());
|
|
|
+}
|
|
|
+
|
|
|
+std::string DAESceneEncoder::findAnimationId(const domAnimation* animationRef)
|
|
|
+{
|
|
|
+ std::string animationId;
|
|
|
+ const char* str = animationRef->getId();
|
|
|
+ if (str)
|
|
|
+ {
|
|
|
+ animationId = str;
|
|
|
+ // check if this ID already exists
|
|
|
+ if (_gamePlayFile.idExists(animationId))
|
|
|
+ {
|
|
|
+ // choose a different id
|
|
|
+ animationId.append("_animation");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (animationId.length() == 0)
|
|
|
+ {
|
|
|
+ animationId.assign("animation"); // TODO: Remove this later
|
|
|
+ }
|
|
|
+ // ensure no duplicate IDs
|
|
|
+ while (_gamePlayFile.idExists(animationId))
|
|
|
+ {
|
|
|
+ animationId.append("_");
|
|
|
+ }
|
|
|
+ return animationId;
|
|
|
+}
|
|
|
+
|
|
|
+void DAESceneEncoder::copyFloats(const domFloat_array* source, std::vector<float>* target)
|
|
|
+{
|
|
|
+ std::vector<float>& 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<domNode>(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<daeSmartRef<daeElement>> 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:
|
|
|
+ {
|
|
|
+ domTranslate* translateNode = dynamic_cast<domTranslate*>(static_cast<daeElement*>(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:
|
|
|
+ {
|
|
|
+ domRotate* rotateNode = dynamic_cast<domRotate*>(static_cast<daeElement*>(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));
|
|
|
+ if (x == 1 && y == 0 && z == 0)
|
|
|
+ {
|
|
|
+ dstTransform.rotateX(angle);
|
|
|
+ }
|
|
|
+ else if (x == 0 && y == 1 && z == 0)
|
|
|
+ {
|
|
|
+ dstTransform.rotateY(angle);
|
|
|
+ }
|
|
|
+ else if (x == 0 && y == 0 && z == 1)
|
|
|
+ {
|
|
|
+ dstTransform.rotateZ(angle);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ dstTransform.rotate(x, y, z, angle);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case COLLADA_TYPE::SCALE:
|
|
|
+ {
|
|
|
+ domScale* scaleNode = dynamic_cast<domScale*>(static_cast<daeElement*>(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:
|
|
|
+ case COLLADA_TYPE::LOOKAT:
|
|
|
+ default:
|
|
|
+ {
|
|
|
+ warning("Skew or lookat transform found but not supported.");
|
|
|
+ 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<domCamera>(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<domLight>(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<domGeometry>(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 controllerInstanceRef = instanceControllers.get(i);
|
|
|
+ xsAnyURI controllerURI = controllerInstanceRef->getUrl();
|
|
|
+ domController* controllerRef = daeSafeCast<domController>(controllerURI.getElement());
|
|
|
+
|
|
|
+ if (controllerRef)
|
|
|
+ {
|
|
|
+ const domSkin* skinElement = controllerRef->getSkin();
|
|
|
+ if (skinElement)
|
|
|
+ {
|
|
|
+ Model* model = loadSkin(skinElement);
|
|
|
+ if (model)
|
|
|
+ {
|
|
|
+ // use only the first skeleton
|
|
|
+ domInstance_controller::domSkeleton_Array& skeletons = controllerInstanceRef->getSkeleton_array();
|
|
|
+ if (skeletons.getCount() == 0)
|
|
|
+ {
|
|
|
+ warning("No skeletons found for instance controller: ");
|
|
|
+ delete model;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else if (skeletons.getCount() > 1)
|
|
|
+ {
|
|
|
+ // Only log a warning, don't exit
|
|
|
+ warning("More than one skeleton found for instance controller. Using first skeleton only.");
|
|
|
+ }
|
|
|
+ // Load the skeleton for this skin
|
|
|
+ loadSkeleton((domInstance_controller::domSkeleton*)skeletons.get((size_t)0), model->getSkin());
|
|
|
+
|
|
|
+ node->setModel(model);
|
|
|
+
|
|
|
+ // TODO: Load animations
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ 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->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<domNode>(resolver.getElement());
|
|
|
+
|
|
|
+ // Get the lookup scene id (sid) and joint index.
|
|
|
+ std::string sid = std::string(rootNode->getSid());
|
|
|
+ std::string id = std::string(skeletonId);
|
|
|
+
|
|
|
+ // Has the root joint been loaded yet?
|
|
|
+ Node* rootJoint = (Node*)_gamePlayFile.getFromRefTable(id);
|
|
|
+
|
|
|
+ // The root node is not loaded yet, so let's load it now
|
|
|
+ if (rootJoint == 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 root node
|
|
|
+ rootJoint = loadNode(topLevelParent, parentNode);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rootJoint == NULL)
|
|
|
+ {
|
|
|
+ // This shouldn't really happen..
|
|
|
+ rootJoint = new Node();
|
|
|
+ rootJoint->setId(id);
|
|
|
+ _gamePlayFile.addNode(rootJoint);
|
|
|
+ }
|
|
|
+
|
|
|
+ skin->setNode(rootJoint);
|
|
|
+
|
|
|
+ // Resolve and set joints array for skin
|
|
|
+ std::list<Node*> joints;
|
|
|
+ const std::list<std::string>& jointNames = skin->getJointNames();
|
|
|
+ for (std::list<std::string>::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();
|
|
|
+
|
|
|
+ // MeshSkins have an animation container but we need to find an appropriate ID for it.
|
|
|
+ // If the <controller> has an ID, use that ID as the animation's ID.
|
|
|
+ // Need to break const qualified in order to get the parent.
|
|
|
+ daeElement* e = ((domSkin*)skinElement)->getParent();
|
|
|
+ if (e->getElementType() == COLLADA_TYPE::CONTROLLER)
|
|
|
+ {
|
|
|
+ const domController* controller = (domController*)e;
|
|
|
+ const char* controllerId = controller->getId();
|
|
|
+ if (controllerId)
|
|
|
+ {
|
|
|
+ skin->setAnimationId(controllerId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 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();
|
|
|
+ domSource* source = (domSource*)(daeElement*)sourceURI->getElement();
|
|
|
+
|
|
|
+ if (equals(inputSemantic, "JOINT"))
|
|
|
+ {
|
|
|
+ // Get the joint Ids's
|
|
|
+ std::list<std::string> 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<std::string>::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)
|
|
|
+ {
|
|
|
+ domNode* node = dynamic_cast<domNode*>(static_cast<daeElement*>(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<std::string>::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<SkinnedVertexWeightPair> 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<domGeometry>(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<DAEPolygonInput*> 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();
|
|
|
+
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 i = 0; i < polygonInputs.size(); i++)
|
|
|
+ delete polygonInputs[i];
|
|
|
+ warning(std::string("Triangles do not all have the same number of input sources for geometry mesh: ") + geometryId);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ 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<domTriangles>(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.reset();
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 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);
|
|
|
+}
|
|
|
+
|
|
|
+void DAESceneEncoder::getJointNames(domSource* source, std::list<std::string>& list)
|
|
|
+{
|
|
|
+ // BLENDER used name_array
|
|
|
+ const domName_arrayRef& nameArray = source->getName_array();
|
|
|
+ if (nameArray.cast())
|
|
|
+ {
|
|
|
+ domListOfNames& ids = nameArray->getValue();
|
|
|
+ size_t jointCount = (size_t)nameArray->getCount();
|
|
|
+ for (size_t j = 0; j < jointCount; j++)
|
|
|
+ {
|
|
|
+ list.push_back(std::string(ids.get(j)));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Seymour used IDREF_array
|
|
|
+ const domIDREF_arrayRef& idArray = source->getIDREF_array();
|
|
|
+ if (idArray.cast())
|
|
|
+ {
|
|
|
+ xsIDREFS& ids = idArray->getValue();
|
|
|
+ size_t jointCount = (size_t)idArray->getCount();
|
|
|
+ for (size_t j = 0; j < jointCount; j++)
|
|
|
+ {
|
|
|
+ list.push_back(std::string(ids.get(j).getID()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+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;
|
|
|
+ }
|
|
|
+ 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)
|
|
|
+{
|
|
|
+}
|