File size: 4,510 Bytes
30c32c8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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;