1 /*
2 Copyright (C) 2005-2007 Feeling Software Inc.
3 Portions of the code are:
4 Copyright (C) 2005-2007 Sony Computer Entertainment America
5
6 MIT License: http://www.opensource.org/licenses/mit-license.php
7 */
8
9 #include "StdAfx.h"
10 #include "FArchiveXML.h"
11 #include "FCDocument/FCDocument.h"
12 #include "FCDocument/FCDExtra.h"
13 #include "FCDocument/FCDGeometrySource.h"
14 #include "FCDocument/FCDGeometryMesh.h"
15 #include "FCDocument/FCDGeometryNURBSSurface.h"
16 #include "FCDocument/FCDGeometry.h"
17 #include "FCDocument/FCDGeometryPolygons.h"
18 #include "FCDocument/FCDGeometryPolygonsInput.h"
19 #include "FCDocument/FCDGeometrySpline.h"
20 #include "FUtils/FUDaeEnum.h"
21 #include "FUtils/FUDaeEnumSyntax.h"
22
WriteGeometrySource(FCDObject * object,xmlNode * parentNode)23 xmlNode* FArchiveXML::WriteGeometrySource(FCDObject* object, xmlNode* parentNode)
24 {
25 FCDGeometrySource* geometrySource = (FCDGeometrySource*)object;
26
27 xmlNode* sourceNode = NULL;
28
29 // Export the source directly, using the correct parameters and the length factor
30 FloatList& sourceData = geometrySource->GetSourceData().GetDataList();
31 uint32 stride = geometrySource->GetStride();
32 switch (geometrySource->GetType())
33 {
34 case FUDaeGeometryInput::POSITION: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::XYZW); break;
35 case FUDaeGeometryInput::NORMAL: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::XYZW); break;
36 case FUDaeGeometryInput::GEOTANGENT: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::XYZW); break;
37 case FUDaeGeometryInput::GEOBINORMAL: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::XYZW); break;
38 case FUDaeGeometryInput::TEXCOORD: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::STPQ); break;
39 case FUDaeGeometryInput::TEXTANGENT: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::XYZW); break;
40 case FUDaeGeometryInput::TEXBINORMAL: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::XYZW); break;
41 case FUDaeGeometryInput::UV: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::XYZW); break;
42 case FUDaeGeometryInput::COLOR: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, FUDaeAccessor::RGBA); break;
43 case FUDaeGeometryInput::EXTRA: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, NULL); break;
44 case FUDaeGeometryInput::UNKNOWN: sourceNode = AddSourceFloat(parentNode, geometrySource->GetDaeId(), sourceData, stride, NULL); break;
45
46 case FUDaeGeometryInput::VERTEX: // Refuse to export these sources
47 default: break;
48 }
49
50 if (!geometrySource->GetName().empty())
51 {
52 AddAttribute(sourceNode, DAE_NAME_ATTRIBUTE, geometrySource->GetName());
53 }
54
55 if (geometrySource->GetExtra() != NULL)
56 {
57 FArchiveXML::WriteTechniquesFCDExtra(geometrySource->GetExtra(), sourceNode);
58 }
59
60 for (size_t i = 0; i < geometrySource->GetAnimatedValues().size(); ++i)
61 {
62 FArchiveXML::WriteAnimatedValue(geometrySource->GetAnimatedValues()[i], sourceNode, "");
63 }
64
65 return sourceNode;
66 }
67
WriteGeometryMesh(FCDObject * object,xmlNode * parentNode)68 xmlNode* FArchiveXML::WriteGeometryMesh(FCDObject* object, xmlNode* parentNode)
69 {
70 FCDGeometryMesh* geometryMesh = (FCDGeometryMesh*)object;
71
72 xmlNode* meshNode = NULL;
73
74 if (geometryMesh->IsConvex() && !geometryMesh->GetConvexHullOf().empty())
75 {
76 meshNode = AddChild(parentNode, DAE_CONVEX_MESH_ELEMENT);
77 FUSStringBuilder convexHullOfName(geometryMesh->GetConvexHullOf());
78 AddAttribute(meshNode, DAE_CONVEX_HULL_OF_ATTRIBUTE, convexHullOfName);
79 }
80 else
81 {
82 meshNode = AddChild(parentNode, DAE_MESH_ELEMENT);
83
84 // Write out the sources
85 for (size_t i = 0; i < geometryMesh->GetSourceCount(); ++i)
86 {
87 FArchiveXML::LetWriteObject(geometryMesh->GetSource(i), meshNode);
88 }
89
90 // Write out the <vertices> element
91 xmlNode* verticesNode = AddChild(meshNode, DAE_VERTICES_ELEMENT);
92 xmlNode* verticesInputExtraNode = NULL,* verticesInputExtraTechniqueNode = NULL;
93 for (size_t i = 0; i < geometryMesh->GetVertexSourceCount(); ++i)
94 {
95 FCDGeometrySource* source = geometryMesh->GetVertexSource(i);
96 const char* semantic = FUDaeGeometryInput::ToString(source->GetType());
97 AddInput(verticesNode, source->GetDaeId(), semantic);
98 if (geometryMesh->GetPolygonsCount() > 0)
99 {
100 FCDGeometryPolygons* firstPolys = geometryMesh->GetPolygons(0);
101 FCDGeometryPolygonsInput* input = firstPolys->FindInput(source);
102 FUAssert(input != NULL, continue);
103 if (input->GetSet() != -1)
104 {
105 // We are interested in the set information, so if it is available, export it as an extra.
106 if (verticesInputExtraNode == NULL)
107 {
108 verticesInputExtraNode = FUXmlWriter::CreateNode(DAE_EXTRA_ELEMENT);
109 verticesInputExtraTechniqueNode = FUXmlWriter::AddChild(verticesInputExtraNode, DAE_TECHNIQUE_ELEMENT);
110 FUXmlWriter::AddAttribute(verticesInputExtraTechniqueNode, DAE_PROFILE_ATTRIBUTE, DAE_FCOLLADA_PROFILE);
111 }
112 AddInput(verticesInputExtraTechniqueNode, source->GetDaeId(), semantic, -1, input->GetSet());
113 }
114 }
115 }
116 if (verticesInputExtraNode != NULL) AddChild(verticesNode, verticesInputExtraNode);
117
118 FUSStringBuilder verticesNodeId(geometryMesh->GetDaeId()); verticesNodeId.append("-vertices");
119 AddAttribute(verticesNode, DAE_ID_ATTRIBUTE, verticesNodeId);
120
121 // Write out the polygons
122 for (size_t i = 0; i < geometryMesh->GetPolygonsCount(); ++i)
123 {
124 FArchiveXML::LetWriteObject(geometryMesh->GetPolygons(i), meshNode);
125 }
126 }
127 return meshNode;
128 }
129
130
WriteGeometry(FCDObject * object,xmlNode * parentNode)131 xmlNode* FArchiveXML::WriteGeometry(FCDObject* object, xmlNode* parentNode)
132 {
133 FCDGeometry* geometry = (FCDGeometry*)object;
134
135 xmlNode* geometryNode = FArchiveXML::WriteToEntityXMLFCDEntity(geometry, parentNode, DAE_GEOMETRY_ELEMENT);
136
137 if (geometry->GetMesh() != NULL) FArchiveXML::LetWriteObject(geometry->GetMesh(), geometryNode);
138 else if (geometry->GetSpline() != NULL) FArchiveXML::LetWriteObject(geometry->GetSpline(), geometryNode);
139
140 FArchiveXML::WriteEntityExtra(geometry, geometryNode);
141 return geometryNode;
142 }
143
WriteGeometryPolygons(FCDObject * object,xmlNode * parentNode)144 xmlNode* FArchiveXML::WriteGeometryPolygons(FCDObject* object, xmlNode* parentNode)
145 {
146 FCDGeometryPolygons* geometryPolygons = (FCDGeometryPolygons*)object;
147
148 // Are there holes? Then, export a <polygons> element.
149 // Are there only non-triangles within the list? Then, export a <polylist> element.
150 // Otherwise, you only have triangles: export a <triangles> element.
151 // That's all nice for polygon lists, otherwise we export the correct primitive type.
152 bool hasHoles = false, hasNPolys = true;
153
154 // Create the base node for these polygons
155 const char* polygonNodeType;
156 switch (geometryPolygons->GetPrimitiveType())
157 {
158 case FCDGeometryPolygons::POLYGONS:
159 // Check for polygon with holes and triangle-only conditions.
160 hasHoles = geometryPolygons->GetHoleFaceCount() > 0;
161 if (!hasHoles) hasNPolys = (geometryPolygons->TestPolyType() != 3);
162
163 if (hasHoles) polygonNodeType = DAE_POLYGONS_ELEMENT;
164 else if (hasNPolys) polygonNodeType = DAE_POLYLIST_ELEMENT;
165 else polygonNodeType = DAE_TRIANGLES_ELEMENT;
166 break;
167
168 case FCDGeometryPolygons::LINES: hasHoles = true; polygonNodeType = DAE_LINES_ELEMENT; break;
169 case FCDGeometryPolygons::LINE_STRIPS: hasHoles = true; polygonNodeType = DAE_LINESTRIPS_ELEMENT; break;
170 case FCDGeometryPolygons::TRIANGLE_FANS: hasHoles = true; polygonNodeType = DAE_TRIFANS_ELEMENT; break;
171 case FCDGeometryPolygons::TRIANGLE_STRIPS: hasHoles = true; polygonNodeType = DAE_TRISTRIPS_ELEMENT; break;
172 case FCDGeometryPolygons::POINTS: polygonNodeType = DAE_POINTS_ELEMENT; break;
173 default: polygonNodeType = emptyCharString; FUFail(break); break;
174 }
175 xmlNode* polygonsNode = AddChild(parentNode, polygonNodeType);
176
177 // Add the inputs
178 // Find which input owner belongs to the <vertices> element. Replace the semantic and the source id accordingly.
179 // Make sure to add that 'vertex' input only once.
180 FUSStringBuilder verticesNodeId(geometryPolygons->GetParent()->GetDaeId()); verticesNodeId.append("-vertices");
181 bool isVertexInputFound = false;
182 fm::pvector<const FCDGeometryPolygonsInput> idxOwners; // Record a list of input data owners.
183 for (size_t i = 0; i < geometryPolygons->GetInputCount(); ++i)
184 {
185 const FCDGeometryPolygonsInput* input = geometryPolygons->GetInput(i);
186 const FCDGeometrySource* source = input->GetSource();
187 if (source != NULL)
188 {
189 if (!geometryPolygons->GetParent()->IsVertexSource(source))
190 {
191 const char* semantic = FUDaeGeometryInput::ToString(input->GetSemantic());
192 FUDaeWriter::AddInput(polygonsNode, source->GetDaeId(), semantic, input->GetOffset(), input->GetSet());
193 }
194 else if (!isVertexInputFound)
195 {
196 FUDaeWriter::AddInput(polygonsNode, verticesNodeId.ToCharPtr(), DAE_VERTEX_INPUT, input->GetOffset());
197 isVertexInputFound = true;
198 }
199 }
200
201 if (input->OwnsIndices())
202 {
203 if (input->GetOffset() >= idxOwners.size()) idxOwners.resize(input->GetOffset() + 1);
204 idxOwners[input->GetOffset()] = input;
205 }
206 }
207
208 FUSStringBuilder builder;
209 builder.reserve(1024);
210
211 // For the poly-list case, export the list of vertex counts
212 if (!hasHoles && hasNPolys)
213 {
214 FUStringConversion::ToString(builder, geometryPolygons->GetFaceVertexCounts(), geometryPolygons->GetFaceVertexCountCount());
215 xmlNode* vcountNode = AddChild(polygonsNode, DAE_VERTEXCOUNT_ELEMENT);
216 AddContentUnprocessed(vcountNode, builder.ToCharPtr());
217 builder.clear();
218 }
219
220 // For the non-holes cases, open only one <p> element for all the data indices
221 xmlNode* pNode = NULL,* phNode = NULL;
222 if (!hasHoles) pNode = AddChild(polygonsNode, DAE_POLYGON_ELEMENT);
223
224 // Export the data indices (tessellation information)
225 size_t faceCount = geometryPolygons->GetFaceCount();
226 uint32 faceVertexOffset = 0;
227 size_t holeOffset = 0;
228 for (size_t faceIndex = 0; faceIndex < faceCount; ++faceIndex)
229 {
230 // For the holes cases, verify whether this face or the next one(s) are holes. We may need to open a new <ph>/<p> element
231 size_t holeCount = 0;
232 if (hasHoles)
233 {
234 holeCount = geometryPolygons->GetHoleCount(faceIndex);
235
236 if (holeCount == 0)
237 {
238 // Just open a <p> element: this is the most common case
239 pNode = AddChild(polygonsNode, DAE_POLYGON_ELEMENT);
240 }
241 else
242 {
243 // Open up a new <ph> element and its <p> element
244 phNode = AddChild(polygonsNode, DAE_POLYGONHOLED_ELEMENT);
245 pNode = AddChild(phNode, DAE_POLYGON_ELEMENT);
246 }
247 }
248
249 for (size_t holeIndex = 0; holeIndex < holeCount + 1; ++holeIndex)
250 {
251 // Write out the tessellation information for all the vertices of this face
252 uint32 faceVertexCount = geometryPolygons->GetFaceVertexCounts()[faceIndex + holeOffset + holeIndex];
253 for (uint32 faceVertexIndex = faceVertexOffset; faceVertexIndex < faceVertexOffset + faceVertexCount; ++faceVertexIndex)
254 {
255 for (fm::pvector<const FCDGeometryPolygonsInput>::iterator itI = idxOwners.begin(); itI != idxOwners.end(); ++itI)
256 {
257 if ((*itI) != NULL)
258 {
259 builder.append((*itI)->GetIndices()[faceVertexIndex]);
260 builder.append(' ');
261 }
262 else builder.append("0 ");
263 }
264 }
265
266 // For the holes cases: write out the indices for every polygon element
267 if (hasHoles)
268 {
269 if (!builder.empty()) builder.pop_back(); // take out the last space
270 AddContentUnprocessed(pNode, builder.ToCharPtr());
271 builder.clear();
272
273 if (holeIndex < holeCount)
274 {
275 // Open up a <h> element
276 pNode = AddChild(phNode, DAE_HOLE_ELEMENT);
277 }
278 }
279
280 faceVertexOffset += faceVertexCount;
281 }
282 holeOffset += holeCount;
283 }
284
285 // For the non-holes cases: write out the indices at the very end, for the single <p> element
286 if (!hasHoles)
287 {
288 if (!builder.empty()) builder.pop_back(); // take out the last space
289 AddContentUnprocessed(pNode, builder.ToCharPtr());
290 }
291
292 // Write out the material semantic and the number of polygons
293 if (!geometryPolygons->GetMaterialSemantic().empty())
294 {
295 AddAttribute(polygonsNode, DAE_MATERIAL_ATTRIBUTE, geometryPolygons->GetMaterialSemantic());
296 }
297
298 // Calculate the primitive count, taking into consideration the LINES special case.
299 size_t primitiveCount = geometryPolygons->GetFaceCount();
300 if (geometryPolygons->GetPrimitiveType() == FCDGeometryPolygons::LINES && primitiveCount > 0)
301 {
302 primitiveCount = geometryPolygons->GetFaceVertexCount(0) / 2;
303 }
304 AddAttribute(polygonsNode, DAE_COUNT_ATTRIBUTE, primitiveCount);
305
306 // Write out the extra information tree.
307 FArchiveXML::LetWriteObject(geometryPolygons->GetExtra(), polygonsNode);
308
309 return polygonsNode;
310 }
311
WriteGeometrySpline(FCDObject * object,xmlNode * parentNode)312 xmlNode* FArchiveXML::WriteGeometrySpline(FCDObject* object, xmlNode* parentNode)
313 {
314 FCDGeometrySpline* geometrySpline = (FCDGeometrySpline*)object;
315
316 // create as many <spline> node as there are splines in the array
317 for (size_t i = 0; i < geometrySpline->GetSplineCount(); ++i)
318 {
319 FCDSpline* colladaSpline = geometrySpline->GetSpline(i);
320 if (colladaSpline == NULL) continue;
321
322 fm::string parentId = geometrySpline->GetParent()->GetDaeId();
323 fm::string splineId = FUStringConversion::ToString(i);
324
325 if (colladaSpline->IsType(FCDNURBSSpline::GetClassType()))
326 {
327 FArchiveXML::WriteNURBSSpline((FCDNURBSSpline*)colladaSpline, parentNode, parentId, splineId);
328 }
329 else
330 {
331 FArchiveXML::WriteSpline(colladaSpline, parentNode, parentId, splineId);
332 }
333 }
334
335 return NULL;
336 }
337
WriteNURBSSpline(FCDNURBSSpline * nURBSSpline,xmlNode * parentNode,const fm::string & parentId,const fm::string & splineId)338 xmlNode* FArchiveXML::WriteNURBSSpline(FCDNURBSSpline* nURBSSpline, xmlNode* parentNode, const fm::string& parentId, const fm::string& splineId)
339 {
340 // Create the <spline> XML tree node and set its 'closed' attribute.
341 xmlNode* splineNode = AddChild(parentNode, DAE_SPLINE_ELEMENT);
342 AddAttribute(splineNode, DAE_CLOSED_ATTRIBUTE, nURBSSpline->IsClosed());
343
344 // Write out the control point, weight and knot sources
345 FUSStringBuilder controlPointSourceId(parentId); controlPointSourceId += "-cvs-" + splineId;
346 AddSourcePosition(splineNode, controlPointSourceId.ToCharPtr(), nURBSSpline->GetCVs());
347 FUSStringBuilder weightSourceId(parentId); weightSourceId += "-weights-" + splineId;
348 AddSourceFloat(splineNode, weightSourceId.ToCharPtr(), nURBSSpline->GetWeights(), "WEIGHT");
349 FUSStringBuilder knotSourceId(parentId); knotSourceId += "-knots-" + splineId;
350 AddSourceFloat(splineNode, knotSourceId.ToCharPtr(), nURBSSpline->GetKnots(), "KNOT");
351
352 // Write out the <control_vertices> element and its inputs
353 xmlNode* verticesNode = AddChild(splineNode, DAE_CONTROL_VERTICES_ELEMENT);
354 AddInput(verticesNode, controlPointSourceId.ToCharPtr(), DAE_CVS_SPLINE_INPUT);
355 AddInput(verticesNode, weightSourceId.ToCharPtr(), DAE_WEIGHT_SPLINE_INPUT);
356 AddInput(verticesNode, knotSourceId.ToCharPtr(), DAE_KNOT_SPLINE_INPUT);
357
358 // Write out the <extra> information: the spline type and degree.
359 xmlNode* extraNode = AddExtraTechniqueChild(splineNode,DAE_FCOLLADA_PROFILE);
360 AddChild(extraNode, DAE_TYPE_ATTRIBUTE, FUDaeSplineType::ToString(nURBSSpline->GetSplineType()));
361 AddChild(extraNode, DAE_DEGREE_ATTRIBUTE, FUStringConversion::ToString(nURBSSpline->GetDegree()));
362 return splineNode;
363 }
364
WriteSpline(FCDSpline * spline,xmlNode * parentNode,const fm::string & parentId,const fm::string & splineId)365 xmlNode* FArchiveXML::WriteSpline(FCDSpline* spline, xmlNode* parentNode, const fm::string& parentId, const fm::string& splineId)
366 {
367 // Create the spline node with its 'closed' attribute
368 xmlNode* splineNode = AddChild(parentNode, DAE_SPLINE_ELEMENT);
369 AddAttribute(splineNode, DAE_CLOSED_ATTRIBUTE, spline->IsClosed());
370
371 // Write out the control point source
372 FUSStringBuilder controlPointSourceId(parentId); controlPointSourceId += "-cvs-" + splineId;
373 AddSourcePosition(splineNode, controlPointSourceId.ToCharPtr(), spline->GetCVs());
374
375 // Write out the <control_vertices> element and its inputs
376 xmlNode* verticesNode = AddChild(splineNode, DAE_CONTROL_VERTICES_ELEMENT);
377 AddInput(verticesNode, controlPointSourceId.ToCharPtr(), DAE_CVS_SPLINE_INPUT);
378
379 // Write out the spline type
380 xmlNode* extraNode = AddExtraTechniqueChild(splineNode, DAE_FCOLLADA_PROFILE);
381 AddChild(extraNode, DAE_TYPE_ATTRIBUTE, FUDaeSplineType::ToString(spline->GetSplineType()));
382 return splineNode;
383 }
384
385