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