bool CheckGeometryMesh(FULogFile& fileOut, FCDGeometryMesh* mesh) { // Verify the mesh and its sources PassIf(mesh->GetSourceCount() == 3); FCDGeometrySource* posSource = NULL,* colorSource = NULL,* dummySource = NULL; for (size_t i = 0; i < 3; ++i) { FCDGeometrySource* source = mesh->GetSource(i); FailIf(source == NULL); switch (source->GetType()) { case FUDaeGeometryInput::POSITION: posSource = source; PassIf(source->GetName() == FC("TestPositionSource")); break; case FUDaeGeometryInput::COLOR: colorSource = source; PassIf(source->GetName() == FC("TestColorSource")); break; case FUDaeGeometryInput::EXTRA: dummySource = source; PassIf(source->GetName() == FC("TestDummySource")); break; default: Fail; break; } } FailIf(posSource == NULL || colorSource == NULL || dummySource == NULL); PassIf(IsEquivalent(posSource->GetData(), posSource->GetDataCount(), positionData, 12)); PassIf(posSource->GetStride() == 3); PassIf(IsEquivalent(colorSource->GetData(), colorSource->GetDataCount(), colorData, 12)); PassIf(colorSource->GetStride() == 4); PassIf(IsEquivalent(dummySource->GetData(), dummySource->GetDataCount(), dummyData, 10)); PassIf(dummySource->GetStride() == 3); PassIf(CheckExtraTree(fileOut, dummySource->GetExtra(), false)); // Find the non-empty polygon set and verify that one of the polygon set is, in fact, empty. FCDGeometryPolygons* polys1 = NULL,* polysEmpty = NULL; for (size_t i = 0; i < mesh->GetPolygonsCount(); ++i) { FCDGeometryPolygons* p = mesh->GetPolygons(i); if (p->GetFaceCount() == 0) { PassIf(polysEmpty == NULL); polysEmpty = p; } else { PassIf(polys1 == NULL); polys1 = p; } CheckExtraTree(fileOut, p->GetExtra(), true); } PassIf(polys1 != NULL && polysEmpty != NULL); // Check that we have the wanted tetrahedron in the non-empty polygon set. PassIf(polys1->GetFaceCount() == 4); PassIf(polys1->GetHoleCount() == 0); PassIf(polys1->GetFaceVertexCount(0) == 3 && polys1->GetFaceVertexCount(1) == 3 && polys1->GetFaceVertexCount(2) == 3 && polys1->GetFaceVertexCount(3) == 3); FCDGeometryPolygonsInput* posInput = polys1->FindInput(posSource); FailIf(posInput == NULL || posInput->GetIndexCount() != 12); FCDGeometryPolygonsInput* colorInput = polys1->FindInput(colorSource); FailIf(colorInput == NULL || colorInput == posInput || colorInput->GetIndexCount() != 12); PassIf(IsEquivalent(posInput->GetIndices(), 12, positionIndices, 12)); PassIf(IsEquivalent(colorInput->GetIndices(), 12, colorIndices, 12)); return true; }
void ReindexGeometry(FCDGeometryPolygons* polys, FCDSkinController* skin) { // Given geometry with: // positions, normals, texcoords, bone blends // each with their own data array and index array, change it to // have a single optimised index array shared by all vertexes. FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION); FCDGeometryPolygonsInput* inputNormal = polys->FindInput(FUDaeGeometryInput::NORMAL); FCDGeometryPolygonsInput* inputTexcoord = polys->FindInput(FUDaeGeometryInput::TEXCOORD); size_t numVertices = polys->GetFaceVertexCount(); assert(inputPosition->GetIndexCount() == numVertices); assert(inputNormal ->GetIndexCount() == numVertices); assert(inputTexcoord->GetIndexCount() == numVertices); const uint32* indicesPosition = inputPosition->GetIndices(); const uint32* indicesNormal = inputNormal->GetIndices(); const uint32* indicesTexcoord = inputTexcoord->GetIndices(); assert(indicesPosition); assert(indicesNormal); assert(indicesTexcoord); // TODO - should be optional, because textureless meshes aren't unreasonable FCDGeometrySourceList texcoordSources; polys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources); FCDGeometrySource* sourcePosition = inputPosition->GetSource(); FCDGeometrySource* sourceNormal = inputNormal ->GetSource(); const float* dataPosition = sourcePosition->GetData(); const float* dataNormal = sourceNormal ->GetData(); if (skin) { #ifndef NDEBUG size_t numVertexPositions = sourcePosition->GetDataCount() / sourcePosition->GetStride(); assert(skin->GetInfluenceCount() == numVertexPositions); #endif } uint32 stridePosition = sourcePosition->GetStride(); uint32 strideNormal = sourceNormal ->GetStride(); std::vector<uint32> indicesCombined; std::vector<VertexData> vertexes; InserterWithoutDuplicates<VertexData> inserter(vertexes); for (size_t i = 0; i < numVertices; ++i) { std::vector<FCDJointWeightPair> weights; if (skin) { FCDSkinControllerVertex* influences = skin->GetVertexInfluence(indicesPosition[i]); assert(influences != NULL); for (size_t j = 0; j < influences->GetPairCount(); ++j) { FCDJointWeightPair* pair = influences->GetPair(j); assert(pair != NULL); weights.push_back(*pair); } CanonicaliseWeights(weights); } std::vector<uv_pair_type> uvs; for (size_t set = 0; set < texcoordSources.size(); ++set) { const float* dataTexcoord = texcoordSources[set]->GetData(); uint32 strideTexcoord = texcoordSources[set]->GetStride(); uv_pair_type p; p.first = dataTexcoord[indicesTexcoord[i]*strideTexcoord]; p.second = dataTexcoord[indicesTexcoord[i]*strideTexcoord + 1]; uvs.push_back(p); } VertexData vtx ( &dataPosition[indicesPosition[i]*stridePosition], &dataNormal [indicesNormal [i]*strideNormal], uvs, weights ); size_t idx = inserter.add(vtx); indicesCombined.push_back((uint32)idx); } // TODO: rearrange indicesCombined (and rearrange vertexes to match) to use // the vertex cache efficiently // (<http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html> etc) FloatList newDataPosition; FloatList newDataNormal; FloatList newDataTexcoord; std::vector<std::vector<FCDJointWeightPair> > newWeightedMatches; for (size_t i = 0; i < vertexes.size(); ++i) { newDataPosition.push_back(vertexes[i].x); newDataPosition.push_back(vertexes[i].y); newDataPosition.push_back(vertexes[i].z); newDataNormal .push_back(vertexes[i].nx); newDataNormal .push_back(vertexes[i].ny); newDataNormal .push_back(vertexes[i].nz); newWeightedMatches.push_back(vertexes[i].weights); } // (Slightly wasteful to duplicate this array so many times, but FCollada // doesn't seem to support multiple inputs with the same source data) inputPosition->SetIndices(&indicesCombined.front(), indicesCombined.size()); inputNormal ->SetIndices(&indicesCombined.front(), indicesCombined.size()); inputTexcoord->SetIndices(&indicesCombined.front(), indicesCombined.size()); for (size_t set = 0; set < texcoordSources.size(); ++set) { newDataTexcoord.clear(); for (size_t i = 0; i < vertexes.size(); ++i) { newDataTexcoord.push_back(vertexes[i].uvs[set].first); newDataTexcoord.push_back(vertexes[i].uvs[set].second); } texcoordSources[set]->SetData(newDataTexcoord, 2); } sourcePosition->SetData(newDataPosition, 3); sourceNormal ->SetData(newDataNormal, 3); if (skin) { skin->SetInfluenceCount(newWeightedMatches.size()); for (size_t i = 0; i < newWeightedMatches.size(); ++i) { skin->GetVertexInfluence(i)->SetPairCount(0); for (size_t j = 0; j < newWeightedMatches[i].size(); ++j) skin->GetVertexInfluence(i)->AddPair(newWeightedMatches[i][j].jointIndex, newWeightedMatches[i][j].weight); } } }
xmlNode* FArchiveXML::WriteGeometrySource(FCDObject* object, xmlNode* parentNode) { FCDGeometrySource* geometrySource = (FCDGeometrySource*)object; xmlNode* sourceNode = NULL; // Export the source directly, using the correct parameters and the length factor FloatList& sourceData = geometrySource->GetSourceData().GetDataList(); uint32 stride = geometrySource->GetStride(); switch (geometrySource->GetType()) { case FUDaeGeometryInput::POSITION: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::XYZW); break; case FUDaeGeometryInput::NORMAL: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::XYZW); break; case FUDaeGeometryInput::GEOTANGENT: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::XYZW); break; case FUDaeGeometryInput::GEOBINORMAL: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::XYZW); break; case FUDaeGeometryInput::TEXCOORD: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::STPQ); break; case FUDaeGeometryInput::TEXTANGENT: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::XYZW); break; case FUDaeGeometryInput::TEXBINORMAL: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::XYZW); break; case FUDaeGeometryInput::UV: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::XYZW); break; case FUDaeGeometryInput::COLOR: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::RGBA); break; case FUDaeGeometryInput::EXTRA: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, NULL); break; case FUDaeGeometryInput::UNKNOWN: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, NULL); break; case FUDaeGeometryInput::VERTEX: // Refuse to export these sources default: break; } if (!geometrySource->GetName().empty()) { AddAttribute(sourceNode, DAE_NAME_ATTRIBUTE, geometrySource->GetName()); } if (geometrySource->GetExtra() != NULL) { FArchiveXML::WriteTechniquesFCDExtra(geometrySource->GetExtra(), sourceNode); } for (size_t i = 0; i < geometrySource->GetAnimatedValues().size(); ++i) { FArchiveXML::WriteAnimatedValue(geometrySource->GetAnimatedValues()[i], sourceNode, ""); } return sourceNode; }