minerender 2.1.0-beta.5 → 2.1.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/browser/esm/Materials.mjs +3 -2
  2. package/dist/browser/esm/Materials.mjs.map +1 -1
  3. package/dist/browser/esm/assets/Fonts.mjs +31 -4
  4. package/dist/browser/esm/assets/Fonts.mjs.map +1 -1
  5. package/dist/browser/esm/assets/_fonts/Unihex.mjs +166 -0
  6. package/dist/browser/esm/assets/_fonts/Unihex.mjs.map +1 -0
  7. package/dist/browser/esm/assets/source/HostedAssetSource.mjs +12 -1
  8. package/dist/browser/esm/assets/source/HostedAssetSource.mjs.map +1 -1
  9. package/dist/browser/esm/assets/source/parser/AssetParsers.mjs +1 -0
  10. package/dist/browser/esm/assets/source/parser/AssetParsers.mjs.map +1 -1
  11. package/dist/browser/esm/cache/Caching.mjs +2 -0
  12. package/dist/browser/esm/cache/Caching.mjs.map +1 -1
  13. package/dist/browser/esm/entity/scene/EntityAnimationPlayer.mjs +62 -11
  14. package/dist/browser/esm/entity/scene/EntityAnimationPlayer.mjs.map +1 -1
  15. package/dist/browser/esm/entity/scene/EntityObject.mjs +53 -20
  16. package/dist/browser/esm/entity/scene/EntityObject.mjs.map +1 -1
  17. package/dist/browser/esm/env/browser/section.worker.mjs +5 -3
  18. package/dist/browser/esm/env/browser/section.worker.mjs.map +1 -1
  19. package/dist/browser/esm/gui/GuiText.mjs +4 -3
  20. package/dist/browser/esm/gui/GuiText.mjs.map +1 -1
  21. package/dist/browser/esm/index.browser.d.mts +52 -10
  22. package/dist/browser/esm/model/scene/ModelObject.mjs +4 -0
  23. package/dist/browser/esm/model/scene/ModelObject.mjs.map +1 -1
  24. package/dist/browser/esm/util/model.mjs +7 -7
  25. package/dist/browser/esm/util/model.mjs.map +1 -1
  26. package/dist/browser/esm/world/SectionGeometry.mjs +5 -3
  27. package/dist/browser/esm/world/SectionGeometry.mjs.map +1 -1
  28. package/dist/browser/esm/world/SectionMesh.mjs +4 -0
  29. package/dist/browser/esm/world/SectionMesh.mjs.map +1 -1
  30. package/dist/browser/esm/world/SectionModels.mjs +2 -2
  31. package/dist/browser/esm/world/SectionModels.mjs.map +1 -1
  32. package/dist/browser/index.d.ts +52 -10
  33. package/dist/browser/index.js +382 -73
  34. package/dist/browser/index.js.map +1 -1
  35. package/dist/bundle.js +425 -73
  36. package/dist/bundle.js.map +1 -1
  37. package/dist/node/index.d.mts +52 -10
  38. package/dist/node/index.d.ts +52 -10
  39. package/dist/node/index.js +387 -73
  40. package/dist/node/index.js.map +1 -1
  41. package/dist/node/index.mjs +386 -73
  42. package/dist/node/index.mjs.map +1 -1
  43. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/cache/Caching.ts"],"sourcesContent":["import { AsyncLoadingCache, Caches, Loader, LoadingCache, SimpleCache } from \"@inventivetalent/loading-cache\";\nimport { BoxGeometryKey, CacheKey, serializeBoxGeometryKey, serializeImageKey, TextureKey } from \"./CacheKey\";\nimport { Time } from \"@inventivetalent/time\";\nimport { ImageInfo, ImageLoader } from \"../image/ImageLoader\";\nimport { BoxGeometry, Material, Mesh, PixelFormat, RGBAFormat, Texture } from \"three\";\nimport { Materials } from \"../Materials\";\nimport { CompatImage } from \"../canvas/CanvasCompat\";\nimport { Model, TextureAsset } from \"../model/Model\";\nimport { WrappedImage } from \"../WrappedImage\";\nimport { TextureAtlas } from \"../texture/TextureAtlas\";\nimport { ExtractableImageData } from \"../ExtractableImageData\";\nimport { MinecraftTextureMeta } from \"../MinecraftTextureMeta\";\nimport type { EntityModelFile } from \"../entity/EntityModel\";\nimport type { EntityAnimationFile } from \"../entity/EntityAnimation\";\nimport type { BlockEntityIndex } from \"../assets/BlockEntities\";\nimport type { ListAsset } from \"../ListAsset\";\nimport { BlockState } from \"../model/block/BlockState\";\nimport type { AssetKey } from \"../assets/AssetKey\";\nimport type { BitmapFont } from \"../assets/Fonts\";\nimport type { BannerPattern } from \"../assets/BannerPatterns\";\nimport type { Biome } from \"../assets/Biomes\";\n\n/** Shared in-memory caches for loaded assets and render resources. Persistent stores are managed separately. */\nexport class Caching {\n\n private static _generation = 0;\n\n /** Changes when shared assets are cleared, so derived caches can discard their old values. */\n public static get generation(): number { return this._generation; }\n\n static readonly rawImageCache: AsyncLoadingCache<CacheKey, ImageInfo> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(5))\n .buildAsync<CacheKey, ImageInfo>();\n static readonly imageDataCache: AsyncLoadingCache<CacheKey, ImageData> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(10))\n .buildAsync<CacheKey, ImageData>();\n static readonly canvasImageDataCache: AsyncLoadingCache<CacheKey, ExtractableImageData> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(10))\n .buildAsync<CacheKey, ExtractableImageData>();\n static readonly wrappedImageCache: AsyncLoadingCache<CacheKey, WrappedImage> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(10))\n .buildAsync<CacheKey, WrappedImage>();\n\n static readonly boxGeometryCache: SimpleCache<CacheKey, BoxGeometry> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(20))\n .build<CacheKey, BoxGeometry>();\n\n static readonly textureCache: SimpleCache<CacheKey, Texture> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .build<CacheKey, Texture>();\n\n static readonly materialCache: SimpleCache<CacheKey, Material> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .build<CacheKey, Material>();\n\n static readonly boxMeshCache: SimpleCache<CacheKey, Mesh> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .build<CacheKey, Mesh>();\n\n static readonly textureAssetCache: AsyncLoadingCache<CacheKey, TextureAsset> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, TextureAsset>();\n static readonly textureMetaCache: AsyncLoadingCache<CacheKey, MinecraftTextureMeta> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, MinecraftTextureMeta>();\n\n static readonly rawModelCache: AsyncLoadingCache<CacheKey, Model> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, Model>();\n static readonly mergedModelCache: AsyncLoadingCache<CacheKey, Model> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, Model>();\n static readonly modelTextureAtlasCache: AsyncLoadingCache<CacheKey, TextureAtlas> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, TextureAtlas>();\n\n static readonly blockStateCache: AsyncLoadingCache<CacheKey, BlockState> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, BlockState>();\n\n static readonly listAssetCache: AsyncLoadingCache<CacheKey, ListAsset> = Caching.createAssetCache<ListAsset>();\n\n static readonly blockTintCache: AsyncLoadingCache<CacheKey, number> = Caching.createAssetCache<number>();\n\n static readonly entityModelCache: AsyncLoadingCache<CacheKey, EntityModelFile> = Caching.createAssetCache<EntityModelFile>();\n\n static readonly entityTextureCache: AsyncLoadingCache<CacheKey, AssetKey> = Caching.createAssetCache<AssetKey>();\n\n static readonly entityAnimationCache: AsyncLoadingCache<CacheKey, EntityAnimationFile> = Caching.createAssetCache<EntityAnimationFile>();\n static readonly blockEntityIndexCache: AsyncLoadingCache<CacheKey, BlockEntityIndex> = Caching.createAssetCache<BlockEntityIndex>();\n static readonly fontCache: AsyncLoadingCache<CacheKey, BitmapFont> = Caching.createAssetCache<BitmapFont>();\n static readonly bannerPatternCache: AsyncLoadingCache<CacheKey, BannerPattern> = Caching.createAssetCache<BannerPattern>();\n static readonly mineRenderDataCache: AsyncLoadingCache<CacheKey, unknown> = Caching.createAssetCache<unknown>();\n static readonly usernameUuidCache: AsyncLoadingCache<CacheKey, string> = Caches.builder()\n .expireAfterWrite(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, string>();\n static readonly biomeCache: AsyncLoadingCache<CacheKey, Biome> = Caching.createAssetCache<Biome>();\n\n private static createAssetCache<T>(): AsyncLoadingCache<CacheKey, T> {\n return Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, T>();\n }\n\n\n /** Every cache above, so clear()/end() can never fall out of sync with the field list again. */\n private static get all(): { invalidateAll(): void; end(): void }[] {\n return [\n this.rawImageCache,\n this.imageDataCache,\n this.canvasImageDataCache,\n this.wrappedImageCache,\n this.boxGeometryCache,\n this.textureCache,\n this.materialCache,\n this.boxMeshCache,\n this.textureAssetCache,\n this.textureMetaCache,\n this.rawModelCache,\n this.mergedModelCache,\n this.modelTextureAtlasCache,\n this.blockStateCache,\n this.listAssetCache,\n this.blockTintCache,\n this.entityModelCache,\n this.entityTextureCache,\n this.entityAnimationCache,\n this.blockEntityIndexCache,\n this.fontCache,\n this.bannerPatternCache,\n this.mineRenderDataCache,\n this.usernameUuidCache,\n this.biomeCache\n ];\n }\n\n /** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */\n public static clear() {\n this._generation++;\n for (const cache of this.all) {\n cache.invalidateAll();\n }\n }\n\n /**\n * Clears every cache and stops its expiry timer.\n *\n * Expiry timers are unref'd in Node, so idle processes can exit without this cleanup.\n * Use {@link shutdown} to also stop requests and the shared Ticker.\n */\n public static end() {\n this._generation++;\n for (const cache of this.all) {\n cache.invalidateAll();\n cache.end();\n }\n }\n\n public static get cacheSizes() {\n return {\n //TODO\n }\n }\n\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/cache/Caching.ts"],"sourcesContent":["import { AsyncLoadingCache, Caches, Loader, LoadingCache, SimpleCache } from \"@inventivetalent/loading-cache\";\nimport { BoxGeometryKey, CacheKey, serializeBoxGeometryKey, serializeImageKey, TextureKey } from \"./CacheKey\";\nimport { Time } from \"@inventivetalent/time\";\nimport { ImageInfo, ImageLoader } from \"../image/ImageLoader\";\nimport { BoxGeometry, Material, Mesh, PixelFormat, RGBAFormat, Texture } from \"three\";\nimport { Materials } from \"../Materials\";\nimport { CompatImage } from \"../canvas/CanvasCompat\";\nimport { Model, TextureAsset } from \"../model/Model\";\nimport { WrappedImage } from \"../WrappedImage\";\nimport { TextureAtlas } from \"../texture/TextureAtlas\";\nimport { ExtractableImageData } from \"../ExtractableImageData\";\nimport { MinecraftTextureMeta } from \"../MinecraftTextureMeta\";\nimport type { EntityModelFile } from \"../entity/EntityModel\";\nimport type { EntityAnimationFile } from \"../entity/EntityAnimation\";\nimport type { BlockEntityIndex } from \"../assets/BlockEntities\";\nimport type { ListAsset } from \"../ListAsset\";\nimport { BlockState } from \"../model/block/BlockState\";\nimport type { AssetKey } from \"../assets/AssetKey\";\nimport type { UnihexFile } from \"../assets/_fonts/Unihex\";\nimport type { BitmapFont } from \"../assets/Fonts\";\nimport type { BannerPattern } from \"../assets/BannerPatterns\";\nimport type { Biome } from \"../assets/Biomes\";\n\n/** Shared in-memory caches for loaded assets and render resources. Persistent stores are managed separately. */\nexport class Caching {\n\n private static _generation = 0;\n\n /** Changes when shared assets are cleared, so derived caches can discard their old values. */\n public static get generation(): number { return this._generation; }\n\n static readonly rawImageCache: AsyncLoadingCache<CacheKey, ImageInfo> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(5))\n .buildAsync<CacheKey, ImageInfo>();\n static readonly imageDataCache: AsyncLoadingCache<CacheKey, ImageData> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(10))\n .buildAsync<CacheKey, ImageData>();\n static readonly canvasImageDataCache: AsyncLoadingCache<CacheKey, ExtractableImageData> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(10))\n .buildAsync<CacheKey, ExtractableImageData>();\n static readonly wrappedImageCache: AsyncLoadingCache<CacheKey, WrappedImage> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(10))\n .buildAsync<CacheKey, WrappedImage>();\n\n static readonly boxGeometryCache: SimpleCache<CacheKey, BoxGeometry> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(20))\n .build<CacheKey, BoxGeometry>();\n\n static readonly textureCache: SimpleCache<CacheKey, Texture> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .build<CacheKey, Texture>();\n\n static readonly materialCache: SimpleCache<CacheKey, Material> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .build<CacheKey, Material>();\n\n static readonly boxMeshCache: SimpleCache<CacheKey, Mesh> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .build<CacheKey, Mesh>();\n\n static readonly textureAssetCache: AsyncLoadingCache<CacheKey, TextureAsset> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, TextureAsset>();\n static readonly textureMetaCache: AsyncLoadingCache<CacheKey, MinecraftTextureMeta> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, MinecraftTextureMeta>();\n\n static readonly rawModelCache: AsyncLoadingCache<CacheKey, Model> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, Model>();\n static readonly mergedModelCache: AsyncLoadingCache<CacheKey, Model> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, Model>();\n static readonly modelTextureAtlasCache: AsyncLoadingCache<CacheKey, TextureAtlas> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, TextureAtlas>();\n\n static readonly blockStateCache: AsyncLoadingCache<CacheKey, BlockState> = Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, BlockState>();\n\n static readonly listAssetCache: AsyncLoadingCache<CacheKey, ListAsset> = Caching.createAssetCache<ListAsset>();\n\n static readonly blockTintCache: AsyncLoadingCache<CacheKey, number> = Caching.createAssetCache<number>();\n\n static readonly entityModelCache: AsyncLoadingCache<CacheKey, EntityModelFile> = Caching.createAssetCache<EntityModelFile>();\n\n static readonly entityTextureCache: AsyncLoadingCache<CacheKey, AssetKey> = Caching.createAssetCache<AssetKey>();\n\n static readonly entityAnimationCache: AsyncLoadingCache<CacheKey, EntityAnimationFile> = Caching.createAssetCache<EntityAnimationFile>();\n static readonly blockEntityIndexCache: AsyncLoadingCache<CacheKey, BlockEntityIndex> = Caching.createAssetCache<BlockEntityIndex>();\n static readonly unihexCache: AsyncLoadingCache<CacheKey, UnihexFile> = Caching.createAssetCache<UnihexFile>();\n static readonly fontCache: AsyncLoadingCache<CacheKey, BitmapFont> = Caching.createAssetCache<BitmapFont>();\n static readonly bannerPatternCache: AsyncLoadingCache<CacheKey, BannerPattern> = Caching.createAssetCache<BannerPattern>();\n static readonly mineRenderDataCache: AsyncLoadingCache<CacheKey, unknown> = Caching.createAssetCache<unknown>();\n static readonly usernameUuidCache: AsyncLoadingCache<CacheKey, string> = Caches.builder()\n .expireAfterWrite(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, string>();\n static readonly biomeCache: AsyncLoadingCache<CacheKey, Biome> = Caching.createAssetCache<Biome>();\n\n private static createAssetCache<T>(): AsyncLoadingCache<CacheKey, T> {\n return Caches.builder()\n .expireAfterWrite(Time.minutes(10))\n .expireAfterAccess(Time.minutes(5))\n .expirationInterval(Time.seconds(30))\n .buildAsync<CacheKey, T>();\n }\n\n\n /** Every cache above, so clear()/end() can never fall out of sync with the field list again. */\n private static get all(): { invalidateAll(): void; end(): void }[] {\n return [\n this.rawImageCache,\n this.imageDataCache,\n this.canvasImageDataCache,\n this.wrappedImageCache,\n this.boxGeometryCache,\n this.textureCache,\n this.materialCache,\n this.boxMeshCache,\n this.textureAssetCache,\n this.textureMetaCache,\n this.rawModelCache,\n this.mergedModelCache,\n this.modelTextureAtlasCache,\n this.blockStateCache,\n this.listAssetCache,\n this.blockTintCache,\n this.entityModelCache,\n this.entityTextureCache,\n this.entityAnimationCache,\n this.blockEntityIndexCache,\n this.fontCache,\n this.unihexCache,\n this.bannerPatternCache,\n this.mineRenderDataCache,\n this.usernameUuidCache,\n this.biomeCache\n ];\n }\n\n /** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */\n public static clear() {\n this._generation++;\n for (const cache of this.all) {\n cache.invalidateAll();\n }\n }\n\n /**\n * Clears every cache and stops its expiry timer.\n *\n * Expiry timers are unref'd in Node, so idle processes can exit without this cleanup.\n * Use {@link shutdown} to also stop requests and the shared Ticker.\n */\n public static end() {\n this._generation++;\n for (const cache of this.all) {\n cache.invalidateAll();\n cache.end();\n }\n }\n\n public static get cacheSizes() {\n return {\n //TODO\n }\n }\n\n}\n"],"mappings":";AAAA,SAA4B,cAAiD;AAE7E,SAAS,YAAY;AAsBd,IAAM,WAAN,MAAM,SAAQ;AAAA;AAAA,EAKjB,WAAkB,aAAqB;AAAE,WAAO,KAAK;AAAA,EAAa;AAAA,EAoGlE,OAAe,mBAAsD;AACjE,WAAO,OAAO,QAAQ,EACjB,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,WAAwB;AAAA,EACjC;AAAA;AAAA,EAIA,WAAmB,MAAgD;AAC/D,WAAO;AAAA,MACH,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACT;AAAA,EACJ;AAAA;AAAA,EAGA,OAAc,QAAQ;AAClB,SAAK;AACL,eAAW,SAAS,KAAK,KAAK;AAC1B,YAAM,cAAc;AAAA,IACxB;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAc,MAAM;AAChB,SAAK;AACL,eAAW,SAAS,KAAK,KAAK;AAC1B,YAAM,cAAc;AACpB,YAAM,IAAI;AAAA,IACd;AAAA,EACJ;AAAA,EAEA,WAAkB,aAAa;AAC3B,WAAO;AAAA;AAAA,IAEP;AAAA,EACJ;AAEJ;AA9Ka,SAEM,cAAc;AAFpB,SAOO,gBAAwD,OAAO,QAAQ,EAClF,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,CAAC,CAAC,EAClC,WAAgC;AAX5B,SAYO,iBAAyD,OAAO,QAAQ,EACnF,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,WAAgC;AAhB5B,SAiBO,uBAA0E,OAAO,QAAQ,EACpG,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,WAA2C;AArBvC,SAsBO,oBAA+D,OAAO,QAAQ,EACzF,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,WAAmC;AA1B/B,SA4BO,mBAAuD,OAAO,QAAQ,EACjF,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,MAA6B;AAhCzB,SAkCO,eAA+C,OAAO,QAAQ,EACzE,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,MAAyB;AAtCrB,SAwCO,gBAAiD,OAAO,QAAQ,EAC3E,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,MAA0B;AA5CtB,SA8CO,eAA4C,OAAO,QAAQ,EACtE,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,MAAsB;AAlDlB,SAoDO,oBAA+D,OAAO,QAAQ,EACzF,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,WAAmC;AAxD/B,SAyDO,mBAAsE,OAAO,QAAQ,EAChG,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,WAA2C;AA7DvC,SA+DO,gBAAoD,OAAO,QAAQ,EAC9E,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,WAA4B;AAnExB,SAoEO,mBAAuD,OAAO,QAAQ,EACjF,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,WAA4B;AAxExB,SAyEO,yBAAoE,OAAO,QAAQ,EAC9F,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,WAAmC;AA7E/B,SA+EO,kBAA2D,OAAO,QAAQ,EACrF,iBAAiB,KAAK,QAAQ,EAAE,CAAC,EACjC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,EACjC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,WAAiC;AAnF7B,SAqFO,iBAAyD,SAAQ,iBAA4B;AArFpG,SAuFO,iBAAsD,SAAQ,iBAAyB;AAvF9F,SAyFO,mBAAiE,SAAQ,iBAAkC;AAzFlH,SA2FO,qBAA4D,SAAQ,iBAA2B;AA3FtG,SA6FO,uBAAyE,SAAQ,iBAAsC;AA7F9H,SA8FO,wBAAuE,SAAQ,iBAAmC;AA9FzH,SA+FO,cAAuD,SAAQ,iBAA6B;AA/FnG,SAgGO,YAAqD,SAAQ,iBAA6B;AAhGjG,SAiGO,qBAAiE,SAAQ,iBAAgC;AAjGhH,SAkGO,sBAA4D,SAAQ,iBAA0B;AAlGrG,SAmGO,oBAAyD,OAAO,QAAQ,EACnF,iBAAiB,KAAK,QAAQ,CAAC,CAAC,EAChC,mBAAmB,KAAK,QAAQ,EAAE,CAAC,EACnC,WAA6B;AAtGzB,SAuGO,aAAiD,SAAQ,iBAAwB;AAvG9F,IAAM,UAAN;","names":[]}
