@dice-o-rolla/dice-assets 0.3.1 → 0.5.0
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.
- package/README.md +95 -6
- package/THIRD_PARTY_NOTICES.md +3 -0
- package/assets/runtime/catalog.json +649 -0
- package/assets/runtime/previews/diagnostic-d10.svg +1 -0
- package/assets/runtime/previews/diagnostic-d100.svg +1 -0
- package/assets/runtime/previews/diagnostic-d12.svg +1 -0
- package/assets/runtime/previews/diagnostic-d20.svg +1 -0
- package/assets/runtime/previews/diagnostic-d4.svg +1 -0
- package/assets/runtime/previews/diagnostic-d6.svg +1 -0
- package/assets/runtime/previews/diagnostic-d66.svg +1 -0
- package/assets/runtime/previews/diagnostic-d8.svg +1 -0
- package/assets/runtime/textures/diagnostic-d10.ktx2 +0 -0
- package/assets/runtime/textures/diagnostic-d100.ktx2 +0 -0
- package/assets/runtime/textures/diagnostic-d12.ktx2 +0 -0
- package/assets/runtime/textures/diagnostic-d20.ktx2 +0 -0
- package/assets/runtime/textures/diagnostic-d4.ktx2 +0 -0
- package/assets/runtime/textures/diagnostic-d6.ktx2 +0 -0
- package/assets/runtime/textures/diagnostic-d66.ktx2 +0 -0
- package/assets/runtime/textures/diagnostic-d8.ktx2 +0 -0
- package/dist/asset-registry.d.ts +2 -1
- package/dist/asset-registry.js +37 -0
- package/dist/catalog-loader.js +19 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/textured-skin-set.d.ts +32 -0
- package/dist/textured-skin-set.js +115 -0
- package/dist/three-material-provider.d.ts +1 -0
- package/dist/three-material-provider.js +61 -20
- package/dist/tools/build.d.ts +2 -0
- package/dist/tools/build.js +232 -0
- package/dist/tools/cli.d.ts +2 -0
- package/dist/tools/cli.js +63 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/template.d.ts +9 -0
- package/dist/tools/template.js +88 -0
- package/dist/tools/types.d.ts +40 -0
- package/dist/tools/types.js +10 -0
- package/dist/tools/write-directory.d.ts +2 -0
- package/dist/tools/write-directory.js +56 -0
- package/dist/types.d.ts +13 -0
- package/package.json +16 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="2048" height="2048" viewBox="0 0 2048 2048"><defs><pattern id="grid" width="16" height="16" patternUnits="userSpaceOnUse"><path d="M 16 0 H 0 V 16" fill="none" stroke="#142035" stroke-opacity=".2" stroke-width="1"/></pattern></defs><polygon points="1024,1290.0430040425795 1638.3999999999999,1290.0430040425795 1331.2,757.9569959574203" fill="#a8d98c" stroke="#a8d98c" stroke-width="16" stroke-linejoin="round"/><polygon points="1024,1290.0430040425795 1331.2,757.9569959574205 716.8000000000001,757.9569959574205" fill="#8ccce8" stroke="#8ccce8" stroke-width="16" stroke-linejoin="round"/><polygon points="1024,1290.0430040425795 716.8000000000001,757.9569959574205 409.59999999999997,1290.0430040425795" fill="#e5a2cb" stroke="#e5a2cb" stroke-width="16" stroke-linejoin="round"/><polygon points="1024,1290.0430040425795 1331.2,1822.1290121277386 1638.3999999999999,1290.0430040425795" fill="#d3bcf3" stroke="#d3bcf3" stroke-width="16" stroke-linejoin="round"/><polygon points="1024,225.87098787226114 716.8000000000001,757.9569959574205 1331.2,757.9569959574205" fill="#f0da85" stroke="#f0da85" stroke-width="16" stroke-linejoin="round"/><polygon points="1945.6000000000001,757.9569959574203 1331.2,757.9569959574203 1638.3999999999999,1290.0430040425795" fill="#87d9cd" stroke="#87d9cd" stroke-width="16" stroke-linejoin="round"/><polygon points="1945.6000000000001,1822.1290121277386 1638.3999999999999,1290.0430040425795 1331.2,1822.1290121277386" fill="#ef9c99" stroke="#ef9c99" stroke-width="16" stroke-linejoin="round"/><polygon points="102.39999999999992,757.9569959574205 409.59999999999997,1290.0430040425795 716.8000000000001,757.9569959574205" fill="#f6b573" stroke="#f6b573" stroke-width="16" stroke-linejoin="round"/><defs><clipPath id="face-1"><polygon points="1024,1290.0430040425795 1638.3999999999999,1290.0430040425795 1331.2,757.9569959574203"/></clipPath></defs><g clip-path="url(#face-1)"><polygon points="1024,1290.0430040425795 1638.3999999999999,1290.0430040425795 1331.2,757.9569959574203" fill="url(#grid)"/><polygon points="1024,1290.0430040425795 1638.3999999999999,1290.0430040425795 1331.2,757.9569959574203" fill="none" stroke="#162338" stroke-width="3"/><path d="M 1208.32 1290.0430040425795 l 50.50336890020568 -72.89533407379194" stroke="#fff" stroke-width="10"/><circle cx="1208.32" cy="1290.0430040425795" r="6" fill="#132035"/><text x="1331.1999999999998" y="1265.212323665272" font-size="30.15154045815901" text-anchor="middle" fill="#142035">0:2</text><path d="M 1546.2399999999998 1130.4172016170317 l -88.38089557536004 -7.289533407379226" stroke="#fff" stroke-width="10"/><circle cx="1546.2399999999998" cy="1130.4172016170317" r="6" fill="#132035"/><text x="1463.2959999999998" y="1036.4153401886535" font-size="30.15154045815901" text-anchor="middle" fill="#142035">2:4</text><path d="M 1116.16 1130.4172016170317 l 88.38089557536004 -7.289533407379233" stroke="#fff" stroke-width="10"/><circle cx="1116.16" cy="1130.4172016170317" r="6" fill="#132035"/><text x="1199.1039999999998" y="1036.4153401886535" font-size="30.15154045815901" text-anchor="middle" fill="#142035">0:4</text><text x="1331.1999999999998" y="1112.6810013475263" font-size="177.36200269505298" font-family="sans-serif" font-weight="bold" text-anchor="middle" dominant-baseline="central" fill="#101a2d" stroke="#fff" stroke-width="1">1</text><path d="M 1224.782798382968 1236.8344032340633 h 212.83440323406356 l -35.4724005390106 -24.83068037730742 m 35.4724005390106 24.83068037730742 l -35.4724005390106 24.83068037730742" fill="none" stroke="#142035" stroke-width="3"/><text x="1331.1999999999998" y="999.1693196226923" font-size="28.377920431208477" text-anchor="middle">d8 / F1</text></g><defs><clipPath id="face-2"><polygon points="1024,1290.0430040425795 1331.2,757.9569959574205 716.8000000000001,757.9569959574205"/></clipPath></defs><g clip-path="url(#face-2)"><polygon points="1024,1290.0430040425795 1331.2,757.9569959574205 716.8000000000001,757.9569959574205" fill="url(#grid)"/><polygon points="1024,1290.0430040425795 1331.2,757.9569959574205 716.8000000000001,757.9569959574205" fill="none" stroke="#162338" stroke-width="3"/><path d="M 1116.1599999999999 1130.4172016170317 l -37.877526675154286 -80.1848674811711" stroke="#fff" stroke-width="10"/><circle cx="1116.1599999999999" cy="1130.4172016170317" r="6" fill="#132035"/><text x="1156.096" y="1011.5846598113462" font-size="30.15154045815901" text-anchor="middle" fill="#142035">0:4</text><path d="M 901.1200000000001 757.9569959574205 l 50.50336890020572 72.89533407379191" stroke="#fff" stroke-width="10"/><circle cx="901.1200000000001" cy="757.9569959574205" r="6" fill="#132035"/><text x="1024" y="782.7876763347278" font-size="30.15154045815901" text-anchor="middle" fill="#142035">3:4</text><path d="M 931.84 1130.4172016170317 l 37.87752667515433 -80.18486748117107" stroke="#fff" stroke-width="10"/><circle cx="931.84" cy="1130.4172016170317" r="6" fill="#132035"/><text x="891.9040000000001" y="1011.5846598113462" font-size="30.15154045815901" text-anchor="middle" fill="#142035">0:3</text><text x="1024" y="935.3189986524735" font-size="177.36200269505298" font-family="sans-serif" font-weight="bold" text-anchor="middle" dominant-baseline="central" fill="#101a2d" stroke="#fff" stroke-width="1">2</text><path d="M 917.5827983829682 1059.4724005390106 h 212.83440323406356 l -35.4724005390106 -24.83068037730742 m 35.4724005390106 24.83068037730742 l -35.4724005390106 24.83068037730742" fill="none" stroke="#142035" stroke-width="3"/><text x="1024" y="821.8073169276396" font-size="28.377920431208477" text-anchor="middle">d8 / F2</text></g><defs><clipPath id="face-3"><polygon points="1024,1290.0430040425795 716.8000000000001,757.9569959574205 409.59999999999997,1290.0430040425795"/></clipPath></defs><g clip-path="url(#face-3)"><polygon points="1024,1290.0430040425795 716.8000000000001,757.9569959574205 409.59999999999997,1290.0430040425795" fill="url(#grid)"/><polygon points="1024,1290.0430040425795 716.8000000000001,757.9569959574205 409.59999999999997,1290.0430040425795" fill="none" stroke="#162338" stroke-width="3"/><path d="M 931.8399999999999 1130.4172016170317 l -88.38089557536006 -7.289533407379137" stroke="#fff" stroke-width="10"/><circle cx="931.8399999999999" cy="1130.4172016170317" r="6" fill="#132035"/><text x="848.8960000000001" y="1036.4153401886538" font-size="30.15154045815901" text-anchor="middle" fill="#142035">0:3</text><path d="M 624.64 917.5827983829682 l 37.87752667515434 80.18486748117107" stroke="#fff" stroke-width="10"/><circle cx="624.64" cy="917.5827983829682" r="6" fill="#132035"/><text x="584.7040000000001" y="1036.4153401886538" font-size="30.15154045815901" text-anchor="middle" fill="#142035">3:5</text><path d="M 839.68 1290.0430040425795 l -50.50336890020572 -72.89533407379191" stroke="#fff" stroke-width="10"/><circle cx="839.68" cy="1290.0430040425795" r="6" fill="#132035"/><text x="716.8" y="1265.212323665272" font-size="30.15154045815901" text-anchor="middle" fill="#142035">0:5</text><text x="716.8000000000001" y="1112.6810013475265" font-size="177.36200269505298" font-family="sans-serif" font-weight="bold" text-anchor="middle" dominant-baseline="central" fill="#101a2d" stroke="#fff" stroke-width="1">3</text><path d="M 610.3827983829683 1236.8344032340635 h 212.83440323406356 l -35.4724005390106 -24.83068037730742 m 35.4724005390106 24.83068037730742 l -35.4724005390106 24.83068037730742" fill="none" stroke="#142035" stroke-width="3"/><text x="716.8000000000001" y="999.1693196226926" font-size="28.377920431208477" text-anchor="middle">d8 / F3</text></g><defs><clipPath id="face-4"><polygon points="1024,1290.0430040425795 1331.2,1822.1290121277386 1638.3999999999999,1290.0430040425795"/></clipPath></defs><g clip-path="url(#face-4)"><polygon points="1024,1290.0430040425795 1331.2,1822.1290121277386 1638.3999999999999,1290.0430040425795" fill="url(#grid)"/><polygon points="1024,1290.0430040425795 1331.2,1822.1290121277386 1638.3999999999999,1290.0430040425795" fill="none" stroke="#162338" stroke-width="3"/><path d="M 1116.1599999999999 1449.668806468127 l 88.38089557536003 7.289533407379318" stroke="#fff" stroke-width="10"/><circle cx="1116.1599999999999" cy="1449.668806468127" r="6" fill="#132035"/><text x="1199.1039999999998" y="1543.6706678965052" font-size="30.15154045815901" text-anchor="middle" fill="#142035">0:5</text><path d="M 1546.24 1449.668806468127 l -88.38089557536003 7.28953340737931" stroke="#fff" stroke-width="10"/><circle cx="1546.24" cy="1449.668806468127" r="6" fill="#132035"/><text x="1463.2959999999998" y="1543.6706678965052" font-size="30.15154045815901" text-anchor="middle" fill="#142035">2:5</text><path d="M 1208.32 1290.0430040425795 l 50.50336890020568 72.89533407379194" stroke="#fff" stroke-width="10"/><circle cx="1208.32" cy="1290.0430040425795" r="6" fill="#132035"/><text x="1331.1999999999998" y="1314.8736844198868" font-size="30.15154045815901" text-anchor="middle" fill="#142035">0:2</text><text x="1331.1999999999998" y="1467.4050067376327" font-size="177.36200269505298" font-family="sans-serif" font-weight="bold" text-anchor="middle" dominant-baseline="central" fill="#101a2d" stroke="#fff" stroke-width="1">4</text><path d="M 1224.782798382968 1591.5584086241697 h 212.83440323406356 l -35.4724005390106 -24.83068037730742 m 35.4724005390106 24.83068037730742 l -35.4724005390106 24.83068037730742" fill="none" stroke="#142035" stroke-width="3"/><text x="1331.1999999999998" y="1353.8933250127989" font-size="28.377920431208477" text-anchor="middle">d8 / F4</text></g><defs><clipPath id="face-5"><polygon points="1024,225.87098787226114 716.8000000000001,757.9569959574205 1331.2,757.9569959574205"/></clipPath></defs><g clip-path="url(#face-5)"><polygon points="1024,225.87098787226114 716.8000000000001,757.9569959574205 1331.2,757.9569959574205" fill="url(#grid)"/><polygon points="1024,225.87098787226114 716.8000000000001,757.9569959574205 1331.2,757.9569959574205" fill="none" stroke="#162338" stroke-width="3"/><path d="M 931.8399999999999 385.49679029780896 l 37.877526675154314 80.1848674811711" stroke="#fff" stroke-width="10"/><circle cx="931.8399999999999" cy="385.49679029780896" r="6" fill="#132035"/><text x="891.9040000000001" y="504.32933210349455" font-size="30.151540458159012" text-anchor="middle" fill="#142035">1:3</text><path d="M 901.12 757.9569959574205 l 50.50336890020574 -72.89533407379193" stroke="#fff" stroke-width="10"/><circle cx="901.12" cy="757.9569959574205" r="6" fill="#132035"/><text x="1024" y="733.1263155801131" font-size="30.151540458159012" text-anchor="middle" fill="#142035">3:4</text><path d="M 1116.16 385.49679029780896 l -37.877526675154314 80.1848674811711" stroke="#fff" stroke-width="10"/><circle cx="1116.16" cy="385.49679029780896" r="6" fill="#132035"/><text x="1156.096" y="504.32933210349455" font-size="30.151540458159012" text-anchor="middle" fill="#142035">1:4</text><text x="1024" y="580.5949932623674" font-size="177.362002695053" font-family="sans-serif" font-weight="bold" text-anchor="middle" dominant-baseline="central" fill="#101a2d" stroke="#fff" stroke-width="1">5</text><path d="M 917.5827983829682 704.7483951489046 h 212.83440323406361 l -35.4724005390106 -24.830680377307424 m 35.4724005390106 24.830680377307424 l -35.4724005390106 24.830680377307424" fill="none" stroke="#142035" stroke-width="3"/><text x="1024" y="467.0833115375335" font-size="28.37792043120848" text-anchor="middle">d8 / F5</text></g><defs><clipPath id="face-6"><polygon points="1945.6000000000001,757.9569959574203 1331.2,757.9569959574203 1638.3999999999999,1290.0430040425795"/></clipPath></defs><g clip-path="url(#face-6)"><polygon points="1945.6000000000001,757.9569959574203 1331.2,757.9569959574203 1638.3999999999999,1290.0430040425795" fill="url(#grid)"/><polygon points="1945.6000000000001,757.9569959574203 1331.2,757.9569959574203 1638.3999999999999,1290.0430040425795" fill="none" stroke="#162338" stroke-width="3"/><path d="M 1761.2800000000002 757.9569959574203 l -50.50336890020583 72.89533407379184" stroke="#fff" stroke-width="10"/><circle cx="1761.2800000000002" cy="757.9569959574203" r="6" fill="#132035"/><text x="1638.4" y="782.7876763347278" font-size="30.15154045815901" text-anchor="middle" fill="#142035">1:4</text><path d="M 1546.24 1130.4172016170317 l 37.87752667515427 -80.18486748117111" stroke="#fff" stroke-width="10"/><circle cx="1546.24" cy="1130.4172016170317" r="6" fill="#132035"/><text x="1506.3039999999999" y="1011.5846598113462" font-size="30.15154045815901" text-anchor="middle" fill="#142035">2:4</text><path d="M 1853.44 917.5827983829681 l -88.38089557536004 7.289533407379172" stroke="#fff" stroke-width="10"/><circle cx="1853.44" cy="917.5827983829681" r="6" fill="#132035"/><text x="1770.4959999999999" y="1011.5846598113462" font-size="30.15154045815901" text-anchor="middle" fill="#142035">1:2</text><text x="1638.3999999999999" y="935.3189986524734" font-size="177.36200269505298" font-family="sans-serif" font-weight="bold" text-anchor="middle" dominant-baseline="central" fill="#101a2d" stroke="#fff" stroke-width="1">6</text><path d="M 1531.982798382968 1059.4724005390106 h 212.83440323406356 l -35.4724005390106 -24.83068037730742 m 35.4724005390106 24.83068037730742 l -35.4724005390106 24.83068037730742" fill="none" stroke="#142035" stroke-width="3"/><text x="1638.3999999999999" y="821.8073169276395" font-size="28.377920431208477" text-anchor="middle">d8 / F6</text></g><defs><clipPath id="face-7"><polygon points="1945.6000000000001,1822.1290121277386 1638.3999999999999,1290.0430040425795 1331.2,1822.1290121277386"/></clipPath></defs><g clip-path="url(#face-7)"><polygon points="1945.6000000000001,1822.1290121277386 1638.3999999999999,1290.0430040425795 1331.2,1822.1290121277386" fill="url(#grid)"/><polygon points="1945.6000000000001,1822.1290121277386 1638.3999999999999,1290.0430040425795 1331.2,1822.1290121277386" fill="none" stroke="#162338" stroke-width="3"/><path d="M 1853.44 1662.5032097021908 l -88.38089557536004 -7.289533407379125" stroke="#fff" stroke-width="10"/><circle cx="1853.44" cy="1662.5032097021908" r="6" fill="#132035"/><text x="1770.4959999999999" y="1568.5013482738127" font-size="30.151540458159005" text-anchor="middle" fill="#142035">1:2</text><path d="M 1546.2399999999998 1449.668806468127 l 37.877526675154286 80.18486748117108" stroke="#fff" stroke-width="10"/><circle cx="1546.2399999999998" cy="1449.668806468127" r="6" fill="#132035"/><text x="1506.3039999999999" y="1568.5013482738127" font-size="30.151540458159005" text-anchor="middle" fill="#142035">2:5</text><path d="M 1761.2800000000002 1822.1290121277386 l -50.503368900205835 -72.89533407379182" stroke="#fff" stroke-width="10"/><circle cx="1761.2800000000002" cy="1822.1290121277386" r="6" fill="#132035"/><text x="1638.4" y="1797.2983317504313" font-size="30.151540458159005" text-anchor="middle" fill="#142035">1:5</text><text x="1638.3999999999999" y="1644.7670094326857" font-size="177.36200269505295" font-family="sans-serif" font-weight="bold" text-anchor="middle" dominant-baseline="central" fill="#101a2d" stroke="#fff" stroke-width="1">7</text><path d="M 1531.9827983829682 1768.9204113192227 h 212.83440323406353 l -35.47240053901059 -24.830680377307417 m 35.47240053901059 24.830680377307417 l -35.47240053901059 24.830680377307417" fill="none" stroke="#142035" stroke-width="3"/><text x="1638.3999999999999" y="1531.2553277078518" font-size="28.377920431208473" text-anchor="middle">d8 / F7</text></g><defs><clipPath id="face-8"><polygon points="102.39999999999992,757.9569959574205 409.59999999999997,1290.0430040425795 716.8000000000001,757.9569959574205"/></clipPath></defs><g clip-path="url(#face-8)"><polygon points="102.39999999999992,757.9569959574205 409.59999999999997,1290.0430040425795 716.8000000000001,757.9569959574205" fill="url(#grid)"/><polygon points="102.39999999999992,757.9569959574205 409.59999999999997,1290.0430040425795 716.8000000000001,757.9569959574205" fill="none" stroke="#162338" stroke-width="3"/><path d="M 194.55999999999992 917.5827983829682 l 88.38089557536006 7.289533407379178" stroke="#fff" stroke-width="10"/><circle cx="194.55999999999992" cy="917.5827983829682" r="6" fill="#132035"/><text x="277.50399999999996" y="1011.5846598113462" font-size="30.15154045815901" text-anchor="middle" fill="#142035">1:5</text><path d="M 624.64 917.5827983829683 l -88.38089557536006 7.289533407379131" stroke="#fff" stroke-width="10"/><circle cx="624.64" cy="917.5827983829683" r="6" fill="#132035"/><text x="541.696" y="1011.5846598113462" font-size="30.15154045815901" text-anchor="middle" fill="#142035">3:5</text><path d="M 286.71999999999997 757.9569959574205 l 50.50336890020576 72.89533407379189" stroke="#fff" stroke-width="10"/><circle cx="286.71999999999997" cy="757.9569959574205" r="6" fill="#132035"/><text x="409.6" y="782.7876763347278" font-size="30.15154045815901" text-anchor="middle" fill="#142035">1:3</text><text x="409.59999999999997" y="935.3189986524735" font-size="177.36200269505298" font-family="sans-serif" font-weight="bold" text-anchor="middle" dominant-baseline="central" fill="#101a2d" stroke="#fff" stroke-width="1">8</text><path d="M 303.1827983829682 1059.4724005390106 h 212.83440323406356 l -35.4724005390106 -24.83068037730742 m 35.4724005390106 24.83068037730742 l -35.4724005390106 24.83068037730742" fill="none" stroke="#142035" stroke-width="3"/><text x="409.59999999999997" y="821.8073169276396" font-size="28.377920431208477" text-anchor="middle">d8 / F8</text></g></svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/asset-registry.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AudioBankDefinition, AudioSpriteManifest, DiceAssetCatalogManifest, DiceFaceAtlasDefinition, DiceMaterialDefinition, DicePatternDefinition, DiceSkinDefinition } from './types.js';
|
|
1
|
+
import type { AudioBankDefinition, AudioSpriteManifest, DiceAssetCatalogManifest, DiceFaceAtlasDefinition, DiceMaterialDefinition, DicePatternDefinition, DiceSkinDefinition, DiceSkinSetDefinition } from './types.js';
|
|
2
2
|
export interface RegisterDiceAssetOptions {
|
|
3
3
|
readonly replace?: boolean;
|
|
4
4
|
}
|
|
@@ -20,6 +20,7 @@ export declare class DiceAssetRegistry {
|
|
|
20
20
|
readonly materials: AssetRegistry<DiceMaterialDefinition>;
|
|
21
21
|
readonly patterns: AssetRegistry<DicePatternDefinition>;
|
|
22
22
|
readonly skins: AssetRegistry<DiceSkinDefinition>;
|
|
23
|
+
readonly skinSets: AssetRegistry<DiceSkinSetDefinition>;
|
|
23
24
|
readonly faces: AssetRegistry<DiceFaceAtlasDefinition>;
|
|
24
25
|
get revision(): number;
|
|
25
26
|
registerCatalog(catalog: DiceAssetCatalogManifest): void;
|
package/dist/asset-registry.js
CHANGED
|
@@ -60,6 +60,7 @@ export class DiceAssetRegistry {
|
|
|
60
60
|
materials = new AssetRegistry(validateMaterial);
|
|
61
61
|
patterns = new AssetRegistry(validatePattern);
|
|
62
62
|
skins = new AssetRegistry(validateSkin);
|
|
63
|
+
skinSets = new AssetRegistry(validateSkinSet);
|
|
63
64
|
faces = new AssetRegistry(validateFaceAtlas);
|
|
64
65
|
get revision() {
|
|
65
66
|
return (this.audio.revision +
|
|
@@ -67,6 +68,7 @@ export class DiceAssetRegistry {
|
|
|
67
68
|
this.materials.revision +
|
|
68
69
|
this.patterns.revision +
|
|
69
70
|
this.skins.revision +
|
|
71
|
+
this.skinSets.revision +
|
|
70
72
|
this.faces.revision);
|
|
71
73
|
}
|
|
72
74
|
registerCatalog(catalog) {
|
|
@@ -84,9 +86,17 @@ export class DiceAssetRegistry {
|
|
|
84
86
|
this.faces.register(asset);
|
|
85
87
|
for (const asset of catalog.skins ?? [])
|
|
86
88
|
this.skins.register(asset);
|
|
89
|
+
for (const asset of catalog.skinSets ?? [])
|
|
90
|
+
this.skinSets.register(asset);
|
|
87
91
|
this.validateReferences();
|
|
88
92
|
}
|
|
89
93
|
validateReferences() {
|
|
94
|
+
for (const set of this.skinSets.list()) {
|
|
95
|
+
for (const [dieType, skinId] of Object.entries(set.skins)) {
|
|
96
|
+
if (!this.skins.has(skinId))
|
|
97
|
+
throw new Error(`Skin set "${set.id}" ${dieType} references missing skin "${skinId}"`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
90
100
|
for (const bank of this.audioBanks.list()) {
|
|
91
101
|
const sprite = this.audio.get(bank.spriteId);
|
|
92
102
|
if (sprite === undefined)
|
|
@@ -177,6 +187,25 @@ function validateMaterial(material) {
|
|
|
177
187
|
}
|
|
178
188
|
}
|
|
179
189
|
function validatePattern(pattern) {
|
|
190
|
+
if (pattern.unwrap !== undefined) {
|
|
191
|
+
assertId(pattern.unwrap.geometryId);
|
|
192
|
+
if (pattern.repeat !== undefined)
|
|
193
|
+
throw new RangeError('unwrapped patterns cannot repeat');
|
|
194
|
+
if (Object.keys(pattern.unwrap.faces).length === 0)
|
|
195
|
+
throw new RangeError('unwrap must contain faces');
|
|
196
|
+
for (const [face, uvs] of Object.entries(pattern.unwrap.faces)) {
|
|
197
|
+
if (!/^[1-9]\d*$/.test(face) ||
|
|
198
|
+
!Array.isArray(uvs) ||
|
|
199
|
+
uvs.length < 3 ||
|
|
200
|
+
!uvs.every((uv) => Array.isArray(uv) &&
|
|
201
|
+
uv.length === 2 &&
|
|
202
|
+
uv.every((value) => Number.isFinite(value) && value >= 0 && value <= 1))) {
|
|
203
|
+
throw new RangeError(`Pattern "${pattern.id}" face ${face}: invalid surface UVs`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (pattern.unwrap.preview !== undefined)
|
|
207
|
+
validateReference(pattern.unwrap.preview, 'unwrap preview');
|
|
208
|
+
}
|
|
180
209
|
validateTexture(pattern.baseColor, 'baseColor');
|
|
181
210
|
if (pattern.normal !== undefined)
|
|
182
211
|
validateTexture(pattern.normal, 'normal');
|
|
@@ -201,3 +230,11 @@ function validateFaceAtlas(atlas) {
|
|
|
201
230
|
throw new RangeError('face atlas dimensions must be positive integers');
|
|
202
231
|
}
|
|
203
232
|
}
|
|
233
|
+
function validateSkinSet(set) {
|
|
234
|
+
if (Object.keys(set.skins).length === 0)
|
|
235
|
+
throw new RangeError('skin set must contain skins');
|
|
236
|
+
for (const [dieType, skinId] of Object.entries(set.skins)) {
|
|
237
|
+
assertId(dieType);
|
|
238
|
+
assertId(skinId);
|
|
239
|
+
}
|
|
240
|
+
}
|
package/dist/catalog-loader.js
CHANGED
|
@@ -35,6 +35,16 @@ export function resolveCatalogReferences(catalog, catalogUri) {
|
|
|
35
35
|
patterns: catalog.patterns.map((pattern) => ({
|
|
36
36
|
...pattern,
|
|
37
37
|
baseColor: resolveReference(pattern.baseColor),
|
|
38
|
+
...(pattern.unwrap === undefined
|
|
39
|
+
? {}
|
|
40
|
+
: {
|
|
41
|
+
unwrap: {
|
|
42
|
+
...pattern.unwrap,
|
|
43
|
+
...(pattern.unwrap.preview === undefined
|
|
44
|
+
? {}
|
|
45
|
+
: { preview: resolveReference(pattern.unwrap.preview) }),
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
38
48
|
...(pattern.normal === undefined ? {} : { normal: resolveReference(pattern.normal) }),
|
|
39
49
|
...(pattern.orm === undefined ? {} : { orm: resolveReference(pattern.orm) }),
|
|
40
50
|
})),
|
|
@@ -69,7 +79,15 @@ function isCatalogManifest(source) {
|
|
|
69
79
|
return false;
|
|
70
80
|
if (source.schemaVersion !== 1)
|
|
71
81
|
return false;
|
|
72
|
-
for (const key of [
|
|
82
|
+
for (const key of [
|
|
83
|
+
'audioSprites',
|
|
84
|
+
'audioBanks',
|
|
85
|
+
'materials',
|
|
86
|
+
'patterns',
|
|
87
|
+
'skins',
|
|
88
|
+
'faces',
|
|
89
|
+
'skinSets',
|
|
90
|
+
]) {
|
|
73
91
|
if (source[key] !== undefined && !Array.isArray(source[key]))
|
|
74
92
|
return false;
|
|
75
93
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ export { resolveImpactGain, WebAudioSpritePlayer, type AudioBufferSourceNodeLike
|
|
|
3
3
|
export { ThreeAssetMaterialProvider, type ThreeAssetMaterialProviderOptions, } from './three-material-provider.js';
|
|
4
4
|
export { DiceAssetCatalogLoader, parseCatalog, resolveCatalogReferences, type DiceAssetCatalogLoaderOptions, } from './catalog-loader.js';
|
|
5
5
|
export { ImpactSoundGate, type SoundCollisionEvent, type SoundImpactEvent, } from './impact-sound-gate.js';
|
|
6
|
-
export type { AudioBankDefinition, AudioBankKind, AudioSpriteClip, AudioSpriteManifest, DiceAssetCatalogManifest, DiceAssetMetadataValue, DiceAssetReference, DiceFaceAtlasDefinition, DiceFaceRegion, DiceMaterialDefinition, DicePatternDefinition, DiceSkinCompositeMode, DiceSkinDefinition, RuntimeTextureReference, } from './types.js';
|
|
6
|
+
export type { AudioBankDefinition, AudioBankKind, AudioSpriteClip, AudioSpriteManifest, DiceAssetCatalogManifest, DiceAssetMetadataValue, DiceAssetReference, DiceFaceAtlasDefinition, DiceFaceRegion, DiceMaterialDefinition, DicePatternDefinition, DiceSkinCompositeMode, DiceSkinDefinition, DiceSkinSetDefinition, DiceSurfaceUnwrap, RuntimeTextureReference, } from './types.js';
|
|
7
|
+
export { prepareTexturedSkinSet, type PreparedTexturedSkinSet, type PrepareTexturedSkinSetOptions, type TexturedSkinSetTarget, type TexturedSkinSetProvider, type TexturedSkinSetSelectionContext, } from './textured-skin-set.js';
|
package/dist/index.js
CHANGED
|
@@ -3,3 +3,4 @@ export { resolveImpactGain, WebAudioSpritePlayer, } from './web-audio-player.js'
|
|
|
3
3
|
export { ThreeAssetMaterialProvider, } from './three-material-provider.js';
|
|
4
4
|
export { DiceAssetCatalogLoader, parseCatalog, resolveCatalogReferences, } from './catalog-loader.js';
|
|
5
5
|
export { ImpactSoundGate, } from './impact-sound-gate.js';
|
|
6
|
+
export { prepareTexturedSkinSet, } from './textured-skin-set.js';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { VisualPresetDescriptor } from '@dice-o-rolla/dice-renderer';
|
|
2
|
+
import type { DiceAssetRegistry } from './asset-registry.js';
|
|
3
|
+
export interface TexturedSkinSetTarget {
|
|
4
|
+
registerVisualPreset(preset: VisualPresetDescriptor): VisualPresetDescriptor;
|
|
5
|
+
unregisterVisualPreset(id: string): boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface TexturedSkinSetProvider {
|
|
8
|
+
readonly registry: DiceAssetRegistry;
|
|
9
|
+
prepareSkin(id: string): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export interface PrepareTexturedSkinSetOptions {
|
|
12
|
+
readonly registry: DiceAssetRegistry;
|
|
13
|
+
readonly provider: TexturedSkinSetProvider;
|
|
14
|
+
readonly skinSetId: string;
|
|
15
|
+
readonly presetPrefix?: string;
|
|
16
|
+
readonly soundPackId?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface TexturedSkinSetSelectionContext {
|
|
19
|
+
readonly physicalDieType: string;
|
|
20
|
+
readonly component?: {
|
|
21
|
+
readonly role: string;
|
|
22
|
+
readonly groupType: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export interface PreparedTexturedSkinSet {
|
|
26
|
+
readonly presets: readonly VisualPresetDescriptor[];
|
|
27
|
+
/** Register without changing defaults. Repeated registration through this API is idempotent. */
|
|
28
|
+
register(engine: TexturedSkinSetTarget): void;
|
|
29
|
+
readonly visualPresetSelector: (context: TexturedSkinSetSelectionContext) => string | undefined;
|
|
30
|
+
}
|
|
31
|
+
/** Validate and load first; engine registration is an explicit, separate operation. */
|
|
32
|
+
export declare function prepareTexturedSkinSet(options: PrepareTexturedSkinSetOptions): Promise<PreparedTexturedSkinSet>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { getDieGeometry } from '@dice-o-rolla/dice-geometry';
|
|
2
|
+
import { validateSurfaceUvs } from '@dice-o-rolla/dice-renderer-three';
|
|
3
|
+
const registrations = new WeakMap();
|
|
4
|
+
const geometries = {
|
|
5
|
+
d4: 'd4',
|
|
6
|
+
d6: 'd6',
|
|
7
|
+
d8: 'd8',
|
|
8
|
+
d10: 'd10',
|
|
9
|
+
d12: 'd12',
|
|
10
|
+
d20: 'd20',
|
|
11
|
+
d100: 'd10',
|
|
12
|
+
d66: 'd6',
|
|
13
|
+
};
|
|
14
|
+
const identifier = /^[a-z0-9][a-z0-9._:-]*$/i;
|
|
15
|
+
/** Validate and load first; engine registration is an explicit, separate operation. */
|
|
16
|
+
export async function prepareTexturedSkinSet(options) {
|
|
17
|
+
const { registry, provider, skinSetId } = options;
|
|
18
|
+
if (provider.registry !== registry)
|
|
19
|
+
throw new Error('The material provider must use the supplied asset registry');
|
|
20
|
+
registry.validateReferences();
|
|
21
|
+
const set = registry.skinSets.get(skinSetId);
|
|
22
|
+
if (set === undefined)
|
|
23
|
+
throw new Error(`Unknown skin set: ${skinSetId}`);
|
|
24
|
+
const prefix = options.presetPrefix ?? `skin-set:${skinSetId}`;
|
|
25
|
+
if (!identifier.test(prefix) ||
|
|
26
|
+
(options.soundPackId !== undefined && !identifier.test(options.soundPackId)))
|
|
27
|
+
throw new RangeError('Preset prefix and sound pack id must be portable identifiers');
|
|
28
|
+
const entries = Object.entries(set.skins).map(([type, skinId]) => {
|
|
29
|
+
const geometryId = Object.hasOwn(geometries, type) ? geometries[type] : undefined;
|
|
30
|
+
if (geometryId === undefined)
|
|
31
|
+
throw new Error(`Skin set "${skinSetId}" has unsupported type "${type}"`);
|
|
32
|
+
const skin = registry.skins.get(skinId);
|
|
33
|
+
const pattern = registry.patterns.get(skin.patternId);
|
|
34
|
+
if (pattern.unwrap?.geometryId !== geometryId)
|
|
35
|
+
throw new Error(`Skin "${skinId}" for ${type} requires an unwrap for ${geometryId}`);
|
|
36
|
+
try {
|
|
37
|
+
validateSurfaceUvs(getDieGeometry(geometryId), pattern.unwrap.faces);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
throw new Error(`Skin "${skinId}": ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
41
|
+
}
|
|
42
|
+
const preset = Object.freeze({
|
|
43
|
+
id: `${prefix}:${type}`,
|
|
44
|
+
dieType: geometryId,
|
|
45
|
+
geometryId,
|
|
46
|
+
skinId,
|
|
47
|
+
...(options.soundPackId === undefined ? {} : { soundPackId: options.soundPackId }),
|
|
48
|
+
});
|
|
49
|
+
return {
|
|
50
|
+
type,
|
|
51
|
+
skinId,
|
|
52
|
+
preset,
|
|
53
|
+
fingerprint: canonical({
|
|
54
|
+
preset,
|
|
55
|
+
skin,
|
|
56
|
+
pattern,
|
|
57
|
+
material: registry.materials.get(skin.materialId),
|
|
58
|
+
atlas: skin.faceAtlasId === undefined ? null : registry.faces.get(skin.faceAtlasId),
|
|
59
|
+
}),
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
await Promise.all([...new Set(entries.map((entry) => entry.skinId))].map((id) => provider.prepareSkin(id)));
|
|
63
|
+
const byType = new Map(entries.map((entry) => [entry.type, entry.preset.id]));
|
|
64
|
+
return Object.freeze({
|
|
65
|
+
presets: Object.freeze(entries.map((entry) => entry.preset)),
|
|
66
|
+
register(engine) {
|
|
67
|
+
const known = registrations.get(engine) ?? new Map();
|
|
68
|
+
for (const entry of entries) {
|
|
69
|
+
const previous = known.get(entry.preset.id);
|
|
70
|
+
if (previous !== undefined && previous !== entry.fingerprint)
|
|
71
|
+
throw new Error(`Conflicting preset: ${entry.preset.id}`);
|
|
72
|
+
}
|
|
73
|
+
const added = [];
|
|
74
|
+
try {
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
if (known.has(entry.preset.id))
|
|
77
|
+
continue;
|
|
78
|
+
engine.registerVisualPreset(entry.preset);
|
|
79
|
+
added.push(entry.preset.id);
|
|
80
|
+
known.set(entry.preset.id, entry.fingerprint);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
const rollbackErrors = [];
|
|
85
|
+
for (const id of added.toReversed()) {
|
|
86
|
+
try {
|
|
87
|
+
engine.unregisterVisualPreset(id);
|
|
88
|
+
known.delete(id);
|
|
89
|
+
}
|
|
90
|
+
catch (rollbackError) {
|
|
91
|
+
rollbackErrors.push(rollbackError);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
registrations.set(engine, known);
|
|
95
|
+
if (rollbackErrors.length > 0)
|
|
96
|
+
throw new AggregateError([error, ...rollbackErrors], 'Preset registration and rollback failed', { cause: error });
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
registrations.set(engine, known);
|
|
100
|
+
},
|
|
101
|
+
visualPresetSelector(context) {
|
|
102
|
+
return byType.get(context.component?.role === 'tens' ? context.component.groupType : context.physicalDieType);
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
function canonical(value) {
|
|
107
|
+
if (Array.isArray(value))
|
|
108
|
+
return `[${value.map(canonical).join(',')}]`;
|
|
109
|
+
if (value !== null && typeof value === 'object')
|
|
110
|
+
return `{${Object.entries(value)
|
|
111
|
+
.toSorted(([a], [b]) => a.localeCompare(b))
|
|
112
|
+
.map(([key, child]) => `${JSON.stringify(key)}:${canonical(child)}`)
|
|
113
|
+
.join(',')}}`;
|
|
114
|
+
return JSON.stringify(value) ?? 'null';
|
|
115
|
+
}
|
|
@@ -10,6 +10,7 @@ export declare class ThreeAssetMaterialProvider implements ThreeFaceMaterialProv
|
|
|
10
10
|
#private;
|
|
11
11
|
readonly registry: DiceAssetRegistry;
|
|
12
12
|
constructor(registry: DiceAssetRegistry, options: ThreeAssetMaterialProviderOptions);
|
|
13
|
+
getSurfaceUvs(...[definition, preset]: Parameters<NonNullable<ThreeFaceMaterialProvider['getSurfaceUvs']>>): Readonly<Record<number, readonly (readonly [u: number, v: number])[]>> | undefined;
|
|
13
14
|
prepareSkin(skinId: string): Promise<void>;
|
|
14
15
|
createFace(context: FaceMaterialContext): FaceMaterialResource;
|
|
15
16
|
dispose(): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ThreeMaterialFactory, } from '@dice-o-rolla/dice-renderer-three';
|
|
2
|
-
import { Color, MeshPhysicalMaterial, RepeatWrapping, SRGBColorSpace, } from 'three';
|
|
1
|
+
import { validateSurfaceUvs, ThreeMaterialFactory, } from '@dice-o-rolla/dice-renderer-three';
|
|
2
|
+
import { Color, ClampToEdgeWrapping, MeshPhysicalMaterial, RepeatWrapping, SRGBColorSpace, } from 'three';
|
|
3
3
|
import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
|
|
4
4
|
/** KTX2-backed PBR adapter; lower-level renderer packages remain asset-schema agnostic. */
|
|
5
5
|
export class ThreeAssetMaterialProvider {
|
|
@@ -8,13 +8,33 @@ export class ThreeAssetMaterialProvider {
|
|
|
8
8
|
#textures = new Map();
|
|
9
9
|
#skins = new Map();
|
|
10
10
|
#fallback = new ThreeMaterialFactory();
|
|
11
|
+
#disposed = false;
|
|
11
12
|
constructor(registry, options) {
|
|
12
13
|
this.registry = registry;
|
|
13
14
|
this.#loader = new KTX2Loader()
|
|
14
15
|
.setTranscoderPath(options.transcoderPath)
|
|
15
16
|
.detectSupport(options.renderer);
|
|
16
17
|
}
|
|
18
|
+
getSurfaceUvs(...[definition, preset]) {
|
|
19
|
+
const skin = preset?.skinId === undefined ? undefined : this.registry.skins.get(preset.skinId);
|
|
20
|
+
if (skin === undefined)
|
|
21
|
+
return undefined;
|
|
22
|
+
const unwrap = this.registry.patterns.get(skin.patternId)?.unwrap;
|
|
23
|
+
if (unwrap === undefined)
|
|
24
|
+
return undefined;
|
|
25
|
+
if (unwrap.geometryId !== definition.id)
|
|
26
|
+
throw new RangeError(`Skin "${skin.id}" requires geometry "${unwrap.geometryId}", received "${definition.id}"`);
|
|
27
|
+
try {
|
|
28
|
+
validateSurfaceUvs(definition, unwrap.faces);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
throw new RangeError(`Skin "${skin.id}": ${error instanceof Error ? error.message : String(error)}`);
|
|
32
|
+
}
|
|
33
|
+
return unwrap.faces;
|
|
34
|
+
}
|
|
17
35
|
async prepareSkin(skinId) {
|
|
36
|
+
if (this.#disposed)
|
|
37
|
+
throw new Error('Asset material provider is disposed');
|
|
18
38
|
if (this.#skins.has(skinId))
|
|
19
39
|
return;
|
|
20
40
|
this.registry.validateReferences();
|
|
@@ -24,16 +44,15 @@ export class ThreeAssetMaterialProvider {
|
|
|
24
44
|
const pattern = this.registry.patterns.get(skin.patternId);
|
|
25
45
|
const atlas = skin.faceAtlasId === undefined ? undefined : this.registry.faces.get(skin.faceAtlasId);
|
|
26
46
|
const [baseColor, normal, orm, faces] = await Promise.all([
|
|
27
|
-
this.#load(pattern.baseColor),
|
|
28
|
-
pattern.normal === undefined
|
|
29
|
-
|
|
47
|
+
this.#load(pattern.baseColor, pattern.unwrap !== undefined),
|
|
48
|
+
pattern.normal === undefined
|
|
49
|
+
? undefined
|
|
50
|
+
: this.#load(pattern.normal, pattern.unwrap !== undefined),
|
|
51
|
+
pattern.orm === undefined ? undefined : this.#load(pattern.orm, pattern.unwrap !== undefined),
|
|
30
52
|
atlas === undefined ? undefined : this.#load(atlas.texture),
|
|
31
53
|
]);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
normal.wrapS = normal.wrapT = RepeatWrapping;
|
|
35
|
-
if (orm !== undefined)
|
|
36
|
-
orm.wrapS = orm.wrapT = RepeatWrapping;
|
|
54
|
+
if (this.#disposed)
|
|
55
|
+
throw new Error('Asset material provider is disposed');
|
|
37
56
|
this.#skins.set(skinId, {
|
|
38
57
|
skin,
|
|
39
58
|
baseColor,
|
|
@@ -70,7 +89,7 @@ export class ThreeAssetMaterialProvider {
|
|
|
70
89
|
if (definition.normalScale !== undefined)
|
|
71
90
|
material.normalScale.setScalar(definition.normalScale);
|
|
72
91
|
if (loaded.faces !== undefined && region !== undefined && atlas !== undefined) {
|
|
73
|
-
configureCompositingShader(material, loaded.faces, region, atlas.width, atlas.height, loaded.skin);
|
|
92
|
+
configureCompositingShader(material, loaded.faces, region, atlas.width, atlas.height, loaded.skin, loaded.baseColor.channel === 1);
|
|
74
93
|
}
|
|
75
94
|
return {
|
|
76
95
|
material,
|
|
@@ -80,23 +99,41 @@ export class ThreeAssetMaterialProvider {
|
|
|
80
99
|
};
|
|
81
100
|
}
|
|
82
101
|
dispose() {
|
|
83
|
-
|
|
84
|
-
|
|
102
|
+
if (this.#disposed)
|
|
103
|
+
return;
|
|
104
|
+
this.#disposed = true;
|
|
105
|
+
const pending = [...this.#textures.values()];
|
|
106
|
+
for (const texture of pending)
|
|
107
|
+
void texture.then((value) => value.dispose(), () => undefined);
|
|
85
108
|
this.#textures.clear();
|
|
86
109
|
this.#skins.clear();
|
|
87
|
-
|
|
110
|
+
// Let active transcodes settle before terminating their workers.
|
|
111
|
+
void Promise.allSettled(pending).then(() => this.#loader.dispose());
|
|
88
112
|
}
|
|
89
|
-
#load(reference) {
|
|
90
|
-
const
|
|
113
|
+
#load(reference, surface = false) {
|
|
114
|
+
const key = `${reference.uri}:${reference.colorSpace}:${surface}`;
|
|
115
|
+
const existing = this.#textures.get(key);
|
|
91
116
|
if (existing !== undefined)
|
|
92
117
|
return existing;
|
|
93
|
-
const pending = this.#loader
|
|
118
|
+
const pending = this.#loader
|
|
119
|
+
.loadAsync(reference.uri)
|
|
120
|
+
.then((texture) => {
|
|
121
|
+
if (this.#disposed) {
|
|
122
|
+
texture.dispose();
|
|
123
|
+
throw new Error('Asset material provider is disposed');
|
|
124
|
+
}
|
|
94
125
|
texture.colorSpace = reference.colorSpace === 'srgb' ? SRGBColorSpace : '';
|
|
126
|
+
texture.channel = surface ? 1 : 0;
|
|
127
|
+
texture.wrapS = texture.wrapT = surface ? ClampToEdgeWrapping : RepeatWrapping;
|
|
95
128
|
texture.generateMipmaps = false;
|
|
96
129
|
texture.needsUpdate = true;
|
|
97
130
|
return texture;
|
|
131
|
+
})
|
|
132
|
+
.catch((error) => {
|
|
133
|
+
this.#textures.delete(key);
|
|
134
|
+
throw new Error(`Texture "${reference.uri}": ${error instanceof Error ? error.message : 'load failed'}`, { cause: error });
|
|
98
135
|
});
|
|
99
|
-
this.#textures.set(
|
|
136
|
+
this.#textures.set(key, pending);
|
|
100
137
|
return pending;
|
|
101
138
|
}
|
|
102
139
|
#createFallback(context) {
|
|
@@ -110,7 +147,7 @@ export class ThreeAssetMaterialProvider {
|
|
|
110
147
|
};
|
|
111
148
|
}
|
|
112
149
|
}
|
|
113
|
-
function configureCompositingShader(material, atlas, region, width, height, skin) {
|
|
150
|
+
function configureCompositingShader(material, atlas, region, width, height, skin, surface) {
|
|
114
151
|
const labelColor = new Color(skin.labelColor ?? '#111111');
|
|
115
152
|
material.onBeforeCompile = (shader) => {
|
|
116
153
|
shader.uniforms.faceAtlas = { value: atlas };
|
|
@@ -124,9 +161,13 @@ function configureCompositingShader(material, atlas, region, width, height, skin
|
|
|
124
161
|
shader.uniforms.compositeMode = {
|
|
125
162
|
value: skin.composite === 'overlay' ? 2 : skin.composite === 'multiply' ? 1 : 0,
|
|
126
163
|
};
|
|
164
|
+
shader.vertexShader = shader.vertexShader
|
|
165
|
+
.replace('#include <common>', '#include <common>\nvarying vec2 diceLabelUv;')
|
|
166
|
+
.replace('#include <uv_vertex>', '#include <uv_vertex>\ndiceLabelUv = uv;');
|
|
127
167
|
shader.fragmentShader = shader.fragmentShader
|
|
168
|
+
.replace('#include <common>', '#include <common>\nvarying vec2 diceLabelUv;')
|
|
128
169
|
.replace('#include <map_pars_fragment>', `#include <map_pars_fragment>\nuniform sampler2D faceAtlas;\nuniform vec4 faceRect;\nuniform vec3 labelColor;\nuniform float skinHue;\nuniform float skinSaturation;\nuniform vec2 patternScale;\nuniform int compositeMode;`)
|
|
129
|
-
.replace('#include <map_fragment>', `#ifdef USE_MAP\nvec4 skinPattern = texture2D(map, fract(vMapUv * patternScale));\nvec3 skinTint = diffuseColor.rgb;\nif (compositeMode == 0) diffuseColor.rgb = mix(skinTint, skinPattern.rgb, skinPattern.a);\nelse if (compositeMode == 1) diffuseColor.rgb = skinTint * skinPattern.rgb;\nelse { vec3 low = 2.0 * skinTint * skinPattern.rgb; vec3 high = 1.0 - 2.0 * (1.0 - skinTint) * (1.0 - skinPattern.rgb); diffuseColor.rgb = mix(low, high, step(vec3(0.5), skinTint)); }\ndiffuseColor.a *= skinPattern.a;\n#endif\nvec3 skinGray = vec3(dot(diffuseColor.rgb, vec3(0.2126, 0.7152, 0.0722)));\ndiffuseColor.rgb = mix(skinGray, diffuseColor.rgb, skinSaturation);\nfloat skinAngle = skinHue * 6.28318530718;\nvec3 skinAxis = normalize(vec3(1.0));\ndiffuseColor.rgb = diffuseColor.rgb * cos(skinAngle) + cross(skinAxis, diffuseColor.rgb) * sin(skinAngle) + skinAxis * dot(skinAxis, diffuseColor.rgb) * (1.0 - cos(skinAngle));\nvec2 faceUv = faceRect.xy +
|
|
170
|
+
.replace('#include <map_fragment>', `#ifdef USE_MAP\nvec4 skinPattern = texture2D(map, ${surface ? 'vMapUv' : 'fract(vMapUv * patternScale)'});\nvec3 skinTint = diffuseColor.rgb;\nif (compositeMode == 0) diffuseColor.rgb = mix(skinTint, skinPattern.rgb, skinPattern.a);\nelse if (compositeMode == 1) diffuseColor.rgb = skinTint * skinPattern.rgb;\nelse { vec3 low = 2.0 * skinTint * skinPattern.rgb; vec3 high = 1.0 - 2.0 * (1.0 - skinTint) * (1.0 - skinPattern.rgb); diffuseColor.rgb = mix(low, high, step(vec3(0.5), skinTint)); }\ndiffuseColor.a *= skinPattern.a;\n#endif\nvec3 skinGray = vec3(dot(diffuseColor.rgb, vec3(0.2126, 0.7152, 0.0722)));\ndiffuseColor.rgb = mix(skinGray, diffuseColor.rgb, skinSaturation);\nfloat skinAngle = skinHue * 6.28318530718;\nvec3 skinAxis = normalize(vec3(1.0));\ndiffuseColor.rgb = diffuseColor.rgb * cos(skinAngle) + cross(skinAxis, diffuseColor.rgb) * sin(skinAngle) + skinAxis * dot(skinAxis, diffuseColor.rgb) * (1.0 - cos(skinAngle));\nvec2 faceUv = faceRect.xy + diceLabelUv * faceRect.zw;\nfloat faceMask = texture2D(faceAtlas, faceUv).a;\ndiffuseColor.rgb = mix(diffuseColor.rgb, labelColor, faceMask);`);
|
|
130
171
|
};
|
|
131
172
|
material.customProgramCacheKey = () => `dice-skin:${skin.id}`;
|
|
132
173
|
}
|