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