@preference-sl/pref-viewer 2.1.9 → 2.3.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/package.json +1 -1
- package/src/index.js +281 -98
- package/src/models/patata.gltf +0 -10059
- package/src/test.global.html +0 -240
- package/src/test.html +0 -62
package/src/test.global.html
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<title>PrefViewer Test (Global Babylon)</title>
|
|
6
|
-
<style>
|
|
7
|
-
html, body {
|
|
8
|
-
margin: 0;
|
|
9
|
-
padding: 0;
|
|
10
|
-
width: 100%;
|
|
11
|
-
height: 100%;
|
|
12
|
-
overflow: hidden;
|
|
13
|
-
}
|
|
14
|
-
#viewer-container {
|
|
15
|
-
width: 100%;
|
|
16
|
-
height: 100%;
|
|
17
|
-
display: flex;
|
|
18
|
-
align-items: center;
|
|
19
|
-
justify-content: center;
|
|
20
|
-
background: #f0f0f0;
|
|
21
|
-
}
|
|
22
|
-
</style>
|
|
23
|
-
|
|
24
|
-
<!-- 1) Load Babylon Core (Global) -->
|
|
25
|
-
<script src="https://cdn.babylonjs.com/babylon.js"></script>
|
|
26
|
-
<!-- 2) Load Babylon glTF loader so SceneLoader.ImportMeshAsync can parse .gltf -->
|
|
27
|
-
<script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
|
|
28
|
-
</head>
|
|
29
|
-
<body>
|
|
30
|
-
<div id="viewer-container">
|
|
31
|
-
<!-- Omit "model" attribute: PrefViewer will default to "./models/patata.gltf" -->
|
|
32
|
-
<pref-viewer style="width: 800px; height: 600px; border: 1px solid #ccc;"></pref-viewer>
|
|
33
|
-
</div>
|
|
34
|
-
|
|
35
|
-
<!-- 3) Define PrefViewer using the global `BABYLON` object -->
|
|
36
|
-
<script>
|
|
37
|
-
(function () {
|
|
38
|
-
const { Engine, Scene, ArcRotateCamera, Vector3, SceneLoader, Color4, HemisphericLight, DirectionalLight } = BABYLON;
|
|
39
|
-
|
|
40
|
-
class PrefViewer extends HTMLElement {
|
|
41
|
-
constructor() {
|
|
42
|
-
super();
|
|
43
|
-
this.attachShadow({ mode: "open" });
|
|
44
|
-
this.canvas = document.createElement("canvas");
|
|
45
|
-
Object.assign(this.canvas.style, {
|
|
46
|
-
width: "100%",
|
|
47
|
-
height: "100%",
|
|
48
|
-
display: "block",
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
const wrapper = document.createElement("div");
|
|
52
|
-
Object.assign(wrapper.style, {
|
|
53
|
-
width: "100%",
|
|
54
|
-
height: "100%",
|
|
55
|
-
position: "relative",
|
|
56
|
-
});
|
|
57
|
-
wrapper.appendChild(this.canvas);
|
|
58
|
-
this.shadowRoot.append(wrapper);
|
|
59
|
-
|
|
60
|
-
this.engine = null;
|
|
61
|
-
this.scene = null;
|
|
62
|
-
this.camera = null;
|
|
63
|
-
this.hemiLight = null;
|
|
64
|
-
this.dirLight = null;
|
|
65
|
-
this.modelUrl = null;
|
|
66
|
-
this._onWindowResize = null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
static get observedAttributes() {
|
|
70
|
-
return ["model"];
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
attributeChangedCallback(name, _oldValue, newValue) {
|
|
74
|
-
if (name === "model" && newValue) {
|
|
75
|
-
this.modelUrl = newValue;
|
|
76
|
-
this._reloadModel();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
connectedCallback() {
|
|
81
|
-
// If no `model` attribute is provided, default to "models/patata.gltf"
|
|
82
|
-
if (!this.hasAttribute("model")) {
|
|
83
|
-
// Note: NO import.meta.url here—just use a relative path.
|
|
84
|
-
this.modelUrl = "models/patata.gltf";
|
|
85
|
-
}
|
|
86
|
-
this._initializeBabylon();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
disconnectedCallback() {
|
|
90
|
-
this._disposeEngine();
|
|
91
|
-
if (this._onWindowResize) {
|
|
92
|
-
window.removeEventListener("resize", this._onWindowResize);
|
|
93
|
-
this._onWindowResize = null;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// —— Private setup methods ——
|
|
98
|
-
|
|
99
|
-
_initializeBabylon() {
|
|
100
|
-
// 1) Create engine & scene
|
|
101
|
-
this.engine = new Engine(this.canvas, true, { preserveDrawingBuffer: true, stencil: true, disableWebGL2Support: false });
|
|
102
|
-
this.scene = new Scene(this.engine);
|
|
103
|
-
this.scene.clearColor = new Color4(1, 1, 1, 1);
|
|
104
|
-
|
|
105
|
-
// 2) Create camera & lights
|
|
106
|
-
this._createCamera();
|
|
107
|
-
this._createLights();
|
|
108
|
-
|
|
109
|
-
// 3) Hook up events
|
|
110
|
-
this._setupEventListeners();
|
|
111
|
-
|
|
112
|
-
// 4) Start render loop
|
|
113
|
-
this.engine.runRenderLoop(() => {
|
|
114
|
-
if (this.scene) {
|
|
115
|
-
this.scene.render();
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
this._onWindowResize = () => this.engine.resize();
|
|
119
|
-
window.addEventListener("resize", this._onWindowResize);
|
|
120
|
-
|
|
121
|
-
// 5) Load the initial model
|
|
122
|
-
this._reloadModel();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
_createCamera() {
|
|
126
|
-
// ArcRotateCamera around (0,0,0)
|
|
127
|
-
const camera = new ArcRotateCamera(
|
|
128
|
-
"camera",
|
|
129
|
-
Math.PI / 2,
|
|
130
|
-
Math.PI / 3,
|
|
131
|
-
10,
|
|
132
|
-
Vector3.Zero(),
|
|
133
|
-
this.scene
|
|
134
|
-
);
|
|
135
|
-
camera.attachControl(this.canvas, true);
|
|
136
|
-
this.camera = camera;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
_createLights() {
|
|
140
|
-
// Hemispheric light (fills in shadows)
|
|
141
|
-
const hemiLight = new HemisphericLight("hemiLight", new Vector3(0, 1, 0), this.scene);
|
|
142
|
-
hemiLight.intensity = 0.8;
|
|
143
|
-
|
|
144
|
-
// Directional key light
|
|
145
|
-
const dirLight = new DirectionalLight("dirLight", new Vector3(-0.5, -1, -0.5), this.scene);
|
|
146
|
-
dirLight.position = new Vector3(0, 5, 0);
|
|
147
|
-
dirLight.intensity = 0.8;
|
|
148
|
-
|
|
149
|
-
this.hemiLight = hemiLight;
|
|
150
|
-
this.dirLight = dirLight;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
_setupEventListeners() {
|
|
154
|
-
// Zoom towards the pointer's picked point on wheel scroll:
|
|
155
|
-
this.canvas.addEventListener("wheel", (evt) => {
|
|
156
|
-
if (!this.scene || !this.camera) return;
|
|
157
|
-
const pickResult = this.scene.pick(this.scene.pointerX, this.scene.pointerY);
|
|
158
|
-
const pivotPoint = pickResult.hit
|
|
159
|
-
? pickResult.pickedPoint.clone()
|
|
160
|
-
: this.camera.target.clone();
|
|
161
|
-
this.camera.target = pivotPoint;
|
|
162
|
-
this.camera.inertialRadiusOffset += evt.deltaY * this.camera.wheelPrecision * 0.01;
|
|
163
|
-
evt.preventDefault();
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// —— Model loading / management ——
|
|
168
|
-
|
|
169
|
-
async _reloadModel() {
|
|
170
|
-
if (!this.scene || !this.modelUrl) return;
|
|
171
|
-
|
|
172
|
-
// Dispose previous meshes (to avoid stacking them up)
|
|
173
|
-
this._disposePreviousMeshes();
|
|
174
|
-
|
|
175
|
-
try {
|
|
176
|
-
const result = await SceneLoader.ImportMeshAsync(
|
|
177
|
-
null,
|
|
178
|
-
"",
|
|
179
|
-
this.modelUrl,
|
|
180
|
-
this.scene,
|
|
181
|
-
undefined,
|
|
182
|
-
".gltf"
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
// Ensure there's at least one camera / light if glTF had none
|
|
186
|
-
this.scene.createDefaultCameraOrLight(true, true, true);
|
|
187
|
-
|
|
188
|
-
// Turn off back-face culling so you can see both sides
|
|
189
|
-
result.meshes.forEach((mesh) => {
|
|
190
|
-
if (mesh.material) {
|
|
191
|
-
mesh.material.backFaceCulling = false;
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
this.dispatchEvent(
|
|
196
|
-
new CustomEvent("model-loaded", {
|
|
197
|
-
detail: { meshes: result.meshes, particleSystems: result.particleSystems },
|
|
198
|
-
bubbles: true,
|
|
199
|
-
composed: true,
|
|
200
|
-
})
|
|
201
|
-
);
|
|
202
|
-
} catch (err) {
|
|
203
|
-
console.error("Error loading model:", err);
|
|
204
|
-
this.dispatchEvent(
|
|
205
|
-
new CustomEvent("model-error", {
|
|
206
|
-
detail: { error: err },
|
|
207
|
-
bubbles: true,
|
|
208
|
-
composed: true,
|
|
209
|
-
})
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
_disposePreviousMeshes() {
|
|
215
|
-
if (!this.scene) return;
|
|
216
|
-
this.scene.meshes.slice().forEach((mesh) => {
|
|
217
|
-
if (mesh.getClassName && mesh.getClassName() === "Mesh") {
|
|
218
|
-
mesh.dispose();
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// —— Cleanup ——
|
|
224
|
-
|
|
225
|
-
_disposeEngine() {
|
|
226
|
-
if (this.engine) {
|
|
227
|
-
this.engine.dispose();
|
|
228
|
-
this.engine = null;
|
|
229
|
-
this.scene = null;
|
|
230
|
-
this.camera = null;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
customElements.define("pref-viewer", PrefViewer);
|
|
236
|
-
})();
|
|
237
|
-
</script>
|
|
238
|
-
|
|
239
|
-
</body>
|
|
240
|
-
</html>
|
package/src/test.html
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<title>PrefViewer Test (Reuse Index.js)</title>
|
|
6
|
-
<style>
|
|
7
|
-
html, body {
|
|
8
|
-
margin: 0;
|
|
9
|
-
padding: 0;
|
|
10
|
-
width: 100%;
|
|
11
|
-
height: 100%;
|
|
12
|
-
overflow: hidden;
|
|
13
|
-
}
|
|
14
|
-
#viewer-container {
|
|
15
|
-
width: 100%;
|
|
16
|
-
height: 100%;
|
|
17
|
-
display: flex;
|
|
18
|
-
align-items: center;
|
|
19
|
-
justify-content: center;
|
|
20
|
-
background: #f0f0f0;
|
|
21
|
-
}
|
|
22
|
-
</style>
|
|
23
|
-
|
|
24
|
-
<!--
|
|
25
|
-
IMPORT MAP
|
|
26
|
-
──────────
|
|
27
|
-
This tells the browser where to fetch "@babylonjs/core" and "@babylonjs/loaders".
|
|
28
|
-
We use jsDelivr's "?module" query so that it automatically serves the ESM build
|
|
29
|
-
corresponding to each package's "module" field in package.json.
|
|
30
|
-
|
|
31
|
-
Make sure the version (5.0.1 here) matches what you have in package.json.
|
|
32
|
-
If you have "@babylonjs/core": "^5.0.2" in your package.json, change both URLs
|
|
33
|
-
to "@5.0.2?module", etc.
|
|
34
|
-
|
|
35
|
-
This import-map must appear *before* you load index.js as a module.
|
|
36
|
-
-->
|
|
37
|
-
<script type="importmap">
|
|
38
|
-
{
|
|
39
|
-
"imports": {
|
|
40
|
-
"@babylonjs/core": "https://cdn.jsdelivr.net/npm/@babylonjs/core@5.0.1?module",
|
|
41
|
-
"@babylonjs/loaders": "https://cdn.jsdelivr.net/npm/@babylonjs/loaders@5.0.1?module"
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
</script>
|
|
45
|
-
</head>
|
|
46
|
-
<body>
|
|
47
|
-
<div id="viewer-container">
|
|
48
|
-
<!--
|
|
49
|
-
By omitting the `model` attribute, PrefViewer’s connectedCallback()
|
|
50
|
-
will fall back to "./models/patata.gltf" (relative to index.js).
|
|
51
|
-
-->
|
|
52
|
-
<pref-viewer style="width: 800px; height: 600px; border: 1px solid #ccc;"></pref-viewer>
|
|
53
|
-
</div>
|
|
54
|
-
|
|
55
|
-
<!--
|
|
56
|
-
Load your existing ES-module index.js. Because of the import-map above,
|
|
57
|
-
any `import { … } from "@babylonjs/core"` inside index.js will resolve to
|
|
58
|
-
"https://cdn.jsdelivr.net/npm/@babylonjs/core@5.0.1?module", etc.
|
|
59
|
-
-->
|
|
60
|
-
<script type="module" src="./index.js"></script>
|
|
61
|
-
</body>
|
|
62
|
-
</html>
|