@@ -1,16 +1,24 @@
1
1
  // src/entity/scene/EntityAnimationPlayer.ts
2
2
  import { Euler, Vector3 } from "three";
3
- import { sampleEntityAnimation } from "../EntityAnimation.mjs";
3
+ import { ENTITY_ANIMATION_CHANNELS, sampleEntityAnimation } from "../EntityAnimation.mjs";
4
4
  var ZERO = [0, 0, 0];
5
5
  var EntityAnimationPlayer = class {
6
6
  constructor() {
7
7
  this.parts = [];
8
+ this._animations = [];
9
+ this._weights = [];
10
+ this.loops = [];
8
11
  this._time = 0;
9
- this.loop = false;
10
12
  this.speed = 1;
11
13
  }
12
14
  get animation() {
13
- return this._animation;
15
+ return this._animations[0];
16
+ }
17
+ get animations() {
18
+ return this._animations.slice();
19
+ }
20
+ get weights() {
21
+ return this._weights.slice();
14
22
  }
15
23
  get time() {
16
24
  return this._time;
@@ -20,10 +28,20 @@ var EntityAnimationPlayer = class {
20
28
  * without them, the current pose is used. Stopping restores the pose from before playback.
21
29
  */
22
30
  play(layers, animation, options = {}, poses) {
31
+ this.playAnimations(layers, [animation], options, poses);
32
+ }
33
+ /**
34
+ * Starts clips on matching parts with a shared clock. Weighted offsets add to the supplied baked poses
35
+ * or, without poses, to each animated part's pose when playback starts.
36
+ */
37
+ playAnimations(layers, animations, options = {}, poses) {
38
+ const weights = validateAnimationWeights(options.weights ?? animations.map(() => 1), animations.length);
23
39
  this.stop();
24
- this._animation = animation;
40
+ this._animations = animations.slice();
41
+ this._weights = weights;
42
+ if (!animations.length) return;
25
43
  this._time = options.time ?? 0;
26
- this.loop = options.loop ?? animation.loop;
44
+ this.loops = animations.map((animation) => options.loop ?? animation.loop);
27
45
  this.speed = options.speed ?? 1;
28
46
  for (const layer of layers) {
29
47
  const objects = [];
@@ -32,7 +50,8 @@ var EntityAnimationPlayer = class {
32
50
  if (poses.has(object)) objects.push(object);
33
51
  });
34
52
  } else {
35
- for (const bone of Object.keys(animation.bones)) {
53
+ const bones = new Set(animations.flatMap((animation) => Object.keys(animation.bones)));
54
+ for (const bone of bones) {
36
55
  const object = layer.getObjectByName(`group:${bone}`);
37
56
  if (object) objects.push(object);
38
57
  }
@@ -56,7 +75,7 @@ var EntityAnimationPlayer = class {
56
75
  }
57
76
  /** Restores the poses from before playback. Returns whether an animation was active. */
58
77
  stop() {
59
- if (!this._animation) return false;
78
+ if (!this._animations.length) return false;
60
79
  for (const part of this.parts) {
61
80
  part.object.position.copy(part.position);
62
81
  part.object.rotation.copy(part.rotation);
@@ -68,12 +87,14 @@ var EntityAnimationPlayer = class {
68
87
  /** Forgets the animation without touching the parts, for parts that no longer exist. */
69
88
  clear() {
70
89
  this.parts = [];
71
- this._animation = void 0;
90
+ this._animations = [];
91
+ this._weights = [];
92
+ this.loops = [];
72
93
  this._time = 0;
73
94
  }
74
95
  /** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
75
96
  setTime(seconds) {
76
- if (!this._animation) return false;
97
+ if (!this._animations.length) return false;
77
98
  this._time = seconds;
78
99
  this.apply();
79
100
  return true;
@@ -82,8 +103,30 @@ var EntityAnimationPlayer = class {
82
103
  advance(deltaSeconds) {
83
104
  return this.setTime(this._time + deltaSeconds * this.speed);
84
105
  }
106
+ /** Changes weights in clip order and reapplies the current time without restarting playback. */
107
+ setWeights(weights) {
108
+ if (!this._animations.length) return false;
109
+ const validated = validateAnimationWeights(weights, this._animations.length);
110
+ this._weights = validated;
111
+ this.apply();
112
+ return true;
113
+ }
85
114
  apply() {
86
- const pose = sampleEntityAnimation(this._animation, this._time, this.loop);
115
+ const pose = /* @__PURE__ */ Object.create(null);
116
+ for (let i = 0; i < this._animations.length; i++) {
117
+ const weight = this._weights[i];
118
+ if (weight === 0) continue;
119
+ const sampled = sampleEntityAnimation(this._animations[i], this._time, this.loops[i]);
120
+ for (const [bone, channels] of Object.entries(sampled)) {
121
+ const blended = pose[bone] ?? (pose[bone] = {});
122
+ for (const channel of ENTITY_ANIMATION_CHANNELS) {
123
+ const value = channels[channel];
124
+ if (!value) continue;
125
+ const target = blended[channel] ?? (blended[channel] = [0, 0, 0]);
126
+ for (let axis = 0; axis < 3; axis++) target[axis] += value[axis] * weight;
127
+ }
128
+ }
129
+ }
87
130
  for (const part of this.parts) {
88
131
  const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone] ?? {};
89
132
  part.object.position.set(part.basePosition.x + position[0], part.basePosition.y + position[1], part.basePosition.z + position[2]);
@@ -92,7 +135,15 @@ var EntityAnimationPlayer = class {
92
135
  }
93
136
  }
94
137
  };
138
+ function validateAnimationWeights(weights, count) {
139
+ const values = Array.from(weights);
140
+ if (values.length !== count || values.some((weight) => !Number.isFinite(weight) || weight < 0)) {
141
+ throw new Error("Animation weights must match the clip count and contain only finite nonnegative numbers");
142
+ }
143
+ return values;
144
+ }
95
145
  export {
96
- EntityAnimationPlayer
146
+ EntityAnimationPlayer,
147
+ validateAnimationWeights
97
148
  };
98
149
  //# sourceMappingURL=EntityAnimationPlayer.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/entity/scene/EntityAnimationPlayer.ts"],"sourcesContent":["import { Euler, Vector3 } from \"three\";\nimport type { Object3D } from \"three\";\nimport { EntityAnimation, sampleEntityAnimation } from \"../EntityAnimation\";\nimport type { EntityModelPart } from \"../EntityModel\";\n\n/** Playback settings passed to {@link EntityObject.playAnimation}, {@link EntityObject.playAnimations}, or {@link EntityAnimationPlayer.play}. */\nexport interface EntityAnimationOptions {\n /** Overrides the animation's own `loop` flag. */\n loop?: boolean;\n /** Playback rate applied to advanced time; defaults to 1. */\n speed?: number;\n /** Start time in seconds; defaults to 0. */\n time?: number;\n}\n\nconst ZERO: readonly number[] = [0, 0, 0];\n\ninterface AnimatedPart {\n bone: string;\n object: Object3D;\n position: Vector3;\n rotation: Euler;\n scale: Vector3;\n basePosition: Vector3;\n baseRotation: Euler;\n baseScale: Vector3;\n}\n\n/**\n * Poses part groups from a keyframe animation the way vanilla does: every update restores the\n * default pose of each animated part, then adds the sampled offsets to its position, Euler angles and scale.\n * It owns no timer; the owner advances it.\n */\nexport class EntityAnimationPlayer {\n\n private parts: AnimatedPart[] = [];\n private _animation?: EntityAnimation;\n private _time = 0;\n private loop = false;\n private speed = 1;\n\n get animation(): EntityAnimation | undefined {\n return this._animation;\n }\n\n get time(): number {\n return this._time;\n }\n\n /**\n * Starts an animation on matching named parts. Supplied baked poses are the baseline for offsets;\n * without them, the current pose is used. Stopping restores the pose from before playback.\n */\n play(layers: Object3D[], animation: EntityAnimation, options: EntityAnimationOptions = {},\n poses?: ReadonlyMap<Object3D, EntityModelPart[\"pose\"]>): void {\n this.stop();\n this._animation = animation;\n this._time = options.time ?? 0;\n this.loop = options.loop ?? animation.loop;\n this.speed = options.speed ?? 1;\n for (const layer of layers) {\n const objects: Object3D[] = [];\n if (poses) {\n layer.traverse(object => { if (poses.has(object)) objects.push(object); });\n } else {\n for (const bone of Object.keys(animation.bones)) {\n const object = layer.getObjectByName(`group:${bone}`);\n if (object) objects.push(object);\n }\n }\n for (const object of objects) {\n const bone = object.name.slice(\"group:\".length);\n const pose = poses?.get(object);\n this.parts.push({\n bone, object, position: object.position.clone(), rotation: object.rotation.clone(), scale: object.scale.clone(),\n basePosition: pose ? new Vector3(...pose.offset) : object.position.clone(),\n baseRotation: pose ? new Euler(...pose.rotation, \"ZYX\") : object.rotation.clone(),\n baseScale: pose ? new Vector3(...(pose.scale ?? [1, 1, 1])) : object.scale.clone()\n });\n }\n }\n this.apply();\n }\n\n /** Restores the poses from before playback. Returns whether an animation was active. */\n stop(): boolean {\n if (!this._animation) return false;\n for (const part of this.parts) {\n part.object.position.copy(part.position);\n part.object.rotation.copy(part.rotation);\n part.object.scale.copy(part.scale);\n }\n this.clear();\n return true;\n }\n\n /** Forgets the animation without touching the parts, for parts that no longer exist. */\n clear(): void {\n this.parts = [];\n this._animation = undefined;\n this._time = 0;\n }\n\n /** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */\n setTime(seconds: number): boolean {\n if (!this._animation) return false;\n this._time = seconds;\n this.apply();\n return true;\n }\n\n /** Advances playback by elapsed seconds multiplied by the speed passed to {@link play}. */\n advance(deltaSeconds: number): boolean {\n return this.setTime(this._time + deltaSeconds * this.speed);\n }\n\n private apply(): void {\n const pose = sampleEntityAnimation(this._animation!, this._time, this.loop);\n for (const part of this.parts) {\n const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone] ?? {};\n part.object.position.set(part.basePosition.x + position[0], part.basePosition.y + position[1], part.basePosition.z + position[2]);\n // Vanilla adds to the Euler angles themselves, so the part keeps its rotation order.\n part.object.rotation.set(part.baseRotation.x + rotation[0], part.baseRotation.y + rotation[1], part.baseRotation.z + rotation[2], part.baseRotation.order);\n part.object.scale.set(part.baseScale.x + scale[0], part.baseScale.y + scale[1], part.baseScale.z + scale[2]);\n }\n }\n\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../src/entity/scene/EntityAnimationPlayer.ts"],"sourcesContent":["import { Euler, Vector3 } from \"three\";\nimport type { Object3D } from \"three\";\nimport { ENTITY_ANIMATION_CHANNELS, EntityAnimation, sampleEntityAnimation } from \"../EntityAnimation\";\nimport type { EntityAnimationPose } from \"../EntityAnimation\";\nimport type { EntityModelPart } from \"../EntityModel\";\n\n/** Playback settings passed to {@link EntityObject.playAnimation}, {@link EntityObject.playAnimations}, or {@link EntityAnimationPlayer.play}. */\nexport interface EntityAnimationOptions {\n /** Overrides the animation's own `loop` flag. */\n loop?: boolean;\n /** Playback rate applied to advanced time; defaults to 1. */\n speed?: number;\n /** Start time in seconds; defaults to 0. */\n time?: number;\n /** Nonnegative finite weights in clip order; defaults to 1 per clip. Offsets add without normalizing weights. */\n weights?: readonly number[];\n}\n\nconst ZERO: readonly number[] = [0, 0, 0];\n\ninterface AnimatedPart {\n bone: string;\n object: Object3D;\n position: Vector3;\n rotation: Euler;\n scale: Vector3;\n basePosition: Vector3;\n baseRotation: Euler;\n baseScale: Vector3;\n}\n\n/**\n * Poses part groups from keyframe animations: every update restores the default pose of each\n * animated part, then adds weighted sampled offsets to its position, Euler angles and scale.\n * It owns no timer; the owner advances it.\n */\nexport class EntityAnimationPlayer {\n\n private parts: AnimatedPart[] = [];\n private _animations: EntityAnimation[] = [];\n private _weights: number[] = [];\n private loops: boolean[] = [];\n private _time = 0;\n private speed = 1;\n\n get animation(): EntityAnimation | undefined {\n return this._animations[0];\n }\n\n get animations(): readonly EntityAnimation[] {\n return this._animations.slice();\n }\n\n get weights(): readonly number[] {\n return this._weights.slice();\n }\n\n get time(): number {\n return this._time;\n }\n\n /**\n * Starts an animation on matching named parts. Supplied baked poses are the baseline for offsets;\n * without them, the current pose is used. Stopping restores the pose from before playback.\n */\n play(layers: Object3D[], animation: EntityAnimation, options: EntityAnimationOptions = {},\n poses?: ReadonlyMap<Object3D, EntityModelPart[\"pose\"]>): void {\n this.playAnimations(layers, [animation], options, poses);\n }\n\n /**\n * Starts clips on matching parts with a shared clock. Weighted offsets add to the supplied baked poses\n * or, without poses, to each animated part's pose when playback starts.\n */\n playAnimations(layers: Object3D[], animations: readonly EntityAnimation[], options: EntityAnimationOptions = {},\n poses?: ReadonlyMap<Object3D, EntityModelPart[\"pose\"]>): void {\n const weights = validateAnimationWeights(options.weights ?? animations.map(() => 1), animations.length);\n this.stop();\n this._animations = animations.slice();\n this._weights = weights;\n if (!animations.length) return;\n this._time = options.time ?? 0;\n this.loops = animations.map(animation => options.loop ?? animation.loop);\n this.speed = options.speed ?? 1;\n for (const layer of layers) {\n const objects: Object3D[] = [];\n if (poses) {\n layer.traverse(object => { if (poses.has(object)) objects.push(object); });\n } else {\n const bones = new Set(animations.flatMap(animation => Object.keys(animation.bones)));\n for (const bone of bones) {\n const object = layer.getObjectByName(`group:${bone}`);\n if (object) objects.push(object);\n }\n }\n for (const object of objects) {\n const bone = object.name.slice(\"group:\".length);\n const pose = poses?.get(object);\n this.parts.push({\n bone, object, position: object.position.clone(), rotation: object.rotation.clone(), scale: object.scale.clone(),\n basePosition: pose ? new Vector3(...pose.offset) : object.position.clone(),\n baseRotation: pose ? new Euler(...pose.rotation, \"ZYX\") : object.rotation.clone(),\n baseScale: pose ? new Vector3(...(pose.scale ?? [1, 1, 1])) : object.scale.clone()\n });\n }\n }\n this.apply();\n }\n\n /** Restores the poses from before playback. Returns whether an animation was active. */\n stop(): boolean {\n if (!this._animations.length) return false;\n for (const part of this.parts) {\n part.object.position.copy(part.position);\n part.object.rotation.copy(part.rotation);\n part.object.scale.copy(part.scale);\n }\n this.clear();\n return true;\n }\n\n /** Forgets the animation without touching the parts, for parts that no longer exist. */\n clear(): void {\n this.parts = [];\n this._animations = [];\n this._weights = [];\n this.loops = [];\n this._time = 0;\n }\n\n /** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */\n setTime(seconds: number): boolean {\n if (!this._animations.length) return false;\n this._time = seconds;\n this.apply();\n return true;\n }\n\n /** Advances playback by elapsed seconds multiplied by the speed passed to {@link play}. */\n advance(deltaSeconds: number): boolean {\n return this.setTime(this._time + deltaSeconds * this.speed);\n }\n\n /** Changes weights in clip order and reapplies the current time without restarting playback. */\n setWeights(weights: readonly number[]): boolean {\n if (!this._animations.length) return false;\n const validated = validateAnimationWeights(weights, this._animations.length);\n this._weights = validated;\n this.apply();\n return true;\n }\n\n private apply(): void {\n const pose: EntityAnimationPose = Object.create(null);\n for (let i = 0; i < this._animations.length; i++) {\n const weight = this._weights[i];\n if (weight === 0) continue;\n const sampled = sampleEntityAnimation(this._animations[i], this._time, this.loops[i]);\n for (const [bone, channels] of Object.entries(sampled)) {\n const blended = pose[bone] ??= {};\n for (const channel of ENTITY_ANIMATION_CHANNELS) {\n const value = channels[channel];\n if (!value) continue;\n const target = blended[channel] ??= [0, 0, 0];\n for (let axis = 0; axis < 3; axis++) target[axis] += value[axis] * weight;\n }\n }\n }\n for (const part of this.parts) {\n const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone] ?? {};\n part.object.position.set(part.basePosition.x + position[0], part.basePosition.y + position[1], part.basePosition.z + position[2]);\n // Vanilla adds to the Euler angles themselves, so the part keeps its rotation order.\n part.object.rotation.set(part.baseRotation.x + rotation[0], part.baseRotation.y + rotation[1], part.baseRotation.z + rotation[2], part.baseRotation.order);\n part.object.scale.set(part.baseScale.x + scale[0], part.baseScale.y + scale[1], part.baseScale.z + scale[2]);\n }\n }\n\n}\n\n/** @internal */\nexport function validateAnimationWeights(weights: readonly number[], count: number): number[] {\n const values = Array.from(weights);\n if (values.length !== count || values.some(weight => !Number.isFinite(weight) || weight < 0)) {\n throw new Error(\"Animation weights must match the clip count and contain only finite nonnegative numbers\");\n }\n return values;\n}\n"],"mappings":"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","names":[]}
@@ -11,7 +11,7 @@ import { Materials } from "../../Materials.mjs";
11
11
  import { MinecraftCubeTexture } from "../../MinecraftCubeTexture.mjs";
12
12
  import { CUBE_FACES } from "../../CubeFace.mjs";
13
13
  import { isMesh } from "../../util/three.mjs";
14
- import { EntityAnimationPlayer } from "./EntityAnimationPlayer.mjs";
14
+ import { EntityAnimationPlayer, validateAnimationWeights } from "./EntityAnimationPlayer.mjs";
15
15
  var _EntityObject = class _EntityObject extends SceneObject {
16
16
  constructor(entity, options) {
17
17
  super();
@@ -49,7 +49,7 @@ var _EntityObject = class _EntityObject extends SceneObject {
49
49
  for (const geometry of this.geometries) geometry.dispose();
50
50
  this.geometries.clear();
51
51
  for (const dispose of this.disposeWireframes.splice(0)) dispose();
52
- for (const player of this.animationPlayers.splice(0)) player.clear();
52
+ for (const { player } of this.animationPlayers.splice(0)) player.clear();
53
53
  this.partPoses.clear();
54
54
  super.disposeAndRemoveAllChildren();
55
55
  }
@@ -84,15 +84,31 @@ var _EntityObject = class _EntityObject extends SceneObject {
84
84
  //<editor-fold desc="ANIMATION">
85
85
  /** The first active clip, until playback is stopped or the object is disposed. */
86
86
  get animation() {
87
- return this.animationPlayers[0]?.animation;
87
+ return this.animationPlayers[0]?.player.animation;
88
88
  }
89
- /** Clips playing together, each targeting a separate geometry layer. */
89
+ /** Clips playing together, in the order passed to {@link playAnimations}. */
90
90
  get activeAnimations() {
91
- return this.animationPlayers.map((player) => player.animation);
91
+ const animations = [];
92
+ for (const { player, indices } of this.animationPlayers) {
93
+ player.animations.forEach((animation, i) => {
94
+ animations[indices[i]] = animation;
95
+ });
96
+ }
97
+ return animations;
98
+ }
99
+ /** Weights of the active clips, in the same order as {@link activeAnimations}. */
100
+ get animationWeights() {
101
+ const weights = [];
102
+ for (const { player, indices } of this.animationPlayers) {
103
+ player.weights.forEach((weight, i) => {
104
+ weights[indices[i]] = weight;
105
+ });
106
+ }
107
+ return weights;
92
108
  }
93
109
  /** Seconds since the animation started, before looping. */
94
110
  get animationTime() {
95
- return this.animationPlayers[0]?.time ?? 0;
111
+ return this.animationPlayers[0]?.player.time ?? 0;
96
112
  }
97
113
  /**
98
114
  * Plays a clip from {@link Entities.getAnimations}, replacing all active clips.
@@ -103,42 +119,59 @@ var _EntityObject = class _EntityObject extends SceneObject {
103
119
  playAnimation(animation, options) {
104
120
  this.playAnimations([animation], options);
105
121
  }
106
- /** Plays clips with a shared clock and no blending. Selected layers without their own clip follow `main`. */
122
+ /**
123
+ * Plays clips with a shared clock, adding their weighted offsets within each geometry layer.
124
+ * Weights default to 1 and are not normalized. Selected layers without their own clip follow `main`.
125
+ */
107
126
  playAnimations(animations, options) {
108
- const targets = /* @__PURE__ */ new Set();
109
- const selections = animations.map((animation) => {
127
+ const weights = validateAnimationWeights(options?.weights ?? animations.map(() => 1), animations.length);
128
+ const selections = /* @__PURE__ */ new Map();
129
+ animations.forEach((animation, index) => {
110
130
  const layer = animation.layer ?? "main";
111
- if (targets.has(layer)) throw new Error(`Multiple animations target layer "${layer}"`);
112
- targets.add(layer);
113
131
  const names = Object.keys(this.entityLayers).filter((name) => name.split("#")[0] === layer);
114
132
  if (!names.length) throw new Error(`Entity ${this.entity.id} has no selected animation layer "${layer}"`);
115
- return { animation, names };
133
+ let selection = selections.get(layer);
134
+ if (!selection) selections.set(layer, selection = { animations: [], names, indices: [] });
135
+ selection.animations.push(animation);
136
+ selection.indices.push(index);
116
137
  });
117
- const main = selections.find(({ animation }) => (animation.layer ?? "main") === "main");
118
- if (main) main.names.push(...Object.keys(this.entityLayers).filter((name) => !targets.has(name.split("#")[0])));
138
+ const main = selections.get("main");
139
+ if (main) main.names.push(...Object.keys(this.entityLayers).filter((name) => !selections.has(name.split("#")[0])));
119
140
  this.createMeshes();
120
141
  this.stopAnimation();
121
- for (const { animation, names } of selections) {
142
+ for (const { animations: clips, names, indices } of selections.values()) {
122
143
  const player = new EntityAnimationPlayer();
123
- player.play(names.map((name) => this.getLayerGroup(name)), animation, options, this.partPoses);
124
- this.animationPlayers.push(player);
144
+ player.playAnimations(
145
+ names.map((name) => this.getLayerGroup(name)),
146
+ clips,
147
+ { ...options, weights: indices.map((index) => weights[index]) },
148
+ this.partPoses
149
+ );
150
+ this.animationPlayers.push({ player, indices });
125
151
  }
126
152
  this.notifyDirty();
127
153
  }
154
+ /** Changes weights in active clip order, notifying the scene without restarting the shared clock. Has no effect without an animation. */
155
+ setAnimationWeights(weights) {
156
+ if (!this.animationPlayers.length) return;
157
+ const values = validateAnimationWeights(weights, this.activeAnimations.length);
158
+ for (const { player, indices } of this.animationPlayers) player.setWeights(indices.map((index) => values[index]));
159
+ this.notifyDirty();
160
+ }
128
161
  /** Stops all clips and restores the poses from before playback. */
129
162
  stopAnimation() {
130
163
  const players = this.animationPlayers.splice(0);
131
- for (const player of players) player.stop();
164
+ for (const { player } of players) player.stop();
132
165
  if (players.length) this.notifyDirty();
133
166
  }
134
167
  /** Poses the object at an explicit time in seconds. Has no effect without an animation. */
135
168
  setAnimationTime(seconds) {
136
- for (const player of this.animationPlayers) player.setTime(seconds);
169
+ for (const { player } of this.animationPlayers) player.setTime(seconds);
137
170
  if (this.animationPlayers.length) this.notifyDirty();
138
171
  }
139
172
  /** Advances the animation by a frame delta in seconds, scaled by its speed. Has no effect without an animation. */
140
173
  advanceAnimation(deltaSeconds) {
141
- for (const player of this.animationPlayers) player.advance(deltaSeconds);
174
+ for (const { player } of this.animationPlayers) player.advance(deltaSeconds);
142
175
  if (this.animationPlayers.length) this.notifyDirty();
143
176
  }
144
177
  //</editor-fold>
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/entity/scene/EntityObject.ts"],"sourcesContent":["import { SceneObject } from \"../../renderer/SceneObject\";\nimport { SceneObjectOptions } from \"../../renderer/SceneObjectOptions\";\nimport { Caching } from \"../../cache/Caching\";\nimport { merge } from \"../../util/_internal/merge\";\nimport { Color, Object3D } from \"three\";\nimport type { BufferGeometry, ColorRepresentation, Material, Mesh, MeshBasicMaterial } from \"three\";\nimport { Ticker } from \"../../Ticker\";\nimport { addWireframeToMesh } from \"../../util/model\";\nimport { ModelTextures } from \"../../assets/ModelTextures\";\nimport { AssetKey, isAssetKey } from \"../../assets/AssetKey\";\nimport { ExtractableImageData } from \"../../ExtractableImageData\";\nimport { Materials } from \"../../Materials\";\nimport { MinecraftCubeTexture } from \"../../MinecraftCubeTexture\";\nimport { CUBE_FACES, CubeFace } from \"../../CubeFace\";\nimport { EntityLayer, EntityModel, EntityModelPart } from \"../EntityModel\";\nimport type { DoubleArray } from \"../../model/Model\";\nimport type { Maybe } from \"../../util/util\";\nimport { isMesh } from \"../../util/three\";\nimport type { EntityAnimation } from \"../EntityAnimation\";\nimport { EntityAnimationOptions, EntityAnimationPlayer } from \"./EntityAnimationPlayer\";\n\n/** Renders selected entity layers as named part groups. Create it through {@link MineRenderScene.addEntity}. */\nexport class EntityObject extends SceneObject {\n\n public readonly isEntityObject: true = true;\n\n public static readonly DEFAULT_OPTIONS: EntityObjectOptions = merge({}, SceneObject.DEFAULT_OPTIONS);\n public readonly options: EntityObjectOptions;\n\n private meshesCreated: boolean = false;\n private readonly geometries = new Set<BufferGeometry>();\n private readonly disposeWireframes: (() => void)[] = [];\n /** Scrolling materials are owned: their texture offset follows this entity's age. */\n private readonly scrollMaterials: { material: Material, offset: { set(x: number, y: number): unknown }, speed: [number, number] }[] = [];\n private scrollTicker: Maybe<number>;\n /** Entity age in ticks, as vanilla's `ageInTicks`; drives the scrolling render modes. */\n public age: number = 0;\n private readonly animationPlayers: EntityAnimationPlayer[] = [];\n private readonly partPoses = new Map<Object3D, EntityModelPart[\"pose\"]>();\n\n constructor(readonly entity: EntityModel, options?: Partial<EntityObjectOptions>) {\n super();\n this.options = merge({}, EntityObject.DEFAULT_OPTIONS, options ?? {});\n this.addEventListener(\"added\", () => this.updateScrollSubscription());\n this.addEventListener(\"removed\", () => this.updateScrollSubscription());\n }\n\n /** An optional shared material replaces texture loading for every selected draw; its owner retains disposal. */\n async init(material?: Material): Promise<void> {\n this.createMeshes();\n if (material) {\n this.clearScrollMaterials();\n this.iterateAllMeshes(mesh => { mesh.material = material; });\n this.notifyDirty();\n } else await this.applyTextures();\n }\n\n dispose() {\n super.dispose();\n }\n\n public disposeAndRemoveAllChildren() {\n this.clearScrollMaterials();\n for (const geometry of this.geometries) geometry.dispose();\n this.geometries.clear();\n for (const dispose of this.disposeWireframes.splice(0)) dispose();\n // The animated part groups are removed with the children.\n for (const player of this.animationPlayers.splice(0)) player.clear();\n this.partPoses.clear();\n super.disposeAndRemoveAllChildren();\n }\n\n private clearScrollMaterials() {\n Ticker.remove(this.scrollTicker);\n this.scrollTicker = undefined;\n for (const { material } of this.scrollMaterials.splice(0)) {\n (material as { map?: { dispose(): void } }).map?.dispose();\n material.dispose();\n }\n }\n\n private updateScrollSubscription() {\n if (this.parent && this.scrollMaterials.length) {\n if (this.scrollTicker === undefined) {\n this.scrollTicker = Ticker.add(() => {\n this.age++;\n this.updateScroll();\n });\n }\n } else {\n Ticker.remove(this.scrollTicker);\n this.scrollTicker = undefined;\n }\n }\n\n /** Applies the entity age to the scrolling materials; the ticker calls this 20 times per second. */\n private updateScroll() {\n for (const { offset, speed } of this.scrollMaterials) {\n // Texture V points up here and down in vanilla.\n offset.set((this.age * speed[0]) % 1, 0 - (this.age * speed[1]) % 1);\n }\n this.notifyDirty();\n }\n\n //<editor-fold desc=\"ANIMATION\">\n\n /** The first active clip, until playback is stopped or the object is disposed. */\n public get animation(): Maybe<EntityAnimation> {\n return this.animationPlayers[0]?.animation;\n }\n\n /** Clips playing together, each targeting a separate geometry layer. */\n public get activeAnimations(): readonly EntityAnimation[] {\n return this.animationPlayers.map(player => player.animation!);\n }\n\n /** Seconds since the animation started, before looping. */\n public get animationTime(): number {\n return this.animationPlayers[0]?.time ?? 0;\n }\n\n /**\n * Plays a clip from {@link Entities.getAnimations}, replacing all active clips.\n * Offsets apply to the baked pose in the clip's layer (`main` by default) and its repeated draws.\n * A `main` clip also poses every selected layer without a clip of its own.\n * The object owns no clock: call {@link advanceAnimation} per frame, or {@link setAnimationTime}.\n */\n public playAnimation(animation: EntityAnimation, options?: EntityAnimationOptions): void {\n this.playAnimations([animation], options);\n }\n\n /** Plays clips with a shared clock and no blending. Selected layers without their own clip follow `main`. */\n public playAnimations(animations: readonly EntityAnimation[], options?: EntityAnimationOptions): void {\n const targets = new Set<string>();\n const selections = animations.map(animation => {\n const layer = animation.layer ?? \"main\";\n if (targets.has(layer)) throw new Error(`Multiple animations target layer \"${layer}\"`);\n targets.add(layer);\n const names = Object.keys(this.entityLayers).filter(name => name.split(\"#\")[0] === layer);\n if (!names.length) throw new Error(`Entity ${this.entity.id} has no selected animation layer \"${layer}\"`);\n return { animation, names };\n });\n const main = selections.find(({ animation }) => (animation.layer ?? \"main\") === \"main\");\n if (main) main.names.push(...Object.keys(this.entityLayers).filter(name => !targets.has(name.split(\"#\")[0])));\n this.createMeshes();\n this.stopAnimation();\n for (const { animation, names } of selections) {\n const player = new EntityAnimationPlayer();\n player.play(names.map(name => this.getLayerGroup(name)!), animation, options, this.partPoses);\n this.animationPlayers.push(player);\n }\n this.notifyDirty();\n }\n\n /** Stops all clips and restores the poses from before playback. */\n public stopAnimation(): void {\n const players = this.animationPlayers.splice(0);\n for (const player of players) player.stop();\n if (players.length) this.notifyDirty();\n }\n\n /** Poses the object at an explicit time in seconds. Has no effect without an animation. */\n public setAnimationTime(seconds: number): void {\n for (const player of this.animationPlayers) player.setTime(seconds);\n if (this.animationPlayers.length) this.notifyDirty();\n }\n\n /** Advances the animation by a frame delta in seconds, scaled by its speed. Has no effect without an animation. */\n public advanceAnimation(deltaSeconds: number): void {\n for (const player of this.animationPlayers) player.advance(deltaSeconds);\n if (this.animationPlayers.length) this.notifyDirty();\n }\n\n //</editor-fold>\n\n /** Finds a selected draw by its layer key, such as `main` or `main#2`, or returns `undefined`. */\n public getLayerGroup(name: string): Maybe<Object3D> {\n return super.getGroupByName(`layer:${name}`);\n }\n\n /** Finds a part name without `group:`. Supply `layerName` to restrict the search to one draw. */\n public getGroupByName(name: string, layerName?: string): Maybe<Object3D> {\n return layerName === undefined ? super.getGroupByName(name)\n : this.getLayerGroup(layerName)?.getObjectByName(`group:${name}`);\n }\n\n /** Finds a mesh name without `mesh:`. Supply `layerName` to restrict the search to one draw. */\n public getMeshByName(name: string, layerName?: string): Maybe<Mesh> {\n return layerName === undefined ? super.getMeshByName(name)\n : this.getLayerGroup(layerName)?.getObjectByName(`mesh:${name}`) as Maybe<Mesh>;\n }\n\n private get entityLayers(): Record<string, EntityLayer> {\n return this.entity.layers ?? { main: this.entity };\n }\n\n private getTextureKey(layer: EntityLayer): AssetKey {\n if (layer.texture) return layer.texture;\n const key = layer.key;\n return new AssetKey(\n key.namespace,\n isAssetKey(key) ? key.getFullPath() : key.path,\n \"textures\",\n \"entity\",\n \"assets\",\n \".png\"\n );\n }\n\n protected async loadTextures(layer: EntityLayer = this.entity): Promise<Maybe<ExtractableImageData>> {\n return ModelTextures.get(this.getTextureKey(layer));\n }\n\n protected createMeshes(force: boolean = false) {\n if (this.meshesCreated && !force) return;\n\n const modelRoot = new Object3D();\n let transformNode = modelRoot;\n // Keep Minecraft's model coordinates separate from caller placement and scale.\n const transform = this.options.flip === undefined ? this.entity.transform : undefined;\n if (transform) {\n // One position, rotation and scale cannot express nonuniform scale followed by rotation.\n // Nested nodes preserve each operation and avoid manual matrices, which leave bounds stale.\n for (const [index, op] of transform.entries()) {\n if (index > 0) {\n const child = new Object3D();\n transformNode.add(child);\n transformNode = child;\n }\n if (\"scale\" in op) transformNode.scale.set(...op.scale);\n else if (\"translate\" in op) transformNode.position.set(...op.translate);\n else transformNode.rotation.set(...op.rotate, \"ZYX\");\n }\n } else if (this.options.flip ?? true) {\n modelRoot.scale.set(-1, -1, 1);\n }\n this.add(modelRoot);\n Object.entries(this.entityLayers).forEach(([name, layer], index) => {\n const group = this.createGroup(`layer:${name}`);\n transformNode.add(group);\n const inward = !Materials.entityModeCulls(layer.render ?? layer.layer.render);\n this.createPart(\"root\", layer.layer.root, group, layer.layer.texture, Materials.MISSING_TEXTURE, index, inward, this.options.faces?.[name]);\n });\n this.meshesCreated = true;\n }\n\n private createPart(name: string, part: EntityModelPart, parent: Object3D, textureSize: DoubleArray, material: Material, renderOrder: number, inward: boolean = true, faces?: CubeFace[]) {\n const anchor = this.createGroup(name);\n anchor.position.fromArray(part.pose.offset);\n anchor.rotation.set(...part.pose.rotation, \"ZYX\");\n anchor.scale.fromArray(part.pose.scale ?? [1, 1, 1]);\n this.partPoses.set(anchor, part.pose);\n parent.add(anchor);\n const size = part.texture ?? textureSize;\n\n for (const cube of part.cubes) {\n const [width, height, depth] = cube.size;\n const [growX, growY, growZ] = cube.grow ?? [0, 0, 0];\n const texture = new MinecraftCubeTexture(...cube.uv, ...size);\n const uv = size[0] === 0 || size[1] === 0 ? new Array<number>(48).fill(0) : texture.toUvArray(width, height, depth, cube.mirror);\n const geometry = this._getBoxGeometryForDimensionsAndUv(\n width + growX * 2, height + growY * 2, depth + growZ * 2, uv\n ).clone();\n this.geometries.add(geometry);\n geometry.translate(cube.origin[0] + width / 2, cube.origin[1] + height / 2, cube.origin[2] + depth / 2);\n if (faces) {\n geometry.setIndex(Array.from(geometry.getIndex()!.array).filter((_, index) => faces.includes(CUBE_FACES[Math.floor(index / 6)])));\n geometry.clearGroups();\n }\n // Vanilla draws most entity render types without backface culling, e.g. chicken legs are only painted on faces seen from inside.\n // Zero-thickness cubes keep one face per side, as their coplanar faces would z-fight.\n if (inward && Math.min(width + growX * 2, height + growY * 2, depth + growZ * 2) > 0) {\n const index = Array.from(geometry.getIndex()!.array);\n geometry.setIndex(index.concat(index.slice().reverse()));\n }\n const mesh = this.createMesh(name, geometry, material);\n mesh.renderOrder = renderOrder;\n anchor.add(mesh);\n if (this.options.wireframe) this.disposeWireframes.push(addWireframeToMesh(geometry, mesh));\n }\n for (const [childName, child] of Object.entries(part.children)) {\n this.createPart(childName, child, anchor, size, material, renderOrder, inward, faces);\n }\n }\n\n protected async applyTextures() {\n this.clearScrollMaterials();\n const results = await Promise.allSettled(Object.entries(this.entityLayers).map(async ([name, layer]) => {\n const mode = layer.render ?? layer.layer.render ?? \"cutout\";\n const tint = layer.tint === undefined ? undefined : this.options.tints?.[layer.tint];\n const scroll = Materials.entityModeScroll(mode);\n const assetKeyStr = this.getTextureKey(layer).serialize();\n const keyStr = `entity:${ mode }:${ tint === undefined ? \"\" : new Color(tint).getHexString() }:${ assetKeyStr }`;\n let mat = scroll ? undefined : Caching.materialCache.getIfPresent(keyStr);\n if (!mat) {\n const cachedAsset = Caching.textureAssetCache.getIfPresent(assetKeyStr);\n const imageData = await this.loadTextures(layer);\n if (!imageData) return;\n const canvas = (imageData.data as CanvasRenderingContext2D).canvas;\n const createMaterial = () => Materials.createEntityCanvasMaterial(canvas, mode, tint);\n // A cache clear during decoding must not restore an older source's material.\n // Scrolling materials stay out of the cache, as their texture offset belongs to this entity.\n mat = !scroll && cachedAsset && Caching.textureAssetCache.getIfPresent(assetKeyStr) === cachedAsset\n ? Caching.materialCache.get(keyStr, createMaterial)\n : createMaterial();\n if (scroll) {\n this.scrollMaterials.push({ material: mat!, offset: (mat as MeshBasicMaterial).map!.offset, speed: scroll });\n }\n }\n this.getLayerGroup(name)?.traverse(object => {\n if (isMesh(object)) object.material = mat!;\n });\n }));\n if (!this.children.length) {\n // Disposed while the textures were loading: nothing is left to scroll.\n this.clearScrollMaterials();\n } else if (this.scrollMaterials.length) {\n this.updateScroll();\n }\n this.updateScrollSubscription();\n this.notifyDirty();\n const failure = results.find(result => result.status === \"rejected\");\n if (failure?.status === \"rejected\") throw failure.reason;\n }\n\n\n}\n\n/** Render settings passed to `scene.addEntity(entity, options)` or the EntityObject constructor. */\nexport interface EntityObjectOptions extends SceneObjectOptions {\n /**\n * `true` applies only vanilla's entity flip, scale (-1, -1, 1); `false` keeps raw model space.\n * By default the model's dataset transform is used, or the flip for models without one.\n */\n flip?: boolean;\n /** Colours for the dataset's tint labels, e.g. `{ wool_color: 0xf9801d }`; a pass whose label is absent stays untinted. */\n tints?: Record<string, ColorRepresentation>;\n /** Cube faces by layer key; omitted layers draw all six faces. */\n faces?: Record<string, CubeFace[]>;\n}\n\nexport function isEntityObject(obj: any): obj is EntityObject {\n return (<EntityObject>obj).isEntityObject;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../src/entity/scene/EntityObject.ts"],"sourcesContent":["import { SceneObject } from \"../../renderer/SceneObject\";\nimport { SceneObjectOptions } from \"../../renderer/SceneObjectOptions\";\nimport { Caching } from \"../../cache/Caching\";\nimport { merge } from \"../../util/_internal/merge\";\nimport { Color, Object3D } from \"three\";\nimport type { BufferGeometry, ColorRepresentation, Material, Mesh, MeshBasicMaterial } from \"three\";\nimport { Ticker } from \"../../Ticker\";\nimport { addWireframeToMesh } from \"../../util/model\";\nimport { ModelTextures } from \"../../assets/ModelTextures\";\nimport { AssetKey, isAssetKey } from \"../../assets/AssetKey\";\nimport { ExtractableImageData } from \"../../ExtractableImageData\";\nimport { Materials } from \"../../Materials\";\nimport { MinecraftCubeTexture } from \"../../MinecraftCubeTexture\";\nimport { CUBE_FACES, CubeFace } from \"../../CubeFace\";\nimport { EntityLayer, EntityModel, EntityModelPart } from \"../EntityModel\";\nimport type { DoubleArray } from \"../../model/Model\";\nimport type { Maybe } from \"../../util/util\";\nimport { isMesh } from \"../../util/three\";\nimport type { EntityAnimation } from \"../EntityAnimation\";\nimport { EntityAnimationOptions, EntityAnimationPlayer, validateAnimationWeights } from \"./EntityAnimationPlayer\";\n\n/** Renders selected entity layers as named part groups. Create it through {@link MineRenderScene.addEntity}. */\nexport class EntityObject extends SceneObject {\n\n public readonly isEntityObject: true = true;\n\n public static readonly DEFAULT_OPTIONS: EntityObjectOptions = merge({}, SceneObject.DEFAULT_OPTIONS);\n public readonly options: EntityObjectOptions;\n\n private meshesCreated: boolean = false;\n private readonly geometries = new Set<BufferGeometry>();\n private readonly disposeWireframes: (() => void)[] = [];\n /** Scrolling materials are owned: their texture offset follows this entity's age. */\n private readonly scrollMaterials: { material: Material, offset: { set(x: number, y: number): unknown }, speed: [number, number] }[] = [];\n private scrollTicker: Maybe<number>;\n /** Entity age in ticks, as vanilla's `ageInTicks`; drives the scrolling render modes. */\n public age: number = 0;\n private readonly animationPlayers: { player: EntityAnimationPlayer, indices: number[] }[] = [];\n private readonly partPoses = new Map<Object3D, EntityModelPart[\"pose\"]>();\n\n constructor(readonly entity: EntityModel, options?: Partial<EntityObjectOptions>) {\n super();\n this.options = merge({}, EntityObject.DEFAULT_OPTIONS, options ?? {});\n this.addEventListener(\"added\", () => this.updateScrollSubscription());\n this.addEventListener(\"removed\", () => this.updateScrollSubscription());\n }\n\n /** An optional shared material replaces texture loading for every selected draw; its owner retains disposal. */\n async init(material?: Material): Promise<void> {\n this.createMeshes();\n if (material) {\n this.clearScrollMaterials();\n this.iterateAllMeshes(mesh => { mesh.material = material; });\n this.notifyDirty();\n } else await this.applyTextures();\n }\n\n dispose() {\n super.dispose();\n }\n\n public disposeAndRemoveAllChildren() {\n this.clearScrollMaterials();\n for (const geometry of this.geometries) geometry.dispose();\n this.geometries.clear();\n for (const dispose of this.disposeWireframes.splice(0)) dispose();\n // The animated part groups are removed with the children.\n for (const { player } of this.animationPlayers.splice(0)) player.clear();\n this.partPoses.clear();\n super.disposeAndRemoveAllChildren();\n }\n\n private clearScrollMaterials() {\n Ticker.remove(this.scrollTicker);\n this.scrollTicker = undefined;\n for (const { material } of this.scrollMaterials.splice(0)) {\n (material as { map?: { dispose(): void } }).map?.dispose();\n material.dispose();\n }\n }\n\n private updateScrollSubscription() {\n if (this.parent && this.scrollMaterials.length) {\n if (this.scrollTicker === undefined) {\n this.scrollTicker = Ticker.add(() => {\n this.age++;\n this.updateScroll();\n });\n }\n } else {\n Ticker.remove(this.scrollTicker);\n this.scrollTicker = undefined;\n }\n }\n\n /** Applies the entity age to the scrolling materials; the ticker calls this 20 times per second. */\n private updateScroll() {\n for (const { offset, speed } of this.scrollMaterials) {\n // Texture V points up here and down in vanilla.\n offset.set((this.age * speed[0]) % 1, 0 - (this.age * speed[1]) % 1);\n }\n this.notifyDirty();\n }\n\n //<editor-fold desc=\"ANIMATION\">\n\n /** The first active clip, until playback is stopped or the object is disposed. */\n public get animation(): Maybe<EntityAnimation> {\n return this.animationPlayers[0]?.player.animation;\n }\n\n /** Clips playing together, in the order passed to {@link playAnimations}. */\n public get activeAnimations(): readonly EntityAnimation[] {\n const animations: EntityAnimation[] = [];\n for (const { player, indices } of this.animationPlayers) {\n player.animations.forEach((animation, i) => { animations[indices[i]] = animation; });\n }\n return animations;\n }\n\n /** Weights of the active clips, in the same order as {@link activeAnimations}. */\n public get animationWeights(): readonly number[] {\n const weights: number[] = [];\n for (const { player, indices } of this.animationPlayers) {\n player.weights.forEach((weight, i) => { weights[indices[i]] = weight; });\n }\n return weights;\n }\n\n /** Seconds since the animation started, before looping. */\n public get animationTime(): number {\n return this.animationPlayers[0]?.player.time ?? 0;\n }\n\n /**\n * Plays a clip from {@link Entities.getAnimations}, replacing all active clips.\n * Offsets apply to the baked pose in the clip's layer (`main` by default) and its repeated draws.\n * A `main` clip also poses every selected layer without a clip of its own.\n * The object owns no clock: call {@link advanceAnimation} per frame, or {@link setAnimationTime}.\n */\n public playAnimation(animation: EntityAnimation, options?: EntityAnimationOptions): void {\n this.playAnimations([animation], options);\n }\n\n /**\n * Plays clips with a shared clock, adding their weighted offsets within each geometry layer.\n * Weights default to 1 and are not normalized. Selected layers without their own clip follow `main`.\n */\n public playAnimations(animations: readonly EntityAnimation[], options?: EntityAnimationOptions): void {\n const weights = validateAnimationWeights(options?.weights ?? animations.map(() => 1), animations.length);\n const selections = new Map<string, { animations: EntityAnimation[], names: string[], indices: number[] }>();\n animations.forEach((animation, index) => {\n const layer = animation.layer ?? \"main\";\n const names = Object.keys(this.entityLayers).filter(name => name.split(\"#\")[0] === layer);\n if (!names.length) throw new Error(`Entity ${this.entity.id} has no selected animation layer \"${layer}\"`);\n let selection = selections.get(layer);\n if (!selection) selections.set(layer, selection = { animations: [], names, indices: [] });\n selection.animations.push(animation);\n selection.indices.push(index);\n });\n const main = selections.get(\"main\");\n if (main) main.names.push(...Object.keys(this.entityLayers).filter(name => !selections.has(name.split(\"#\")[0])));\n this.createMeshes();\n this.stopAnimation();\n for (const { animations: clips, names, indices } of selections.values()) {\n const player = new EntityAnimationPlayer();\n player.playAnimations(names.map(name => this.getLayerGroup(name)!), clips,\n { ...options, weights: indices.map(index => weights[index]) }, this.partPoses);\n this.animationPlayers.push({ player, indices });\n }\n this.notifyDirty();\n }\n\n /** Changes weights in active clip order, notifying the scene without restarting the shared clock. Has no effect without an animation. */\n public setAnimationWeights(weights: readonly number[]): void {\n if (!this.animationPlayers.length) return;\n const values = validateAnimationWeights(weights, this.activeAnimations.length);\n for (const { player, indices } of this.animationPlayers) player.setWeights(indices.map(index => values[index]));\n this.notifyDirty();\n }\n\n /** Stops all clips and restores the poses from before playback. */\n public stopAnimation(): void {\n const players = this.animationPlayers.splice(0);\n for (const { player } of players) player.stop();\n if (players.length) this.notifyDirty();\n }\n\n /** Poses the object at an explicit time in seconds. Has no effect without an animation. */\n public setAnimationTime(seconds: number): void {\n for (const { player } of this.animationPlayers) player.setTime(seconds);\n if (this.animationPlayers.length) this.notifyDirty();\n }\n\n /** Advances the animation by a frame delta in seconds, scaled by its speed. Has no effect without an animation. */\n public advanceAnimation(deltaSeconds: number): void {\n for (const { player } of this.animationPlayers) player.advance(deltaSeconds);\n if (this.animationPlayers.length) this.notifyDirty();\n }\n\n //</editor-fold>\n\n /** Finds a selected draw by its layer key, such as `main` or `main#2`, or returns `undefined`. */\n public getLayerGroup(name: string): Maybe<Object3D> {\n return super.getGroupByName(`layer:${name}`);\n }\n\n /** Finds a part name without `group:`. Supply `layerName` to restrict the search to one draw. */\n public getGroupByName(name: string, layerName?: string): Maybe<Object3D> {\n return layerName === undefined ? super.getGroupByName(name)\n : this.getLayerGroup(layerName)?.getObjectByName(`group:${name}`);\n }\n\n /** Finds a mesh name without `mesh:`. Supply `layerName` to restrict the search to one draw. */\n public getMeshByName(name: string, layerName?: string): Maybe<Mesh> {\n return layerName === undefined ? super.getMeshByName(name)\n : this.getLayerGroup(layerName)?.getObjectByName(`mesh:${name}`) as Maybe<Mesh>;\n }\n\n private get entityLayers(): Record<string, EntityLayer> {\n return this.entity.layers ?? { main: this.entity };\n }\n\n private getTextureKey(layer: EntityLayer): AssetKey {\n if (layer.texture) return layer.texture;\n const key = layer.key;\n return new AssetKey(\n key.namespace,\n isAssetKey(key) ? key.getFullPath() : key.path,\n \"textures\",\n \"entity\",\n \"assets\",\n \".png\"\n );\n }\n\n protected async loadTextures(layer: EntityLayer = this.entity): Promise<Maybe<ExtractableImageData>> {\n return ModelTextures.get(this.getTextureKey(layer));\n }\n\n protected createMeshes(force: boolean = false) {\n if (this.meshesCreated && !force) return;\n\n const modelRoot = new Object3D();\n let transformNode = modelRoot;\n // Keep Minecraft's model coordinates separate from caller placement and scale.\n const transform = this.options.flip === undefined ? this.entity.transform : undefined;\n if (transform) {\n // One position, rotation and scale cannot express nonuniform scale followed by rotation.\n // Nested nodes preserve each operation and avoid manual matrices, which leave bounds stale.\n for (const [index, op] of transform.entries()) {\n if (index > 0) {\n const child = new Object3D();\n transformNode.add(child);\n transformNode = child;\n }\n if (\"scale\" in op) transformNode.scale.set(...op.scale);\n else if (\"translate\" in op) transformNode.position.set(...op.translate);\n else transformNode.rotation.set(...op.rotate, \"ZYX\");\n }\n } else if (this.options.flip ?? true) {\n modelRoot.scale.set(-1, -1, 1);\n }\n this.add(modelRoot);\n Object.entries(this.entityLayers).forEach(([name, layer], index) => {\n const group = this.createGroup(`layer:${name}`);\n transformNode.add(group);\n const inward = !Materials.entityModeCulls(layer.render ?? layer.layer.render);\n this.createPart(\"root\", layer.layer.root, group, layer.layer.texture, Materials.MISSING_TEXTURE, index, inward, this.options.faces?.[name]);\n });\n this.meshesCreated = true;\n }\n\n private createPart(name: string, part: EntityModelPart, parent: Object3D, textureSize: DoubleArray, material: Material, renderOrder: number, inward: boolean = true, faces?: CubeFace[]) {\n const anchor = this.createGroup(name);\n anchor.position.fromArray(part.pose.offset);\n anchor.rotation.set(...part.pose.rotation, \"ZYX\");\n anchor.scale.fromArray(part.pose.scale ?? [1, 1, 1]);\n this.partPoses.set(anchor, part.pose);\n parent.add(anchor);\n const size = part.texture ?? textureSize;\n\n for (const cube of part.cubes) {\n const [width, height, depth] = cube.size;\n const [growX, growY, growZ] = cube.grow ?? [0, 0, 0];\n const texture = new MinecraftCubeTexture(...cube.uv, ...size);\n const uv = size[0] === 0 || size[1] === 0 ? new Array<number>(48).fill(0) : texture.toUvArray(width, height, depth, cube.mirror);\n const geometry = this._getBoxGeometryForDimensionsAndUv(\n width + growX * 2, height + growY * 2, depth + growZ * 2, uv\n ).clone();\n this.geometries.add(geometry);\n geometry.translate(cube.origin[0] + width / 2, cube.origin[1] + height / 2, cube.origin[2] + depth / 2);\n if (faces) {\n geometry.setIndex(Array.from(geometry.getIndex()!.array).filter((_, index) => faces.includes(CUBE_FACES[Math.floor(index / 6)])));\n geometry.clearGroups();\n }\n // Vanilla draws most entity render types without backface culling, e.g. chicken legs are only painted on faces seen from inside.\n // Zero-thickness cubes keep one face per side, as their coplanar faces would z-fight.\n if (inward && Math.min(width + growX * 2, height + growY * 2, depth + growZ * 2) > 0) {\n const index = Array.from(geometry.getIndex()!.array);\n geometry.setIndex(index.concat(index.slice().reverse()));\n }\n const mesh = this.createMesh(name, geometry, material);\n mesh.renderOrder = renderOrder;\n anchor.add(mesh);\n if (this.options.wireframe) this.disposeWireframes.push(addWireframeToMesh(geometry, mesh));\n }\n for (const [childName, child] of Object.entries(part.children)) {\n this.createPart(childName, child, anchor, size, material, renderOrder, inward, faces);\n }\n }\n\n protected async applyTextures() {\n this.clearScrollMaterials();\n const results = await Promise.allSettled(Object.entries(this.entityLayers).map(async ([name, layer]) => {\n const mode = layer.render ?? layer.layer.render ?? \"cutout\";\n const tint = layer.tint === undefined ? undefined : this.options.tints?.[layer.tint];\n const scroll = Materials.entityModeScroll(mode);\n const assetKeyStr = this.getTextureKey(layer).serialize();\n const keyStr = `entity:${ mode }:${ tint === undefined ? \"\" : new Color(tint).getHexString() }:${ assetKeyStr }`;\n let mat = scroll ? undefined : Caching.materialCache.getIfPresent(keyStr);\n if (!mat) {\n const cachedAsset = Caching.textureAssetCache.getIfPresent(assetKeyStr);\n const imageData = await this.loadTextures(layer);\n if (!imageData) return;\n const canvas = (imageData.data as CanvasRenderingContext2D).canvas;\n const createMaterial = () => Materials.createEntityCanvasMaterial(canvas, mode, tint);\n // A cache clear during decoding must not restore an older source's material.\n // Scrolling materials stay out of the cache, as their texture offset belongs to this entity.\n mat = !scroll && cachedAsset && Caching.textureAssetCache.getIfPresent(assetKeyStr) === cachedAsset\n ? Caching.materialCache.get(keyStr, createMaterial)\n : createMaterial();\n if (scroll) {\n this.scrollMaterials.push({ material: mat!, offset: (mat as MeshBasicMaterial).map!.offset, speed: scroll });\n }\n }\n this.getLayerGroup(name)?.traverse(object => {\n if (isMesh(object)) object.material = mat!;\n });\n }));\n if (!this.children.length) {\n // Disposed while the textures were loading: nothing is left to scroll.\n this.clearScrollMaterials();\n } else if (this.scrollMaterials.length) {\n this.updateScroll();\n }\n this.updateScrollSubscription();\n this.notifyDirty();\n const failure = results.find(result => result.status === \"rejected\");\n if (failure?.status === \"rejected\") throw failure.reason;\n }\n\n\n}\n\n/** Render settings passed to `scene.addEntity(entity, options)` or the EntityObject constructor. */\nexport interface EntityObjectOptions extends SceneObjectOptions {\n /**\n * `true` applies only vanilla's entity flip, scale (-1, -1, 1); `false` keeps raw model space.\n * By default the model's dataset transform is used, or the flip for models without one.\n */\n flip?: boolean;\n /** Colours for the dataset's tint labels, e.g. `{ wool_color: 0xf9801d }`; a pass whose label is absent stays untinted. */\n tints?: Record<string, ColorRepresentation>;\n /** Cube faces by layer key; omitted layers draw all six faces. */\n faces?: Record<string, CubeFace[]>;\n}\n\nexport function isEntityObject(obj: any): obj is EntityObject {\n return (<EntityObject>obj).isEntityObject;\n}\n"],"mappings":"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","names":[]}
@@ -196,7 +196,7 @@ function buildSectionGeometry(input) {
196
196
  const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
197
197
  for (const template of templateData) {
198
198
  const { quads } = template;
199
- if (template.positions.length !== quads * 12 || template.normals.length !== quads * 12 || template.uvs.length !== quads * 8 || template.uvBounds.length !== quads * 16 || template.colors.length !== quads * 12 || template.indices.length !== quads * 6 || template.cullFaces.length !== quads || template.biomeTint && template.biomeTint.length !== quads) {
199
+ if (template.positions.length !== quads * 12 || template.normals.length !== quads * 12 || template.uvs.length !== quads * 8 || template.uvBounds.length !== quads * 16 || template.colors.length !== quads * 12 || template.indices.length !== quads * 6 || template.cullFaces.length !== quads || template.shade && template.shade.length !== quads * 4 || template.biomeTint && template.biomeTint.length !== quads) {
200
200
  throw new RangeError("Section template arrays do not match its quad count");
201
201
  }
202
202
  }
@@ -287,6 +287,7 @@ function buildSectionGeometry(input) {
287
287
  }
288
288
  const positions = new Float32Array(faces * 12), normals = new Float32Array(faces * 12);
289
289
  const uvs = new Float32Array(faces * 8), uvBounds = new Float32Array(faces * 16), colors = new Float32Array(faces * 12);
290
+ const shade = new Float32Array(faces * 4).fill(1);
290
291
  const outputIndices = new Uint32Array(faces * 6);
291
292
  const blockIndices = new Uint16Array(faces);
292
293
  let vertex = 0, indexOffset = 0;
@@ -316,6 +317,7 @@ function buildSectionGeometry(input) {
316
317
  normals[vertex * 3] = template.normals[source * 3];
317
318
  normals[vertex * 3 + 1] = template.normals[source * 3 + 1];
318
319
  normals[vertex * 3 + 2] = template.normals[source * 3 + 2];
320
+ shade[vertex] = template.shade?.[source] ?? 1;
319
321
  uvs[vertex * 2] = mapU(template.uvs[source * 2]);
320
322
  uvs[vertex * 2 + 1] = mapV(template.uvs[source * 2 + 1]);
321
323
  uvBounds[vertex * 4] = mapU(template.uvBounds[source * 4]);
@@ -368,11 +370,11 @@ function buildSectionGeometry(input) {
368
370
  indexOffset += 6;
369
371
  }
370
372
  }
371
- return { layer: page.layer, width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, indices: outputIndices, blockIndices };
373
+ return { layer: page.layer, width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, shade, indices: outputIndices, blockIndices };
372
374
  });
373
375
  }
374
376
  function sectionGeometryTransferables(pages) {
375
- return pages.flatMap((page) => [page.positions.buffer, page.normals.buffer, page.uvs.buffer, page.uvBounds.buffer, page.colors.buffer, page.indices.buffer, page.blockIndices.buffer]);
377
+ return pages.flatMap((page) => [page.positions.buffer, page.normals.buffer, page.uvs.buffer, page.uvBounds.buffer, page.colors.buffer, page.shade.buffer, page.indices.buffer, page.blockIndices.buffer]);
376
378
  }
377
379
 
378
380
  // src/env/browser/section.worker.ts