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.graphics.materials.generic; 59 import dagon.logics.entity; 60 61 /* 62 * A simple asset package format based on Box container (https://github.com/gecko0307/box). 63 * It is an archive that stores entities, meshes, materials and textures. 64 */ 65 66 class PackageAssetOwner: Owner 67 { 68 this(Owner o) 69 { 70 super(o); 71 } 72 } 73 74 class PackageAsset: Asset 75 { 76 Dict!(OBJAsset, string) meshes; 77 Dict!(EntityAsset, string) entities; 78 Dict!(TextureAsset, string) textures; 79 Dict!(MaterialAsset, string) materials; 80 81 string filename; 82 string index; 83 BoxFileSystem boxfs; 84 AssetManager assetManager; 85 Scene scene; 86 Entity rootEntity; 87 PackageAssetOwner assetOwner; 88 89 this(Scene scene, Owner o) 90 { 91 super(o); 92 this.scene = scene; 93 94 rootEntity = New!Entity(scene.eventManager, this); 95 } 96 97 ~this() 98 { 99 release(); 100 } 101 102 override bool loadThreadSafePart(string filename, InputStream istrm, ReadOnlyFileSystem fs, AssetManager mngr) 103 { 104 this.filename = filename; 105 meshes = New!(Dict!(OBJAsset, string))(); 106 entities = New!(Dict!(EntityAsset, string))(); 107 textures = New!(Dict!(TextureAsset, string))(); 108 materials = New!(Dict!(MaterialAsset, string))(); 109 boxfs = New!BoxFileSystem(fs, filename); 110 111 if (fileExists("INDEX")) 112 { 113 auto fstrm = boxfs.openForInput("INDEX"); 114 index = readText(fstrm); 115 Delete(fstrm); 116 } 117 118 assetManager = mngr; 119 120 assetOwner = New!PackageAssetOwner(null); 121 122 return true; 123 } 124 125 override bool loadThreadUnsafePart() 126 { 127 return true; 128 } 129 130 bool loadAsset(Asset asset, string filename) 131 { 132 if (!fileExists(filename)) 133 { 134 writefln("Error: cannot find file \"%s\" in package", filename); 135 return false; 136 } 137 138 auto fstrm = boxfs.openForInput(filename); 139 bool res = asset.loadThreadSafePart(filename, fstrm, boxfs, assetManager); 140 asset.threadSafePartLoaded = res; 141 Delete(fstrm); 142 143 if (!res) 144 { 145 writefln("Error: failed to load asset \"%s\" from package", filename); 146 return false; 147 } 148 else 149 { 150 res = asset.loadThreadUnsafePart(); 151 asset.threadUnsafePartLoaded = res; 152 if (!res) 153 { 154 writefln("Error: failed to load asset \"%s\" from package", filename); 155 return false; 156 } 157 else 158 { 159 return true; 160 } 161 } 162 } 163 164 Mesh mesh(string filename) 165 { 166 if (!(filename in meshes)) 167 { 168 OBJAsset objAsset = New!OBJAsset(assetOwner); 169 if (loadAsset(objAsset, filename)) 170 { 171 meshes[filename] = objAsset; 172 return objAsset.mesh; 173 } 174 else 175 { 176 return null; 177 } 178 } 179 else 180 { 181 return meshes[filename].mesh; 182 } 183 } 184 185 Entity entity(string filename) 186 { 187 if (!(filename in entities)) 188 { 189 EntityAsset entityAsset = New!EntityAsset(assetOwner); 190 191 if (loadAsset(entityAsset, filename)) 192 { 193 entities[filename] = entityAsset; 194 195 Entity parent = rootEntity; 196 197 if ("parent" in entityAsset.props) 198 { 199 parent = entity(entityAsset.props.parent.toString); 200 } 201 202 entityAsset.entity = New!Entity(parent, assetOwner); 203 entityAsset.entity.visible = true; 204 entityAsset.entity.castShadow = true; 205 entityAsset.entity.useMotionBlur = true; 206 entityAsset.entity.layer = 1; 207 entityAsset.entity.solid = true; 208 entityAsset.entity.material = scene.defaultMaterial3D; 209 210 if ("position" in entityAsset.props) 211 { 212 entityAsset.entity.position = entityAsset.props.position.toVector3f; 213 } 214 215 if ("rotation" in entityAsset.props) 216 { 217 entityAsset.entity.rotation = Quaternionf(entityAsset.props.rotation.toVector4f).normalized; 218 } 219 220 if ("scale" in entityAsset.props) 221 { 222 entityAsset.entity.scaling = entityAsset.props.scale.toVector3f; 223 } 224 225 entityAsset.entity.updateTransformation(); 226 227 if ("visible" in entityAsset.props) 228 { 229 entityAsset.entity.visible = entityAsset.props.visible.toBool; 230 } 231 232 if ("castShadow" in entityAsset.props) 233 { 234 entityAsset.entity.castShadow = entityAsset.props.castShadow.toBool; 235 } 236 237 if ("useMotionBlur" in entityAsset.props) 238 { 239 entityAsset.entity.useMotionBlur = entityAsset.props.useMotionBlur.toBool; 240 } 241 242 if ("solid" in entityAsset.props) 243 { 244 entityAsset.entity.solid = entityAsset.props.solid.toBool; 245 } 246 247 if ("layer" in entityAsset.props) 248 { 249 entityAsset.entity.layer = entityAsset.props.layer.toInt; 250 } 251 252 if ("mesh" in entityAsset.props) 253 { 254 entityAsset.entity.drawable = mesh(entityAsset.props.mesh.toString); 255 } 256 257 if ("material" in entityAsset.props) 258 { 259 entityAsset.entity.material = material(entityAsset.props.material.toString); 260 } 261 262 if (entityAsset.entity.parent) 263 scene.sortEntities(entityAsset.entity.parent.children); 264 265 return entityAsset.entity; 266 } 267 else 268 { 269 return null; 270 } 271 } 272 else 273 { 274 return entities[filename].entity; 275 } 276 } 277 278 Texture texture(string filename) 279 { 280 if (!(filename in textures)) 281 { 282 TextureAsset texAsset = New!TextureAsset(assetManager.imageFactory, assetManager.hdrImageFactory, assetOwner); 283 if (loadAsset(texAsset, filename)) 284 { 285 textures[filename] = texAsset; 286 return texAsset.texture; 287 } 288 else 289 { 290 return null; 291 } 292 } 293 else 294 { 295 return textures[filename].texture; 296 } 297 } 298 299 Material material(string filename) 300 { 301 if (!(filename in materials)) 302 { 303 MaterialAsset matAsset = New!MaterialAsset(assetOwner); 304 if (loadAsset(matAsset, filename)) 305 { 306 materials[filename] = matAsset; 307 matAsset.material = createMaterial(); 308 309 // diffuse 310 if ("diffuse" in matAsset.props) 311 { 312 if (matAsset.props.diffuse.type == DPropType.String) 313 { 314 matAsset.material.diffuse = texture(matAsset.props.diffuse.toString); 315 } 316 else 317 { 318 Vector3f diffCol = matAsset.props.diffuse.toVector3f; 319 matAsset.material.diffuse = Color4f(diffCol.r, diffCol.g, diffCol.b, 1.0f); 320 } 321 } 322 323 // emission 324 if ("emission" in matAsset.props) 325 { 326 if (matAsset.props.emission.type == DPropType.String) 327 { 328 matAsset.material.emission = texture(matAsset.props.emission.toString); 329 } 330 else 331 { 332 Vector3f emissionCol = matAsset.props.emission.toVector3f; 333 matAsset.material.emission = Color4f(emissionCol.r, emissionCol.g, emissionCol.b, 1.0f); 334 } 335 } 336 337 // energy 338 if ("energy" in matAsset.props) 339 { 340 matAsset.material.energy = matAsset.props.energy.toFloat; 341 } 342 343 // normal 344 if ("normal" in matAsset.props) 345 { 346 if (matAsset.props.normal.type == DPropType.String) 347 { 348 matAsset.material.normal = texture(matAsset.props.normal.toString); 349 } 350 } 351 352 // height 353 if ("height" in matAsset.props) 354 { 355 if (matAsset.props.height.type == DPropType.String) 356 { 357 matAsset.material.height = texture(matAsset.props.height.toString); 358 } 359 } 360 361 // roughness 362 if ("roughness" in matAsset.props) 363 { 364 if (matAsset.props.roughness.type == DPropType.String) 365 { 366 matAsset.material.roughness = texture(matAsset.props.roughness.toString); 367 } 368 else 369 { 370 matAsset.material.roughness = matAsset.props.roughness.toFloat; 371 } 372 } 373 374 // metallic 375 if ("metallic" in matAsset.props) 376 { 377 if (matAsset.props.metallic.type == DPropType.String) 378 { 379 matAsset.material.metallic = texture(matAsset.props.metallic.toString); 380 } 381 else 382 { 383 matAsset.material.metallic = matAsset.props.metallic.toFloat; 384 } 385 } 386 387 // parallax 388 if ("parallax" in matAsset.props) 389 { 390 matAsset.material.parallax = matAsset.props.parallax.toInt; 391 } 392 393 // parallaxScale 394 if ("parallaxScale" in matAsset.props) 395 { 396 matAsset.material.parallaxScale = matAsset.props.parallaxScale.toFloat; 397 } 398 399 // parallaxBias 400 if ("parallaxBias" in matAsset.props) 401 { 402 matAsset.material.parallaxBias = matAsset.props.parallaxBias.toFloat; 403 } 404 405 // shadeless 406 if ("shadeless" in matAsset.props) 407 { 408 matAsset.material.shadeless = matAsset.props.shadeless.toBool; 409 } 410 411 // culling 412 if ("culling" in matAsset.props) 413 { 414 matAsset.material.culling = matAsset.props.culling.toBool; 415 } 416 417 // colorWrite 418 if ("colorWrite" in matAsset.props) 419 { 420 matAsset.material.colorWrite = matAsset.props.colorWrite.toBool; 421 } 422 423 // depthWrite 424 if ("depthWrite" in matAsset.props) 425 { 426 matAsset.material.depthWrite = matAsset.props.depthWrite.toBool; 427 } 428 429 // useShadows 430 if ("useShadows" in matAsset.props) 431 { 432 matAsset.material.shadowsEnabled = matAsset.props.useShadows.toBool; 433 } 434 435 // useFog 436 if ("useFog" in matAsset.props) 437 { 438 matAsset.material.fogEnabled = matAsset.props.useFog.toBool; 439 } 440 441 // shadowFilter 442 if ("shadowFilter" in matAsset.props) 443 { 444 matAsset.material.shadowFilter = matAsset.props.shadowFilter.toInt; 445 } 446 447 // blendingMode 448 if ("blendingMode" in matAsset.props) 449 { 450 matAsset.material.blending = matAsset.props.blendingMode.toInt; 451 } 452 453 // transparency 454 if ("transparency" in matAsset.props) 455 { 456 matAsset.material.transparency = matAsset.props.transparency.toFloat; 457 } 458 459 return matAsset.material; 460 } 461 else 462 { 463 return null; 464 } 465 } 466 else 467 { 468 return materials[filename].material; 469 } 470 } 471 472 Entity entity() 473 { 474 if (index.length) 475 foreach(path; lineSplitter(index)) 476 { 477 Entity e = entity(path); 478 } 479 480 return rootEntity; 481 } 482 483 GenericMaterial createMaterial() 484 { 485 GenericMaterial m = New!GenericMaterial(scene.defaultMaterialBackend, 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 } 512