1 /* 2 Copyright (c) 2018 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.filesystem.filesystem; 38 import dlib.filesystem.stdfs; 39 import dlib.container.array; 40 import dlib.container.dict; 41 import dlib.math.vector; 42 import dlib.math.quaternion; 43 import dlib.image.color; 44 45 import dagon.core.ownership; 46 import dagon.core.interfaces; 47 import dagon.resource.asset; 48 import dagon.resource.boxfs; 49 import dagon.resource.obj; 50 import dagon.resource.textureasset; 51 import dagon.resource.entityasset; 52 import dagon.resource.materialasset; 53 import dagon.resource.scene; 54 import dagon.resource.props; 55 import dagon.graphics.mesh; 56 import dagon.graphics.texture; 57 import dagon.graphics.material; 58 import dagon.logics.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(scene.eventManager, 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 return true; 127 } 128 129 bool loadAsset(Asset asset, string filename) 130 { 131 if (!fileExists(filename)) 132 { 133 writefln("Error: cannot find file \"%s\" in package", filename); 134 return false; 135 } 136 137 auto fstrm = boxfs.openForInput(filename); 138 bool res = asset.loadThreadSafePart(filename, fstrm, boxfs, assetManager); 139 asset.threadSafePartLoaded = res; 140 Delete(fstrm); 141 142 if (!res) 143 { 144 writefln("Error: failed to load asset \"%s\" from package", filename); 145 return false; 146 } 147 else 148 { 149 res = asset.loadThreadUnsafePart(); 150 asset.threadUnsafePartLoaded = res; 151 if (!res) 152 { 153 writefln("Error: failed to load asset \"%s\" from package", filename); 154 return false; 155 } 156 else 157 { 158 return true; 159 } 160 } 161 } 162 163 Mesh mesh(string filename) 164 { 165 if (!(filename in meshes)) 166 { 167 OBJAsset objAsset = New!OBJAsset(assetOwner); 168 if (loadAsset(objAsset, filename)) 169 { 170 meshes[filename] = objAsset; 171 return objAsset.mesh; 172 } 173 else 174 { 175 return null; 176 } 177 } 178 else 179 { 180 return meshes[filename].mesh; 181 } 182 } 183 184 Entity entity(string filename) 185 { 186 if (!(filename in entities)) 187 { 188 EntityAsset entityAsset = New!EntityAsset(assetOwner); 189 190 if (loadAsset(entityAsset, filename)) 191 { 192 entities[filename] = entityAsset; 193 194 Entity parent = rootEntity; 195 196 if ("parent" in entityAsset.props) 197 { 198 parent = entity(entityAsset.props.parent.toString); 199 } 200 201 entityAsset.entity = New!Entity(parent, assetOwner); 202 entityAsset.entity.visible = true; 203 entityAsset.entity.castShadow = true; 204 entityAsset.entity.useMotionBlur = true; 205 entityAsset.entity.layer = 1; 206 entityAsset.entity.solid = true; 207 entityAsset.entity.material = scene.defaultMaterial3D; 208 209 if ("position" in entityAsset.props) 210 { 211 entityAsset.entity.position = entityAsset.props.position.toVector3f; 212 } 213 214 if ("rotation" in entityAsset.props) 215 { 216 entityAsset.entity.rotation = Quaternionf(entityAsset.props.rotation.toVector4f).normalized; 217 } 218 219 if ("scale" in entityAsset.props) 220 { 221 entityAsset.entity.scaling = entityAsset.props.scale.toVector3f; 222 } 223 224 entityAsset.entity.updateTransformation(0.0); 225 226 if ("visible" in entityAsset.props) 227 { 228 entityAsset.entity.visible = entityAsset.props.visible.toBool; 229 } 230 231 if ("castShadow" in entityAsset.props) 232 { 233 entityAsset.entity.castShadow = entityAsset.props.castShadow.toBool; 234 } 235 236 if ("useMotionBlur" in entityAsset.props) 237 { 238 entityAsset.entity.useMotionBlur = entityAsset.props.useMotionBlur.toBool; 239 } 240 241 if ("solid" in entityAsset.props) 242 { 243 entityAsset.entity.solid = entityAsset.props.solid.toBool; 244 } 245 246 if ("layer" in entityAsset.props) 247 { 248 entityAsset.entity.layer = entityAsset.props.layer.toInt; 249 } 250 251 if ("mesh" in entityAsset.props) 252 { 253 entityAsset.entity.drawable = mesh(entityAsset.props.mesh.toString); 254 } 255 256 if ("material" in entityAsset.props) 257 { 258 entityAsset.entity.material = material(entityAsset.props.material.toString); 259 } 260 261 if (entityAsset.entity.parent) 262 scene.sortEntities(entityAsset.entity.parent.children); 263 264 return entityAsset.entity; 265 } 266 else 267 { 268 return null; 269 } 270 } 271 else 272 { 273 return entities[filename].entity; 274 } 275 } 276 277 Texture texture(string filename) 278 { 279 if (!(filename in textures)) 280 { 281 TextureAsset texAsset = New!TextureAsset(assetManager.imageFactory, assetManager.hdrImageFactory, assetOwner); 282 if (loadAsset(texAsset, filename)) 283 { 284 textures[filename] = texAsset; 285 return texAsset.texture; 286 } 287 else 288 { 289 return null; 290 } 291 } 292 else 293 { 294 return textures[filename].texture; 295 } 296 } 297 298 Material material(string filename) 299 { 300 if (!(filename in materials)) 301 { 302 MaterialAsset matAsset = New!MaterialAsset(assetOwner); 303 if (loadAsset(matAsset, filename)) 304 { 305 materials[filename] = matAsset; 306 matAsset.material = createMaterial(); 307 308 // diffuse 309 if ("diffuse" in matAsset.props) 310 { 311 if (matAsset.props.diffuse.type == DPropType.String) 312 { 313 matAsset.material.diffuse = texture(matAsset.props.diffuse.toString); 314 } 315 else 316 { 317 Vector3f diffCol = matAsset.props.diffuse.toVector3f; 318 matAsset.material.diffuse = Color4f(diffCol.r, diffCol.g, diffCol.b, 1.0f); 319 } 320 } 321 322 // emission 323 if ("emission" in matAsset.props) 324 { 325 if (matAsset.props.emission.type == DPropType.String) 326 { 327 matAsset.material.emission = texture(matAsset.props.emission.toString); 328 } 329 else 330 { 331 Vector3f emissionCol = matAsset.props.emission.toVector3f; 332 matAsset.material.emission = Color4f(emissionCol.r, emissionCol.g, emissionCol.b, 1.0f); 333 } 334 } 335 336 // energy 337 if ("energy" in matAsset.props) 338 { 339 matAsset.material.energy = matAsset.props.energy.toFloat; 340 } 341 342 // normal 343 if ("normal" in matAsset.props) 344 { 345 if (matAsset.props.normal.type == DPropType.String) 346 { 347 matAsset.material.normal = texture(matAsset.props.normal.toString); 348 } 349 } 350 351 // height 352 if ("height" in matAsset.props) 353 { 354 if (matAsset.props.height.type == DPropType.String) 355 { 356 matAsset.material.height = texture(matAsset.props.height.toString); 357 } 358 } 359 360 // roughness 361 if ("roughness" in matAsset.props) 362 { 363 if (matAsset.props.roughness.type == DPropType.String) 364 { 365 matAsset.material.roughness = texture(matAsset.props.roughness.toString); 366 } 367 else 368 { 369 matAsset.material.roughness = matAsset.props.roughness.toFloat; 370 } 371 } 372 373 // metallic 374 if ("metallic" in matAsset.props) 375 { 376 if (matAsset.props.metallic.type == DPropType.String) 377 { 378 matAsset.material.metallic = texture(matAsset.props.metallic.toString); 379 } 380 else 381 { 382 matAsset.material.metallic = matAsset.props.metallic.toFloat; 383 } 384 } 385 386 // parallax 387 if ("parallax" in matAsset.props) 388 { 389 matAsset.material.parallax = matAsset.props.parallax.toInt; 390 } 391 392 // parallaxScale 393 if ("parallaxScale" in matAsset.props) 394 { 395 matAsset.material.parallaxScale = matAsset.props.parallaxScale.toFloat; 396 } 397 398 // parallaxBias 399 if ("parallaxBias" in matAsset.props) 400 { 401 matAsset.material.parallaxBias = matAsset.props.parallaxBias.toFloat; 402 } 403 404 // shadeless 405 if ("shadeless" in matAsset.props) 406 { 407 matAsset.material.shadeless = matAsset.props.shadeless.toBool; 408 } 409 410 // culling 411 if ("culling" in matAsset.props) 412 { 413 matAsset.material.culling = matAsset.props.culling.toBool; 414 } 415 416 // colorWrite 417 if ("colorWrite" in matAsset.props) 418 { 419 matAsset.material.colorWrite = matAsset.props.colorWrite.toBool; 420 } 421 422 // depthWrite 423 if ("depthWrite" in matAsset.props) 424 { 425 matAsset.material.depthWrite = matAsset.props.depthWrite.toBool; 426 } 427 428 // useShadows 429 if ("useShadows" in matAsset.props) 430 { 431 matAsset.material.shadowsEnabled = matAsset.props.useShadows.toBool; 432 } 433 434 // useFog 435 if ("useFog" in matAsset.props) 436 { 437 matAsset.material.fogEnabled = matAsset.props.useFog.toBool; 438 } 439 440 // shadowFilter 441 if ("shadowFilter" in matAsset.props) 442 { 443 matAsset.material.shadowFilter = matAsset.props.shadowFilter.toInt; 444 } 445 446 // blendingMode 447 if ("blendingMode" in matAsset.props) 448 { 449 matAsset.material.blending = matAsset.props.blendingMode.toInt; 450 } 451 452 // transparency 453 if ("transparency" in matAsset.props) 454 { 455 matAsset.material.transparency = matAsset.props.transparency.toFloat; 456 } 457 458 return matAsset.material; 459 } 460 else 461 { 462 return null; 463 } 464 } 465 else 466 { 467 return materials[filename].material; 468 } 469 } 470 471 Entity entity() 472 { 473 if (index.length) 474 foreach(path; lineSplitter(index)) 475 { 476 Entity e = entity(path); 477 } 478 479 return rootEntity; 480 } 481 482 Material createMaterial() 483 { 484 auto m = New!Material(scene.standardShader, assetOwner); 485 return m; 486 } 487 488 bool fileExists(string filename) 489 { 490 FileStat stat; 491 return boxfs.stat(filename, stat); 492 } 493 494 override void release() 495 { 496 Delete(boxfs); 497 498 Delete(meshes); 499 Delete(entities); 500 Delete(textures); 501 Delete(materials); 502 503 Delete(assetOwner); 504 505 rootEntity.release(); 506 507 if (index.length) 508 Delete(index); 509 } 510 } 511