blockymodel-web 0.1.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/LICENSE +21 -0
- package/dist/blockymodel-web.js +2717 -0
- package/dist/blockymodel-web.js.map +1 -0
- package/dist/blockymodel-web.umd.cjs +122 -0
- package/dist/blockymodel-web.umd.cjs.map +1 -0
- package/dist/editor/Editor.d.ts +94 -0
- package/dist/editor/Editor.d.ts.map +1 -0
- package/dist/editor/History.d.ts +52 -0
- package/dist/editor/History.d.ts.map +1 -0
- package/dist/editor/SelectionManager.d.ts +57 -0
- package/dist/editor/SelectionManager.d.ts.map +1 -0
- package/dist/editor/Serializer.d.ts +44 -0
- package/dist/editor/Serializer.d.ts.map +1 -0
- package/dist/editor/TransformManager.d.ts +73 -0
- package/dist/editor/TransformManager.d.ts.map +1 -0
- package/dist/editor/commands/AddNodeCommand.d.ts +24 -0
- package/dist/editor/commands/AddNodeCommand.d.ts.map +1 -0
- package/dist/editor/commands/Command.d.ts +50 -0
- package/dist/editor/commands/Command.d.ts.map +1 -0
- package/dist/editor/commands/RemoveNodeCommand.d.ts +28 -0
- package/dist/editor/commands/RemoveNodeCommand.d.ts.map +1 -0
- package/dist/editor/commands/SetPositionCommand.d.ts +24 -0
- package/dist/editor/commands/SetPositionCommand.d.ts.map +1 -0
- package/dist/editor/commands/SetPropertyCommand.d.ts +41 -0
- package/dist/editor/commands/SetPropertyCommand.d.ts.map +1 -0
- package/dist/editor/commands/SetRotationCommand.d.ts +24 -0
- package/dist/editor/commands/SetRotationCommand.d.ts.map +1 -0
- package/dist/editor/commands/SetScaleCommand.d.ts +24 -0
- package/dist/editor/commands/SetScaleCommand.d.ts.map +1 -0
- package/dist/editor/index.d.ts +15 -0
- package/dist/editor/index.d.ts.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/loaders/BlockyModelLoader.d.ts +48 -0
- package/dist/loaders/BlockyModelLoader.d.ts.map +1 -0
- package/dist/types/blockymodel.d.ts +72 -0
- package/dist/types/blockymodel.d.ts.map +1 -0
- package/dist/ui/HierarchyPanel.d.ts +73 -0
- package/dist/ui/HierarchyPanel.d.ts.map +1 -0
- package/dist/ui/PropertyPanel.d.ts +59 -0
- package/dist/ui/PropertyPanel.d.ts.map +1 -0
- package/dist/ui/UVEditor.d.ts +56 -0
- package/dist/ui/UVEditor.d.ts.map +1 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/viewer/ViewerController.d.ts +71 -0
- package/dist/viewer/ViewerController.d.ts.map +1 -0
- package/package.json +63 -0
- package/src/editor/Editor.ts +196 -0
- package/src/editor/History.ts +123 -0
- package/src/editor/SelectionManager.ts +183 -0
- package/src/editor/Serializer.ts +212 -0
- package/src/editor/TransformManager.ts +270 -0
- package/src/editor/commands/AddNodeCommand.ts +53 -0
- package/src/editor/commands/Command.ts +63 -0
- package/src/editor/commands/RemoveNodeCommand.ts +59 -0
- package/src/editor/commands/SetPositionCommand.ts +51 -0
- package/src/editor/commands/SetPropertyCommand.ts +100 -0
- package/src/editor/commands/SetRotationCommand.ts +51 -0
- package/src/editor/commands/SetScaleCommand.ts +51 -0
- package/src/editor/index.ts +19 -0
- package/src/index.ts +49 -0
- package/src/loaders/BlockyModelLoader.ts +281 -0
- package/src/main.ts +290 -0
- package/src/styles.css +597 -0
- package/src/types/blockymodel.ts +82 -0
- package/src/ui/HierarchyPanel.ts +343 -0
- package/src/ui/PropertyPanel.ts +434 -0
- package/src/ui/UVEditor.ts +336 -0
- package/src/ui/index.ts +4 -0
- package/src/viewer/ViewerController.ts +295 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import type { Editor } from "../editor/Editor";
|
|
3
|
+
import { SetPositionCommand } from "../editor/commands/SetPositionCommand";
|
|
4
|
+
import { SetRotationCommand } from "../editor/commands/SetRotationCommand";
|
|
5
|
+
import { SetScaleCommand } from "../editor/commands/SetScaleCommand";
|
|
6
|
+
import { SetPropertyCommand } from "../editor/commands/SetPropertyCommand";
|
|
7
|
+
import type { ShadingMode } from "../types/blockymodel";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Property panel for editing selected object properties
|
|
11
|
+
*/
|
|
12
|
+
export class PropertyPanel {
|
|
13
|
+
private editor: Editor;
|
|
14
|
+
private container: HTMLElement;
|
|
15
|
+
private currentObject: THREE.Object3D | null = null;
|
|
16
|
+
private isUpdating: boolean = false;
|
|
17
|
+
|
|
18
|
+
// Input elements
|
|
19
|
+
private nameInput!: HTMLInputElement;
|
|
20
|
+
private posInputs!: { x: HTMLInputElement; y: HTMLInputElement; z: HTMLInputElement };
|
|
21
|
+
private rotInputs!: { x: HTMLInputElement; y: HTMLInputElement; z: HTMLInputElement };
|
|
22
|
+
private scaleInputs!: { x: HTMLInputElement; y: HTMLInputElement; z: HTMLInputElement };
|
|
23
|
+
private sizeInputs!: { x: HTMLInputElement; y: HTMLInputElement; z: HTMLInputElement };
|
|
24
|
+
private offsetInputs!: { x: HTMLInputElement; y: HTMLInputElement; z: HTMLInputElement };
|
|
25
|
+
private visibleCheckbox!: HTMLInputElement;
|
|
26
|
+
private doubleSidedCheckbox!: HTMLInputElement;
|
|
27
|
+
private shadingModeSelect!: HTMLSelectElement;
|
|
28
|
+
private shapeTypeSpan!: HTMLElement;
|
|
29
|
+
|
|
30
|
+
constructor(editor: Editor, containerId: string) {
|
|
31
|
+
this.editor = editor;
|
|
32
|
+
const container = document.getElementById(containerId);
|
|
33
|
+
if (!container) {
|
|
34
|
+
throw new Error(`Property panel container not found: ${containerId}`);
|
|
35
|
+
}
|
|
36
|
+
this.container = container;
|
|
37
|
+
|
|
38
|
+
this.buildUI();
|
|
39
|
+
this.setupEventListeners();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Build the property panel UI
|
|
44
|
+
*/
|
|
45
|
+
private buildUI(): void {
|
|
46
|
+
this.container.innerHTML = `
|
|
47
|
+
<div class="property-section" id="properties-transform">
|
|
48
|
+
<div class="section-header">Transform</div>
|
|
49
|
+
<div class="property-row">
|
|
50
|
+
<label>Name</label>
|
|
51
|
+
<input type="text" id="prop-name" class="prop-input prop-text" />
|
|
52
|
+
</div>
|
|
53
|
+
<div class="property-row">
|
|
54
|
+
<label>Position</label>
|
|
55
|
+
<div class="vec3-inputs">
|
|
56
|
+
<input type="number" id="prop-pos-x" class="prop-input" step="0.1" />
|
|
57
|
+
<input type="number" id="prop-pos-y" class="prop-input" step="0.1" />
|
|
58
|
+
<input type="number" id="prop-pos-z" class="prop-input" step="0.1" />
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="property-row">
|
|
62
|
+
<label>Rotation</label>
|
|
63
|
+
<div class="vec3-inputs">
|
|
64
|
+
<input type="number" id="prop-rot-x" class="prop-input" step="1" />
|
|
65
|
+
<input type="number" id="prop-rot-y" class="prop-input" step="1" />
|
|
66
|
+
<input type="number" id="prop-rot-z" class="prop-input" step="1" />
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="property-row">
|
|
70
|
+
<label>Scale</label>
|
|
71
|
+
<div class="vec3-inputs">
|
|
72
|
+
<input type="number" id="prop-scale-x" class="prop-input" step="0.1" min="0.01" />
|
|
73
|
+
<input type="number" id="prop-scale-y" class="prop-input" step="0.1" min="0.01" />
|
|
74
|
+
<input type="number" id="prop-scale-z" class="prop-input" step="0.1" min="0.01" />
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<div class="property-section" id="properties-shape">
|
|
80
|
+
<div class="section-header">Shape</div>
|
|
81
|
+
<div class="property-row">
|
|
82
|
+
<label>Type</label>
|
|
83
|
+
<span id="prop-shape-type" class="prop-readonly">-</span>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="property-row">
|
|
86
|
+
<label>Size</label>
|
|
87
|
+
<div class="vec3-inputs">
|
|
88
|
+
<input type="number" id="prop-size-x" class="prop-input" step="1" min="0" />
|
|
89
|
+
<input type="number" id="prop-size-y" class="prop-input" step="1" min="0" />
|
|
90
|
+
<input type="number" id="prop-size-z" class="prop-input" step="1" min="0" />
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="property-row">
|
|
94
|
+
<label>Offset</label>
|
|
95
|
+
<div class="vec3-inputs">
|
|
96
|
+
<input type="number" id="prop-offset-x" class="prop-input" step="0.5" />
|
|
97
|
+
<input type="number" id="prop-offset-y" class="prop-input" step="0.5" />
|
|
98
|
+
<input type="number" id="prop-offset-z" class="prop-input" step="0.5" />
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
<div class="property-row">
|
|
102
|
+
<label>Visible</label>
|
|
103
|
+
<input type="checkbox" id="prop-visible" class="prop-checkbox" />
|
|
104
|
+
</div>
|
|
105
|
+
<div class="property-row">
|
|
106
|
+
<label>Double Sided</label>
|
|
107
|
+
<input type="checkbox" id="prop-doublesided" class="prop-checkbox" />
|
|
108
|
+
</div>
|
|
109
|
+
<div class="property-row">
|
|
110
|
+
<label>Shading</label>
|
|
111
|
+
<select id="prop-shading" class="prop-select">
|
|
112
|
+
<option value="standard">Standard</option>
|
|
113
|
+
<option value="flat">Flat</option>
|
|
114
|
+
<option value="fullbright">Fullbright</option>
|
|
115
|
+
<option value="reflective">Reflective</option>
|
|
116
|
+
</select>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<div class="property-empty" id="properties-empty">
|
|
121
|
+
<p>Select an object to view properties</p>
|
|
122
|
+
</div>
|
|
123
|
+
`;
|
|
124
|
+
|
|
125
|
+
// Cache input references
|
|
126
|
+
this.nameInput = document.getElementById("prop-name") as HTMLInputElement;
|
|
127
|
+
this.posInputs = {
|
|
128
|
+
x: document.getElementById("prop-pos-x") as HTMLInputElement,
|
|
129
|
+
y: document.getElementById("prop-pos-y") as HTMLInputElement,
|
|
130
|
+
z: document.getElementById("prop-pos-z") as HTMLInputElement,
|
|
131
|
+
};
|
|
132
|
+
this.rotInputs = {
|
|
133
|
+
x: document.getElementById("prop-rot-x") as HTMLInputElement,
|
|
134
|
+
y: document.getElementById("prop-rot-y") as HTMLInputElement,
|
|
135
|
+
z: document.getElementById("prop-rot-z") as HTMLInputElement,
|
|
136
|
+
};
|
|
137
|
+
this.scaleInputs = {
|
|
138
|
+
x: document.getElementById("prop-scale-x") as HTMLInputElement,
|
|
139
|
+
y: document.getElementById("prop-scale-y") as HTMLInputElement,
|
|
140
|
+
z: document.getElementById("prop-scale-z") as HTMLInputElement,
|
|
141
|
+
};
|
|
142
|
+
this.sizeInputs = {
|
|
143
|
+
x: document.getElementById("prop-size-x") as HTMLInputElement,
|
|
144
|
+
y: document.getElementById("prop-size-y") as HTMLInputElement,
|
|
145
|
+
z: document.getElementById("prop-size-z") as HTMLInputElement,
|
|
146
|
+
};
|
|
147
|
+
this.offsetInputs = {
|
|
148
|
+
x: document.getElementById("prop-offset-x") as HTMLInputElement,
|
|
149
|
+
y: document.getElementById("prop-offset-y") as HTMLInputElement,
|
|
150
|
+
z: document.getElementById("prop-offset-z") as HTMLInputElement,
|
|
151
|
+
};
|
|
152
|
+
this.visibleCheckbox = document.getElementById("prop-visible") as HTMLInputElement;
|
|
153
|
+
this.doubleSidedCheckbox = document.getElementById("prop-doublesided") as HTMLInputElement;
|
|
154
|
+
this.shadingModeSelect = document.getElementById("prop-shading") as HTMLSelectElement;
|
|
155
|
+
this.shapeTypeSpan = document.getElementById("prop-shape-type") as HTMLElement;
|
|
156
|
+
|
|
157
|
+
// Initially hide property sections
|
|
158
|
+
this.showEmpty();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Setup event listeners
|
|
163
|
+
*/
|
|
164
|
+
private setupEventListeners(): void {
|
|
165
|
+
// Editor events
|
|
166
|
+
this.editor.on("selectionChanged", (object) => {
|
|
167
|
+
this.setObject(object as THREE.Object3D | null);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
this.editor.on("objectChanged", () => {
|
|
171
|
+
if (this.currentObject) {
|
|
172
|
+
this.updateFromObject();
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Listen for live transform updates
|
|
177
|
+
this.editor.transform.on("objectChanging", () => {
|
|
178
|
+
if (this.currentObject) {
|
|
179
|
+
this.updateFromObject();
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Name input
|
|
184
|
+
this.nameInput.addEventListener("change", () => {
|
|
185
|
+
if (this.currentObject && !this.isUpdating) {
|
|
186
|
+
const oldName = this.currentObject.name;
|
|
187
|
+
const newName = this.nameInput.value;
|
|
188
|
+
this.editor.execute(new SetPropertyCommand(this.currentObject, "name", newName, oldName));
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Position inputs
|
|
193
|
+
this.setupVec3Input(this.posInputs, (value) => {
|
|
194
|
+
if (this.currentObject && !this.isUpdating) {
|
|
195
|
+
const oldPos = this.currentObject.position.clone();
|
|
196
|
+
this.editor.execute(new SetPositionCommand(this.currentObject, value, oldPos));
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Rotation inputs (degrees to radians)
|
|
201
|
+
this.setupVec3Input(this.rotInputs, (value) => {
|
|
202
|
+
if (this.currentObject && !this.isUpdating) {
|
|
203
|
+
const oldRot = this.currentObject.rotation.clone();
|
|
204
|
+
const newRot = new THREE.Euler(
|
|
205
|
+
THREE.MathUtils.degToRad(value.x),
|
|
206
|
+
THREE.MathUtils.degToRad(value.y),
|
|
207
|
+
THREE.MathUtils.degToRad(value.z)
|
|
208
|
+
);
|
|
209
|
+
this.editor.execute(new SetRotationCommand(this.currentObject, newRot, oldRot));
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Scale inputs
|
|
214
|
+
this.setupVec3Input(this.scaleInputs, (value) => {
|
|
215
|
+
if (this.currentObject && !this.isUpdating) {
|
|
216
|
+
const oldScale = this.currentObject.scale.clone();
|
|
217
|
+
this.editor.execute(new SetScaleCommand(this.currentObject, value, oldScale));
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Size inputs
|
|
222
|
+
this.setupVec3Input(this.sizeInputs, (value) => {
|
|
223
|
+
if (this.currentObject && !this.isUpdating) {
|
|
224
|
+
const oldSize = this.currentObject.userData.originalSize || { x: 1, y: 1, z: 1 };
|
|
225
|
+
this.editor.execute(
|
|
226
|
+
new SetPropertyCommand(this.currentObject, "userData.originalSize", { x: value.x, y: value.y, z: value.z }, oldSize)
|
|
227
|
+
);
|
|
228
|
+
this.rebuildGeometry();
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Offset inputs
|
|
233
|
+
this.setupVec3Input(this.offsetInputs, (value) => {
|
|
234
|
+
if (this.currentObject && !this.isUpdating) {
|
|
235
|
+
const oldOffset = this.currentObject.userData.offset || { x: 0, y: 0, z: 0 };
|
|
236
|
+
this.editor.execute(
|
|
237
|
+
new SetPropertyCommand(this.currentObject, "userData.offset", { x: value.x, y: value.y, z: value.z }, oldOffset)
|
|
238
|
+
);
|
|
239
|
+
this.rebuildGeometry();
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Visible checkbox
|
|
244
|
+
this.visibleCheckbox.addEventListener("change", () => {
|
|
245
|
+
if (this.currentObject && !this.isUpdating) {
|
|
246
|
+
const oldValue = this.currentObject.visible;
|
|
247
|
+
const newValue = this.visibleCheckbox.checked;
|
|
248
|
+
this.editor.execute(new SetPropertyCommand(this.currentObject, "visible", newValue, oldValue));
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Double sided checkbox
|
|
253
|
+
this.doubleSidedCheckbox.addEventListener("change", () => {
|
|
254
|
+
if (this.currentObject && !this.isUpdating && this.currentObject instanceof THREE.Mesh) {
|
|
255
|
+
const material = this.currentObject.material as THREE.MeshStandardMaterial;
|
|
256
|
+
const oldValue = material.side === THREE.DoubleSide;
|
|
257
|
+
const newValue = this.doubleSidedCheckbox.checked;
|
|
258
|
+
material.side = newValue ? THREE.DoubleSide : THREE.FrontSide;
|
|
259
|
+
this.currentObject.userData.doubleSided = newValue;
|
|
260
|
+
this.editor.execute(
|
|
261
|
+
new SetPropertyCommand(this.currentObject, "userData.doubleSided", newValue, oldValue)
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Shading mode select
|
|
267
|
+
this.shadingModeSelect.addEventListener("change", () => {
|
|
268
|
+
if (this.currentObject && !this.isUpdating) {
|
|
269
|
+
const oldValue = this.currentObject.userData.shadingMode || "standard";
|
|
270
|
+
const newValue = this.shadingModeSelect.value as ShadingMode;
|
|
271
|
+
this.currentObject.userData.shadingMode = newValue;
|
|
272
|
+
this.editor.execute(
|
|
273
|
+
new SetPropertyCommand(this.currentObject, "userData.shadingMode", newValue, oldValue)
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Setup Vec3 input handlers
|
|
281
|
+
*/
|
|
282
|
+
private setupVec3Input(
|
|
283
|
+
inputs: { x: HTMLInputElement; y: HTMLInputElement; z: HTMLInputElement },
|
|
284
|
+
onChange: (value: THREE.Vector3) => void
|
|
285
|
+
): void {
|
|
286
|
+
const handler = () => {
|
|
287
|
+
const value = new THREE.Vector3(
|
|
288
|
+
parseFloat(inputs.x.value) || 0,
|
|
289
|
+
parseFloat(inputs.y.value) || 0,
|
|
290
|
+
parseFloat(inputs.z.value) || 0
|
|
291
|
+
);
|
|
292
|
+
onChange(value);
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
inputs.x.addEventListener("change", handler);
|
|
296
|
+
inputs.y.addEventListener("change", handler);
|
|
297
|
+
inputs.z.addEventListener("change", handler);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Set the object to display properties for
|
|
302
|
+
*/
|
|
303
|
+
setObject(object: THREE.Object3D | null): void {
|
|
304
|
+
this.currentObject = object;
|
|
305
|
+
|
|
306
|
+
if (object) {
|
|
307
|
+
this.showProperties();
|
|
308
|
+
this.updateFromObject();
|
|
309
|
+
} else {
|
|
310
|
+
this.showEmpty();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Update UI from current object
|
|
316
|
+
*/
|
|
317
|
+
private updateFromObject(): void {
|
|
318
|
+
if (!this.currentObject) return;
|
|
319
|
+
|
|
320
|
+
this.isUpdating = true;
|
|
321
|
+
|
|
322
|
+
const obj = this.currentObject;
|
|
323
|
+
|
|
324
|
+
// Name
|
|
325
|
+
this.nameInput.value = obj.name || "";
|
|
326
|
+
|
|
327
|
+
// Position
|
|
328
|
+
this.posInputs.x.value = obj.position.x.toFixed(2);
|
|
329
|
+
this.posInputs.y.value = obj.position.y.toFixed(2);
|
|
330
|
+
this.posInputs.z.value = obj.position.z.toFixed(2);
|
|
331
|
+
|
|
332
|
+
// Rotation (radians to degrees)
|
|
333
|
+
this.rotInputs.x.value = THREE.MathUtils.radToDeg(obj.rotation.x).toFixed(1);
|
|
334
|
+
this.rotInputs.y.value = THREE.MathUtils.radToDeg(obj.rotation.y).toFixed(1);
|
|
335
|
+
this.rotInputs.z.value = THREE.MathUtils.radToDeg(obj.rotation.z).toFixed(1);
|
|
336
|
+
|
|
337
|
+
// Scale
|
|
338
|
+
this.scaleInputs.x.value = obj.scale.x.toFixed(2);
|
|
339
|
+
this.scaleInputs.y.value = obj.scale.y.toFixed(2);
|
|
340
|
+
this.scaleInputs.z.value = obj.scale.z.toFixed(2);
|
|
341
|
+
|
|
342
|
+
// Shape properties
|
|
343
|
+
const shapeType = obj.userData.shapeType || "none";
|
|
344
|
+
this.shapeTypeSpan.textContent = shapeType;
|
|
345
|
+
|
|
346
|
+
// Size
|
|
347
|
+
const size = obj.userData.originalSize || { x: 1, y: 1, z: 1 };
|
|
348
|
+
this.sizeInputs.x.value = size.x?.toString() || "1";
|
|
349
|
+
this.sizeInputs.y.value = size.y?.toString() || "1";
|
|
350
|
+
this.sizeInputs.z.value = size.z?.toString() || "1";
|
|
351
|
+
|
|
352
|
+
// Offset
|
|
353
|
+
const offset = obj.userData.offset || { x: 0, y: 0, z: 0 };
|
|
354
|
+
this.offsetInputs.x.value = offset.x?.toString() || "0";
|
|
355
|
+
this.offsetInputs.y.value = offset.y?.toString() || "0";
|
|
356
|
+
this.offsetInputs.z.value = offset.z?.toString() || "0";
|
|
357
|
+
|
|
358
|
+
// Visible
|
|
359
|
+
this.visibleCheckbox.checked = obj.visible;
|
|
360
|
+
|
|
361
|
+
// Double sided
|
|
362
|
+
if (obj instanceof THREE.Mesh) {
|
|
363
|
+
const material = obj.material as THREE.MeshStandardMaterial;
|
|
364
|
+
this.doubleSidedCheckbox.checked = material.side === THREE.DoubleSide;
|
|
365
|
+
} else {
|
|
366
|
+
this.doubleSidedCheckbox.checked = false;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Shading mode
|
|
370
|
+
this.shadingModeSelect.value = obj.userData.shadingMode || "standard";
|
|
371
|
+
|
|
372
|
+
this.isUpdating = false;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Rebuild geometry after size/offset changes
|
|
377
|
+
*/
|
|
378
|
+
private rebuildGeometry(): void {
|
|
379
|
+
if (!this.currentObject || !(this.currentObject instanceof THREE.Mesh)) return;
|
|
380
|
+
|
|
381
|
+
const mesh = this.currentObject;
|
|
382
|
+
const size = mesh.userData.originalSize || { x: 1, y: 1, z: 1 };
|
|
383
|
+
const offset = mesh.userData.offset || { x: 0, y: 0, z: 0 };
|
|
384
|
+
const stretch = mesh.userData.stretch || { x: 1, y: 1, z: 1 };
|
|
385
|
+
|
|
386
|
+
const finalSize = {
|
|
387
|
+
x: size.x * stretch.x,
|
|
388
|
+
y: size.y * stretch.y,
|
|
389
|
+
z: size.z * stretch.z,
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// Dispose old geometry
|
|
393
|
+
mesh.geometry.dispose();
|
|
394
|
+
|
|
395
|
+
// Create new geometry based on shape type
|
|
396
|
+
if (mesh.userData.shapeType === "box") {
|
|
397
|
+
mesh.geometry = new THREE.BoxGeometry(finalSize.x, finalSize.y, finalSize.z);
|
|
398
|
+
mesh.geometry.translate(offset.x, offset.y, offset.z);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Show property sections
|
|
404
|
+
*/
|
|
405
|
+
private showProperties(): void {
|
|
406
|
+
const transform = document.getElementById("properties-transform");
|
|
407
|
+
const shape = document.getElementById("properties-shape");
|
|
408
|
+
const empty = document.getElementById("properties-empty");
|
|
409
|
+
|
|
410
|
+
if (transform) transform.style.display = "block";
|
|
411
|
+
if (shape) shape.style.display = "block";
|
|
412
|
+
if (empty) empty.style.display = "none";
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Show empty state
|
|
417
|
+
*/
|
|
418
|
+
private showEmpty(): void {
|
|
419
|
+
const transform = document.getElementById("properties-transform");
|
|
420
|
+
const shape = document.getElementById("properties-shape");
|
|
421
|
+
const empty = document.getElementById("properties-empty");
|
|
422
|
+
|
|
423
|
+
if (transform) transform.style.display = "none";
|
|
424
|
+
if (shape) shape.style.display = "none";
|
|
425
|
+
if (empty) empty.style.display = "block";
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Cleanup
|
|
430
|
+
*/
|
|
431
|
+
dispose(): void {
|
|
432
|
+
this.container.innerHTML = "";
|
|
433
|
+
}
|
|
434
|
+
}
|