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