Skip to content
This repository has been archived by the owner on May 23, 2024. It is now read-only.

Shape Component #90

Merged
merged 10 commits into from
Apr 11, 2018
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ require('./src/components/math');
require('./src/components/body/body');
require('./src/components/body/dynamic-body');
require('./src/components/body/static-body');
require('./src/components/shape/shape')
require('./src/components/constraint');
require('./src/system');

Expand Down
102 changes: 74 additions & 28 deletions src/components/body/body.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ var Body = {
* component.
*/
initBody: function () {
var shape,
el = this.el,
data = this.data;
var el = this.el,
data = this.data;

var obj = this.el.object3D;
var pos = obj.position;
Expand Down Expand Up @@ -71,25 +70,63 @@ var Body = {
el.addEventListener('object3dset', this.initBody.bind(this));
return;
}

this.body.addShape(shape, shape.offset, shape.orientation);

// Show wireframe
if (this.system.debug) {
this.createWireframe(this.body, shape);
this.shouldUpdateWireframe = true;
}

this.isLoaded = true;
}

this.el.body = this.body;
this.body.el = el;
this.isLoaded = true;

// If component wasn't initialized when play() was called, finish up.
if (this.isPlaying) {
this._play();
}

this.el.emit('body-loaded', {body: this.el.body});
if (this.isLoaded) {
this.el.emit('body-loaded', {body: this.el.body});
}
},

addShape: function(shape, offset = null, orientation = null) {
if (this.data.shape !== 'none') {
console.warn('shape can only be added if shape property is none');
return;
}

if (!shape) {
console.warn('shape cannot be null');
return;
}

this.body.addShape(shape, offset, orientation);

if (this.system.debug) {
this.shouldUpdateWireframe = true;
}

this.shouldUpdateBody = true;
},

tick: function () {
if (this.shouldUpdateBody) {
this.isLoaded = true;

this._play();

this.el.emit('body-loaded', {body: this.el.body});
this.shouldUpdateBody = false;
}

if (this.shouldUpdateWireframe) {
this.createWireframe(this.body);
this.shouldUpdateWireframe = false;
}
},

/**
Expand Down Expand Up @@ -173,30 +210,39 @@ var Body = {
* @param {CANNON.Body} body
* @param {CANNON.Shape} shape
*/
createWireframe: function (body, shape) {
var offset = shape.offset,
orientation = shape.orientation,
mesh = CANNON.shape2mesh(body).children[0];

this.wireframe = new THREE.LineSegments(
new THREE.EdgesGeometry(mesh.geometry),
new THREE.LineBasicMaterial({color: 0xff0000})
);

if (offset) {
this.wireframe.offset = offset.clone();
createWireframe: function (body) {
if (this.wireframe) {
this.el.sceneEl.object3D.remove(this.wireframe);
delete this.wireframe;
}

if (orientation) {
orientation.inverse(orientation);
this.wireframe.orientation = new THREE.Quaternion(
orientation.x,
orientation.y,
orientation.z,
orientation.w
this.wireframe = new THREE.Object3D();
this.el.sceneEl.object3D.add(this.wireframe);

var offset, mesh;
var orientation = new THREE.Quaternion();
for (var i = 0; i < this.body.shapes.length; i++)
{
offset = this.body.shapeOffsets[i],
orientation.copy(this.body.shapeOrientations[i]),
mesh = CANNON.shape2mesh(this.body).children[i];

var wireframe = new THREE.LineSegments(
new THREE.EdgesGeometry(mesh.geometry),
new THREE.LineBasicMaterial({color: 0xff0000})
);
}

if (offset) {
wireframe.position.copy(offset);
}

if (orientation) {
orientation.inverse(orientation);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a reference to a quaternion still in body.shapeOrientations, so I think we want to make a copy before inverting.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will add .clone() where orientation is set.

orientation = this.body.shapeOrientations[i].clone(),

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh nevermind that's a threejs method, not a cannonjs one.

wireframe.quaternion.copy(orientation);
}

this.wireframe.add(wireframe);
}

this.syncWireframe();
},

Expand Down
105 changes: 105 additions & 0 deletions src/components/shape/shape.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
var CANNON = require('cannon');

var Shape = {
schema: {
shape: {default: 'box', oneOf: ['box', 'sphere', 'cylinder']},
radius: {type: 'number', default: 1, if: {shape: ['sphere']}},
offset: {type: 'vec3', default: {x: 0, y: 0, z: 0}},
halfExtents: {type: 'vec3', default: {x: 1, y: 1, z: 1}, if: {shape: ['box']}},
orientation: {type: 'vec4', default: {x: 0, y: 0, z: 0, w: 1}, if: {shape: ['box', 'cylinder']}},
radiusTop: {type: 'number', default: 1, if: {shape: ['cylinder']}},
radiusBottom: {type: 'number', default: 1, if: {shape: ['cylinder']}},
height: {type: 'number', default: 1, if: {shape: ['cylinder']}},
numSegments: {type: 'int', default: 8, if: {shape: ['cylinder']}}
},

multiple: true,

init: function() {
if (this.el.sceneEl.hasLoaded) {
this.initShape();
} else {
this.el.sceneEl.addEventListener('loaded', this.initShape.bind(this));
}
},

initShape: function() {
this.bodyEl = this.el;
var bodyType = this._findType(this.bodyEl);
var data = this.data;

while (!bodyType && this.bodyEl.parentNode) {
this.bodyEl = this.bodyEl.parentNode;
bodyType = this._findType(this.bodyEl);
}

var scale = new THREE.Vector3();
this.bodyEl.object3D.getWorldScale(scale);
var shape, offset, orientation;

if (data.hasOwnProperty('offset')) {
offset = new CANNON.Vec3(
data.offset.x * scale.x,
data.offset.y * scale.y,
data.offset.z * scale.z
);
}

if (data.hasOwnProperty('orientation')) {
orientation = new CANNON.Quaternion();
orientation.copy(data.orientation);
}

switch(data.shape) {
case 'sphere':
shape = new CANNON.Sphere(data.radius * scale.x);
break;
case 'box':
var halfExtents = new CANNON.Vec3(
data.halfExtents.x * scale.x,
data.halfExtents.y * scale.y,
data.halfExtents.z * scale.z
);
shape = new CANNON.Box(halfExtents);
break;
case 'cylinder':
shape = new CANNON.Cylinder(
data.radiusTop * scale.x,
data.radiusBottom * scale.x,
data.height * scale.y,
data.numSegments
);

//rotate by 90 degrees similar to mesh2shape:createCylinderShape
var quat = new CANNON.Quaternion();
quat.setFromEuler(-90 * THREE.Math.DEG2RAD, 0, 0, 'XYZ').normalize();
orientation.mult(quat, orientation);
break;
default:
console.warn(data.shape + ' shape not supported');
return;
}

this.bodyEl.components[bodyType].addShape(shape, offset, orientation);
},

_findType: function(el) {
if (el.hasAttribute('body')) {
return 'body';
} else if (el.hasAttribute('dynamic-body')) {
return 'dynamic-body';
} else if (el.hasAttribute('static-body')) {
return'static-body';
}
return null;
},

remove: function() {
if (this.bodyEl.parentNode) {
console.warn('removing shape component not currently supported');
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a case where remove could be called but no parent node exists?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the parent is removed first we don't need to warn about the shape being removed.

}
};

module.exports.definition = Shape;
module.exports.Component = AFRAME.registerComponent('shape', Shape);