1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2010-2016, The OpenClonk Team and contributors
5  *
6  * Distributed under the terms of the ISC license; see accompanying file
7  * "COPYING" for details.
8  *
9  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
10  * See accompanying file "TRADEMARK" for details.
11  *
12  * To redistribute this file separately, substitute the full license texts
13  * for the above references.
14  */
15 
16 #include "C4Include.h"
17 #include "lib/StdMeshLoaderBinaryChunks.h"
18 
19 // deleter-agnostic unique_ptr static caster
20 template<typename To, typename From>
static_unique_cast(From && p)21 std::unique_ptr<To> static_unique_cast(From&& p) {
22 	return std::unique_ptr<To>(static_cast<To*>(p.release()));
23 }
24 
25 using std::move;
26 
27 namespace Ogre
28 {
29 	namespace Mesh
30 	{
31 		const uint32_t ChunkFileHeader::CurrentVersion = 1080; // Major * 1000 + Minor
32 		const std::map<std::string, uint32_t> ChunkFileHeader::VersionTable = {
33 		    // 1.8: Current version
34 		    std::make_pair("[MeshSerializer_v1.8]",  CurrentVersion),
35 		    // 1.41: Changes to morph keyframes and poses. We don't use either, so no special handling needed
36 		    std::make_pair("[MeshSerializer_v1.41]", 1041),
37 		    // 1.40: Changes to CID_Mesh_LOD chunks, we ignore those, so no special handling needed
38 		    std::make_pair("[MeshSerializer_v1.40]", 1040)
39 		};
40 
41 		// Chunk factory
Read(DataStream * stream)42 		std::unique_ptr<Chunk> Chunk::Read(DataStream *stream)
43 		{
44 			assert(stream->GetRemainingBytes() >= ChunkHeaderLength);
45 
46 			// Read metadata
47 			ChunkID id = CID_Invalid;
48 			id = static_cast<ChunkID>(stream->Read<uint16_t>());
49 			size_t size = 0;
50 			// Special case: CID_Header doesn't have any size info.
51 			if (id != CID_Header)
52 			{
53 				// All others are proper chunks.
54 				size = stream->Read<uint32_t>();
55 				size -= ChunkHeaderLength;
56 			}
57 
58 			// Create chunk
59 			std::unique_ptr<Chunk> chunk;
60 			switch (id)
61 			{
62 			case CID_Header: chunk = std::make_unique<Ogre::Mesh::ChunkFileHeader>(); break;
63 			case CID_Mesh: chunk = std::make_unique<Ogre::Mesh::ChunkMesh>(); break;
64 			case CID_Mesh_Bone_Assignment:
65 			case CID_Submesh_Bone_Assignment:
66 				chunk = std::make_unique<Ogre::Mesh::ChunkMeshBoneAssignments>(); break;
67 			case CID_Mesh_Skeleton_Link: chunk = std::make_unique<Ogre::Mesh::ChunkMeshSkeletonLink>(); break;
68 			case CID_Mesh_Bounds: chunk = std::make_unique<Ogre::Mesh::ChunkMeshBounds>(); break;
69 			case CID_Submesh: chunk = std::make_unique<Ogre::Mesh::ChunkSubmesh>(); break;
70 			case CID_Submesh_Op: chunk = std::make_unique<Ogre::Mesh::ChunkSubmeshOp>(); break;
71 			case CID_Geometry: chunk = std::make_unique<Ogre::Mesh::ChunkGeometry>(); break;
72 			case CID_Geometry_Vertex_Buffer: chunk = std::make_unique<Ogre::Mesh::ChunkGeometryVertexBuffer>(); break;
73 			case CID_Geometry_Vertex_Data: chunk = std::make_unique<Ogre::Mesh::ChunkGeometryVertexData>(); break;
74 			case CID_Geometry_Vertex_Decl: chunk = std::make_unique<Ogre::Mesh::ChunkGeometryVertexDecl>(); break;
75 			case CID_Geometry_Vertex_Decl_Element: chunk = std::make_unique<Ogre::Mesh::ChunkGeometryVertexDeclElement>(); break;
76 			default:
77 				LogF("StdMeshLoader: I don't know what to do with a chunk of type 0x%xu", id);
78 				// Fall through
79 			case CID_Edge_List: case CID_Submesh_Name_Table:
80 				// We don't care about these
81 				chunk = std::make_unique<Ogre::Mesh::ChunkUnknown>(); break;
82 			};
83 			chunk->type = id;
84 			chunk->size = size;
85 			chunk->ReadImpl(stream);
86 			return chunk;
87 		}
88 
ReadImpl(DataStream * stream)89 		void ChunkUnknown::ReadImpl(DataStream *stream) { stream->Seek(GetSize()); }
90 
ReadImpl(DataStream * stream)91 		void ChunkFileHeader::ReadImpl(DataStream *stream)
92 		{
93 			// Simple version check
94 			VersionTable_t::const_iterator it = VersionTable.find(stream->Read<std::string>());
95 			if (it == VersionTable.end())
96 				throw InvalidVersion();
97 		}
98 
ReadImpl(DataStream * stream)99 		void ChunkMesh::ReadImpl(DataStream *stream)
100 		{
101 			hasAnimatedSkeleton = stream->Read<bool>();
102 			for (ChunkID id = Chunk::Peek(stream);
103 			     id == CID_Geometry || id == CID_Submesh || id == CID_Mesh_Skeleton_Link || id == CID_Mesh_Bone_Assignment || id == CID_Mesh_LOD || id == CID_Submesh_Name_Table || id == CID_Mesh_Bounds || id == CID_Edge_List || id == CID_Pose_List || id == CID_Animation_List;
104 			     id = Chunk::Peek(stream)
105 			    )
106 			{
107 				std::unique_ptr<Chunk> chunk = Chunk::Read(stream);
108 				switch (chunk->GetType())
109 				{
110 				case CID_Geometry:
111 					if (geometry)
112 						throw MultipleSingletonChunks("There's only one CID_Geometry chunk allowed within a CID_Mesh chunk");
113 					geometry = static_unique_cast<ChunkGeometry>(move(chunk));
114 					break;
115 				case CID_Submesh:
116 					submeshes.push_back(static_unique_cast<ChunkSubmesh>(move(chunk)));
117 					break;
118 				case CID_Mesh_Skeleton_Link:
119 					if (!skeletonFile.empty())
120 						throw MultipleSingletonChunks("There's only one CID_Mesh_Skeleton_Link chunk allowed within a CID_Mesh chunk");
121 					skeletonFile = static_cast<ChunkMeshSkeletonLink*>(chunk.get())->skeleton;
122 					break;
123 				case CID_Mesh_Bounds:
124 					bounds = static_cast<ChunkMeshBounds*>(chunk.get())->bounds;
125 					radius = static_cast<ChunkMeshBounds*>(chunk.get())->radius;
126 					break;
127 				case CID_Mesh_Bone_Assignment:
128 					// Collect bone assignments
129 					{
130 					ChunkMeshBoneAssignments *assignments = static_cast<ChunkMeshBoneAssignments*>(chunk.get());
131 					boneAssignments.insert(boneAssignments.end(), assignments->assignments.begin(), assignments->assignments.end());
132 					break;
133 					}
134 				default:
135 					LogF("StdMeshLoader: I don't know what to do with a chunk of type 0x%xu inside a CID_Mesh chunk", chunk->GetType());
136 					// Fall through
137 				case CID_Submesh_Name_Table:
138 				case CID_Edge_List:
139 					// Ignore those
140 					break;
141 				}
142 				if (stream->AtEof()) break;
143 			}
144 		}
145 
ReadImpl(DataStream * stream)146 		void ChunkMeshSkeletonLink::ReadImpl(DataStream *stream)
147 		{
148 			skeleton = stream->Read<std::string>();
149 		}
150 
ReadImpl(DataStream * stream)151 		void ChunkSubmesh::ReadImpl(DataStream *stream)
152 		{
153 			operation = SO_TriList; // default if no CID_Submesh_Op chunk exists
154 			material = stream->Read<std::string>();
155 			hasSharedVertices = stream->Read<bool>();
156 			size_t index_count = stream->Read<uint32_t>();
157 			bool indexes_are_32bit = stream->Read<bool>();
158 			faceVertices.reserve(index_count);
159 			while (index_count--)
160 			{
161 				size_t index;
162 				if (indexes_are_32bit)
163 					index = stream->Read<uint32_t>();
164 				else
165 					index = stream->Read<uint16_t>();
166 				faceVertices.push_back(index);
167 			}
168 			for (ChunkID id = Chunk::Peek(stream);
169 			     id == CID_Geometry || id == CID_Submesh_Op || id == CID_Submesh_Bone_Assignment;
170 			     id = Chunk::Peek(stream)
171 			    )
172 			{
173 				std::unique_ptr<Chunk> chunk = Chunk::Read(stream);
174 
175 				switch (chunk->GetType())
176 				{
177 				case CID_Geometry:
178 					if (hasSharedVertices)
179 						// Can't have own vertices and at the same time use those of the parent
180 						throw SharedVertexGeometryForbidden();
181 					if (geometry)
182 						throw MultipleSingletonChunks("There's only one CID_Geometry chunk allowed within a CID_Submesh chunk");
183 					geometry = static_unique_cast<ChunkGeometry>(move(chunk));
184 					break;
185 				case CID_Submesh_Op:
186 					operation = static_cast<ChunkSubmeshOp*>(chunk.get())->operation;
187 					break;
188 				case CID_Submesh_Bone_Assignment:
189 				{
190 					// Collect bone assignments
191 					ChunkMeshBoneAssignments *assignments = static_cast<ChunkMeshBoneAssignments*>(chunk.get());
192 					boneAssignments.insert(boneAssignments.end(), assignments->assignments.begin(), assignments->assignments.end());
193 				}
194 				break;
195 				default:
196 					LogF("StdMeshLoader: I don't know what to do with a chunk of type 0x%xu inside a CID_Submesh chunk", chunk->GetType());
197 					break;
198 				}
199 				if (stream->AtEof()) break;
200 			}
201 		}
202 
ReadImpl(DataStream * stream)203 		void ChunkSubmeshOp::ReadImpl(DataStream *stream)
204 		{
205 			uint32_t op = stream->Read<uint16_t>();
206 			if (op < ChunkSubmesh::SO_MIN || op > ChunkSubmesh::SO_MAX)
207 				throw InvalidSubmeshOp();
208 			operation = static_cast<ChunkSubmesh::SubmeshOperation>(op);
209 		}
210 
ReadImpl(DataStream * stream)211 		void ChunkMeshBoneAssignments::ReadImpl(DataStream *stream)
212 		{
213 			size_t bone_count = GetSize() / (sizeof(uint32_t)+sizeof(uint16_t)+sizeof(float));
214 			BoneAssignment assignment;
215 			while (bone_count-- > 0)
216 			{
217 				assignment.vertex = stream->Read<uint32_t>();
218 				assignment.bone = stream->Read<uint16_t>();
219 				assignment.weight = stream->Read<float>();
220 				assignments.push_back(assignment);
221 			}
222 		}
223 
ReadImpl(DataStream * stream)224 		void ChunkMeshBounds::ReadImpl(DataStream *stream)
225 		{
226 			bounds.x1 = stream->Read<float>();
227 			bounds.y1 = stream->Read<float>();
228 			bounds.z1 = stream->Read<float>();
229 			bounds.x2 = stream->Read<float>();
230 			bounds.y2 = stream->Read<float>();
231 			bounds.z2 = stream->Read<float>();
232 			radius = stream->Read<float>();
233 		}
234 
ReadImpl(DataStream * stream)235 		void ChunkGeometry::ReadImpl(DataStream *stream)
236 		{
237 			vertexCount = stream->Read<uint32_t>();
238 			for (ChunkID id = Chunk::Peek(stream);
239 			     id == CID_Geometry_Vertex_Decl || id == CID_Geometry_Vertex_Buffer;
240 			     id = Chunk::Peek(stream)
241 			    )
242 			{
243 				std::unique_ptr<Chunk> chunk = Chunk::Read(stream);
244 
245 				switch (chunk->GetType())
246 				{
247 				case CID_Geometry_Vertex_Decl:
248 					if (!vertexDeclaration.empty())
249 						throw MultipleSingletonChunks("There's only one CID_Geometry_Vertex_Decl chunk allowed within a CID_Geometry chunk");
250 					vertexDeclaration.swap(static_cast<ChunkGeometryVertexDecl*>(chunk.get())->declaration);
251 					break;
252 				case CID_Geometry_Vertex_Buffer:
253 					vertexBuffers.push_back(static_unique_cast<ChunkGeometryVertexBuffer>(move(chunk)));
254 					break;
255 				default:
256 					LogF("StdMeshLoader: I don't know what to do with a chunk of type 0x%xu inside a CID_Geometry chunk", chunk->GetType());
257 					break;
258 				}
259 				if (stream->AtEof()) break;
260 			}
261 		}
262 
ReadImpl(DataStream * stream)263 		void ChunkGeometryVertexDecl::ReadImpl(DataStream *stream)
264 		{
265 			while (Chunk::Peek(stream) == CID_Geometry_Vertex_Decl_Element)
266 			{
267 				std::unique_ptr<Chunk> chunk = Chunk::Read(stream);
268 				assert(chunk->GetType() == CID_Geometry_Vertex_Decl_Element);
269 				declaration.push_back(static_unique_cast<ChunkGeometryVertexDeclElement>(chunk));
270 				if (stream->AtEof()) break;
271 			}
272 		}
273 
ReadImpl(DataStream * stream)274 		void ChunkGeometryVertexDeclElement::ReadImpl(DataStream *stream)
275 		{
276 			source = stream->Read<uint16_t>();
277 			int32_t t = stream->Read<uint16_t>();
278 			if (t < VDET_MIN || t > VDET_MAX)
279 				throw InvalidVertexType();
280 			type = static_cast<Type>(t);
281 			t = stream->Read<uint16_t>();
282 			if (t < VDES_MIN || t > VDES_MAX)
283 				throw InvalidVertexSemantic();
284 			semantic = static_cast<Semantic>(t);
285 			offset = stream->Read<uint16_t>();
286 			index = stream->Read<uint16_t>();
287 		}
288 
ReadImpl(DataStream * stream)289 		void ChunkGeometryVertexBuffer::ReadImpl(DataStream *stream)
290 		{
291 			index = stream->Read<uint16_t>();
292 			vertexSize = stream->Read<uint16_t>();
293 
294 			while (Chunk::Peek(stream) == CID_Geometry_Vertex_Data)
295 			{
296 				std::unique_ptr<Chunk> chunk = Chunk::Read(stream);
297 				assert(chunk->GetType() == CID_Geometry_Vertex_Data);
298 				if (data)
299 					throw MultipleSingletonChunks("There's only one CID_Geometry_Vertex_Data chunk allowed within a CID_Geometry_Vertex_Buffer chunk");
300 				data = static_unique_cast<ChunkGeometryVertexData>(move(chunk));
301 				if (stream->AtEof()) break;
302 			}
303 		}
304 
ReadImpl(DataStream * stream)305 		void ChunkGeometryVertexData::ReadImpl(DataStream *stream)
306 		{
307 			data = new char[GetSize()];
308 			stream->Read(data, GetSize());
309 		}
310 	}
311 
312 	namespace Skeleton
313 	{
314 		const uint32_t ChunkFileHeader::CurrentVersion = 1080; // Major * 1000 + Minor
315 		const std::map<std::string, uint32_t> ChunkFileHeader::VersionTable = {
316 		    // 1.80: Current version
317 		    std::make_pair("[Serializer_v1.80]",  CurrentVersion),
318 		    // 1.10: adds SKELETON_BLENDMODE and SKELETON_ANIMATION_BASEINFO chunks. The chunks have been added to the loader, but we ignore them for now.
319 		    std::make_pair("[Serializer_v1.10]", 1010)
320 		};
321 
Read(DataStream * stream)322 		std::unique_ptr<Chunk> Chunk::Read(DataStream *stream)
323 		{
324 			assert(stream->GetRemainingBytes() >= ChunkHeaderLength);
325 
326 			// Read metadata
327 			ChunkID id = CID_Invalid;
328 			id = static_cast<ChunkID>(stream->Read<uint16_t>());
329 			size_t size = 0;
330 			// Special case: CID_Header doesn't have any size info.
331 			if (id != CID_Header)
332 			{
333 				// All others are proper chunks.
334 				size = stream->Read<uint32_t>();
335 				size -= ChunkHeaderLength;
336 			}
337 
338 			// Create chunk
339 			std::unique_ptr<Chunk> chunk;
340 			switch (id)
341 			{
342 			case CID_Header: chunk = std::make_unique<Ogre::Skeleton::ChunkFileHeader>(); break;
343 			case CID_BlendMode: chunk = std::make_unique<Ogre::Skeleton::ChunkBlendMode>(); break;
344 			case CID_Bone: chunk = std::make_unique<Ogre::Skeleton::ChunkBone>(); break;
345 			case CID_Bone_Parent: chunk = std::make_unique<Ogre::Skeleton::ChunkBoneParent>(); break;
346 			case CID_Animation: chunk = std::make_unique<Ogre::Skeleton::ChunkAnimation>(); break;
347 			case CID_Animation_BaseInfo: chunk = std::make_unique<Ogre::Skeleton::ChunkAnimationBaseInfo>(); break;
348 			case CID_Animation_Track: chunk = std::make_unique<Ogre::Skeleton::ChunkAnimationTrack>(); break;
349 			case CID_Animation_Track_KF: chunk = std::make_unique<Ogre::Skeleton::ChunkAnimationTrackKF>(); break;
350 			case CID_Animation_Link: chunk = std::make_unique<Ogre::Skeleton::ChunkAnimationLink>(); break;
351 			default:
352 				LogF("StdMeshLoader: I don't know what to do with a chunk of type 0x%xu", id);
353 				chunk = std::make_unique<Ogre::Skeleton::ChunkUnknown>(); break;
354 			};
355 			chunk->type = id;
356 			chunk->size = size;
357 			chunk->ReadImpl(stream);
358 			return chunk;
359 		}
360 
ReadImpl(DataStream * stream)361 		void ChunkUnknown::ReadImpl(DataStream *stream) { stream->Seek(GetSize()); }
362 
ReadImpl(DataStream * stream)363 		void ChunkFileHeader::ReadImpl(DataStream *stream)
364 		{
365 			// Simple version check
366 			VersionTable_t::const_iterator it = VersionTable.find(stream->Read<std::string>());
367 			if (it == VersionTable.end())
368 				throw InvalidVersion();
369 		}
370 
ReadImpl(DataStream * stream)371 		void ChunkBlendMode::ReadImpl(DataStream* stream)
372 		{
373 			blend_mode = stream->Read<uint16_t>();
374 		}
375 
ReadImpl(DataStream * stream)376 		void ChunkBone::ReadImpl(DataStream *stream)
377 		{
378 			name = stream->Read<std::string>();
379 			handle = stream->Read<uint16_t>();
380 			position.x = stream->Read<float>();
381 			position.y = stream->Read<float>();
382 			position.z = stream->Read<float>();
383 			orientation.x = stream->Read<float>();
384 			orientation.y = stream->Read<float>();
385 			orientation.z = stream->Read<float>();
386 			orientation.w = stream->Read<float>();
387 			// Guess whether we have a scale element
388 			if (GetSize() > name.size() + 1 + sizeof(handle) + sizeof(float) * 7)
389 			{
390 				scale.x = stream->Read<float>();
391 				scale.y = stream->Read<float>();
392 				scale.z = stream->Read<float>();
393 			}
394 			else
395 			{
396 				scale = StdMeshVector::UnitScale();
397 			}
398 		}
399 
ReadImpl(DataStream * stream)400 		void ChunkBoneParent::ReadImpl(DataStream *stream)
401 		{
402 			childHandle = stream->Read<uint16_t>();
403 			parentHandle = stream->Read<uint16_t>();
404 		}
405 
ReadImpl(DataStream * stream)406 		void ChunkAnimation::ReadImpl(DataStream *stream)
407 		{
408 			name = stream->Read<std::string>();
409 			duration = stream->Read<float>();
410 
411 			if(!stream->AtEof() && Chunk::Peek(stream) == CID_Animation_BaseInfo)
412 			{
413 				std::unique_ptr<Chunk> chunk = Chunk::Read(stream);
414 				assert(chunk->GetType() == CID_Animation_BaseInfo);
415 				// TODO: Handle it
416 				LogF("StdMeshLoader: CID_Animation_BaseInfo not implemented. Skeleton might not be imported properly.");
417 			}
418 
419 			while (!stream->AtEof() && Chunk::Peek(stream) == CID_Animation_Track)
420 			{
421 				std::unique_ptr<Chunk> chunk = Chunk::Read(stream);
422 				assert(chunk->GetType() == CID_Animation_Track);
423 				tracks.push_back(static_unique_cast<ChunkAnimationTrack>(move(chunk)));
424 			}
425 		}
426 
ReadImpl(DataStream * stream)427 		void ChunkAnimationBaseInfo::ReadImpl(DataStream* stream)
428 		{
429 			base_animation_name = stream->Read<std::string>();
430 			base_key_frame_time = stream->Read<float>();
431 		}
432 
ReadImpl(DataStream * stream)433 		void ChunkAnimationTrack::ReadImpl(DataStream *stream)
434 		{
435 			bone = stream->Read<uint16_t>();
436 			while (Chunk::Peek(stream) == CID_Animation_Track_KF)
437 			{
438 				std::unique_ptr<Chunk> chunk = Chunk::Read(stream);
439 				assert(chunk->GetType() == CID_Animation_Track_KF);
440 				keyframes.push_back(static_unique_cast<ChunkAnimationTrackKF>(move(chunk)));
441 				if (stream->AtEof()) break;
442 			}
443 		}
444 
ReadImpl(DataStream * stream)445 		void ChunkAnimationTrackKF::ReadImpl(DataStream *stream)
446 		{
447 			time = stream->Read<float>();
448 			rotation.x = stream->Read<float>();
449 			rotation.y = stream->Read<float>();
450 			rotation.z = stream->Read<float>();
451 			rotation.w = stream->Read<float>();
452 			translation.x = stream->Read<float>();
453 			translation.y = stream->Read<float>();
454 			translation.z = stream->Read<float>();
455 			// Guess whether we have a scale element
456 			if (GetSize() > sizeof(float) * 8)
457 			{
458 				scale.x = stream->Read<float>();
459 				scale.y = stream->Read<float>();
460 				scale.z = stream->Read<float>();
461 			}
462 			else
463 			{
464 				scale = StdMeshVector::UnitScale();
465 			}
466 		}
467 
ReadImpl(DataStream * stream)468 		void ChunkAnimationLink::ReadImpl(DataStream *stream)
469 		{
470 			file = stream->Read<std::string>();
471 			scale.x = stream->Read<float>();
472 			scale.y = stream->Read<float>();
473 			scale.z = stream->Read<float>();
474 		}
475 	}
476 }
477