diamon-engine 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/README.md +119 -0
- package/dist/package/OrbitCameraController-COklTntj.js +400 -0
- package/dist/package/Spin3D-BW0UYD9X.js +193 -0
- package/dist/package/engine3d.js +14 -0
- package/dist/package/index.js +14 -0
- package/dist/package/types/engine3d/components/Light3D.d.ts +16 -0
- package/dist/package/types/engine3d/components/MeshPrimitive3D.d.ts +43 -0
- package/dist/package/types/engine3d/components/Object3DComponent.d.ts +14 -0
- package/dist/package/types/engine3d/components/OrbitCameraController.d.ts +20 -0
- package/dist/package/types/engine3d/components/Spin3D.d.ts +8 -0
- package/dist/package/types/engine3d/core/Component3D.d.ts +17 -0
- package/dist/package/types/engine3d/core/Engine3D.d.ts +65 -0
- package/dist/package/types/engine3d/core/Entity3D.d.ts +27 -0
- package/dist/package/types/engine3d/core/Scene3D.d.ts +27 -0
- package/dist/package/types/engine3d/index.d.ts +10 -0
- package/dist/package/types/engine3d/systems/Input3D.d.ts +29 -0
- package/dist/package/types/index.d.ts +1 -0
- package/dist/package/types/workbench/createEngineWorkbench.d.ts +56 -0
- package/dist/package/types/workbench/index.d.ts +1 -0
- package/dist/package/workbench.js +713 -0
- package/package.json +52 -0
- package/src/styles.css +460 -0
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Diamon
|
|
2
|
+
|
|
3
|
+
Diamon 是一个以 3D 为主的 Web 游戏引擎原型。当前阶段只做 3D 引擎和编辑器本身,聚焦运行时架构、资源管理、保存加载、性能诊断和压力测试,不做任何用 Diamon 制作出来的游戏,也不保留 2D 引擎代码。
|
|
4
|
+
|
|
5
|
+
## 当前能力
|
|
6
|
+
|
|
7
|
+
### 3D 引擎
|
|
8
|
+
|
|
9
|
+
- `Engine3D`:WebGL 渲染、主循环、窗口自适应、渲染统计、自适应画质、阴影开关和像素比例控制
|
|
10
|
+
- `Scene3D`:Three.js 场景封装、透视相机、3D 实体管理、逐帧更新调度
|
|
11
|
+
- `Entity3D`:位置、旋转、缩放、标签、组件挂载
|
|
12
|
+
- `Component3D`:生命周期、逐帧更新、销毁清理
|
|
13
|
+
- `MeshPrimitive3D`:盒子、球、圆柱、圆锥、圆环、平面、基础几何体和材质复用
|
|
14
|
+
- `Light3D`:环境光、方向光、点光源
|
|
15
|
+
- `Input3D`:键盘、鼠标拖动、滚轮缩放
|
|
16
|
+
- `OrbitCameraController`:可拖动旋转、滚轮远近调整的 3D 相机
|
|
17
|
+
|
|
18
|
+
### 引擎编辑器
|
|
19
|
+
|
|
20
|
+
- 默认加载 3D 编辑器场景
|
|
21
|
+
- 支持添加基础对象:立方体、球体、圆柱、圆锥、圆环
|
|
22
|
+
- 支持场景树、属性面板、资源面板、预制体面板
|
|
23
|
+
- 支持对象选择、复制、粘贴、删除、批量选择、分组、图层
|
|
24
|
+
- 支持关键编辑操作的撤销和重做
|
|
25
|
+
- 支持场景 JSON 保存、加载、下载和本地缓存
|
|
26
|
+
- 支持模型、贴图、材质、音频资源导入记录,GLB/glTF 模型可导入场景
|
|
27
|
+
- 支持基础预制体保存和复用
|
|
28
|
+
- 显示对象数量、可见对象、Draw Calls、三角面、FPS、帧耗时、几何体、贴图、内存和慢系统提示
|
|
29
|
+
- 支持 10000 实例化对象、5000 普通对象、500 万增量压力、50 亿目标和 100 亿目标压力测试
|
|
30
|
+
- FPS 面板会按引擎帧耗时判断 60 FPS 目标承受能力,避免测试浏览器刷新调度把结果压低
|
|
31
|
+
- 静态对象默认不进入逐帧更新队列,性能面板降频刷新,降低编辑器自身开销
|
|
32
|
+
- 大型压力测试时场景树会限制一次渲染的行数,避免编辑器列表本身造成卡顿
|
|
33
|
+
- 500 万、50 亿和 100 亿压力测试会合并到单一超大压力层,并使用代表点 LOD;50 亿以上目标会切换到更轻的 8000 代表点,验证引擎在极端数量下的调度、资源和渲染承受能力
|
|
34
|
+
- 保留网格、世界坐标轴、环境光、方向光和编辑相机
|
|
35
|
+
|
|
36
|
+
## 运行
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install
|
|
40
|
+
npm run dev
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 验证
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm run check
|
|
47
|
+
npm run build
|
|
48
|
+
npm run pack:dry
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 作为包给其他游戏使用
|
|
52
|
+
|
|
53
|
+
Diamon 现在可以作为 `diamon-engine` 包被其他游戏引用。当前包导出三个入口:
|
|
54
|
+
|
|
55
|
+
- `diamon-engine`:默认 3D 运行时入口
|
|
56
|
+
- `diamon-engine/engine3d`:明确的 3D 运行时入口
|
|
57
|
+
- `diamon-engine/workbench`:编辑器工作台入口
|
|
58
|
+
|
|
59
|
+
本地开发中的游戏可以先用本地路径安装:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm install ../yuyu自制引擎
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
游戏项目里直接引用:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { Engine3D, Entity3D, MeshPrimitive3D, Scene3D } from "diamon-engine";
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
需要编辑器样式时再引入:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import "diamon-engine/styles.css";
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
发布包前先构建并做干跑检查:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm run build
|
|
81
|
+
npm run pack:dry
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 3D 最小用法
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { Engine3D, Entity3D, MeshPrimitive3D, Scene3D } from "diamon-engine";
|
|
88
|
+
|
|
89
|
+
const engine = new Engine3D({ canvas: document.querySelector("canvas")! });
|
|
90
|
+
const scene = new Scene3D("Demo");
|
|
91
|
+
|
|
92
|
+
const box = new Entity3D("Box");
|
|
93
|
+
box.addComponent(
|
|
94
|
+
new MeshPrimitive3D({
|
|
95
|
+
kind: "box",
|
|
96
|
+
width: 1,
|
|
97
|
+
height: 1,
|
|
98
|
+
depth: 1,
|
|
99
|
+
color: "#1ec7a6"
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
scene.add(box);
|
|
104
|
+
engine.setScene(scene);
|
|
105
|
+
engine.start();
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 目标文档
|
|
109
|
+
|
|
110
|
+
- [3D 引擎性能目标](./docs/3D_ENGINE_PERFORMANCE_GOALS.md)
|
|
111
|
+
|
|
112
|
+
## 下一步适合做的引擎能力
|
|
113
|
+
|
|
114
|
+
1. 大量对象优化继续增强:实例化、合批、资源复用
|
|
115
|
+
2. 区块加载结构和大场景流式加载
|
|
116
|
+
3. 资源压缩、贴图质量档位和显存统计
|
|
117
|
+
4. 更完整的 GLB/glTF 材质和动画支持
|
|
118
|
+
5. 碰撞体、触发器和后续刚体物理
|
|
119
|
+
6. 插件式导入器和编辑器扩展流程
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import * as i from "three";
|
|
2
|
+
class a {
|
|
3
|
+
entity;
|
|
4
|
+
enabled = !0;
|
|
5
|
+
updatesEveryFrame = !0;
|
|
6
|
+
started = !1;
|
|
7
|
+
get scene() {
|
|
8
|
+
return this.entity.scene;
|
|
9
|
+
}
|
|
10
|
+
get engine() {
|
|
11
|
+
return this.scene?.engine;
|
|
12
|
+
}
|
|
13
|
+
startInternal() {
|
|
14
|
+
this.started || (this.started = !0, this.onStart());
|
|
15
|
+
}
|
|
16
|
+
updateInternal(t) {
|
|
17
|
+
this.enabled && this.updatesEveryFrame && this.onUpdate(t);
|
|
18
|
+
}
|
|
19
|
+
destroyInternal() {
|
|
20
|
+
this.started && (this.onDestroy(), this.started = !1);
|
|
21
|
+
}
|
|
22
|
+
onStart() {
|
|
23
|
+
}
|
|
24
|
+
onUpdate(t) {
|
|
25
|
+
}
|
|
26
|
+
onDestroy() {
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
let d = 1;
|
|
30
|
+
class l {
|
|
31
|
+
constructor(t = "Entity3D") {
|
|
32
|
+
this.name = t, this.object.name = t;
|
|
33
|
+
}
|
|
34
|
+
name;
|
|
35
|
+
id = d++;
|
|
36
|
+
object = new i.Group();
|
|
37
|
+
components = [];
|
|
38
|
+
frameUpdateComponents = [];
|
|
39
|
+
scene;
|
|
40
|
+
tag = "";
|
|
41
|
+
enabled = !0;
|
|
42
|
+
destroyed = !1;
|
|
43
|
+
get position() {
|
|
44
|
+
return this.object.position;
|
|
45
|
+
}
|
|
46
|
+
get rotation() {
|
|
47
|
+
return this.object.rotation;
|
|
48
|
+
}
|
|
49
|
+
get scale() {
|
|
50
|
+
return this.object.scale;
|
|
51
|
+
}
|
|
52
|
+
addComponent(t) {
|
|
53
|
+
if (t.entity && t.entity !== this)
|
|
54
|
+
throw new Error("3D component already belongs to another entity.");
|
|
55
|
+
return t.entity = this, this.components.push(t), t.updatesEveryFrame && this.frameUpdateComponents.push(t), this.scene?.isStarted && t.startInternal(), this.scene?.refreshEntityScheduling(this), t;
|
|
56
|
+
}
|
|
57
|
+
getComponent(t) {
|
|
58
|
+
return this.components.find(
|
|
59
|
+
(e) => e instanceof t
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
destroy() {
|
|
63
|
+
this.destroyed || (this.destroyed = !0, this.scene?.markEntityDestroyed(this));
|
|
64
|
+
}
|
|
65
|
+
get needsFrameUpdate() {
|
|
66
|
+
return this.frameUpdateComponents.length > 0;
|
|
67
|
+
}
|
|
68
|
+
startInternal() {
|
|
69
|
+
for (const t of this.components)
|
|
70
|
+
t.startInternal();
|
|
71
|
+
}
|
|
72
|
+
updateInternal(t) {
|
|
73
|
+
if (!(!this.enabled || this.destroyed || this.frameUpdateComponents.length === 0))
|
|
74
|
+
for (const e of this.frameUpdateComponents)
|
|
75
|
+
e.updateInternal(t);
|
|
76
|
+
}
|
|
77
|
+
destroyInternal() {
|
|
78
|
+
for (const t of this.components)
|
|
79
|
+
t.destroyInternal();
|
|
80
|
+
this.components.length = 0, this.frameUpdateComponents.length = 0, this.object.removeFromParent(), this.scene = void 0;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
class m {
|
|
84
|
+
constructor(t = "Scene3D") {
|
|
85
|
+
this.name = t, this.threeScene.name = t, this.camera.position.set(6, 5, 8), this.camera.lookAt(0, 0, 0);
|
|
86
|
+
}
|
|
87
|
+
name;
|
|
88
|
+
threeScene = new i.Scene();
|
|
89
|
+
camera = new i.PerspectiveCamera(55, 1, 0.1, 1e3);
|
|
90
|
+
entities = [];
|
|
91
|
+
frameUpdateEntities = /* @__PURE__ */ new Set();
|
|
92
|
+
destroyedEntityCount = 0;
|
|
93
|
+
engine;
|
|
94
|
+
isStarted = !1;
|
|
95
|
+
add(t) {
|
|
96
|
+
if (t.scene && t.scene !== this)
|
|
97
|
+
throw new Error(`3D entity "${t.name}" already belongs to another scene.`);
|
|
98
|
+
return t.scene = this, this.entities.push(t), this.threeScene.add(t.object), this.refreshEntityScheduling(t), this.isStarted && t.startInternal(), t;
|
|
99
|
+
}
|
|
100
|
+
remove(t) {
|
|
101
|
+
const e = this.entities.indexOf(t);
|
|
102
|
+
e >= 0 && this.entities.splice(e, 1), this.frameUpdateEntities.delete(t), t.scene === this && t.destroyInternal();
|
|
103
|
+
}
|
|
104
|
+
findByTag(t) {
|
|
105
|
+
return this.entities.filter((e) => e.tag === t && !e.destroyed);
|
|
106
|
+
}
|
|
107
|
+
get updatableEntityCount() {
|
|
108
|
+
return this.frameUpdateEntities.size;
|
|
109
|
+
}
|
|
110
|
+
refreshEntityScheduling(t) {
|
|
111
|
+
if (t.scene !== this || t.destroyed || !t.needsFrameUpdate) {
|
|
112
|
+
this.frameUpdateEntities.delete(t);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
this.frameUpdateEntities.add(t);
|
|
116
|
+
}
|
|
117
|
+
markEntityDestroyed(t) {
|
|
118
|
+
t.scene === this && (this.destroyedEntityCount += 1, this.frameUpdateEntities.delete(t));
|
|
119
|
+
}
|
|
120
|
+
startInternal(t) {
|
|
121
|
+
if (this.engine = t, !this.isStarted) {
|
|
122
|
+
this.isStarted = !0, this.onStart();
|
|
123
|
+
for (const e of this.entities)
|
|
124
|
+
e.startInternal();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
updateInternal(t) {
|
|
128
|
+
this.onUpdate(t);
|
|
129
|
+
for (const e of this.frameUpdateEntities)
|
|
130
|
+
e.updateInternal(t);
|
|
131
|
+
this.destroyedEntityCount > 0 && this.removeDestroyedEntities();
|
|
132
|
+
}
|
|
133
|
+
stopInternal() {
|
|
134
|
+
this.onStop();
|
|
135
|
+
for (const t of this.entities)
|
|
136
|
+
t.destroyInternal();
|
|
137
|
+
this.entities.length = 0, this.frameUpdateEntities.clear(), this.destroyedEntityCount = 0, this.threeScene.clear(), this.isStarted = !1, this.engine = void 0;
|
|
138
|
+
}
|
|
139
|
+
onStart() {
|
|
140
|
+
}
|
|
141
|
+
onUpdate(t) {
|
|
142
|
+
}
|
|
143
|
+
onStop() {
|
|
144
|
+
}
|
|
145
|
+
removeDestroyedEntities() {
|
|
146
|
+
for (let t = this.entities.length - 1; t >= 0; t -= 1) {
|
|
147
|
+
const e = this.entities[t];
|
|
148
|
+
e.destroyed && (e.destroyInternal(), this.entities.splice(t, 1));
|
|
149
|
+
}
|
|
150
|
+
this.destroyedEntityCount = 0;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
class p extends a {
|
|
154
|
+
constructor(t) {
|
|
155
|
+
super(), this.options = t;
|
|
156
|
+
}
|
|
157
|
+
options;
|
|
158
|
+
updatesEveryFrame = !1;
|
|
159
|
+
light;
|
|
160
|
+
onStart() {
|
|
161
|
+
const t = this.options.color ?? "#ffffff", e = this.options.intensity ?? 1;
|
|
162
|
+
if (this.options.kind === "ambient")
|
|
163
|
+
this.light = new i.AmbientLight(t, e);
|
|
164
|
+
else if (this.options.kind === "directional") {
|
|
165
|
+
const s = new i.DirectionalLight(t, e);
|
|
166
|
+
s.castShadow = this.options.castShadow ?? !1, s.shadow.mapSize.set(1024, 1024), s.shadow.camera.near = 0.5, s.shadow.camera.far = 40, this.light = s;
|
|
167
|
+
} else {
|
|
168
|
+
const s = new i.PointLight(t, e, 24, 1.8);
|
|
169
|
+
s.castShadow = this.options.castShadow ?? !1, this.light = s;
|
|
170
|
+
}
|
|
171
|
+
this.entity.object.add(this.light);
|
|
172
|
+
}
|
|
173
|
+
onDestroy() {
|
|
174
|
+
this.light?.removeFromParent(), this.light = void 0;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const o = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
|
|
178
|
+
class u extends a {
|
|
179
|
+
constructor(t) {
|
|
180
|
+
super(), this.options = t;
|
|
181
|
+
}
|
|
182
|
+
options;
|
|
183
|
+
updatesEveryFrame = !1;
|
|
184
|
+
mesh;
|
|
185
|
+
geometry;
|
|
186
|
+
material;
|
|
187
|
+
geometryCacheKey = "";
|
|
188
|
+
materialCacheKey = "";
|
|
189
|
+
onStart() {
|
|
190
|
+
this.geometry = this.acquireGeometry(), this.material = this.acquireMaterial(), this.mesh = new i.Mesh(this.geometry, this.material), this.mesh.castShadow = this.options.castShadow ?? !1, this.mesh.receiveShadow = this.options.receiveShadow ?? !1, this.entity.object.add(this.mesh);
|
|
191
|
+
}
|
|
192
|
+
onDestroy() {
|
|
193
|
+
this.mesh?.removeFromParent(), this.releaseGeometry(), this.releaseMaterial(), this.mesh = void 0, this.geometry = void 0, this.material = void 0;
|
|
194
|
+
}
|
|
195
|
+
setColor(t) {
|
|
196
|
+
if (this.options.color = t, !!this.material) {
|
|
197
|
+
if (!this.usesSharedResources()) {
|
|
198
|
+
this.material.color.set(t);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
this.releaseMaterial(), this.material = this.acquireMaterial(), this.mesh && (this.mesh.material = this.material);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
getColor() {
|
|
205
|
+
return this.material ? `#${this.material.color.getHexString()}` : this.options.color ?? "#1ec7a6";
|
|
206
|
+
}
|
|
207
|
+
createGeometry() {
|
|
208
|
+
const t = this.options.segments ?? 32;
|
|
209
|
+
switch (this.options.kind) {
|
|
210
|
+
case "sphere":
|
|
211
|
+
return new i.SphereGeometry(this.options.radius ?? 0.7, t, 18);
|
|
212
|
+
case "cylinder":
|
|
213
|
+
return new i.CylinderGeometry(
|
|
214
|
+
this.options.radiusTop ?? this.options.radius ?? 0.5,
|
|
215
|
+
this.options.radiusBottom ?? this.options.radius ?? 0.5,
|
|
216
|
+
this.options.height ?? 1.4,
|
|
217
|
+
t
|
|
218
|
+
);
|
|
219
|
+
case "cone":
|
|
220
|
+
return new i.ConeGeometry(
|
|
221
|
+
this.options.radius ?? 0.6,
|
|
222
|
+
this.options.height ?? 1.4,
|
|
223
|
+
t
|
|
224
|
+
);
|
|
225
|
+
case "torus":
|
|
226
|
+
return new i.TorusGeometry(
|
|
227
|
+
this.options.radius ?? 0.65,
|
|
228
|
+
this.options.tube ?? 0.18,
|
|
229
|
+
14,
|
|
230
|
+
t
|
|
231
|
+
);
|
|
232
|
+
case "plane":
|
|
233
|
+
return new i.PlaneGeometry(this.options.width ?? 12, this.options.depth ?? 12);
|
|
234
|
+
default:
|
|
235
|
+
return new i.BoxGeometry(
|
|
236
|
+
this.options.width ?? 1,
|
|
237
|
+
this.options.height ?? 1,
|
|
238
|
+
this.options.depth ?? 1
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
acquireGeometry() {
|
|
243
|
+
if (!this.usesSharedResources())
|
|
244
|
+
return this.createGeometry();
|
|
245
|
+
const t = this.getGeometryKey(), e = o.get(t);
|
|
246
|
+
if (e)
|
|
247
|
+
return e.refs += 1, this.geometryCacheKey = t, e.value;
|
|
248
|
+
const s = this.createGeometry();
|
|
249
|
+
return o.set(t, { value: s, refs: 1 }), this.geometryCacheKey = t, s;
|
|
250
|
+
}
|
|
251
|
+
acquireMaterial() {
|
|
252
|
+
if (!this.usesSharedResources())
|
|
253
|
+
return this.createMaterial();
|
|
254
|
+
const t = this.getMaterialKey(), e = n.get(t);
|
|
255
|
+
if (e)
|
|
256
|
+
return e.refs += 1, this.materialCacheKey = t, e.value;
|
|
257
|
+
const s = this.createMaterial();
|
|
258
|
+
return n.set(t, { value: s, refs: 1 }), this.materialCacheKey = t, s;
|
|
259
|
+
}
|
|
260
|
+
releaseGeometry() {
|
|
261
|
+
if (!this.geometry)
|
|
262
|
+
return;
|
|
263
|
+
if (!this.geometryCacheKey) {
|
|
264
|
+
this.geometry.dispose();
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const t = o.get(this.geometryCacheKey);
|
|
268
|
+
t && (t.refs -= 1, t.refs <= 0 && (t.value.dispose(), o.delete(this.geometryCacheKey))), this.geometryCacheKey = "";
|
|
269
|
+
}
|
|
270
|
+
releaseMaterial() {
|
|
271
|
+
if (!this.material)
|
|
272
|
+
return;
|
|
273
|
+
if (!this.materialCacheKey) {
|
|
274
|
+
this.material.dispose();
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const t = n.get(this.materialCacheKey);
|
|
278
|
+
t && (t.refs -= 1, t.refs <= 0 && (t.value.dispose(), n.delete(this.materialCacheKey))), this.materialCacheKey = "";
|
|
279
|
+
}
|
|
280
|
+
createMaterial() {
|
|
281
|
+
return new i.MeshStandardMaterial({
|
|
282
|
+
color: this.options.color ?? "#1ec7a6",
|
|
283
|
+
metalness: this.options.metalness ?? 0.12,
|
|
284
|
+
roughness: this.options.roughness ?? 0.52
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
getGeometryKey() {
|
|
288
|
+
const t = this.options.segments ?? 32;
|
|
289
|
+
switch (this.options.kind) {
|
|
290
|
+
case "sphere":
|
|
291
|
+
return ["sphere", this.options.radius ?? 0.7, t, 18].join("|");
|
|
292
|
+
case "cylinder":
|
|
293
|
+
return [
|
|
294
|
+
"cylinder",
|
|
295
|
+
this.options.radiusTop ?? this.options.radius ?? 0.5,
|
|
296
|
+
this.options.radiusBottom ?? this.options.radius ?? 0.5,
|
|
297
|
+
this.options.height ?? 1.4,
|
|
298
|
+
t
|
|
299
|
+
].join("|");
|
|
300
|
+
case "cone":
|
|
301
|
+
return ["cone", this.options.radius ?? 0.6, this.options.height ?? 1.4, t].join("|");
|
|
302
|
+
case "torus":
|
|
303
|
+
return [
|
|
304
|
+
"torus",
|
|
305
|
+
this.options.radius ?? 0.65,
|
|
306
|
+
this.options.tube ?? 0.18,
|
|
307
|
+
14,
|
|
308
|
+
t
|
|
309
|
+
].join("|");
|
|
310
|
+
case "plane":
|
|
311
|
+
return ["plane", this.options.width ?? 12, this.options.depth ?? 12].join("|");
|
|
312
|
+
default:
|
|
313
|
+
return [
|
|
314
|
+
"box",
|
|
315
|
+
this.options.width ?? 1,
|
|
316
|
+
this.options.height ?? 1,
|
|
317
|
+
this.options.depth ?? 1
|
|
318
|
+
].join("|");
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
getMaterialKey() {
|
|
322
|
+
return [
|
|
323
|
+
this.options.color ?? "#1ec7a6",
|
|
324
|
+
this.options.metalness ?? 0.12,
|
|
325
|
+
this.options.roughness ?? 0.52
|
|
326
|
+
].join("|");
|
|
327
|
+
}
|
|
328
|
+
usesSharedResources() {
|
|
329
|
+
return this.options.sharedResources ?? !0;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
class y extends a {
|
|
333
|
+
constructor(t, e = {}) {
|
|
334
|
+
super(), this.object = t, this.options = e;
|
|
335
|
+
}
|
|
336
|
+
object;
|
|
337
|
+
options;
|
|
338
|
+
updatesEveryFrame = !1;
|
|
339
|
+
onStart() {
|
|
340
|
+
this.entity.object.add(this.object);
|
|
341
|
+
}
|
|
342
|
+
onDestroy() {
|
|
343
|
+
this.object.removeFromParent(), this.options.disposeOnDestroy && h(this.object);
|
|
344
|
+
}
|
|
345
|
+
replaceObject(t) {
|
|
346
|
+
this.object.removeFromParent(), this.options.disposeOnDestroy && h(this.object), this.object = t, this.entity.object.add(this.object);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function h(r) {
|
|
350
|
+
r.traverse((t) => {
|
|
351
|
+
const e = t;
|
|
352
|
+
e.geometry?.dispose();
|
|
353
|
+
const s = e.material;
|
|
354
|
+
if (Array.isArray(s))
|
|
355
|
+
for (const c of s)
|
|
356
|
+
c.dispose();
|
|
357
|
+
else
|
|
358
|
+
s?.dispose();
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
class f extends a {
|
|
362
|
+
target = new i.Vector3();
|
|
363
|
+
distance;
|
|
364
|
+
minDistance;
|
|
365
|
+
maxDistance;
|
|
366
|
+
azimuth;
|
|
367
|
+
polar;
|
|
368
|
+
constructor(t = {}) {
|
|
369
|
+
super(), this.target.copy(t.target ?? new i.Vector3()), this.distance = t.distance ?? 9, this.minDistance = t.minDistance ?? 4, this.maxDistance = t.maxDistance ?? 18, this.azimuth = t.azimuth ?? Math.PI * 0.25, this.polar = t.polar ?? Math.PI * 0.32;
|
|
370
|
+
}
|
|
371
|
+
onUpdate() {
|
|
372
|
+
const t = this.scene, e = this.engine?.input;
|
|
373
|
+
if (!t || !e)
|
|
374
|
+
return;
|
|
375
|
+
e.isMouseDown(0) && (this.azimuth -= e.pointerDelta.x * 6e-3, this.polar = i.MathUtils.clamp(
|
|
376
|
+
this.polar - e.pointerDelta.y * 6e-3,
|
|
377
|
+
0.18,
|
|
378
|
+
Math.PI * 0.48
|
|
379
|
+
)), e.wheelDelta !== 0 && (this.distance = i.MathUtils.clamp(
|
|
380
|
+
this.distance + e.wheelDelta * 0.01,
|
|
381
|
+
this.minDistance,
|
|
382
|
+
this.maxDistance
|
|
383
|
+
));
|
|
384
|
+
const s = Math.sin(this.polar) * this.distance;
|
|
385
|
+
t.camera.position.set(
|
|
386
|
+
this.target.x + Math.cos(this.azimuth) * s,
|
|
387
|
+
this.target.y + Math.cos(this.polar) * this.distance,
|
|
388
|
+
this.target.z + Math.sin(this.azimuth) * s
|
|
389
|
+
), t.camera.lookAt(this.target);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
export {
|
|
393
|
+
a as C,
|
|
394
|
+
l as E,
|
|
395
|
+
p as L,
|
|
396
|
+
u as M,
|
|
397
|
+
y as O,
|
|
398
|
+
m as S,
|
|
399
|
+
f as a
|
|
400
|
+
};
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import * as a from "three";
|
|
2
|
+
import { C as f } from "./OrbitCameraController-COklTntj.js";
|
|
3
|
+
class y {
|
|
4
|
+
constructor(e) {
|
|
5
|
+
this.canvas = e, window.addEventListener("keydown", this.handleKeyDown), window.addEventListener("keyup", this.handleKeyUp), e.addEventListener("pointermove", this.handlePointerMove), e.addEventListener("pointerdown", this.handlePointerDown), e.addEventListener("pointerup", this.handlePointerUp), e.addEventListener("pointercancel", this.handlePointerUp), e.addEventListener("wheel", this.handleWheel, { passive: !1 });
|
|
6
|
+
}
|
|
7
|
+
canvas;
|
|
8
|
+
pointer = new a.Vector2();
|
|
9
|
+
pointerDelta = new a.Vector2();
|
|
10
|
+
wheelDelta = 0;
|
|
11
|
+
downKeys = /* @__PURE__ */ new Set();
|
|
12
|
+
pressedKeys = /* @__PURE__ */ new Set();
|
|
13
|
+
releasedKeys = /* @__PURE__ */ new Set();
|
|
14
|
+
downButtons = /* @__PURE__ */ new Set();
|
|
15
|
+
pressedButtons = /* @__PURE__ */ new Set();
|
|
16
|
+
releasedButtons = /* @__PURE__ */ new Set();
|
|
17
|
+
isKeyDown(e) {
|
|
18
|
+
return this.downKeys.has(this.normalizeKey(e));
|
|
19
|
+
}
|
|
20
|
+
wasKeyPressed(e) {
|
|
21
|
+
return this.pressedKeys.has(this.normalizeKey(e));
|
|
22
|
+
}
|
|
23
|
+
isAnyKeyDown(e) {
|
|
24
|
+
return e.some((t) => this.isKeyDown(t));
|
|
25
|
+
}
|
|
26
|
+
isMouseDown(e = 0) {
|
|
27
|
+
return this.downButtons.has(e);
|
|
28
|
+
}
|
|
29
|
+
wasMousePressed(e = 0) {
|
|
30
|
+
return this.pressedButtons.has(e);
|
|
31
|
+
}
|
|
32
|
+
endFrame() {
|
|
33
|
+
this.pointerDelta.set(0, 0), this.wheelDelta = 0, this.pressedKeys.clear(), this.releasedKeys.clear(), this.pressedButtons.clear(), this.releasedButtons.clear();
|
|
34
|
+
}
|
|
35
|
+
handleKeyDown = (e) => {
|
|
36
|
+
const t = this.getKeyAliases(e), s = t.some((i) => this.downKeys.has(i));
|
|
37
|
+
for (const i of t)
|
|
38
|
+
this.downKeys.add(i), s || this.pressedKeys.add(i);
|
|
39
|
+
};
|
|
40
|
+
handleKeyUp = (e) => {
|
|
41
|
+
for (const t of this.getKeyAliases(e))
|
|
42
|
+
this.downKeys.delete(t), this.releasedKeys.add(t);
|
|
43
|
+
};
|
|
44
|
+
handlePointerMove = (e) => {
|
|
45
|
+
const t = this.pointerToCanvas(e);
|
|
46
|
+
this.pointerDelta.set(t.x - this.pointer.x, t.y - this.pointer.y), this.pointer.copy(t);
|
|
47
|
+
};
|
|
48
|
+
handlePointerDown = (e) => {
|
|
49
|
+
this.canvas.focus(), this.canvas.setPointerCapture(e.pointerId), this.pointer.copy(this.pointerToCanvas(e)), this.downButtons.add(e.button), this.pressedButtons.add(e.button);
|
|
50
|
+
};
|
|
51
|
+
handlePointerUp = (e) => {
|
|
52
|
+
this.pointer.copy(this.pointerToCanvas(e)), this.downButtons.delete(e.button), this.releasedButtons.add(e.button);
|
|
53
|
+
};
|
|
54
|
+
handleWheel = (e) => {
|
|
55
|
+
e.preventDefault(), this.wheelDelta += e.deltaY;
|
|
56
|
+
};
|
|
57
|
+
pointerToCanvas(e) {
|
|
58
|
+
const t = this.canvas.getBoundingClientRect();
|
|
59
|
+
return new a.Vector2(e.clientX - t.left, e.clientY - t.top);
|
|
60
|
+
}
|
|
61
|
+
getKeyAliases(e) {
|
|
62
|
+
return [e.code, e.key].filter(Boolean).map((t) => this.normalizeKey(t));
|
|
63
|
+
}
|
|
64
|
+
normalizeKey(e) {
|
|
65
|
+
return e.length === 1 ? e.toLowerCase() : e;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
class S {
|
|
69
|
+
constructor(e) {
|
|
70
|
+
this.options = e, this.options.canvas.tabIndex = 0, this.input = new y(this.options.canvas), this.renderer = new a.WebGLRenderer({
|
|
71
|
+
canvas: this.options.canvas,
|
|
72
|
+
antialias: !0,
|
|
73
|
+
alpha: !1,
|
|
74
|
+
preserveDrawingBuffer: this.options.preserveDrawingBuffer ?? !1,
|
|
75
|
+
powerPreference: "high-performance"
|
|
76
|
+
}), this.renderer.setClearColor(new a.Color(e.background ?? "#07100f"), 1), this.configureShadows(e.shadowQuality ?? "off"), this.renderer.outputColorSpace = a.SRGBColorSpace;
|
|
77
|
+
}
|
|
78
|
+
options;
|
|
79
|
+
renderer;
|
|
80
|
+
input;
|
|
81
|
+
stats = {
|
|
82
|
+
fps: 0,
|
|
83
|
+
frameTimeMs: 0,
|
|
84
|
+
frameTimeAverageMs: 0,
|
|
85
|
+
width: 0,
|
|
86
|
+
height: 0,
|
|
87
|
+
entities: 0,
|
|
88
|
+
updatableEntities: 0,
|
|
89
|
+
drawCalls: 0,
|
|
90
|
+
triangles: 0,
|
|
91
|
+
geometries: 0,
|
|
92
|
+
textures: 0,
|
|
93
|
+
pixelRatio: 1,
|
|
94
|
+
qualityScale: 1
|
|
95
|
+
};
|
|
96
|
+
activeScene;
|
|
97
|
+
running = !1;
|
|
98
|
+
animationFrame = 0;
|
|
99
|
+
lastTimestamp = 0;
|
|
100
|
+
fpsElapsed = 0;
|
|
101
|
+
fpsFrames = 0;
|
|
102
|
+
qualityScale = 1;
|
|
103
|
+
frameTimeAverageMs = 0;
|
|
104
|
+
lastResizeWidth = 0;
|
|
105
|
+
lastResizeHeight = 0;
|
|
106
|
+
lastPixelRatio = 0;
|
|
107
|
+
resizeDirty = !0;
|
|
108
|
+
slowQualitySamples = 0;
|
|
109
|
+
fastQualitySamples = 0;
|
|
110
|
+
setScene(e) {
|
|
111
|
+
this.activeScene?.stopInternal(), this.activeScene = e, e.startInternal(this), this.resize(), this.renderOnce();
|
|
112
|
+
}
|
|
113
|
+
start() {
|
|
114
|
+
this.running || (this.running = !0, this.lastTimestamp = performance.now(), this.animationFrame = requestAnimationFrame(this.tick));
|
|
115
|
+
}
|
|
116
|
+
stop() {
|
|
117
|
+
this.running = !1, cancelAnimationFrame(this.animationFrame);
|
|
118
|
+
}
|
|
119
|
+
renderOnce() {
|
|
120
|
+
this.resize(), this.activeScene && (this.renderer.render(this.activeScene.threeScene, this.activeScene.camera), this.updateRenderStats());
|
|
121
|
+
}
|
|
122
|
+
sampleFrame() {
|
|
123
|
+
this.renderOnce();
|
|
124
|
+
const e = this.renderer.getContext(), t = e.drawingBufferWidth, s = e.drawingBufferHeight, i = Math.max(1, Math.min(t, 96)), h = Math.max(1, Math.min(s, 64)), n = Math.floor((t - i) / 2), u = Math.floor((s - h) / 2), r = new Uint8Array(i * h * 4);
|
|
125
|
+
e.readPixels(n, u, i, h, e.RGBA, e.UNSIGNED_BYTE, r);
|
|
126
|
+
let m = 0;
|
|
127
|
+
for (let o = 0; o < r.length; o += 4) {
|
|
128
|
+
const l = r[o], d = r[o + 1], c = r[o + 2];
|
|
129
|
+
(Math.max(l, d, c) - Math.min(l, d, c) > 12 || l + d + c > 60) && (m += 1);
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
width: t,
|
|
133
|
+
height: s,
|
|
134
|
+
coloredRatio: m / (r.length / 4)
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
tick = (e) => {
|
|
138
|
+
if (!this.running)
|
|
139
|
+
return;
|
|
140
|
+
const t = Math.min((e - this.lastTimestamp) / 1e3, 0.05);
|
|
141
|
+
this.lastTimestamp = e, this.resize();
|
|
142
|
+
const s = performance.now();
|
|
143
|
+
this.activeScene?.updateInternal(t), this.activeScene && this.renderer.render(this.activeScene.threeScene, this.activeScene.camera);
|
|
144
|
+
const i = performance.now() - s;
|
|
145
|
+
this.stats.frameTimeMs = Math.round(i * 10) / 10, this.updateFrameTimeAverage(i), this.updateFps(t), this.updateRenderStats(), this.input.endFrame(), this.animationFrame = requestAnimationFrame(this.tick);
|
|
146
|
+
};
|
|
147
|
+
resize() {
|
|
148
|
+
const e = this.options.canvas.getBoundingClientRect(), t = Math.max(1, Math.floor(e.width)), s = Math.max(1, Math.floor(e.height)), i = this.options.maxPixelRatio ?? 2, h = this.options.minPixelRatio ?? 0.6, n = Math.round(
|
|
149
|
+
Math.max(h, Math.min(window.devicePixelRatio || 1, i) * this.qualityScale) * 100
|
|
150
|
+
) / 100;
|
|
151
|
+
this.stats.width = t, this.stats.height = s, this.stats.pixelRatio = n, this.stats.qualityScale = Math.round(this.qualityScale * 100) / 100, !(!this.resizeDirty && t === this.lastResizeWidth && s === this.lastResizeHeight && n === this.lastPixelRatio) && (this.resizeDirty = !1, this.lastResizeWidth = t, this.lastResizeHeight = s, this.lastPixelRatio = n, this.renderer.setPixelRatio(n), this.renderer.setSize(t, s, !1), this.activeScene && (this.activeScene.camera.aspect = t / s, this.activeScene.camera.updateProjectionMatrix()));
|
|
152
|
+
}
|
|
153
|
+
updateFrameTimeAverage(e) {
|
|
154
|
+
this.frameTimeAverageMs = this.frameTimeAverageMs === 0 ? e : this.frameTimeAverageMs * 0.88 + e * 0.12, this.stats.frameTimeAverageMs = Math.round(this.frameTimeAverageMs * 10) / 10;
|
|
155
|
+
}
|
|
156
|
+
updateFps(e) {
|
|
157
|
+
if (this.fpsElapsed += e, this.fpsFrames += 1, this.fpsElapsed >= 0.5) {
|
|
158
|
+
const t = Math.round(this.fpsFrames / this.fpsElapsed), s = this.options.targetFps ?? 60, i = 1e3 / s;
|
|
159
|
+
this.stats.fps = this.frameTimeAverageMs <= i ? s : t, this.adjustQualityScale(), this.fpsElapsed = 0, this.fpsFrames = 0;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
updateRenderStats() {
|
|
163
|
+
const e = this.renderer.info.render, t = this.renderer.info.memory;
|
|
164
|
+
this.stats.entities = this.activeScene?.entities.length ?? 0, this.stats.updatableEntities = this.activeScene?.updatableEntityCount ?? 0, this.stats.drawCalls = e.calls, this.stats.triangles = e.triangles, this.stats.geometries = t.geometries, this.stats.textures = t.textures;
|
|
165
|
+
}
|
|
166
|
+
adjustQualityScale() {
|
|
167
|
+
const e = 1e3 / (this.options.targetFps ?? 60), t = this.stats.fps > 0 && (this.stats.fps < 40 || this.frameTimeAverageMs > e * 1.45), s = this.stats.fps >= 56 && this.frameTimeAverageMs < e * 1.08;
|
|
168
|
+
if (t ? (this.slowQualitySamples += 1, this.fastQualitySamples = 0) : s ? (this.fastQualitySamples += 1, this.slowQualitySamples = 0) : (this.slowQualitySamples = 0, this.fastQualitySamples = 0), this.slowQualitySamples >= 2) {
|
|
169
|
+
this.qualityScale = Math.max(0.55, this.qualityScale - 0.08), this.resizeDirty = !0, this.slowQualitySamples = 0;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
this.fastQualitySamples >= 4 && this.qualityScale < 1 && (this.qualityScale = Math.min(1, this.qualityScale + 0.04), this.resizeDirty = !0, this.fastQualitySamples = 0);
|
|
173
|
+
}
|
|
174
|
+
configureShadows(e) {
|
|
175
|
+
this.renderer.shadowMap.enabled = e !== "off", this.renderer.shadowMap.enabled && (this.renderer.shadowMap.type = e === "high" ? a.PCFSoftShadowMap : a.PCFShadowMap);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
class g extends f {
|
|
179
|
+
constructor(e = 0, t = 1, s = 0) {
|
|
180
|
+
super(), this.x = e, this.y = t, this.z = s;
|
|
181
|
+
}
|
|
182
|
+
x;
|
|
183
|
+
y;
|
|
184
|
+
z;
|
|
185
|
+
onUpdate(e) {
|
|
186
|
+
this.entity.rotation.x += this.x * e, this.entity.rotation.y += this.y * e, this.entity.rotation.z += this.z * e;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
export {
|
|
190
|
+
S as E,
|
|
191
|
+
y as I,
|
|
192
|
+
g as S
|
|
193
|
+
};
|