@dice-o-rolla/dice-renderer-three 0.1.1 → 0.2.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/dist/index.d.ts +1 -1
- package/dist/mesh-factory.d.ts +18 -4
- package/dist/mesh-factory.js +27 -7
- package/dist/three-dice-renderer.d.ts +6 -1
- package/dist/three-dice-renderer.js +55 -8
- package/dist/top-down-dice-renderer.d.ts +3 -1
- package/dist/top-down-dice-renderer.js +43 -6
- package/package.json +4 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { ThreeMaterialFactory, type DiceMaterialStyle, type FaceLabel, type FaceMaterialOptions, } from './material-factory.js';
|
|
2
|
-
export { createPolyhedronGeometry, createFaceUvs, DEFAULT_THREE_THEME, getFaceLabel, ThreeDiceMeshFactory, type ThreeDiceMesh, } from './mesh-factory.js';
|
|
2
|
+
export { createPolyhedronGeometry, createFaceUvs, DEFAULT_THREE_THEME, getFaceLabel, ThreeDiceMeshFactory, type ThreeDiceMesh, type FaceMaterialContext, type FaceMaterialResource, type ThreeFaceMaterialProvider, } from './mesh-factory.js';
|
|
3
3
|
export { ThreeCamera, ThreeLighting, ThreeScene } from './scene.js';
|
|
4
4
|
export { TopDownCamera, type TopDownCameraOptions } from './top-down-camera.js';
|
|
5
5
|
export { TopDownDiceRenderer, type TopDownDiceRendererOptions } from './top-down-dice-renderer.js';
|
package/dist/mesh-factory.d.ts
CHANGED
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
import type { PolygonDefinition, PolyhedronDefinition } from '@dice-o-rolla/dice-geometry';
|
|
2
|
-
import type { RendererTheme } from '@dice-o-rolla/dice-renderer';
|
|
2
|
+
import type { RendererTheme, VisualPresetDescriptor } from '@dice-o-rolla/dice-renderer';
|
|
3
3
|
import { BufferGeometry, Mesh, type MeshStandardMaterial } from 'three';
|
|
4
|
-
import { ThreeMaterialFactory } from './material-factory.js';
|
|
5
4
|
type Vector2Tuple = readonly [u: number, v: number];
|
|
6
5
|
export declare const DEFAULT_THREE_THEME: RendererTheme;
|
|
7
6
|
export interface ThreeDiceMesh {
|
|
8
7
|
readonly mesh: Mesh<BufferGeometry, MeshStandardMaterial[]>;
|
|
9
8
|
dispose(): void;
|
|
10
9
|
}
|
|
10
|
+
export interface FaceMaterialContext {
|
|
11
|
+
readonly label: string | number | readonly number[];
|
|
12
|
+
readonly faceValue: number;
|
|
13
|
+
readonly theme: RendererTheme;
|
|
14
|
+
readonly preset?: VisualPresetDescriptor;
|
|
15
|
+
readonly labelScale?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface FaceMaterialResource {
|
|
18
|
+
readonly material: MeshStandardMaterial;
|
|
19
|
+
dispose(): void;
|
|
20
|
+
}
|
|
21
|
+
export interface ThreeFaceMaterialProvider {
|
|
22
|
+
createFace(context: FaceMaterialContext): FaceMaterialResource;
|
|
23
|
+
dispose?(): void;
|
|
24
|
+
}
|
|
11
25
|
export declare function createPolyhedronGeometry(definition: PolyhedronDefinition, scale?: number): BufferGeometry;
|
|
12
26
|
export declare class ThreeDiceMeshFactory {
|
|
13
27
|
#private;
|
|
14
|
-
constructor(materials?:
|
|
15
|
-
create(definition: PolyhedronDefinition, theme?: RendererTheme, scale?: number, faceLabels?: Readonly<Record<number, string | number
|
|
28
|
+
constructor(materials?: ThreeFaceMaterialProvider);
|
|
29
|
+
create(definition: PolyhedronDefinition, theme?: RendererTheme, scale?: number, faceLabels?: Readonly<Record<number, string | number>>, preset?: VisualPresetDescriptor): ThreeDiceMesh;
|
|
16
30
|
createD6(theme?: RendererTheme, scale?: number): ThreeDiceMesh;
|
|
17
31
|
}
|
|
18
32
|
export declare function createFaceUvs(definition: PolyhedronDefinition, face: PolygonDefinition): readonly Vector2Tuple[];
|
package/dist/mesh-factory.js
CHANGED
|
@@ -42,12 +42,19 @@ export function createPolyhedronGeometry(definition, scale = 1) {
|
|
|
42
42
|
}
|
|
43
43
|
export class ThreeDiceMeshFactory {
|
|
44
44
|
#materials;
|
|
45
|
-
constructor(materials =
|
|
45
|
+
constructor(materials = defaultMaterialProvider()) {
|
|
46
46
|
this.#materials = materials;
|
|
47
47
|
}
|
|
48
|
-
create(definition, theme = DEFAULT_THREE_THEME, scale = 1, faceLabels) {
|
|
48
|
+
create(definition, theme = DEFAULT_THREE_THEME, scale = 1, faceLabels, preset) {
|
|
49
49
|
const geometry = createPolyhedronGeometry(definition, scale);
|
|
50
|
-
const
|
|
50
|
+
const resources = definition.faces.map((face) => this.#materials.createFace({
|
|
51
|
+
label: getFaceLabel(definition, face, faceLabels),
|
|
52
|
+
faceValue: face.value,
|
|
53
|
+
theme,
|
|
54
|
+
...(preset === undefined ? {} : { preset }),
|
|
55
|
+
...(definition.id === 'd10' ? { labelScale: D10_LABEL_SCALE } : {}),
|
|
56
|
+
}));
|
|
57
|
+
const materials = resources.map(({ material }) => material);
|
|
51
58
|
const mesh = new Mesh(geometry, materials);
|
|
52
59
|
mesh.castShadow = true;
|
|
53
60
|
mesh.receiveShadow = true;
|
|
@@ -55,10 +62,8 @@ export class ThreeDiceMeshFactory {
|
|
|
55
62
|
mesh,
|
|
56
63
|
dispose() {
|
|
57
64
|
geometry.dispose();
|
|
58
|
-
for (const
|
|
59
|
-
|
|
60
|
-
material.dispose();
|
|
61
|
-
}
|
|
65
|
+
for (const resource of resources)
|
|
66
|
+
resource.dispose();
|
|
62
67
|
},
|
|
63
68
|
};
|
|
64
69
|
}
|
|
@@ -66,6 +71,21 @@ export class ThreeDiceMeshFactory {
|
|
|
66
71
|
return this.create(D6_DEFINITION, theme, scale);
|
|
67
72
|
}
|
|
68
73
|
}
|
|
74
|
+
function defaultMaterialProvider() {
|
|
75
|
+
const factory = new ThreeMaterialFactory();
|
|
76
|
+
return {
|
|
77
|
+
createFace(context) {
|
|
78
|
+
const material = factory.createFace(context.label, context.theme, context.labelScale === undefined ? {} : { labelScale: context.labelScale });
|
|
79
|
+
return {
|
|
80
|
+
material,
|
|
81
|
+
dispose() {
|
|
82
|
+
material.map?.dispose();
|
|
83
|
+
material.dispose();
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
69
89
|
export function createFaceUvs(definition, face) {
|
|
70
90
|
if (definition.id !== 'd10') {
|
|
71
91
|
return face.indices.map((_, index) => {
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
import type { DiceRenderer, RenderDieState, RendererTheme, RendererViewport } from '@dice-o-rolla/dice-renderer';
|
|
1
|
+
import type { DiceRenderer, RenderDieState, RendererTheme, RendererViewport, VisualPresetDescriptor } from '@dice-o-rolla/dice-renderer';
|
|
2
|
+
import { WebGLRenderer } from 'three';
|
|
3
|
+
import { type ThreeFaceMaterialProvider } from './mesh-factory.js';
|
|
2
4
|
export interface ThreeDiceRendererOptions extends Partial<RendererTheme> {
|
|
3
5
|
readonly antialias?: boolean;
|
|
4
6
|
readonly observeResize?: boolean;
|
|
5
7
|
readonly maxPixelRatio?: number;
|
|
6
8
|
readonly maxViewportDimension?: number;
|
|
7
9
|
readonly maxFramebufferPixels?: number;
|
|
10
|
+
readonly materialProvider?: ThreeFaceMaterialProvider | ((renderer: WebGLRenderer) => ThreeFaceMaterialProvider);
|
|
8
11
|
}
|
|
9
12
|
export declare class ThreeDiceRenderer implements DiceRenderer {
|
|
10
13
|
#private;
|
|
11
14
|
constructor(container: HTMLElement, options?: ThreeDiceRendererOptions);
|
|
12
15
|
initialize(): void;
|
|
16
|
+
registerPreset(preset: VisualPresetDescriptor): void;
|
|
17
|
+
unregisterPreset(id: string): void;
|
|
13
18
|
createDie(state: RenderDieState): void;
|
|
14
19
|
updateDie(state: RenderDieState): void;
|
|
15
20
|
removeDie(id: string): void;
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { getDieGeometry, getRegisteredDieTypes } from '@dice-o-rolla/dice-geometry';
|
|
2
2
|
import { PCFShadowMap, WebGLRenderer } from 'three';
|
|
3
|
-
import { DEFAULT_THREE_THEME, ThreeDiceMeshFactory } from './mesh-factory.js';
|
|
3
|
+
import { DEFAULT_THREE_THEME, ThreeDiceMeshFactory, } from './mesh-factory.js';
|
|
4
4
|
import { ThreeCamera, ThreeScene } from './scene.js';
|
|
5
5
|
import { applyInterpolatedTransform } from './transform.js';
|
|
6
6
|
import { fitViewportToLimits, resolveViewportLimits, validateViewport, } from './viewport-limits.js';
|
|
7
7
|
function copyState(state) {
|
|
8
8
|
return {
|
|
9
9
|
id: state.id,
|
|
10
|
+
presetId: state.presetId,
|
|
10
11
|
geometryId: state.geometryId,
|
|
12
|
+
scale: state.scale,
|
|
11
13
|
...(state.faceLabels === undefined ? {} : { faceLabels: { ...state.faceLabels } }),
|
|
12
14
|
previous: {
|
|
13
15
|
position: { ...state.previous.position },
|
|
@@ -22,8 +24,11 @@ function copyState(state) {
|
|
|
22
24
|
export class ThreeDiceRenderer {
|
|
23
25
|
#container;
|
|
24
26
|
#options;
|
|
25
|
-
#meshFactory
|
|
27
|
+
#meshFactory;
|
|
28
|
+
#materialProvider;
|
|
26
29
|
#entries = new Map();
|
|
30
|
+
#presets = new Map();
|
|
31
|
+
#pendingPresetRemovals = new Set();
|
|
27
32
|
#viewportLimits;
|
|
28
33
|
#theme;
|
|
29
34
|
#scene;
|
|
@@ -34,6 +39,9 @@ export class ThreeDiceRenderer {
|
|
|
34
39
|
constructor(container, options = {}) {
|
|
35
40
|
this.#container = container;
|
|
36
41
|
this.#options = options;
|
|
42
|
+
this.#materialProvider =
|
|
43
|
+
typeof options.materialProvider === 'function' ? undefined : options.materialProvider;
|
|
44
|
+
this.#meshFactory = new ThreeDiceMeshFactory(this.#materialProvider);
|
|
37
45
|
this.#viewportLimits = resolveViewportLimits(options);
|
|
38
46
|
this.#theme = {
|
|
39
47
|
material: options.material ?? DEFAULT_THREE_THEME.material,
|
|
@@ -54,6 +62,10 @@ export class ThreeDiceRenderer {
|
|
|
54
62
|
alpha: true,
|
|
55
63
|
powerPreference: 'high-performance',
|
|
56
64
|
});
|
|
65
|
+
if (typeof this.#options.materialProvider === 'function') {
|
|
66
|
+
this.#materialProvider = this.#options.materialProvider(renderer);
|
|
67
|
+
this.#meshFactory = new ThreeDiceMeshFactory(this.#materialProvider);
|
|
68
|
+
}
|
|
57
69
|
renderer.shadowMap.enabled = true;
|
|
58
70
|
renderer.shadowMap.type = PCFShadowMap;
|
|
59
71
|
this.#container.append(renderer.domElement);
|
|
@@ -66,11 +78,24 @@ export class ThreeDiceRenderer {
|
|
|
66
78
|
this.#resizeObserver.observe(this.#container);
|
|
67
79
|
}
|
|
68
80
|
}
|
|
81
|
+
registerPreset(preset) {
|
|
82
|
+
this.#assertAlive();
|
|
83
|
+
this.#pendingPresetRemovals.delete(preset.id);
|
|
84
|
+
this.#presets.set(preset.id, preset);
|
|
85
|
+
}
|
|
86
|
+
unregisterPreset(id) {
|
|
87
|
+
this.#assertAlive();
|
|
88
|
+
if ([...this.#entries.values()].some((entry) => entry.state.presetId === id)) {
|
|
89
|
+
this.#pendingPresetRemovals.add(id);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this.#presets.delete(id);
|
|
93
|
+
}
|
|
69
94
|
createDie(state) {
|
|
70
95
|
this.#assertInitialized();
|
|
71
96
|
if (this.#entries.has(state.id))
|
|
72
97
|
throw new Error(`A render die with id "${state.id}" already exists`);
|
|
73
|
-
const resource = this.#createResource(state
|
|
98
|
+
const resource = this.#createResource(state);
|
|
74
99
|
const ownedState = copyState(state);
|
|
75
100
|
applyInterpolatedTransform(resource.mesh, ownedState, 1);
|
|
76
101
|
this.#scene?.value.add(resource.mesh);
|
|
@@ -84,6 +109,9 @@ export class ThreeDiceRenderer {
|
|
|
84
109
|
if (state.geometryId !== entry.state.geometryId) {
|
|
85
110
|
throw new Error('A die geometry cannot be changed after creation');
|
|
86
111
|
}
|
|
112
|
+
if (state.presetId !== entry.state.presetId || state.scale !== entry.state.scale) {
|
|
113
|
+
throw new Error('A die visual preset cannot be changed after creation');
|
|
114
|
+
}
|
|
87
115
|
if (!haveEqualFaceLabels(state.faceLabels, entry.state.faceLabels)) {
|
|
88
116
|
throw new Error('Die face labels cannot be changed after creation');
|
|
89
117
|
}
|
|
@@ -97,6 +125,7 @@ export class ThreeDiceRenderer {
|
|
|
97
125
|
this.#scene?.value.remove(entry.resource.mesh);
|
|
98
126
|
entry.resource.dispose();
|
|
99
127
|
this.#entries.delete(id);
|
|
128
|
+
this.#releasePendingPreset(entry.state.presetId);
|
|
100
129
|
}
|
|
101
130
|
render(alpha) {
|
|
102
131
|
this.#assertInitialized();
|
|
@@ -116,7 +145,7 @@ export class ThreeDiceRenderer {
|
|
|
116
145
|
this.#assertAlive();
|
|
117
146
|
this.#theme = { ...theme };
|
|
118
147
|
for (const entry of this.#entries.values()) {
|
|
119
|
-
const replacement = this.#createResource(entry.state
|
|
148
|
+
const replacement = this.#createResource(entry.state);
|
|
120
149
|
applyInterpolatedTransform(replacement.mesh, entry.state, 1);
|
|
121
150
|
this.#scene?.value.remove(entry.resource.mesh);
|
|
122
151
|
entry.resource.dispose();
|
|
@@ -144,6 +173,10 @@ export class ThreeDiceRenderer {
|
|
|
144
173
|
this.#scene = undefined;
|
|
145
174
|
this.#camera = undefined;
|
|
146
175
|
this.#renderer = undefined;
|
|
176
|
+
this.#presets.clear();
|
|
177
|
+
this.#pendingPresetRemovals.clear();
|
|
178
|
+
this.#materialProvider?.dispose?.();
|
|
179
|
+
this.#materialProvider = undefined;
|
|
147
180
|
this.#destroyed = true;
|
|
148
181
|
}
|
|
149
182
|
#resizeToContainer() {
|
|
@@ -156,11 +189,25 @@ export class ThreeDiceRenderer {
|
|
|
156
189
|
pixelRatio: globalThis.devicePixelRatio || 1,
|
|
157
190
|
}, this.#viewportLimits));
|
|
158
191
|
}
|
|
159
|
-
#createResource(
|
|
160
|
-
const
|
|
192
|
+
#createResource(state) {
|
|
193
|
+
const preset = this.#presets.get(state.presetId);
|
|
194
|
+
if (preset === undefined)
|
|
195
|
+
throw new Error(`Unknown visual preset: ${state.presetId}`);
|
|
196
|
+
if (preset.geometryId !== state.geometryId || (preset.scale ?? 1) !== state.scale) {
|
|
197
|
+
throw new Error(`Render state does not match visual preset "${state.presetId}"`);
|
|
198
|
+
}
|
|
199
|
+
const type = getRegisteredDieTypes().find((registered) => registered === state.geometryId);
|
|
161
200
|
if (type === undefined)
|
|
162
|
-
throw new Error(`Unsupported geometry: ${geometryId}`);
|
|
163
|
-
return this.#meshFactory.create(getDieGeometry(type), this.#theme,
|
|
201
|
+
throw new Error(`Unsupported geometry: ${state.geometryId}`);
|
|
202
|
+
return this.#meshFactory.create(getDieGeometry(type), this.#theme, state.scale, state.faceLabels, preset);
|
|
203
|
+
}
|
|
204
|
+
#releasePendingPreset(presetId) {
|
|
205
|
+
if (!this.#pendingPresetRemovals.has(presetId))
|
|
206
|
+
return;
|
|
207
|
+
if ([...this.#entries.values()].some((entry) => entry.state.presetId === presetId))
|
|
208
|
+
return;
|
|
209
|
+
this.#pendingPresetRemovals.delete(presetId);
|
|
210
|
+
this.#presets.delete(presetId);
|
|
164
211
|
}
|
|
165
212
|
#assertAlive() {
|
|
166
213
|
if (this.#destroyed)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DiceRenderer, RenderDieState, RendererTheme, RendererViewport } from '@dice-o-rolla/dice-renderer';
|
|
1
|
+
import type { DiceRenderer, RenderDieState, RendererTheme, RendererViewport, VisualPresetDescriptor } from '@dice-o-rolla/dice-renderer';
|
|
2
2
|
import { type TopDownCameraOptions } from './top-down-camera.js';
|
|
3
3
|
import { type ViewportLimitOptions } from './viewport-limits.js';
|
|
4
4
|
export interface TopDownDiceRendererOptions extends Partial<RendererTheme>, ViewportLimitOptions, TopDownCameraOptions {
|
|
@@ -9,6 +9,8 @@ export declare class TopDownDiceRenderer implements DiceRenderer {
|
|
|
9
9
|
#private;
|
|
10
10
|
constructor(container: HTMLElement, options?: TopDownDiceRendererOptions);
|
|
11
11
|
initialize(): void;
|
|
12
|
+
registerPreset(preset: VisualPresetDescriptor): void;
|
|
13
|
+
unregisterPreset(id: string): void;
|
|
12
14
|
createDie(state: RenderDieState): void;
|
|
13
15
|
updateDie(state: RenderDieState): void;
|
|
14
16
|
removeDie(id: string): void;
|
|
@@ -9,6 +9,8 @@ export class TopDownDiceRenderer {
|
|
|
9
9
|
#options;
|
|
10
10
|
#meshFactory = new ThreeDiceMeshFactory();
|
|
11
11
|
#entries = new Map();
|
|
12
|
+
#presets = new Map();
|
|
13
|
+
#pendingPresetRemovals = new Set();
|
|
12
14
|
#viewportLimits;
|
|
13
15
|
#trayWidth;
|
|
14
16
|
#trayDepth;
|
|
@@ -79,12 +81,25 @@ export class TopDownDiceRenderer {
|
|
|
79
81
|
this.#resizeObserver.observe(this.#container);
|
|
80
82
|
}
|
|
81
83
|
}
|
|
84
|
+
registerPreset(preset) {
|
|
85
|
+
this.#assertAlive();
|
|
86
|
+
this.#pendingPresetRemovals.delete(preset.id);
|
|
87
|
+
this.#presets.set(preset.id, preset);
|
|
88
|
+
}
|
|
89
|
+
unregisterPreset(id) {
|
|
90
|
+
this.#assertAlive();
|
|
91
|
+
if ([...this.#entries.values()].some((entry) => entry.state.presetId === id)) {
|
|
92
|
+
this.#pendingPresetRemovals.add(id);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
this.#presets.delete(id);
|
|
96
|
+
}
|
|
82
97
|
createDie(state) {
|
|
83
98
|
this.#assertInitialized();
|
|
84
99
|
if (this.#entries.has(state.id)) {
|
|
85
100
|
throw new Error(`A render die with id "${state.id}" already exists`);
|
|
86
101
|
}
|
|
87
|
-
const resource = this.#createResource(state
|
|
102
|
+
const resource = this.#createResource(state);
|
|
88
103
|
const ownedState = copyState(state);
|
|
89
104
|
applyInterpolatedTransform(resource.mesh, ownedState, 1);
|
|
90
105
|
this.#scene?.add(resource.mesh);
|
|
@@ -98,6 +113,9 @@ export class TopDownDiceRenderer {
|
|
|
98
113
|
if (entry.state.geometryId !== state.geometryId) {
|
|
99
114
|
throw new Error('A die geometry cannot be changed after creation');
|
|
100
115
|
}
|
|
116
|
+
if (state.presetId !== entry.state.presetId || state.scale !== entry.state.scale) {
|
|
117
|
+
throw new Error('A die visual preset cannot be changed after creation');
|
|
118
|
+
}
|
|
101
119
|
if (!haveEqualFaceLabels(entry.state.faceLabels, state.faceLabels)) {
|
|
102
120
|
throw new Error('Die face labels cannot be changed after creation');
|
|
103
121
|
}
|
|
@@ -111,6 +129,7 @@ export class TopDownDiceRenderer {
|
|
|
111
129
|
this.#scene?.remove(entry.resource.mesh);
|
|
112
130
|
entry.resource.dispose();
|
|
113
131
|
this.#entries.delete(id);
|
|
132
|
+
this.#releasePendingPreset(entry.state.presetId);
|
|
114
133
|
}
|
|
115
134
|
render(alpha) {
|
|
116
135
|
this.#assertInitialized();
|
|
@@ -130,7 +149,7 @@ export class TopDownDiceRenderer {
|
|
|
130
149
|
this.#assertAlive();
|
|
131
150
|
this.#theme = { ...theme };
|
|
132
151
|
for (const entry of this.#entries.values()) {
|
|
133
|
-
const replacement = this.#createResource(entry.state
|
|
152
|
+
const replacement = this.#createResource(entry.state);
|
|
134
153
|
applyInterpolatedTransform(replacement.mesh, entry.state, 1);
|
|
135
154
|
this.#scene?.remove(entry.resource.mesh);
|
|
136
155
|
entry.resource.dispose();
|
|
@@ -164,6 +183,8 @@ export class TopDownDiceRenderer {
|
|
|
164
183
|
this.#floorMaterial = undefined;
|
|
165
184
|
this.#keyLight = undefined;
|
|
166
185
|
this.#resizeObserver = undefined;
|
|
186
|
+
this.#presets.clear();
|
|
187
|
+
this.#pendingPresetRemovals.clear();
|
|
167
188
|
this.#destroyed = true;
|
|
168
189
|
}
|
|
169
190
|
#resizeToContainer() {
|
|
@@ -176,11 +197,25 @@ export class TopDownDiceRenderer {
|
|
|
176
197
|
pixelRatio: globalThis.devicePixelRatio || 1,
|
|
177
198
|
}, this.#viewportLimits));
|
|
178
199
|
}
|
|
179
|
-
#createResource(
|
|
180
|
-
const
|
|
200
|
+
#createResource(state) {
|
|
201
|
+
const preset = this.#presets.get(state.presetId);
|
|
202
|
+
if (preset === undefined)
|
|
203
|
+
throw new Error(`Unknown visual preset: ${state.presetId}`);
|
|
204
|
+
if (preset.geometryId !== state.geometryId || (preset.scale ?? 1) !== state.scale) {
|
|
205
|
+
throw new Error(`Render state does not match visual preset "${state.presetId}"`);
|
|
206
|
+
}
|
|
207
|
+
const type = getRegisteredDieTypes().find((registered) => registered === state.geometryId);
|
|
181
208
|
if (type === undefined)
|
|
182
|
-
throw new Error(`Unsupported geometry: ${geometryId}`);
|
|
183
|
-
return this.#meshFactory.create(getDieGeometry(type), this.#theme,
|
|
209
|
+
throw new Error(`Unsupported geometry: ${state.geometryId}`);
|
|
210
|
+
return this.#meshFactory.create(getDieGeometry(type), this.#theme, state.scale, state.faceLabels);
|
|
211
|
+
}
|
|
212
|
+
#releasePendingPreset(presetId) {
|
|
213
|
+
if (!this.#pendingPresetRemovals.has(presetId))
|
|
214
|
+
return;
|
|
215
|
+
if ([...this.#entries.values()].some((entry) => entry.state.presetId === presetId))
|
|
216
|
+
return;
|
|
217
|
+
this.#pendingPresetRemovals.delete(presetId);
|
|
218
|
+
this.#presets.delete(presetId);
|
|
184
219
|
}
|
|
185
220
|
#assertAlive() {
|
|
186
221
|
if (this.#destroyed)
|
|
@@ -196,7 +231,9 @@ export class TopDownDiceRenderer {
|
|
|
196
231
|
function copyState(state) {
|
|
197
232
|
return {
|
|
198
233
|
id: state.id,
|
|
234
|
+
presetId: state.presetId,
|
|
199
235
|
geometryId: state.geometryId,
|
|
236
|
+
scale: state.scale,
|
|
200
237
|
...(state.faceLabels === undefined ? {} : { faceLabels: { ...state.faceLabels } }),
|
|
201
238
|
previous: {
|
|
202
239
|
position: { ...state.previous.position },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dice-o-rolla/dice-renderer-three",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Three.js WebGL renderers for animated physical dice.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dice",
|
|
@@ -40,8 +40,9 @@
|
|
|
40
40
|
"registry": "https://registry.npmjs.org/"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@dice-o-rolla/dice-geometry": "0.
|
|
44
|
-
"@dice-o-rolla/dice-renderer": "0.
|
|
43
|
+
"@dice-o-rolla/dice-geometry": "0.2.0",
|
|
44
|
+
"@dice-o-rolla/dice-renderer": "0.2.0",
|
|
45
|
+
"@types/three": "^0.185.0",
|
|
45
46
|
"three": "^0.185.0"
|
|
46
47
|
},
|
|
47
48
|
"engines": {
|