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 }