1 /*
2 Copyright (c) 2014-2015 Timur Gafarov 
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 
6 Permission is hereby granted, free of charge, to any person or organization
7 obtaining a copy of the software and accompanying documentation covered by
8 this license (the "Software") to use, reproduce, display, distribute,
9 execute, and transmit the Software, and to prepare derivative works of the
10 Software, and to permit third-parties to whom the Software is furnished to
11 do so, all subject to the following:
12 
13 The copyright notices in the Software and this entire statement, including
14 the above license grant, this restriction and the following disclaimer,
15 must be included in all copies of the Software, in whole or in part, and
16 all derivative works of the Software, unless such copies or derivative
17 works are solely in the form of machine-executable object code generated by
18 a source language processor.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 DEALINGS IN THE SOFTWARE.
27 */
28 
29 module dgl.asset.dgl2;
30 
31 import std.stdio;
32 import std.string;
33 import std.conv;
34 import std.format;
35 import std.path;
36 
37 import dlib.core.memory;
38 import dlib.core.stream;
39 import dlib.filesystem.filesystem;
40 import dlib.math.vector;
41 import dlib.math.quaternion;
42 import dlib.geometry.triangle;
43 
44 import dgl.dml.dml;
45 import dgl.graphics.material;
46 import dgl.graphics.texture;
47 import dgl.graphics.scene;
48 import dgl.graphics.entity;
49 import dgl.graphics.mesh;
50 import dgl.asset.resman;
51 import dgl.asset.serialization;
52 
53 /*
54  * DGL2 is a simple chunk-based binary scene format for DGL. It is a successor of DGL format (DGL1).
55  * Main change from DGL1 is the usage of DML (DGL Markup Language) instead of JSON for serializing game-specific properties.
56  * DGL2 supports meshes, entities and per-polygon materials with maximum of 8 texture slots.
57  * You can create DGL2 scenes in Blender, via our exporter (io_export_dgl2.py).
58  */
59 
60 //version = DGLDebug;
61 
62 enum ChunkType
63 {
64     HEADER = 0, 
65     END = 1,
66     TRIMESH = 2,
67     MATERIAL = 3,
68     ENTITY = 4
69 }
70 
71 struct DataChunk
72 {
73     ushort type;
74     int id;
75     ushort nameSize;
76     uint dataSize;
77     string name;
78     ubyte[] data;
79 
80     string toString()
81     {
82         return format(
83             "type = %s\n"
84             "id = %s\n"
85             "nameSize = %s\n"
86             "dataSize = %s\n"
87             "name = %s",
88             type.to!ChunkType, id,
89             nameSize, dataSize, name
90         );
91     }
92 
93     void free()
94     {
95         if (name.length)
96             Delete(name);
97         if (data.length)
98             Delete(data);
99     }
100 }
101 
102 struct DGLTriangle
103 {
104     int m;
105     Vector3f[3] v;
106     Vector3f[3] n;
107     Vector2f[3] uv1;
108     Vector2f[3] uv2;
109 }
110 
111 void calcTriangleData(Triangle* tri, DGLTriangle* dglTri)
112 {
113     tri.v[0] = dglTri.v[0];
114     tri.v[1] = dglTri.v[1];
115     tri.v[2] = dglTri.v[2];
116 
117     tri.n[0] = dglTri.n[0];
118     tri.n[1] = dglTri.n[1];
119     tri.n[2] = dglTri.n[2];
120 
121     tri.t1[0] = dglTri.uv1[0];
122     tri.t1[1] = dglTri.uv1[1];
123     tri.t1[2] = dglTri.uv1[2];
124                 
125     tri.t2[0] = dglTri.uv2[0];
126     tri.t2[1] = dglTri.uv2[1];
127     tri.t2[2] = dglTri.uv2[2];
128                 
129     tri.materialIndex = dglTri.m;
130 
131     tri.normal = planeNormal(tri.v[0], tri.v[1], tri.v[2]);
132                 
133     tri.barycenter = (tri.v[0] + tri.v[1] + tri.v[2]) / 3;
134 
135     tri.d = (tri.v[0].x * tri.normal.x + 
136              tri.v[0].y * tri.normal.y + 
137              tri.v[0].z * tri.normal.z);
138 
139     tri.edges[0] = tri.v[1] - tri.v[0];
140     tri.edges[1] = tri.v[2] - tri.v[1];
141     tri.edges[2] = tri.v[0] - tri.v[2];
142 }
143 
144 void loadDGL2(InputStream istrm, Scene scene)
145 {   
146     assert(scene !is null);
147 
148     DataChunk readChunk()
149     {
150         DataChunk chunk;
151         chunk.type = read!ushort(istrm);
152         chunk.id = read!int(istrm);
153         chunk.nameSize = read!ushort(istrm);
154         chunk.dataSize = read!uint(istrm);
155 
156         if (chunk.nameSize > 0)
157         {
158             auto nameData = New!(ubyte[])(chunk.nameSize);
159             istrm.fillArray(nameData);
160             chunk.name = cast(string)nameData;
161         }
162 
163         if (chunk.dataSize > 0)
164         {
165             chunk.data = New!(ubyte[])(chunk.dataSize);
166             istrm.fillArray(chunk.data);
167         }
168 
169         return chunk;
170     }
171 
172     DataChunk chunk;
173     while (chunk.type != ChunkType.END && istrm.readable)
174     {
175         chunk = readChunk();
176         version(DGLDebug) writefln("----\nChunk:\n%s", chunk);
177 
178         if (chunk.type == ChunkType.ENTITY)
179         {
180             Entity e = New!Entity();
181             e.id = chunk.id;
182             // TODO: duplicate
183             //e.name = chunk.name;
184             auto dataStrm = New!ArrayStream(chunk.data, chunk.data.length);
185             decodeEntity(e, dataStrm, scene);
186             Delete(dataStrm);
187             
188             version(DGLDebug) writefln("----\nEntity:\n%s", e);
189             scene.addEntity(chunk.name, e);
190         }
191         else if (chunk.type == ChunkType.MATERIAL)
192         {
193             Material mat = New!Material();
194             mat.id = chunk.id;
195             // TODO: duplicate
196             //mat.name = chunk.name;
197             auto dataStrm = New!ArrayStream(chunk.data, chunk.data.length);
198             decodeMaterial(mat, dataStrm, scene);
199             Delete(dataStrm);
200 
201             version(DGLDebug) writefln("----\nMaterial:\n%s", mat);
202             scene.addMaterial(chunk.name, mat);
203         }
204         else if (chunk.type == ChunkType.TRIMESH)
205         {
206             assert(!(chunk.data.length % DGLTriangle.sizeof)); // Check data integrity
207             size_t numTris = chunk.data.length / DGLTriangle.sizeof;
208             Triangle[] tris = New!(Triangle[])(numTris);
209             auto mtris = cast(DGLTriangle[])chunk.data;
210             uint offset = 0;
211 
212             foreach(i, mtri; mtris)
213             {
214                 Triangle* tri = &tris[offset];
215                 calcTriangleData(tri, &mtri);
216                 offset++;
217             }
218 
219             Mesh mesh = New!Mesh(tris);
220             mesh.id = chunk.id;
221             // TODO: duplicate
222             //mesh.name = chunk.name;
223             scene.addMesh(chunk.name, mesh);
224 
225             version(DGLDebug) writefln("numTris: %s", numTris);
226         }
227 
228         chunk.free();
229     }
230 
231     version(DGLDebug) writeln("end");
232 }
233 
234 void decodeEntity(Entity e, InputStream istrm, Scene scene)
235 {
236     e.type = read!uint(istrm);
237     e.materialId = read!int(istrm);
238     e.meshId = read!int(istrm);
239 
240     Vector3f position = read!(Vector3f, true)(istrm);
241     Quaternionf rotation = read!(Quaternionf, true)(istrm);
242     Vector3f scaling = read!(Vector3f, true)(istrm);
243     e.setTransformation(position, rotation, scaling);
244 
245     DMLData dml;
246     auto dmlSize = read!uint(istrm);
247     if (dmlSize > 0)
248     {
249         auto dmlBytes = New!(ubyte[])(dmlSize);
250         istrm.fillArray(dmlBytes);
251         string dmlStr = cast(string)dmlBytes;
252         version(DGLDebug) writefln("----\ndmlStr:\n%s", dmlStr);
253         parseDML(dmlStr, &dml);
254         Delete(dmlBytes);
255     }
256     e.props = dml;
257     
258     if ("visible" in dml.root.data)
259         e.visible = cast(bool)dml.root.data["visible"].toInt;
260 }
261 
262 void decodeMaterial(Material m, InputStream istrm, Scene scene)
263 {
264     DMLData dml;
265 
266     auto dmlSize = read!uint(istrm);
267     if (dmlSize > 0)
268     {
269         auto dmlBytes = New!(ubyte[])(dmlSize);
270         istrm.fillArray(dmlBytes);
271         string dmlStr = cast(string)dmlBytes;
272         version(DGLDebug) writefln("----\ndmlStr:\n%s", dmlStr);
273         parseDML(dmlStr, &dml);
274         Delete(dmlBytes);
275     }
276 
277     if ("diffuseColor" in dml.root.data)
278         m.diffuseColor = dml.root.data["diffuseColor"].toColor4f;
279 
280     if ("specularColor" in dml.root.data)
281         m.specularColor = dml.root.data["specularColor"].toColor4f;
282 
283     if ("shadeless" in dml.root.data)
284         m.shadeless = dml.root.data["shadeless"].toBool;
285 
286     if ("texturesNum" in dml.root.data)
287     {
288         int numTextures = dml.root.data["texturesNum"].toInt;
289         foreach(i; 0..numTextures)
290         {
291             string texId = format("texture%s", i);
292             if (texId in dml.root.data)
293             {
294                 string texStr = dml.root.data[texId].toString;
295                 string filename;
296                 int blendType;
297                 formattedRead(texStr, "[%s, %s]", &filename, &blendType);
298                 Texture tex = scene.getTexture(filename);
299                 m.textures[i] = tex;
300             }
301         }
302     }
303     
304     dml.free();
305 }
306 
307