@kite3d/engine 0.15.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/authoring.d.ts +54 -0
- package/dist/authoring.js +165 -0
- package/dist/authoring.js.map +1 -0
- package/dist/authoringValidation.d.ts +110 -0
- package/dist/authoringValidation.js +488 -0
- package/dist/authoringValidation.js.map +1 -0
- package/dist/defaults.d.ts +2 -0
- package/dist/fileTypes.d.ts +5 -0
- package/dist/fileTypes.js +51 -0
- package/dist/fileTypes.js.map +1 -0
- package/dist/importMap.d.ts +15 -0
- package/dist/importMap.js +62 -0
- package/dist/importMap.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +2756 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations.js +5 -0
- package/dist/migrations.js.map +1 -0
- package/dist/paths.d.ts +9 -0
- package/dist/paths.js +13 -0
- package/dist/paths.js.map +1 -0
- package/dist/plugins/GeneratorComponent.d.ts +40 -0
- package/dist/plugins/HtmlUiComponent.d.ts +52 -0
- package/dist/plugins/HtmlUiComponent.example.d.ts +0 -0
- package/dist/plugins/cannon/Cannon3DBodyComponent.d.ts +27 -0
- package/dist/plugins/cannon/Cannon3DShapeComponent.d.ts +57 -0
- package/dist/plugins/cannon/CannonPhysicsPlugin.d.ts +63 -0
- package/dist/plugins/cannon/CannonRagdollComponent.d.ts +148 -0
- package/dist/plugins/cannon/helper.d.ts +24 -0
- package/dist/plugins/cannon/threeToCannon.d.ts +63 -0
- package/dist/plugins/cannon/utils.d.ts +9 -0
- package/dist/projectFormat.js +108 -0
- package/dist/projectFormat.js.map +1 -0
- package/dist/runtime/createGame.d.ts +29 -0
- package/dist/runtime/index.d.ts +18 -0
- package/dist/runtime/migrations.d.ts +5 -0
- package/dist/runtime/nestedAssets.d.ts +26 -0
- package/dist/runtime/projectFormat.d.ts +83 -0
- package/dist/runtime/version.d.ts +1 -0
- package/dist/runtime.js +72835 -0
- package/dist/runtime.js.map +1 -0
- package/dist/sceneSerialization.d.ts +17 -0
- package/dist/sceneSerialization.js +174 -0
- package/dist/sceneSerialization.js.map +1 -0
- package/dist/scripts.d.ts +17 -0
- package/dist/version.js +7 -0
- package/dist/version.js.map +1 -0
- package/package.json +86 -0
- package/src/authoring.ts +293 -0
- package/src/authoringValidation.ts +815 -0
- package/src/defaults.ts +277 -0
- package/src/fileTypes.ts +53 -0
- package/src/importMap.ts +105 -0
- package/src/index.ts +15 -0
- package/src/paths.ts +9 -0
- package/src/plugins/GeneratorComponent.ts +275 -0
- package/src/plugins/HtmlUiComponent.example.ts +202 -0
- package/src/plugins/HtmlUiComponent.md +219 -0
- package/src/plugins/HtmlUiComponent.ts +320 -0
- package/src/plugins/cannon/Cannon3DBodyComponent.ts +227 -0
- package/src/plugins/cannon/Cannon3DShapeComponent.ts +258 -0
- package/src/plugins/cannon/CannonPhysicsPlugin.ts +409 -0
- package/src/plugins/cannon/CannonRagdollComponent.ts +1170 -0
- package/src/plugins/cannon/helper.ts +255 -0
- package/src/plugins/cannon/threeToCannon.ts +441 -0
- package/src/plugins/cannon/utils.ts +185 -0
- package/src/runtime/createGame.ts +337 -0
- package/src/runtime/index.ts +19 -0
- package/src/runtime/migrations.ts +6 -0
- package/src/runtime/nestedAssets.ts +226 -0
- package/src/runtime/projectFormat.ts +233 -0
- package/src/runtime/version.ts +3 -0
- package/src/sceneSerialization.ts +289 -0
- package/src/scripts.ts +52 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AHelperWidget,
|
|
3
|
+
ComponentDefn,
|
|
4
|
+
ComponentJSON,
|
|
5
|
+
EntityComponentPlugin, Group,
|
|
6
|
+
IMaterial,
|
|
7
|
+
IObject3D,
|
|
8
|
+
literalStrings,
|
|
9
|
+
Mesh,
|
|
10
|
+
Object3DComponent, Object3DWidgetsPlugin,
|
|
11
|
+
PartialRecord
|
|
12
|
+
} from "threepipe"
|
|
13
|
+
import {getShapeParameters, ShapeParameters, ShapeResult, ShapeType, threeToCannon} from "./threeToCannon"
|
|
14
|
+
import {Box, Quaternion as CQuaternion, Shape, Vec3} from "cannon-es"
|
|
15
|
+
import {Cannon3DBodyComponent} from "./Cannon3DBodyComponent.ts";
|
|
16
|
+
import {CannonDebugger} from "./helper.ts";
|
|
17
|
+
|
|
18
|
+
export const typeToCannon = {
|
|
19
|
+
autoBox: ShapeType.BOX,
|
|
20
|
+
autoCylinder: ShapeType.CYLINDER,
|
|
21
|
+
autoSphere: ShapeType.SPHERE,
|
|
22
|
+
autoConvex: ShapeType.HULL,
|
|
23
|
+
autoTrimesh: ShapeType.MESH,
|
|
24
|
+
box: ShapeType.BOX,
|
|
25
|
+
sphere: ShapeType.SPHERE,
|
|
26
|
+
cylinder: ShapeType.CYLINDER,
|
|
27
|
+
convex: ShapeType.HULL,
|
|
28
|
+
trimesh: ShapeType.MESH,
|
|
29
|
+
// plane: ShapeType.PLANE,
|
|
30
|
+
} as const
|
|
31
|
+
|
|
32
|
+
export type CannonShapeType = keyof typeof typeToCannon
|
|
33
|
+
|
|
34
|
+
export const cannonShapeTypes = Object.keys(typeToCannon) as CannonShapeType[]
|
|
35
|
+
|
|
36
|
+
export const cannonShapeTypeParams: PartialRecord<CannonShapeType, ShapeParameters['params']> = {
|
|
37
|
+
box: {x: 1, y: 1, z: 1},
|
|
38
|
+
sphere: {radius: 1},
|
|
39
|
+
cylinder: {radiusTop: 1, radiusBottom: 1, height: 1, segments: 8},
|
|
40
|
+
convex: {vertices: new Float32Array([1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 0, 0, -1]), faces: [[0, 1, 2, 3], [4, 0, 3], [4, 3, 2], [4, 2, 1], [4, 1, 0]]},
|
|
41
|
+
trimesh: {vertices: new Float32Array([1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1]), indices: new Uint32Array([0, 1, 2, 0, 3, 1, 0, 2, 3, 1, 3, 2])},
|
|
42
|
+
// not for auto
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class Cannon3DShapeComponent extends Object3DComponent {
|
|
46
|
+
static ComponentType = 'Cannon3DShapeComponent'
|
|
47
|
+
static StateProperties: ComponentDefn['StateProperties'] = [{
|
|
48
|
+
key: 'type',
|
|
49
|
+
type: literalStrings(cannonShapeTypes),
|
|
50
|
+
}]
|
|
51
|
+
static EmptyResult: ShapeResult = {shape: new Box(new Vec3(0.025, 0.025, 0.025))}
|
|
52
|
+
|
|
53
|
+
result: ShapeResult = Cannon3DShapeComponent.EmptyResult
|
|
54
|
+
bodyRef: Cannon3DBodyComponent | undefined
|
|
55
|
+
type: CannonShapeType = 'autoBox'
|
|
56
|
+
params: ShapeParameters | null = null
|
|
57
|
+
|
|
58
|
+
constructor() {
|
|
59
|
+
super()
|
|
60
|
+
this.onStateChange('type', () => {
|
|
61
|
+
// const shapeType = typeToCannon[this.shapeType]
|
|
62
|
+
const params = cannonShapeTypeParams[this.type]
|
|
63
|
+
if (params) {
|
|
64
|
+
const params1 = {}
|
|
65
|
+
Object.entries(params).forEach(([k, v]) => {
|
|
66
|
+
(params1 as any)[k] = (v as Array<any>).slice ? (v as Array<any>).slice() : v
|
|
67
|
+
})
|
|
68
|
+
this.params = {
|
|
69
|
+
type: typeToCannon[this.type],
|
|
70
|
+
params: params1 as any,
|
|
71
|
+
offset: new Vec3(),
|
|
72
|
+
orientation: new CQuaternion(),
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
this.params = null
|
|
76
|
+
}
|
|
77
|
+
if (!this.object) return
|
|
78
|
+
this.refreshShape()
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// static IsCompatible(_object: IObject3D) { return true }
|
|
83
|
+
|
|
84
|
+
init(object: IObject3D, state: ComponentJSON['state']) {
|
|
85
|
+
super.init(object, state)
|
|
86
|
+
this.refreshShape(false)
|
|
87
|
+
const body = EntityComponentPlugin.GetComponentInParent(object, Cannon3DBodyComponent)
|
|
88
|
+
if (!body) {
|
|
89
|
+
// console.warn('Cannon3DShapeComponent: No Cannon3DBodyComponent found in parent hierarchy')
|
|
90
|
+
} else {
|
|
91
|
+
body.addShape(this)
|
|
92
|
+
}
|
|
93
|
+
this.object.addEventListener('objectUpdate', this.objectUpdate)
|
|
94
|
+
this.object.addEventListener('childadded', this.childadded)
|
|
95
|
+
this.object.addEventListener('childremoved', this.childremoved)
|
|
96
|
+
this.ctx.viewer.addEventListener('preFrame', this.preFrame)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
destroy(): Record<string, any> {
|
|
100
|
+
if (this.bodyRef) {
|
|
101
|
+
this.bodyRef.removeShape(this, false)
|
|
102
|
+
this.bodyRef = undefined
|
|
103
|
+
}
|
|
104
|
+
this.object.removeEventListener('objectUpdate', this.objectUpdate)
|
|
105
|
+
this.ctx.viewer.removeEventListener('preFrame', this.preFrame)
|
|
106
|
+
this.result = Cannon3DShapeComponent.EmptyResult
|
|
107
|
+
return super.destroy()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
refreshShape(refreshBody = true) {
|
|
111
|
+
// todo auto refresh on geometry or hierarchy changed if set to auto
|
|
112
|
+
if (!this.object.__cannonShapes) this.object.__cannonShapes = []
|
|
113
|
+
|
|
114
|
+
if(this.result){
|
|
115
|
+
const index = this.object.__cannonShapes.indexOf(this.result)
|
|
116
|
+
if(index !== -1) this.object.__cannonShapes.splice(index, 1)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const shapeType = typeToCannon[this.type]
|
|
120
|
+
const shapeParameters = this.params ?? (shapeType ? getShapeParameters(this.object, {type: shapeType}) : null)
|
|
121
|
+
|
|
122
|
+
// console.log('shapeParameters', shapeParameters)
|
|
123
|
+
if (shapeParameters) {
|
|
124
|
+
this.result = threeToCannon(this.object, undefined, shapeParameters)!
|
|
125
|
+
} else {
|
|
126
|
+
this.result = Cannon3DShapeComponent.EmptyResult
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!this.object.__cannonShapes.includes(this.result)) this.object.__cannonShapes.push(this.result)
|
|
130
|
+
|
|
131
|
+
if (refreshBody && this.bodyRef) this.bodyRef.refreshShapes()
|
|
132
|
+
|
|
133
|
+
this.object.setDirty?.()
|
|
134
|
+
this.ctx.plugin(Object3DWidgetsPlugin)?.refreshObject(this.object)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private _needsUpdate = false
|
|
138
|
+
objectUpdate = (e: any)=>{
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
childadded = (e: any)=>{
|
|
142
|
+
this._needsUpdate = true
|
|
143
|
+
e.child.addEventListener('objectUpdate', this.objectUpdate)
|
|
144
|
+
e.child.addEventListener('childadded', this.childadded)
|
|
145
|
+
e.child.addEventListener('childremoved', this.childremoved)
|
|
146
|
+
// console.log('childadded', {...e})
|
|
147
|
+
}
|
|
148
|
+
childremoved = (e: any)=>{
|
|
149
|
+
this._needsUpdate = true
|
|
150
|
+
e.child.removeEventListener('objectUpdate', this.objectUpdate)
|
|
151
|
+
e.child.removeEventListener('childadded', this.childadded)
|
|
152
|
+
e.child.removeEventListener('childremoved', this.childremoved)
|
|
153
|
+
// console.log('childremoved', {...e})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
preFrame = ()=>{
|
|
157
|
+
if(this._needsUpdate){
|
|
158
|
+
this._needsUpdate = false
|
|
159
|
+
this.refreshShape()
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export class Cannon3DShapeHelper extends AHelperWidget {
|
|
165
|
+
autoUpgradeChildren = false // used elsewhere
|
|
166
|
+
private _shapes: ShapeResult[] = []
|
|
167
|
+
private _meshes = new WeakMap<Shape, Mesh>()
|
|
168
|
+
declare object: IObject3D | undefined
|
|
169
|
+
|
|
170
|
+
private _gp = new Group()
|
|
171
|
+
constructor(object: IObject3D) {
|
|
172
|
+
super(object, false)
|
|
173
|
+
|
|
174
|
+
this.attach(object)
|
|
175
|
+
this.add(this._gp)
|
|
176
|
+
this.visible = false
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
update() {
|
|
180
|
+
super.update()
|
|
181
|
+
|
|
182
|
+
this._gp.scale.setFromMatrixScale(this.matrixWorld)
|
|
183
|
+
this._gp.scale.set(
|
|
184
|
+
1./this._gp.scale.x,
|
|
185
|
+
1./this._gp.scale.y,
|
|
186
|
+
1./this._gp.scale.z
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
const shapes = (this.object?.__cannonShapes || []) as ShapeResult[]
|
|
190
|
+
const added = shapes.filter(s => !this._shapes.includes(s))
|
|
191
|
+
const removed = this._shapes.filter(s => !shapes.includes(s))
|
|
192
|
+
if (!added.length && !removed.length) return
|
|
193
|
+
|
|
194
|
+
for (const shape of removed) {
|
|
195
|
+
const mesh = this._meshes.get(shape.shape)
|
|
196
|
+
if (mesh) {
|
|
197
|
+
this._gp.remove(mesh)
|
|
198
|
+
mesh.geometry.dispose()
|
|
199
|
+
;(mesh.material as IMaterial).dispose()
|
|
200
|
+
this._meshes.delete(shape.shape)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
this._shapes = []
|
|
204
|
+
for (const shape of shapes) {
|
|
205
|
+
const current = this._meshes.get(shape.shape)
|
|
206
|
+
// update
|
|
207
|
+
const mesh = CannonDebugger.updateMesh(current, shape.shape)
|
|
208
|
+
if (mesh !== current && mesh) {
|
|
209
|
+
this._gp.add(mesh)
|
|
210
|
+
if (current) {
|
|
211
|
+
current.removeFromParent()
|
|
212
|
+
current.geometry.dispose()
|
|
213
|
+
;(current.material as IMaterial).dispose()
|
|
214
|
+
}
|
|
215
|
+
this._meshes.set(shape.shape, mesh)
|
|
216
|
+
}
|
|
217
|
+
if(mesh){
|
|
218
|
+
if(shape.offset){
|
|
219
|
+
mesh.position.set(shape.offset.x, shape.offset.y, shape.offset.z)
|
|
220
|
+
}
|
|
221
|
+
if(shape.orientation){
|
|
222
|
+
mesh.quaternion.set(shape.orientation.x, shape.orientation.y, shape.orientation.z, shape.orientation.w)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
this._shapes.push(shape)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
dispose() {
|
|
231
|
+
this._shapes = []
|
|
232
|
+
this.children.forEach(c => {
|
|
233
|
+
if (c instanceof Mesh) {
|
|
234
|
+
c.geometry.dispose()
|
|
235
|
+
;(c.material as IMaterial).dispose()
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
super.dispose()
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
static Check(obj: IObject3D) {
|
|
242
|
+
return !!EntityComponentPlugin.GetComponentData(obj, Cannon3DShapeComponent)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
static Create(obj: IObject3D) {
|
|
246
|
+
return new Cannon3DShapeHelper(obj as any)
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
declare module 'threepipe' {
|
|
251
|
+
interface IObject3D{
|
|
252
|
+
/**
|
|
253
|
+
* Shapes associated with this object
|
|
254
|
+
* @internal - for cannon js physics
|
|
255
|
+
*/
|
|
256
|
+
['__cannonShapes']?: ShapeResult[]
|
|
257
|
+
}
|
|
258
|
+
}
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import {Body, ContactMaterial, Material as CannonMaterial, Shape, World} from "cannon-es"
|
|
2
|
+
import {
|
|
3
|
+
AViewerPluginSync,
|
|
4
|
+
EntityComponentPlugin,
|
|
5
|
+
FrameFadePlugin, generateUUID,
|
|
6
|
+
IObject3D,
|
|
7
|
+
IViewerEvent,
|
|
8
|
+
Matrix4,
|
|
9
|
+
Object3DWidgetsPlugin,
|
|
10
|
+
Quaternion,
|
|
11
|
+
serialize,
|
|
12
|
+
ThreeSerialization,
|
|
13
|
+
ThreeViewer,
|
|
14
|
+
TObject3DComponent,
|
|
15
|
+
TypeSystem,
|
|
16
|
+
uiConfig,
|
|
17
|
+
uiFolderContainer, uiInput, uiMonitor, uiNumber,
|
|
18
|
+
uiToggle,
|
|
19
|
+
Vector3
|
|
20
|
+
} from "threepipe"
|
|
21
|
+
import {Cannon3DShapeComponent, Cannon3DShapeHelper} from "./Cannon3DShapeComponent.ts";
|
|
22
|
+
import {Cannon3DBodyComponent} from "./Cannon3DBodyComponent.ts";
|
|
23
|
+
import {CannonRagdollComponent} from "./CannonRagdollComponent.ts";
|
|
24
|
+
|
|
25
|
+
@uiFolderContainer('Cannon Material')
|
|
26
|
+
export class CannonMaterial2 extends CannonMaterial{
|
|
27
|
+
@uiInput()
|
|
28
|
+
declare name: string
|
|
29
|
+
@uiMonitor()
|
|
30
|
+
uuid: string = generateUUID()
|
|
31
|
+
@uiNumber()
|
|
32
|
+
declare friction
|
|
33
|
+
@uiNumber()
|
|
34
|
+
declare restitution
|
|
35
|
+
}
|
|
36
|
+
ThreeSerialization.MakeSerializable(CannonMaterial as any, 'CannonMaterial', ['friction', 'restitution']) // this is not needed actually, but just in case
|
|
37
|
+
ThreeSerialization.MakeSerializable(CannonMaterial2 as any, 'CannonMaterial', ['name', 'uuid', 'friction', 'restitution']) // this should be after CannonMaterial
|
|
38
|
+
ThreeSerialization.MakeSerializable(ContactMaterial as any, 'CannonContactMaterial', [/* 'materials', */'friction', 'restitution', 'contactEquationStiffness', 'contactEquationRelaxation', 'frictionEquationStiffness', 'frictionEquationRelaxation'])
|
|
39
|
+
TypeSystem.AddClass({
|
|
40
|
+
key: 'CannonMaterial',
|
|
41
|
+
getId(obj: CannonMaterial2) {
|
|
42
|
+
return obj.uuid
|
|
43
|
+
},
|
|
44
|
+
getLabel(obj?: CannonMaterial2) {
|
|
45
|
+
return obj ? obj.name : 'Cannon Material'
|
|
46
|
+
},
|
|
47
|
+
setName(obj: CannonMaterial2, name: string) {
|
|
48
|
+
obj.name = name
|
|
49
|
+
},
|
|
50
|
+
ctor: CannonMaterial2,
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// uses https://github.com/pmndrs/cannon-es
|
|
54
|
+
|
|
55
|
+
@uiFolderContainer('Physics')
|
|
56
|
+
export class CannonPhysicsPlugin extends AViewerPluginSync {
|
|
57
|
+
public static readonly PluginType = 'CannonPhysicsPlugin'
|
|
58
|
+
private _world: World = new World()
|
|
59
|
+
get world() { return this._world }
|
|
60
|
+
// dependencies: Class<IViewerPlugin<any>>[]
|
|
61
|
+
|
|
62
|
+
@serialize()
|
|
63
|
+
@uiToggle()
|
|
64
|
+
enabled = true
|
|
65
|
+
|
|
66
|
+
static CannonTypes = {
|
|
67
|
+
Body, Shape, World, Material: CannonMaterial, ContactMaterial,
|
|
68
|
+
}
|
|
69
|
+
@uiToggle('Running', (_that: CannonPhysicsPlugin)=>({onChange: ()=>{return}}))
|
|
70
|
+
running = true
|
|
71
|
+
|
|
72
|
+
nextSteps = 0
|
|
73
|
+
|
|
74
|
+
@uiConfig()
|
|
75
|
+
stepPhysics: any = {
|
|
76
|
+
stepCount: 100,
|
|
77
|
+
delta: 0.1,
|
|
78
|
+
step: () => {
|
|
79
|
+
this.nextSteps = this.stepPhysics.stepCount
|
|
80
|
+
},
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@uiConfig()
|
|
84
|
+
@serialize()
|
|
85
|
+
defaultMaterial = new CannonMaterial2('default')
|
|
86
|
+
|
|
87
|
+
// @uiButton('Make Root Bodies')
|
|
88
|
+
// makeRootBodies = () => {
|
|
89
|
+
// this._viewer?.scene.modelRoot.children.forEach((child) => {
|
|
90
|
+
// this.makeBody(child)
|
|
91
|
+
// })
|
|
92
|
+
// }
|
|
93
|
+
|
|
94
|
+
// stepMultiple = () => {
|
|
95
|
+
// console.log(this.enabled, this.nextSteps)
|
|
96
|
+
// if (this.enabled) return
|
|
97
|
+
// this.nextSteps = 100
|
|
98
|
+
// // this.enabled = true
|
|
99
|
+
// // this._viewer?.doOnce('postFrame', () => {
|
|
100
|
+
// // // Step the physics world
|
|
101
|
+
// // for (let i = 0; i < 100; i++) {
|
|
102
|
+
// // this._world.step(0.1)
|
|
103
|
+
// // }
|
|
104
|
+
// // // this._viewer?.doOnce('postFrame', () => {
|
|
105
|
+
// // // this.enabled = false
|
|
106
|
+
// // // })
|
|
107
|
+
// // })
|
|
108
|
+
// // this._viewer?.setDirty()
|
|
109
|
+
// }
|
|
110
|
+
|
|
111
|
+
constructor(enabled = true, running = false) {
|
|
112
|
+
super()
|
|
113
|
+
this.enabled = enabled
|
|
114
|
+
this.running = running
|
|
115
|
+
|
|
116
|
+
// replaced for UI
|
|
117
|
+
this.defaultMaterial.friction = 0
|
|
118
|
+
this.defaultMaterial.restitution = 0
|
|
119
|
+
this._world.defaultMaterial = this.defaultMaterial
|
|
120
|
+
this._world.defaultContactMaterial.materials = [this.defaultMaterial, this.defaultMaterial]
|
|
121
|
+
|
|
122
|
+
const collisionEv: CollisionEvent = {} as any
|
|
123
|
+
this._world.addEventListener('beginContact', (e: any)=>{
|
|
124
|
+
const compA = this.cannonBodies.get(e.bodyA)
|
|
125
|
+
const compB = this.cannonBodies.get(e.bodyB)
|
|
126
|
+
if(!compA || !compB){
|
|
127
|
+
console.warn('[CannonPhysicsPlugin] Contact bodies not managed by plugin', e.bodyA, e.bodyB)
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
collisionEv.object = compB.object
|
|
131
|
+
collisionEv.body = compB
|
|
132
|
+
EntityComponentPlugin.ObjectDispatch(compA.object, 'onBeginContact', collisionEv)
|
|
133
|
+
collisionEv.object = compA.object
|
|
134
|
+
collisionEv.body = compA
|
|
135
|
+
EntityComponentPlugin.ObjectDispatch(compB.object, 'onBeginContact', collisionEv)
|
|
136
|
+
})
|
|
137
|
+
this._world.addEventListener('endContact', (e: any)=>{
|
|
138
|
+
const compA = this.cannonBodies.get(e.bodyA)
|
|
139
|
+
const compB = this.cannonBodies.get(e.bodyB)
|
|
140
|
+
if(!compA || !compB){
|
|
141
|
+
console.warn('[CannonPhysicsPlugin] Contact bodies not managed by plugin', e.bodyA, e.bodyB)
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
collisionEv.object = compB.object
|
|
145
|
+
collisionEv.body = compB
|
|
146
|
+
EntityComponentPlugin.ObjectDispatch(compA.object, 'onEndContact', collisionEv)
|
|
147
|
+
collisionEv.object = compA.object
|
|
148
|
+
collisionEv.body = compA
|
|
149
|
+
EntityComponentPlugin.ObjectDispatch(compB.object, 'onEndContact', collisionEv)
|
|
150
|
+
})
|
|
151
|
+
// this._world.addEventListener('beginShapeContact', (e)=>{
|
|
152
|
+
// console.log(e)
|
|
153
|
+
// })
|
|
154
|
+
// this._world.addEventListener('endShapeContact', (e)=>{
|
|
155
|
+
// console.log(e)
|
|
156
|
+
// })
|
|
157
|
+
// this._world.addEventListener(Body.COLLIDE_EVENT_NAME, (e)=>{
|
|
158
|
+
// console.log(e)
|
|
159
|
+
// })
|
|
160
|
+
this._world.gravity.set(0, -9.81, 0)
|
|
161
|
+
// this._world.gravity.set(0, 0, 0)
|
|
162
|
+
|
|
163
|
+
//
|
|
164
|
+
// // Max solver iterations: Use more for better force propagation, but keep in mind that it's not very computationally cheap!
|
|
165
|
+
// this._world.solver.iterations = 20
|
|
166
|
+
//
|
|
167
|
+
// // Tweak contact properties.
|
|
168
|
+
// // Contact stiffness - use to make softer/harder contacts
|
|
169
|
+
// this._world.defaultContactMaterial.contactEquationStiffness = 1e10
|
|
170
|
+
// this._world.defaultContactMaterial.contactEquationRelaxation = 10
|
|
171
|
+
//
|
|
172
|
+
|
|
173
|
+
// coeff of restitution: 0 = no bounce, 1 = perfect bounce
|
|
174
|
+
|
|
175
|
+
// test
|
|
176
|
+
|
|
177
|
+
// const mat = new CannonMaterial('defaultMat')
|
|
178
|
+
// mat.friction = 0.351
|
|
179
|
+
// mat.restitution = 0.63
|
|
180
|
+
// const contactMat = new ContactMaterial(mat, mat, {friction: 0.13, restitution: 0.563})
|
|
181
|
+
//
|
|
182
|
+
// const mjson = ThreeSerialization.Serialize(mat)
|
|
183
|
+
// const mat2 = ThreeSerialization.Deserialize(mjson, null) as CannonMaterial
|
|
184
|
+
// const mat3 = ThreeSerialization.Deserialize(mjson, mat) as CannonMaterial
|
|
185
|
+
// console.log(mjson, mat2, mat3, mat3 === mat)
|
|
186
|
+
//
|
|
187
|
+
// const cmjson = ThreeSerialization.Serialize(contactMat)
|
|
188
|
+
// const contactMat2 = ThreeSerialization.Deserialize(cmjson, null) as ContactMaterial
|
|
189
|
+
// const contactMat3 = ThreeSerialization.Deserialize(cmjson, contactMat) as ContactMaterial
|
|
190
|
+
// console.log(cmjson, contactMat2, contactMat3, contactMat3 === contactMat)
|
|
191
|
+
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
dependencies = [EntityComponentPlugin]
|
|
195
|
+
|
|
196
|
+
// private _bodyMeshMap = new WeakMap<Body, Object3D>()
|
|
197
|
+
|
|
198
|
+
private _v1 = new Vector3()
|
|
199
|
+
private _v2 = new Vector3()
|
|
200
|
+
private _s2 = new Vector3()
|
|
201
|
+
private _q1 = new Quaternion()
|
|
202
|
+
private _q2 = new Quaternion()
|
|
203
|
+
private _m1 = new Matrix4()
|
|
204
|
+
private _m2 = new Matrix4()
|
|
205
|
+
frameFadeToggled = false
|
|
206
|
+
|
|
207
|
+
protected _preFrame = (e: IViewerEvent) => {
|
|
208
|
+
const viewer = this._viewer
|
|
209
|
+
if (!viewer) return
|
|
210
|
+
if(this.isDisabled()) return
|
|
211
|
+
const frameFadePlugin = viewer.getPlugin(FrameFadePlugin)
|
|
212
|
+
// todo use isDisabled
|
|
213
|
+
if (!this.running) {
|
|
214
|
+
if (this.nextSteps > 0) this.running = true
|
|
215
|
+
else {
|
|
216
|
+
if (frameFadePlugin && this.frameFadeToggled) {
|
|
217
|
+
frameFadePlugin.enable(CannonPhysicsPlugin.PluginType)
|
|
218
|
+
this.frameFadeToggled = false
|
|
219
|
+
}
|
|
220
|
+
this._dirty = false
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (frameFadePlugin && !this.frameFadeToggled) {
|
|
225
|
+
frameFadePlugin.disable(CannonPhysicsPlugin.PluginType)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
// Step the physics world
|
|
230
|
+
if (this.nextSteps > 0) {
|
|
231
|
+
for (let i = 0; i < this.stepPhysics.stepCount; i++) {
|
|
232
|
+
this._world.step(this.stepPhysics.delta)
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
// todo use dt
|
|
236
|
+
// const dt = e.deltaTime
|
|
237
|
+
this._world.fixedStep()
|
|
238
|
+
}
|
|
239
|
+
}catch (e) {
|
|
240
|
+
console.error('[CannonPhysicsPlugin] Error stepping physics world')
|
|
241
|
+
console.error(e)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
this._dirty = false
|
|
245
|
+
let dirty = false
|
|
246
|
+
// Update the bodies
|
|
247
|
+
for (const bodyC of this.bodyComponents.values()) {
|
|
248
|
+
if (bodyC.mass === 0) continue
|
|
249
|
+
const mesh = bodyC.object
|
|
250
|
+
const body = bodyC.body
|
|
251
|
+
if (!mesh) continue
|
|
252
|
+
dirty = false
|
|
253
|
+
mesh.updateWorldMatrix(true, false)
|
|
254
|
+
mesh.getWorldPosition(this._v2)
|
|
255
|
+
mesh.getWorldQuaternion(this._q2)
|
|
256
|
+
mesh.getWorldScale(this._s2)
|
|
257
|
+
this._v1.copy(body.position)
|
|
258
|
+
if (!dirty && this._v2.manhattanDistanceTo(this._v1) > 0) {
|
|
259
|
+
dirty = true
|
|
260
|
+
// world = parent.local
|
|
261
|
+
// parent-1 . world = local
|
|
262
|
+
}
|
|
263
|
+
this._q1.copy(body.quaternion as any)
|
|
264
|
+
if (!dirty && this._q2.angleTo(this._q1) > 0) {
|
|
265
|
+
dirty = true
|
|
266
|
+
}
|
|
267
|
+
if (dirty) {
|
|
268
|
+
this._m1.compose(this._v1, this._q1, this._s2)
|
|
269
|
+
if (!mesh.parent) {
|
|
270
|
+
// throw new Error('no parent')
|
|
271
|
+
console.error('[CannonPhysicsPlugin] Mesh has no parent, cannot set world transform', mesh)
|
|
272
|
+
}else {
|
|
273
|
+
this._m2.copy(mesh.parent.matrixWorld).invert()
|
|
274
|
+
this._m2.multiply(this._m1)
|
|
275
|
+
this._m2.decompose(this._v1, this._q1, this._s2)
|
|
276
|
+
mesh.position.copy(this._v1)
|
|
277
|
+
mesh.quaternion.copy(this._q1)
|
|
278
|
+
mesh.setDirty && mesh.setDirty({change: 'transform', source: 'CannonPhysicsPlugin'})
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
this._dirty = dirty
|
|
283
|
+
viewer.setDirty()
|
|
284
|
+
// viewer.scene.setDirty({sceneUpdate: false})
|
|
285
|
+
viewer.renderManager.resetShadows()
|
|
286
|
+
|
|
287
|
+
if (this.nextSteps > 0) {
|
|
288
|
+
this.running = false
|
|
289
|
+
this.nextSteps = 0
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private _onRemove: (()=>void)[] = []
|
|
294
|
+
|
|
295
|
+
async onAdded(viewer: ThreeViewer): Promise<
|
|
296
|
+
void> {
|
|
297
|
+
super.onAdded(viewer)
|
|
298
|
+
|
|
299
|
+
const offUpdate = viewer.on('preFrame', {
|
|
300
|
+
order: -1, // before most things
|
|
301
|
+
callback: this._preFrame,
|
|
302
|
+
})
|
|
303
|
+
if (offUpdate) this._onRemove.push(offUpdate)
|
|
304
|
+
|
|
305
|
+
Object.values(this.componentTypes).forEach((compType) => {
|
|
306
|
+
viewer.getPlugin(EntityComponentPlugin)?.addComponentType(compType)
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
viewer.object3dManager.getObjects().forEach(object=>this._objectAdd({object}))
|
|
310
|
+
viewer.object3dManager.addEventListener('objectAdd', this._objectAdd)
|
|
311
|
+
viewer.object3dManager.addEventListener('objectRemove', this._objectRemove)
|
|
312
|
+
|
|
313
|
+
viewer.forPlugin(Object3DWidgetsPlugin, (wplugin) => {
|
|
314
|
+
wplugin.helpers.push(Cannon3DShapeHelper)
|
|
315
|
+
}, (wplugin)=>{
|
|
316
|
+
const i = wplugin.helpers.indexOf(Cannon3DShapeHelper)
|
|
317
|
+
if (i >= 0) wplugin.helpers.splice(i, 1)
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
// viewer.scene.addEventListener('update', (e) => {
|
|
321
|
+
// this._bodyMeshMap.forEach((mesh, body) => {
|
|
322
|
+
// body.velocity.set(0, 0, 0)
|
|
323
|
+
// body.angularVelocity.set(0, 0, 0)
|
|
324
|
+
// })
|
|
325
|
+
// })
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private _objectAdd = (e: {object?: IObject3D})=>{
|
|
329
|
+
const obj = e.object
|
|
330
|
+
// console.log('pbject add', obj?.name, obj?.userData, obj)
|
|
331
|
+
if (obj?.userData?.physicsMass === undefined) return
|
|
332
|
+
const ecs = this._viewer?.getPlugin(EntityComponentPlugin)
|
|
333
|
+
if (!ecs) return
|
|
334
|
+
const existing = EntityComponentPlugin.GetComponentData(obj, this.componentTypes.body)
|
|
335
|
+
if (existing) return
|
|
336
|
+
// const pBody = ecs.addComponent(obj, this.componentTypes.body).component
|
|
337
|
+
// const pShape = ecs.addComponent(obj, this.componentTypes.shape).component
|
|
338
|
+
// if (!pBody || !pShape) return
|
|
339
|
+
// pBody.mass = typeof obj.userData.physicsMass === 'number' ? obj.userData.physicsMass : 0
|
|
340
|
+
// if (obj.userData.physicsBodyType !== undefined) {
|
|
341
|
+
// if (typeof obj.userData.physicsBodyType === 'string' && physicsBodyType.includes(obj.userData.physicsBodyType as PhysicsBodyType)) {
|
|
342
|
+
// pBody.type = obj.userData.physicsBodyType as PhysicsBodyType
|
|
343
|
+
// }
|
|
344
|
+
// }
|
|
345
|
+
// if (obj.userData.physicsShape !== undefined) {
|
|
346
|
+
// if (typeof obj.userData.physicsShape === 'string' && cannonShapeTypes.includes(obj.userData.physicsShape as CannonShapeType)) {
|
|
347
|
+
// pShape.type = obj.userData.physicsShape as CannonShapeType
|
|
348
|
+
// }
|
|
349
|
+
// }
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private _objectRemove = (e: {object?: IObject3D})=>{
|
|
353
|
+
const obj = e.object
|
|
354
|
+
if (!obj?.userData?.physicsMass === undefined) return
|
|
355
|
+
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
onRemove(viewer: ThreeViewer) {
|
|
359
|
+
this._onRemove.forEach(off=>off())
|
|
360
|
+
Object.values(this.componentTypes).forEach((compType) => {
|
|
361
|
+
viewer.getPlugin(EntityComponentPlugin)?.removeComponentType(compType)
|
|
362
|
+
})
|
|
363
|
+
viewer.object3dManager.removeEventListener('objectAdd', this._objectAdd)
|
|
364
|
+
viewer.object3dManager.removeEventListener('objectRemove', this._objectRemove)
|
|
365
|
+
viewer.object3dManager.getObjects().forEach(object=>this._objectRemove({object}))
|
|
366
|
+
|
|
367
|
+
// todo assert all bodies removed?
|
|
368
|
+
|
|
369
|
+
super.onRemove(viewer)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
readonly componentTypes = {
|
|
373
|
+
body: Cannon3DBodyComponent,
|
|
374
|
+
shape: Cannon3DShapeComponent,
|
|
375
|
+
ragdoll: CannonRagdollComponent,
|
|
376
|
+
} satisfies Record<string, TObject3DComponent>
|
|
377
|
+
|
|
378
|
+
bodyComponents = new Set<Cannon3DBodyComponent>()
|
|
379
|
+
cannonBodies = new WeakMap<Body, Cannon3DBodyComponent>()
|
|
380
|
+
addBody(body: Cannon3DBodyComponent) {
|
|
381
|
+
if (this.bodyComponents.has(body)) return
|
|
382
|
+
this.bodyComponents.add(body)
|
|
383
|
+
this._world.addBody(body.body)
|
|
384
|
+
this.cannonBodies.set(body.body, body)
|
|
385
|
+
// this._bodyMeshMap.set(body.body, body.object)
|
|
386
|
+
// this._viewer?.setDirty()
|
|
387
|
+
}
|
|
388
|
+
removeBody(body: Cannon3DBodyComponent) {
|
|
389
|
+
if (!this.bodyComponents.has(body)) return
|
|
390
|
+
this.bodyComponents.delete(body)
|
|
391
|
+
this._world.removeBody(body.body)
|
|
392
|
+
this.cannonBodies.delete(body.body)
|
|
393
|
+
// this._bodyMeshMap.delete(body.body)
|
|
394
|
+
// this._viewer?.setDirty()
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export interface CollisionEvent{
|
|
400
|
+
object: IObject3D
|
|
401
|
+
body: Cannon3DBodyComponent
|
|
402
|
+
}
|
|
403
|
+
declare module 'threepipe'{
|
|
404
|
+
interface Object3DComponent{
|
|
405
|
+
onBeginContact?(collisionEvent: any):void
|
|
406
|
+
onEndContact?(collisionEvent: any):void
|
|
407
|
+
|
|
408
|
+
}
|
|
409
|
+
}
|