1 /*
2 Copyright (c) 2018-2020 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.resource.packageasset;
29 
30 import std.stdio;
31 import std.string;
32 import std.format;
33 import std.path;
34 
35 import dlib.core.memory;
36 import dlib.core.stream;
37 import dlib.core.ownership;
38 import dlib.filesystem.filesystem;
39 import dlib.filesystem.stdfs;
40 import dlib.container.array;
41 import dlib.container.dict;
42 import dlib.math.vector;
43 import dlib.math.quaternion;
44 import dlib.image.color;
45 
46 import dagon.core.props;
47 import dagon.resource.asset;
48 import dagon.resource.boxfs;
49 import dagon.resource.obj;
50 import dagon.resource.texture;
51 import dagon.resource.material;
52 import dagon.resource.scene;
53 import dagon.resource.entity;
54 import dagon.graphics.drawable;
55 import dagon.graphics.mesh;
56 import dagon.graphics.texture;
57 import dagon.graphics.material;
58 import dagon.graphics.entity;
59 
60 /*
61  * A simple asset package format based on Box container (https://github.com/gecko0307/box).
62  * It is an archive that stores entities, meshes, materials and textures.
63  */
64 
65 class PackageAssetOwner: Owner
66 {
67     this(Owner o)
68     {
69         super(o);
70     }
71 }
72 
73 class PackageAsset: Asset
74 {
75     Dict!(OBJAsset, string) meshes;
76     Dict!(EntityAsset, string) entities;
77     Dict!(TextureAsset, string) textures;
78     Dict!(MaterialAsset, string) materials;
79 
80     string filename;
81     string index;
82     BoxFileSystem boxfs;
83     AssetManager assetManager;
84     Scene scene;
85     Entity rootEntity;
86     PackageAssetOwner assetOwner;
87 
88     this(Scene scene, Owner o)
89     {
90         super(o);
91         this.scene = scene;
92 
93         rootEntity = New!Entity(this);
94     }
95 
96     ~this()
97     {
98         release();
99     }
100 
101     override bool loadThreadSafePart(string filename, InputStream istrm, ReadOnlyFileSystem fs, AssetManager mngr)
102     {
103         this.filename = filename;
104         meshes = New!(Dict!(OBJAsset, string))();
105         entities = New!(Dict!(EntityAsset, string))();
106         textures = New!(Dict!(TextureAsset, string))();
107         materials = New!(Dict!(MaterialAsset, string))();
108         boxfs = New!BoxFileSystem(fs, filename);
109 
110         if (fileExists("INDEX"))
111         {
112             auto fstrm = boxfs.openForInput("INDEX");
113             index = readText(fstrm);
114             Delete(fstrm);
115         }
116 
117         assetManager = mngr;
118 
119         assetOwner = New!PackageAssetOwner(null);
120 
121         return true;
122     }
123 
124     override bool loadThreadUnsafePart()
125     {
126         if (index.length)
127         foreach(path; lineSplitter(index))
128         {
129             Entity e = entity(path);
130         }
131         
132         return true;
133     }
134 
135     bool loadAsset(Asset asset, string filename)
136     {
137         if (!fileExists(filename))
138         {
139             writefln("Error: cannot find file \"%s\" in package", filename);
140             return false;
141         }
142 
143         auto fstrm = boxfs.openForInput(filename);
144         bool res = asset.loadThreadSafePart(filename, fstrm, boxfs, assetManager);
145         asset.threadSafePartLoaded = res;
146         Delete(fstrm);
147 
148         if (!res)
149         {
150             writefln("Error: failed to load asset \"%s\" from package", filename);
151             return false;
152         }
153         else
154         {
155             res = asset.loadThreadUnsafePart();
156             asset.threadUnsafePartLoaded = res;
157             if (!res)
158             {
159                 writefln("Error: failed to load asset \"%s\" from package", filename);
160                 return false;
161             }
162             else
163             {
164                 return true;
165             }
166         }
167     }
168 
169     Mesh mesh(string filename)
170     {
171         if (!(filename in meshes))
172         {
173             OBJAsset objAsset = New!OBJAsset(assetOwner);
174             if (loadAsset(objAsset, filename))
175             {
176                 meshes[filename] = objAsset;
177                 return objAsset.mesh;
178             }
179             else
180             {
181                 return null;
182             }
183         }
184         else
185         {
186             return meshes[filename].mesh;
187         }
188     }
189 
190     Entity entity(string filename)
191     {
192         if (!(filename in entities))
193         {
194             EntityAsset entityAsset = New!EntityAsset(assetOwner);
195 
196             if (loadAsset(entityAsset, filename))
197             {
198                 entities[filename] = entityAsset;
199 
200                 Entity parent = rootEntity;
201 
202                 if ("parent" in entityAsset.props)
203                 {
204                     parent = entity(entityAsset.props.parent.toString);
205                 }
206 
207                 entityAsset.entity = New!Entity(assetOwner);
208                 entityAsset.entity.setParent(parent);
209                 entityAsset.entity.layer = EntityLayer.Spatial;
210                 entityAsset.entity.visible = true;
211                 entityAsset.entity.castShadow = true;
212                 entityAsset.entity.solid = true;
213 
214                 if ("position" in entityAsset.props)
215                 {
216                     entityAsset.entity.position = entityAsset.props.position.toVector3f;
217                 }
218 
219                 if ("rotation" in entityAsset.props)
220                 {
221                     entityAsset.entity.rotation = Quaternionf(entityAsset.props.rotation.toVector4f).normalized;
222                 }
223 
224                 if ("scale" in entityAsset.props)
225                 {
226                     entityAsset.entity.scaling = entityAsset.props.scale.toVector3f;
227                 }
228 
229                 entityAsset.entity.updateTransformation();
230 
231                 if ("visible" in entityAsset.props)
232                 {
233                     entityAsset.entity.visible = entityAsset.props.visible.toBool;
234                 }
235 
236                 if ("castShadow" in entityAsset.props)
237                 {
238                     entityAsset.entity.castShadow = entityAsset.props.castShadow.toBool;
239                 }
240 
241                 /*
242                 if ("useMotionBlur" in entityAsset.props)
243                 {
244                     entityAsset.entity.useMotionBlur = entityAsset.props.useMotionBlur.toBool;
245                 }
246                 */
247 
248                 if ("solid" in entityAsset.props)
249                 {
250                     entityAsset.entity.solid = entityAsset.props.solid.toBool;
251                 }
252 
253                 if ("layer" in entityAsset.props)
254                 {
255                     entityAsset.entity.layer = cast(EntityLayer)entityAsset.props.layer.toInt;
256                 }
257 
258                 if ("mesh" in entityAsset.props)
259                 {
260                     entityAsset.entity.drawable = mesh(entityAsset.props.mesh.toString);
261                 }
262 
263                 if ("material" in entityAsset.props)
264                 {
265                     entityAsset.entity.material = material(entityAsset.props.material.toString);
266                 }
267 
268                 //if (entityAsset.entity.parent)
269                 //    scene.sortEntities(entityAsset.entity.parent.children);
270 
271                 return entityAsset.entity;
272             }
273             else
274             {
275                 return null;
276             }
277         }
278         else
279         {
280             return entities[filename].entity;
281         }
282     }
283 
284     Texture texture(string filename)
285     {
286         if (!(filename in textures))
287         {
288             TextureAsset texAsset = New!TextureAsset(assetManager.imageFactory, assetManager.hdrImageFactory, assetOwner);
289             if (loadAsset(texAsset, filename))
290             {
291                 textures[filename] = texAsset;
292                 return texAsset.texture;
293             }
294             else
295             {
296                 return null;
297             }
298         }
299         else
300         {
301             return textures[filename].texture;
302         }
303     }
304 
305     Material material(string filename)
306     {
307         if (!(filename in materials))
308         {
309             MaterialAsset matAsset = New!MaterialAsset(assetOwner);
310             if (loadAsset(matAsset, filename))
311             {
312                 materials[filename] = matAsset;
313                 matAsset.material = createMaterial();
314 
315                 // diffuse
316                 if ("diffuse" in matAsset.props)
317                 {
318                     if (matAsset.props.diffuse.type == DPropType.String)
319                     {
320                         matAsset.material.diffuse = texture(matAsset.props.diffuse.toString);
321                     }
322                     else
323                     {
324                         Vector3f diffCol = matAsset.props.diffuse.toVector3f;
325                         matAsset.material.diffuse = Color4f(diffCol.r, diffCol.g, diffCol.b, 1.0f);
326                     }
327                 }
328 
329                 // emission
330                 if ("emission" in matAsset.props)
331                 {
332                     if (matAsset.props.emission.type == DPropType.String)
333                     {
334                         matAsset.material.emission = texture(matAsset.props.emission.toString);
335                     }
336                     else
337                     {
338                         Vector3f emissionCol = matAsset.props.emission.toVector3f;
339                         matAsset.material.emission = Color4f(emissionCol.r, emissionCol.g, emissionCol.b, 1.0f);
340                     }
341                 }
342 
343                 // energy
344                 if ("energy" in matAsset.props)
345                 {
346                     matAsset.material.energy = matAsset.props.energy.toFloat;
347                 }
348 
349                 // normal
350                 if ("normal" in matAsset.props)
351                 {
352                     if (matAsset.props.normal.type == DPropType.String)
353                     {
354                         matAsset.material.normal = texture(matAsset.props.normal.toString);
355                     }
356                 }
357 
358                 // height
359                 if ("height" in matAsset.props)
360                 {
361                     if (matAsset.props.height.type == DPropType.String)
362                     {
363                         matAsset.material.height = texture(matAsset.props.height.toString);
364                     }
365                 }
366 
367                 // roughness
368                 if ("roughness" in matAsset.props)
369                 {
370                     if (matAsset.props.roughness.type == DPropType.String)
371                     {
372                         matAsset.material.roughness = texture(matAsset.props.roughness.toString);
373                     }
374                     else
375                     {
376                         matAsset.material.roughness = matAsset.props.roughness.toFloat;
377                     }
378                 }
379 
380                 // metallic
381                 if ("metallic" in matAsset.props)
382                 {
383                     if (matAsset.props.metallic.type == DPropType.String)
384                     {
385                         matAsset.material.metallic = texture(matAsset.props.metallic.toString);
386                     }
387                     else
388                     {
389                         matAsset.material.metallic = matAsset.props.metallic.toFloat;
390                     }
391                 }
392 
393                 // parallax
394                 if ("parallax" in matAsset.props)
395                 {
396                     matAsset.material.parallax = matAsset.props.parallax.toInt;
397                 }
398 
399                 // parallaxScale
400                 if ("parallaxScale" in matAsset.props)
401                 {
402                     matAsset.material.parallaxScale = matAsset.props.parallaxScale.toFloat;
403                 }
404 
405                 // parallaxBias
406                 if ("parallaxBias" in matAsset.props)
407                 {
408                     matAsset.material.parallaxBias = matAsset.props.parallaxBias.toFloat;
409                 }
410 
411                 // shadeless
412                 if ("shadeless" in matAsset.props)
413                 {
414                     matAsset.material.shadeless = matAsset.props.shadeless.toBool;
415                 }
416 
417                 // culling
418                 if ("culling" in matAsset.props)
419                 {
420                     matAsset.material.culling = matAsset.props.culling.toBool;
421                 }
422 
423                 // colorWrite
424                 if ("colorWrite" in matAsset.props)
425                 {
426                     matAsset.material.colorWrite = matAsset.props.colorWrite.toBool;
427                 }
428 
429                 // depthWrite
430                 if ("depthWrite" in matAsset.props)
431                 {
432                     matAsset.material.depthWrite = matAsset.props.depthWrite.toBool;
433                 }
434 
435                 // useShadows
436                 if ("useShadows" in matAsset.props)
437                 {
438                     matAsset.material.shadowsEnabled = matAsset.props.useShadows.toBool;
439                 }
440 
441                 // useFog
442                 if ("useFog" in matAsset.props)
443                 {
444                     matAsset.material.fogEnabled = matAsset.props.useFog.toBool;
445                 }
446 
447                 // shadowFilter
448                 if ("shadowFilter" in matAsset.props)
449                 {
450                     matAsset.material.shadowFilter = matAsset.props.shadowFilter.toInt;
451                 }
452 
453                 // blendingMode
454                 if ("blendingMode" in matAsset.props)
455                 {
456                     matAsset.material.blending = matAsset.props.blendingMode.toInt;
457                 }
458 
459                 // transparency
460                 if ("transparency" in matAsset.props)
461                 {
462                     matAsset.material.transparency = matAsset.props.transparency.toFloat;
463                 }
464 
465                 return matAsset.material;
466             }
467             else
468             {
469                 return null;
470             }
471         }
472         else
473         {
474             return materials[filename].material;
475         }
476     }
477 
478     Entity entity()
479     {
480         return rootEntity;
481     }
482 
483     Material createMaterial()
484     {
485         auto m = New!Material(assetOwner);
486         return m;
487     }
488 
489     bool fileExists(string filename)
490     {
491         FileStat stat;
492         return boxfs.stat(filename, stat);
493     }
494 
495     override void release()
496     {
497         Delete(boxfs);
498 
499         Delete(meshes);
500         Delete(entities);
501         Delete(textures);
502         Delete(materials);
503 
504         Delete(assetOwner);
505 
506         rootEntity.release();
507 
508         if (index.length)
509             Delete(index);
510     }
511 }