Spaces:
Runtime error
Runtime error
const formatMessage = require('format-message'); | |
const BlockType = require('../../extension-support/block-type'); | |
const ArgumentType = require('../../extension-support/argument-type'); | |
const Cast = require('../../util/cast'); | |
const CANNON = require('./cannon.min.js'); | |
const Icon = require('./icon.png'); | |
/** | |
* Class for 3d Physics blocks | |
*/ | |
class Fr3DBlocks { | |
constructor(runtime) { | |
/** | |
* The runtime instantiating this block package. | |
*/ | |
this.runtime = runtime; | |
this.CANNON = CANNON | |
// Create the Cannon.js world before initializing other properties | |
this.world = new this.CANNON.World(); | |
this._3d = {}; | |
this.Three = {}; | |
if (!vm.runtime.ext_jg3d) { | |
vm.extensionManager.loadExtensionURL('jg3d') | |
.then(() => { | |
this._3d = vm.runtime.ext_jg3d; | |
this.Three = this._3d.three; | |
}) | |
} else { | |
this._3d = vm.runtime.ext_jg3d; | |
this.Three = this._3d.three; | |
} | |
} | |
/** | |
* metadata for this extension and its blocks. | |
* @returns {object} | |
*/ | |
getInfo() { | |
return { | |
id: 'fr3d', | |
name: '3D Physics', | |
color1: '#D066FE', | |
color2: '#8000BC', | |
blockIconURI: Icon, | |
blocks: [ | |
{ | |
opcode: 'step', | |
text: 'step simulation', | |
blockType: BlockType.COMMAND, | |
}, | |
{ | |
opcode: 'addp', | |
text: 'enable physics for [NAME1]', | |
blockType: BlockType.COMMAND, | |
arguments: { | |
NAME1: { type: ArgumentType.STRING, defaultValue: "Object1" } | |
} | |
}, | |
{ | |
opcode: 'rmp', | |
text: 'disable physics for [NAME1]', | |
blockType: BlockType.COMMAND, | |
arguments: { | |
NAME1: { type: ArgumentType.STRING, defaultValue: "Object1" } | |
} | |
} | |
] | |
}; | |
} | |
createShapeFromGeometry(geometry) { | |
if (geometry instanceof this.Three.BufferGeometry) { | |
const vertices = geometry.attributes.position.array; | |
const indices = []; | |
for (let i = 0; i < vertices.length / 3; i++) { | |
indices.push(i); | |
} | |
return new this.CANNON.Trimesh(vertices, indices); | |
} else if (geometry instanceof this.Three.Geometry) { | |
return new this.CANNON.ConvexPolyhedron( | |
geometry.vertices.map((v) => new this.CANNON.Vec3(v.x, v.y, v.z)), | |
geometry.faces.map((f) => [f.a, f.b, f.c]), | |
); | |
} else { | |
console.warn('Unsupported geometry type for collision shape creation:', geometry.type); | |
return null; | |
} | |
} | |
enablePhysicsForObject(objectName) { | |
if (!this._3d.scene) return; | |
const object = this._3d.scene.getObjectByName(objectName); | |
if (!object || !this._3d.scene) return; | |
const shape = this.createShapeFromGeometry(object.geometry); | |
if (!shape) { | |
console.warn('Failed to create a valid shape for the object:', object.name); | |
return; | |
} | |
const body = new this.CANNON.Body({ | |
mass: 1, // You might want to adjust mass based on object size/type | |
}); | |
body.addShape(shape); | |
this.world.addBody(body); // Add the body to the Cannon.js world | |
object.userData.physicsBody = body; | |
} | |
disablePhysicsForObject(objectName) { | |
const object = this._3d.scene.getObjectByName(objectName); | |
if (!object || !object.userData || !object.userData.physicsBody) return; | |
this.world.removeBody(object.userData.physicsBody); // Remove from world | |
delete object.userData.physicsBody; | |
} | |
step() { | |
// Step the Cannon.js world to simulate physics | |
this.world.step(1/60); // Update at 60 fps (adjust timestep as needed) | |
// Update Three.js object positions and rotations from physics bodies | |
this._3d.scene.traverse((object) => { | |
if (object.userData && object.userData.physicsBody) { | |
object.position.copy(object.userData.physicsBody.position); | |
object.quaternion.copy(object.userData.physicsBody.quaternion); | |
} | |
}); | |
} | |
addp(args) { | |
this.enablePhysicsForObject(Cast.toString(args.NAME1)) | |
} | |
rmp(args) { | |
this.disablePhysicsForObject(Cast.toString(args.NAME1)) | |
} | |
} | |
module.exports = Fr3DBlocks; | |