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\nid = %s\nnameSize = %s\ndataSize = %s\nname = %s",
84             type.to!ChunkType, id,
85             nameSize, dataSize, name
86         );
87     }
88 
89     void free()
90     {
91         if (name.length)
92             Delete(name);
93         if (data.length)
94             Delete(data);
95     }
96 }
97 
98 struct DGLTriangle
99 {
100     int m;
101     Vector3f[3] v;
102     Vector3f[3] n;
103     Vector2f[3] uv1;
104     Vector2f[3] uv2;
105 }
106 
107 void calcTriangleData(Triangle* tri, DGLTriangle* dglTri)
108 {
109     tri.v[0] = dglTri.v[0];
110     tri.v[1] = dglTri.v[1];
111     tri.v[2] = dglTri.v[2];
112 
113     tri.n[0] = dglTri.n[0];
114     tri.n[1] = dglTri.n[1];
115     tri.n[2] = dglTri.n[2];
116 
117     tri.t1[0] = dglTri.uv1[0];
118     tri.t1[1] = dglTri.uv1[1];
119     tri.t1[2] = dglTri.uv1[2];
120                 
121     tri.t2[0] = dglTri.uv2[0];
122     tri.t2[1] = dglTri.uv2[1];
123     tri.t2[2] = dglTri.uv2[2];
124                 
125     tri.materialIndex = dglTri.m;
126 
127     tri.normal = planeNormal(tri.v[0], tri.v[1], tri.v[2]);
128                 
129     tri.barycenter = (tri.v[0] + tri.v[1] + tri.v[2]) / 3;
130 
131     tri.d = (tri.v[0].x * tri.normal.x + 
132              tri.v[0].y * tri.normal.y + 
133              tri.v[0].z * tri.normal.z);
134 
135     tri.edges[0] = tri.v[1] - tri.v[0];
136     tri.edges[1] = tri.v[2] - tri.v[1];
137     tri.edges[2] = tri.v[0] - tri.v[2];
138 }
139 
140 void loadDGL2(InputStream istrm, Scene scene)
141 {   
142     assert(scene !is null);
143 
144     DataChunk readChunk()
145     {
146         DataChunk chunk;
147         chunk.type = read!ushort(istrm);
148         chunk.id = read!int(istrm);
149         chunk.nameSize = read!ushort(istrm);
150         chunk.dataSize = read!uint(istrm);
151 
152         if (chunk.nameSize > 0)
153         {
154             auto nameData = New!(ubyte[])(chunk.nameSize);
155             istrm.fillArray(nameData);
156             chunk.name = cast(string)nameData;
157         }
158 
159         if (chunk.dataSize > 0)
160         {
161             chunk.data = New!(ubyte[])(chunk.dataSize);
162             istrm.fillArray(chunk.data);
163         }
164 
165         return chunk;
166     }
167 
168     DataChunk chunk;
169     while (chunk.type != ChunkType.END && istrm.readable)
170     {
171         chunk = readChunk();
172         version(DGLDebug) writefln("----\nChunk:\n%s", chunk);
173 
174         if (chunk.type == ChunkType.ENTITY)
175         {
176             Entity e = New!Entity();
177             e.id = chunk.id;
178             // TODO: duplicate
179             //e.name = chunk.name;
180             auto dataStrm = New!ArrayStream(chunk.data, chunk.data.length);
181             decodeEntity(e, dataStrm, scene);
182             Delete(dataStrm);
183             
184             version(DGLDebug) writefln("----\nEntity:\n%s", e);
185             scene.addEntity(chunk.name, e);
186         }
187         else if (chunk.type == ChunkType.MATERIAL)
188         {
189             Material mat = New!Material();
190             mat.id = chunk.id;
191             // TODO: duplicate
192             //mat.name = chunk.name;
193             auto dataStrm = New!ArrayStream(chunk.data, chunk.data.length);
194             decodeMaterial(mat, dataStrm, scene);
195             Delete(dataStrm);
196 
197             version(DGLDebug) writefln("----\nMaterial:\n%s", mat);
198             scene.addMaterial(chunk.name, mat);
199         }
200         else if (chunk.type == ChunkType.TRIMESH)
201         {
202             assert(!(chunk.data.length % DGLTriangle.sizeof)); // Check data integrity
203             size_t numTris = chunk.data.length / DGLTriangle.sizeof;
204             Triangle[] tris = New!(Triangle[])(numTris);
205             auto mtris = cast(DGLTriangle[])chunk.data;
206             uint offset = 0;
207 
208             foreach(i, mtri; mtris)
209             {
210                 Triangle* tri = &tris[offset];
211                 calcTriangleData(tri, &mtri);
212                 offset++;
213             }
214 
215             Mesh mesh = New!Mesh(tris);
216             mesh.id = chunk.id;
217             // TODO: duplicate
218             //mesh.name = chunk.name;
219             scene.addMesh(chunk.name, mesh);
220 
221             version(DGLDebug) writefln("numTris: %s", numTris);
222         }
223 
224         chunk.free();
225     }
226 
227     version(DGLDebug) writeln("end");
228 }
229 
230 void decodeEntity(Entity e, InputStream istrm, Scene scene)
231 {
232     e.type = read!uint(istrm);
233     e.materialId = read!int(istrm);
234     e.meshId = read!int(istrm);
235 
236     Vector3f position = read!(Vector3f, true)(istrm);
237     Quaternionf rotation = read!(Quaternionf, true)(istrm);
238     Vector3f scaling = read!(Vector3f, true)(istrm);
239     e.setTransformation(position, rotation, scaling);
240 
241     DMLData dml;
242     auto dmlSize = read!uint(istrm);
243     if (dmlSize > 0)
244     {
245         auto dmlBytes = New!(ubyte[])(dmlSize);
246         istrm.fillArray(dmlBytes);
247         string dmlStr = cast(string)dmlBytes;
248         version(DGLDebug) writefln("----\ndmlStr:\n%s", dmlStr);
249         parseDML(dmlStr, &dml);
250         Delete(dmlBytes);
251     }
252     e.props = dml;
253     
254     if ("visible" in dml.root.data)
255         e.visible = cast(bool)dml.root.data["visible"].toInt;
256 }
257 
258 void decodeMaterial(Material m, InputStream istrm, Scene scene)
259 {
260     DMLData dml;
261 
262     auto dmlSize = read!uint(istrm);
263     if (dmlSize > 0)
264     {
265         auto dmlBytes = New!(ubyte[])(dmlSize);
266         istrm.fillArray(dmlBytes);
267         string dmlStr = cast(string)dmlBytes;
268         version(DGLDebug) writefln("----\ndmlStr:\n%s", dmlStr);
269         parseDML(dmlStr, &dml);
270         Delete(dmlBytes);
271     }
272 
273     if ("diffuseColor" in dml.root.data)
274         m.diffuseColor = dml.root.data["diffuseColor"].toColor4f;
275 
276     if ("specularColor" in dml.root.data)
277         m.specularColor = dml.root.data["specularColor"].toColor4f;
278 
279     if ("shadeless" in dml.root.data)
280         m.shadeless = dml.root.data["shadeless"].toBool;
281 
282     if ("texturesNum" in dml.root.data)
283     {
284         int numTextures = dml.root.data["texturesNum"].toInt;
285         foreach(i; 0..numTextures)
286         {
287             string texId = format("texture%s", i);
288             if (texId in dml.root.data)
289             {
290                 string texStr = dml.root.data[texId].toString;
291                 string filename;
292                 int blendType;
293                 formattedRead(texStr, "[%s, %s]", &filename, &blendType);
294                 Texture tex = scene.getTexture(filename);
295                 m.textures[i] = tex;
296             }
297         }
298     }
299     
300     dml.free();
301 }
302 
303