UNPKG

elation-engine

Version:
892 lines (845 loc) 36.9 kB
elation.require(["physics.cyclone"], function() { elation.extend("engine.systems.physics", function(args) { elation.implement(this, elation.engine.systems.system); this.system = false; this.timescale = 1; this.debugwindows = {}; this.debugvis = {}; this.debugthings = {}; this.async = true; this.asyncframerate = 120; this.system_attach = function(ev) { console.log('INIT: physics'); this.system = new elation.physics.system({autostart: false, processortype: 'cpu'}); // Only show second framerate gauge if physics system is decoupled from framerate if (this.async && ENV_IS_BROWSER) { //this.initstats(); } } this.engine_start = function(ev) { //console.log("PHYSICS: starting"); this.system.start({subprocessor: 'cpu', processorargs: { path: 'https://baicoianu.com/~bai/janusweb/build/1.5.32/media/lib/physx/'}}); this.lasttime = performance.now(); if (this.interval) { clearInterval(this.interval); } if (this.async) { this.interval = setInterval(elation.bind(this, function() { var now = performance.now(); //this.system.step(this.timescale * (now - this.lasttime) / 1000); //if (this.stats) this.stats.update(); this.step((now - this.lasttime) / 1000); this.lasttime = now; }), 1000/this.asyncframerate); } } this.engine_frame = function(ev) { // console.log("FRAME: physics"); if (!this.async) { this.step(ev.data.delta); } } this.engine_stop = function(ev) { console.log('SHUTDOWN: physics'); if (this.interval) { clearInterval(this.interval); this.interval = false; } } this.initstats = function() { this.stats = new Stats(); this.stats.domElement.style.position = 'absolute'; this.stats.domElement.style.top = '0px'; this.stats.domElement.style.zIndex = 100; document.body.appendChild(this.stats.domElement); this.stats.domElement.style.right = this.stats.domElement.offsetWidth + 'px'; this.stats.domElement.childNodes[0].childNodes[0].style.color = '#900'; this.stats.domElement.childNodes[0].childNodes[1].style.backgroundColor = '#900'; } this.add = function(obj) { this.system.add(obj); } this.remove = function(obj) { this.system.remove(obj); } this.step = function(delta) { this.system.step(this.timescale * delta); if (this.stats) this.stats.update(); if (this.debugthing) { this.debugupdate(this.debugthing); } } this.debug = function(thing) { /* if (!this.debugwindows[thing.name]) { this.debugwindows[thing.name] = elation.ui.window(null, elation.html.create({tag: 'div', append: document.body}), {title: "Physics Debug - " + thing.name, center: true, right: 10}); this.debugwindows[thing.name].setcontent(elation.html.create('ul')); elation.events.add(this.debugwindows[thing.name], 'ui_window_close', elation.bind(this, function(ev) { console.log('REMOVE IT', thing.name); this.debugvis[thing.name].die(); this.debugvis[thing.name] = false; if (thing == this.debugthing) this.debugthing = false; this.debugwindows[thing.name] = false; delete this.debugthings[thing.name]; })); } */ if (!this.debugvis[thing.name]) { this.debugvis[thing.name]= thing.spawn('physics_vis', thing.name + '_physvis', {target: thing, pickable: false, persist: false, physical: false, window: this.debugwindows[thing.name]}); } else if (!this.debugvis[thing.name].parent) { thing.add(this.debugvis[thing.name]); } if (!this.debugthings[thing.name]) { this.debugthings[thing.name] = thing; } /* if (!this.debugvis[thing.name].parent || this.debugvis[thing.name].parent != thing || this.debugvis[thing.name].objects['3d'].parent != thing.objects['3d']) { this.debugvis[thing.name].reparent(thing); } */ //this.debugwindows[thing.name].focus(); this.debugthing = thing; this.debugupdate(thing); } this.disableDebug = function(thing) { if (this.debugvis[thing.name] && this.debugvis[thing.name].parent) { this.debugvis[thing.name].parent.remove(this.debugvis[thing.name]); } } this.debugupdate = function(thing) { for (var name in this.debugthings) { var thing = this.debugthings[name]; var win = this.debugwindows[name]; if (win) { if (thing.objects.dynamics) { var ul = win.content; if (ul.innerHTML == '' || !thing.objects.dynamics.state.sleeping) { ul.innerHTML = ''; var values = ['state', 'mass', 'position', 'velocity', 'acceleration', 'angular', 'angularacceleration', 'force_accumulator', 'damping']; for (var i = 0; i < values.length; i++) { var li = elation.html.create('li'); var content = "<strong>" + values[i] + ":</strong> "; var value = thing.objects.dynamics[values[i]]; if (value instanceof THREE.Vector3) { content += "[ " + (+value.x).toFixed(4) + ", " + (+value.y).toFixed(4) + ", " + (+value.z).toFixed(4) + " ]"; } else if (values[i] == 'state') { for (var k in value) { var tag = (value[k] ? "add" : "del"); content += "<" + tag + ">" + k + "</" + tag + "> "; } } else if (values[i] == 'damping') { content += "[" + thing.objects.dynamics.linearDamping + ', ' + thing.objects.dynamics.angularDamping + "]"; } else { content += value; } li.innerHTML = content; ul.appendChild(li); } } } else { this.debugwindows[thing.name].close(); } } } } }); elation.component.add("engine.things.physics_vis", function(args) { this.postinit = function() { this.defineProperties({ target: { type: 'thing' }, forcescale: { type: 'float', default: .1 }, window: { type: 'object' } }); } this.createObject3D = function() { //this.objects['3d'] = new THREE.Object3D(); this.objects['3d'] = new THREE.Object3D(); var obj = this.properties.target; elation.events.add(obj, 'mouseover', (ev) => this.handleMouseOver(ev)); elation.events.add(obj, 'mouseout', (ev) => this.handleMouseOut(ev)); if (obj.objects['3d']) { /* if (!obj.objects['3d'].geometry.boundingBox) { obj.objects['3d'].geometry.computeBoundingBox(); } */ //var bbox = obj.objects['3d'].geometry.boundingBox; var bbox = obj.getBoundingBox(); if (obj.objects['dynamics']) { this.velocityvis = this.spawn('physics_velocity', this.name + '_velocity', {body: obj.objects['dynamics'], pickable: false, mouseevents: false, persist: false, physical: false}, true); this.collidervis = this.spawn('physics_collider', this.name + '_collider', {body: obj.objects['dynamics'], pickable: false, mouseevents: false, persist: false, physical: false}); var forceargs = { body: obj.objects['dynamics'], boundingbox: bbox, forcescale: this.properties.forcescale, pickable: false, mouseevents: false, persist: false, physical: false }; for (var k in obj.objects['dynamics'].forces) { forceargs.force = obj.objects['dynamics'].forces[k]; var type = false; for (var f in elation.physics.forces) { if (forceargs.force instanceof elation.physics.forces[f]) { this.spawn('physics_forces_' + f, obj.name + '_force_' + f + '_' + k, forceargs); } } } } } if (this.properties.window) { var foo = new THREE.CSS3DSprite(this.properties.window.container); foo.position.set(2.5,0,0); foo.scale.set(.01,.01,.01); this.objects['3d'].add(foo); } return this.objects['3d']; } this.handleMouseOver = function(ev) { this.showHighlight(); } this.handleMouseOut = function(ev) { this.hideHighlight(); } this.showHighlight = function() { //this.collidervis.showHighlight(); } this.hideHighlight = function() { //this.collidervis.hideHighlight(); } }, elation.engine.things.generic); elation.component.add("engine.things.physics_velocity", function(args) { this.postinit = function() { this.defineProperties({ body: {type: 'object'}, fadetime: {type: 'float', default: 5.0}, }); this.segmentOffset = 0; this.numSegments = this.fadetime * 60 / 2; elation.events.add(this.engine, 'engine_frame', this); elation.events.add(this.body.object, 'thing_remove', (ev) => this.handleThingRemove(ev)); } this.createObject3D = function() { let geo = new THREE.BufferGeometry(); let positions = new Float32Array(this.numSegments * 3 * 2); let opacity = new Float32Array(this.numSegments * 2); geo.setAttribute('position', new THREE.BufferAttribute(positions, 3)); geo.setAttribute('opacity', new THREE.BufferAttribute(opacity, 1)); let material1 = new THREE.LineBasicMaterial({color: 0x00ffff, transparent: true, depthWrite: false, depthTest: false, opacity: .05, blending: THREE.AdditiveBlending, linewidth: 4}); let material2 = new THREE.LineBasicMaterial({color: 0x00ffff, transparent: true, depthWrite: false, depthTest: true, opacity: .2, blending: THREE.AdditiveBlending, linewidth: 4}); this.geometry = geo; geo.computeBoundingSphere(); let obj = new THREE.Object3D(); obj.add(new THREE.LineSegments(geo, material1)); obj.add(new THREE.LineSegments(geo, material2)); return obj; }, this.engine_frame = (function() { let v0 = new THREE.Vector3(), v1 = new THREE.Vector3(), vel = new THREE.Vector3(); return function(ev) { if (!this.body.parent) return; let position = this.geometry.attributes.position.array; if (!this.lastpos) { this.lastpos = new THREE.Vector3(); this.body.localToWorldPos(this.lastpos.set(0,0,0)); for (let i = 0; i < this.numSegments; i++) { let idx = i * 3; position[idx] = this.lastpos.x; position[idx+1] = this.lastpos.y; position[idx+2] = this.lastpos.z; } return; } //this.body.localToWorldPos(v0.set(0,0,0)); //v1.copy(v0).add(vel.copy(this.body.velocity).multiplyScalar(ev.data.delta)); v0.copy(this.lastpos); this.body.localToWorldPos(v1.set(0,0,0)); this.lastpos.copy(v1); let idx = this.segmentOffset * 3 * 2; this.segmentOffset = (this.segmentOffset + 1) % this.numSegments; position[idx] = v0.x; position[idx+1] = v0.y; position[idx+2] = v0.z; position[idx+3] = v1.x; position[idx+4] = v1.y; position[idx+5] = v1.z; this.geometry.attributes.position.needsUpdate = true; let l = v1.length(); if (l > this.geometry.boundingSphere.radius) { this.geometry.boundingSphere.radius = l; } } })(); this.handleThingRemove = function(ev) { setTimeout(() => { this.die(); }, this.fadetime * 1000); } }, elation.engine.things.generic); elation.component.add("engine.things.physics_collider", function(args) { this.postinit = function() { this.defineProperties({ body: {type: 'object'}, boundingbox: { type: 'object' }, forcescale: { type: 'float', default: .1 } }); elation.events.add(this.properties.body, 'physics_collide', this); this.collisioncount = 0; this.nummarkers = 100; this.currentmarker = 0; this.markerpool = []; /* for (let i = 0; i < this.nummarkers; i++) { this.markerpool[i] = this.spawn('physics_collision', this.name + '_collision_' + i, { pickable: false, mouseevents: false, persist: false, physical: false, }, true); } */ } this.createObject3D = function() { if (this.objects['3d']) return this.objects['3d']; var collider = this.properties.body.collider; var obj = false; if (collider) { switch (collider.type) { case 'sphere': obj = this.createBoundingSphere(collider); break; case 'plane': obj = this.createBoundingPlane(collider); break; case 'cylinder': obj = this.createBoundingCylinder(collider); break; case 'mesh': obj = this.createBoundingMesh(collider); break; case 'capsule': obj = this.createBoundingCapsule(collider); break; case 'box': default: obj = this.createBoundingBox(collider); break; } } else { obj = new THREE.Object3D(); } if (this.properties.body.mass > 0) { var cg = this.createCG(); obj.add(cg); } return obj; } this.createBoundingBox = function(collider) { var bbox = collider; var corners = [ [bbox.min.x, bbox.max.y, bbox.min.z], [bbox.max.x, bbox.max.y, bbox.min.z], [bbox.max.x, bbox.max.y, bbox.max.z], [bbox.min.x, bbox.max.y, bbox.max.z], [bbox.min.x, bbox.min.y, bbox.min.z], [bbox.max.x, bbox.min.y, bbox.min.z], [bbox.max.x, bbox.min.y, bbox.max.z], [bbox.min.x, bbox.min.y, bbox.max.z], ]; var edges = [ // top [corners[0], corners[1]], [corners[1], corners[2]], [corners[2], corners[3]], [corners[3], corners[0]], // bottom [corners[4], corners[5]], [corners[5], corners[6]], [corners[6], corners[7]], [corners[7], corners[4]], // sides [corners[0], corners[4]], [corners[1], corners[5]], [corners[2], corners[6]], [corners[3], corners[7]] ] /* var linegeo = new THREE.BufferGeometry(); for (var i = 0; i < edges.length; i++) { var edge = edges[i]; linegeo.vertices.push(new THREE.Vector3(edge[0][0], edge[0][1], edge[0][2])); linegeo.vertices.push(new THREE.Vector3(edge[1][0], edge[1][1], edge[1][2])); } */ //var boxgeo = new THREE.BoxGeometry(bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y, bbox.max.z - bbox.min.z); let boxgeo = new THREE.BoxGeometry(bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y, bbox.max.z - bbox.min.z, 10, 10, 10); var boxmat = new THREE.LineBasicMaterial({color: 0x0000ff, transparent: true, depthWrite: false, depthTest: false, opacity: .2, blending: THREE.AdditiveBlending}); var outline = new THREE.LineSegments(boxgeo, boxmat, THREE.LinePieces); var volume = new THREE.Mesh(boxgeo, new THREE.MeshPhongMaterial({color: 0xaaaaaa, emissive: 0x666666, depthTest: true, depthWrite: true, opacity: .1, transparent: true})); volume.position.addVectors(bbox.max, bbox.min).multiplyScalar(.5); console.log(this.properties.body); //outline.add(volume); //outline.add(new THREE.AxisHelper(.5)); // temporary helpers for debugging coordinate space transforms /* var forward = new THREE.Vector3(0,0,-1); this.arrows = { forward: new THREE.ArrowHelper(forward, new THREE.Vector3(0,0,0), 2, 0x00ffff), forward_world: new THREE.ArrowHelper(this.properties.body.localToWorldDir(forward.clone()), new THREE.Vector3(0,0,0), 2, 0x00ff66), forward_local: new THREE.ArrowHelper(this.properties.body.worldToLocalDir(forward.clone()), new THREE.Vector3(0,0,0), 2, 0x0066ff) }; outline.add(this.arrows.forward); outline.add(this.arrows.forward_world); outline.add(this.arrows.forward_local); elation.events.add(this.properties.body.object.engine, 'engine_frame', this); */ return outline; } this.createBoundingSphere = function(collider) { var spheregeo = new THREE.SphereGeometry(collider.radius, 18, 9); //var spheregeo = new THREE.IcosahedronGeometry(collider.radius, 2); var spheremat = new THREE.MeshBasicMaterial({color: 0x00ffff, transparent: true, opacity: .2, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -1, polygonOffsetUnits: 1, wireframe: false, blending: THREE.AdditiveBlending}); var spherewiremat = new THREE.MeshBasicMaterial({color: 0xff0000, transparent: true, opacity: .1, depthWrite: false, depthTest: false, wireframe: true, blending: THREE.AdditiveBlending}); var outline = new THREE.Mesh(spheregeo, spherewiremat); //outline.add(new THREE.Mesh(spheregeo, spheremat)); return outline; } this.createBoundingPlane = function(collider) { var plane = new THREE.PlaneGeometry(1000, 1000); var planemat = new THREE.MeshBasicMaterial({color: 0x00ffff, transparent: true, opacity: .04, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -5, polygonOffsetUnits: 1, wireframe: false, blending: THREE.AdditiveBlending }); // FIXME - this only really works for horizontal planes var mat = new THREE.Matrix4().makeRotationFromQuaternion(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), -Math.PI/2)); plane.applyMatrix4(mat); var mesh = new THREE.Mesh(plane, planemat); return mesh; } this.createBoundingCylinder = function(collider) { var cyl = new THREE.CylinderGeometry(collider.radius, collider.radius, collider.height, 32, 1); //var cylmat = new THREE.MeshBasicMaterial({color: 0x00ffff, transparent: true, opacity: .1, depthWrite: false, depthTest: false, wireframe: true, blending: THREE.AdditiveBlending}); var cylmat = new THREE.MeshBasicMaterial({color: 0xff0000, transparent: true, opacity: .1, depthWrite: false, depthTest: false, wireframe: true, blending: THREE.AdditiveBlending}); var mesh = new THREE.Mesh(cyl, cylmat); console.log('my offset', collider.offset, collider); let offset = collider.offset.clone();//.multiply(collider.body.scale); cyl.applyMatrix4(new THREE.Matrix4().makeTranslation(offset.x, offset.y, offset.z)); return mesh; } this.createBoundingMesh = function(collider) { if (collider.mesh) { let userData = collider.mesh.userData; collider.mesh.traverse(n => n.userData = {}); let mesh = collider.mesh.clone(); collider.mesh.traverse(n => { n.userData = userData; }); mesh.traverse(n => { if (n.material) { n.material = new THREE.MeshBasicMaterial({color: 0xff0000, transparent: true, opacity: .1, depthWrite: false, depthTest: false, wireframe: true, blending: THREE.AdditiveBlending}); } }); return mesh; } return new THREE.Object3D(); } this.createBoundingCapsule = function(collider) { let collidergeom = elation.engine.geometries.generate('capsule', { radius: collider.radius, length: collider.length, radialSegments: 8, offset: collider.offset, }); let wiremat = new THREE.MeshBasicMaterial({color: 0xff0000, transparent: true, opacity: .1, depthWrite: false, depthTest: false, wireframe: true, blending: THREE.AdditiveBlending}); return new THREE.Mesh(collidergeom, wiremat); } this.createCG = function() { // Create yellow checkerboard texture var cgmatcanvas = elation.html.create('canvas'); var cw = 128, ch = 64; cgmatcanvas.width = cw; cgmatcanvas.height = ch; var ctx = cgmatcanvas.getContext('2d'); ctx.fillStyle = 'yellow'; ctx.fillRect(0, 0, cw, ch); ctx.fillStyle = 'black'; ctx.fillRect(0, 0, cw/2, ch/2); ctx.fillRect(cw/2, ch/2, cw, ch); var tex = new THREE.Texture(cgmatcanvas); tex.minFilter = tex.maxFilter = THREE.LinearFilter; tex.needsUpdate = true; var cg = new THREE.Mesh(new THREE.SphereGeometry(.05), new THREE.MeshPhongMaterial({emissive: 0x666600, map: tex, transparent: true, depthWrite: false, depthTest: false, opacity: 1})); return cg; } /* this.engine_frame = function() { var forward = new THREE.Vector3(0,0,-1); this.arrows.forward.setDirection(forward); this.arrows.forward_world.setDirection(this.properties.body.localToWorldDir(forward.clone())); this.arrows.forward_local.setDirection(this.properties.body.worldToLocalDir(forward.clone())); } */ this.physics_collide = function(ev) { var collision = ev.data; let obj1 = ev.data.bodies[0].object, obj2 = ev.data.bodies[1].object; if (obj1.collidable && obj2.collidable) { this.getCollisionMarker(collision); } } this.getCollisionMarker = function(collision) { let markerpool = this.getMarkerPool(); let marker = markerpool[markerpool.currentmarker]; marker.ping(collision); markerpool.currentmarker = (markerpool.currentmarker + 1) % markerpool.length; } this.getMarkerPool = function() { if (!elation.engine.things.physics_collision.markerpool) { elation.engine.things.physics_collision.markerpool = []; for (let i = 0; i < this.nummarkers; i++) { elation.engine.things.physics_collision.markerpool[i] = this.spawn('physics_collision', this.name + '_collision_' + i, { pickable: false, mouseevents: false, persist: false, physical: false, visible: false, }, true); } elation.engine.things.physics_collision.markerpool.currentmarker = 0; } return elation.engine.things.physics_collision.markerpool; } }, elation.engine.things.generic); elation.component.add("engine.things.physics_collision", function(args) { this.postinit = function() { this.defineProperties({ collision: {type: 'object'}, forcescale: { type: 'float', default: .1 }, fadetime: { type: 'float', default: 10.0 } }); if (!elation.engine.things.physics_collision.texture) { elation.engine.things.physics_collision.texture = this.generateGrid(0xff0000); } this.texture = elation.engine.things.physics_collision.texture; elation.events.add(this.engine, 'engine_frame', this); } this.ping = function(collision) { this.position.copy(collision.point); this.spawntime = new Date().getTime(); this.elapsed = 0; this.plane.lookAt(collision.normal.clone().add(collision.point)); this.visible = true; //this.updateArrow(this.arrows[3], collision.impulses[1]); //elation.events.add(collision.bodies[0], 'physics_collision_resolved', this); } this.createObject3D = function() { this.materials = []; var collision = this.properties.collision; var planegeo = new THREE.PlaneGeometry(1, 1); var planemat = new THREE.MeshBasicMaterial({ map: this.texture, wireframe: false, transparent: true, depthWrite: false, depthTest: false, opacity: 1, blending: THREE.NormalBlending, side: THREE.DoubleSide, }); var plane = new THREE.Mesh(planegeo, planemat); var obj = new THREE.Object3D(); obj.add(plane); this.plane = plane; this.materials.push(planemat); this.objects['3d'] = obj; //console.log('IMPULSES', collision, collision.impulses, collision.contactToWorld); var origin = new THREE.Vector3(0,0,0); /* var m = collision.contactToWorld.elements; //var arrowaxisx = this.generateArrow(new THREE.Vector3(m[0], m[1], m[2]), origin, 1, 0xff0000); //obj.add(arrowaxisx); var arrowaxisy = this.generateArrow(new THREE.Vector3(m[4], m[5], m[6]), origin, 1, 0x00ff00); obj.add(arrowaxisy); //var arrowaxisz = this.generateArrow(new THREE.Vector3(m[8], m[9], m[10]), origin, 1, 0x0000ff); //obj.add(arrowaxisz); */ let arrowaxisy = this.generateArrow(new THREE.Vector3(0, 0, 1), origin, 1, 0x00ff00); plane.add(arrowaxisy); /* let velocity_orig = this.generateArrow(new THREE.Vector3(0, 0, 1), origin, 1, 0x0000ff); plane.add(velocity_orig); let velocity_reflect = this.generateArrow(new THREE.Vector3(0, 0, 1), origin, 1, 0x0000ff); plane.add(velocity_reflect); let impulse = this.generateArrow(new THREE.Vector3(0, 0, 1), origin, 1, 0xffff00); plane.add(impulse); */ this.arrows = [arrowaxisy]; //, velocity_orig, velocity_reflect, impulse]; //[arrowaxisx, arrowaxisy, arrowaxisz]; return obj; } this.generateArrow = function(dir, origin, len, color) { var arrow = new THREE.ArrowHelper(dir, origin, len, color); for (var j = 0; j < arrow.children.length; j++) { var mat = arrow.children[j].material; mat.transparent = true; mat.depthWrite = mat.depthTest = false; this.materials.push(mat); } return arrow; } this.updateArrow = function(arrow, dir, len=null, origin=null) { arrow.setDirection(dir); if (len !== null) { arrow.setLength(len); } if (origin !== null) { arrow.position.copy(origin); } } this.generateGrid = function(color, lines) { if (!color) color = 0xff0000; if (!lines) lines = 10; var canvas = elation.html.create('canvas'); var cw = 512, ch = 512; var gridsize = cw / lines; canvas.width = cw; canvas.height = ch; var ctx = canvas.getContext('2d'); ctx.fillStyle = 'rgba(255,0,0,.25)'; ctx.fillRect(0, 0, cw, ch); ctx.strokeStyle = 'rgba(255,0,0,.75)'; for (var y = 0; y <= lines; y++) { for (var x = 0; x <= lines; x++) { ctx.beginPath(); ctx.moveTo(0, y * gridsize); ctx.lineTo(cw, y * gridsize); ctx.stroke(); ctx.beginPath(); ctx.moveTo(x * gridsize, 0); ctx.lineTo(x * gridsize, ch); ctx.stroke(); } } ctx.globalCompositeOperation = 'destination-out'; var grd = ctx.createRadialGradient(cw/2,ch/2,0,cw/2,ch/2,cw/2); grd.addColorStop(0, 'rgba(0,0,0,.5)'); grd.addColorStop(.5, 'rgba(0,0,0,.5)'); grd.addColorStop(1, 'rgba(0,0,0,1)'); ctx.fillStyle = grd; ctx.fillRect(0,0,cw,ch); ctx.globalCompositeOperation = 'source-over'; ctx.fillStyle = '#ff0000'; ctx.strokeStyle = '#990000'; ctx.lineWidth = 3; ctx.beginPath(); ctx.arc(cw/2, ch/2, 8, 0, 2 * Math.PI, false); ctx.fill(); ctx.stroke(); var tex = new THREE.Texture(canvas); tex.minFilter = tex.maxFilter = THREE.LinearFilter; tex.needsUpdate = true; return tex; } this.physics_collision_resolved = function(ev) { var collision = ev.data; var origin = new THREE.Vector3(0,0,0); for (var i = 0; i < collision.impulses.length; i++) { if (collision.impulses[i]) { var len = collision.impulses[i].length(); console.log('ffff', collision.impulses[i], len); if (len > 0) { var dir = collision.impulses[i].clone().divideScalar(len); var impulsearrow = this.generateArrow(dir, origin, len * this.properties.forcescale, 0x990099); //this.arrows.push(impulsearrow); //this.plane.add(impulsearrow); // FIXME - need to remove object from proper parent when done fading //collision.bodies[i].object.objects['3d'].add(impulsearrow); } } } } this.engine_frame = function(ev) { var fadetime = this.properties.fadetime; this.elapsed += ev.data.delta * this.engine.systems.physics.timescale; var opacity = (fadetime - this.elapsed) / fadetime; if (opacity > 0) { for (var i = 0; i < this.materials.length; i++) { this.materials[i].opacity = opacity; } } else { /* for (var i = 0; i < this.arrows.length; i++) { this.arrows[i].parent.remove(this.arrows[i]); } elation.events.remove(this.engine, 'engine_frame', this); this.die(); */ } } }, elation.engine.things.generic); elation.component.add("engine.things.physics_forces_static", function(args) { this.postinit = function() { this.defineProperties({ body: {type: 'object'}, force: {type: 'object'}, boundingbox: {type: 'object'}, forcescale: {type: 'float'} }); elation.events.add(this.properties.force, 'physics_force_apply', this); } this.createObject3D = function() { var force = (this.force.absolute ? this.properties.body.worldToLocalDir(this.properties.force.force.clone()) : this.properties.force.force.clone()); var len = force.length(); force.divideScalar(len); this.arrow = new THREE.ArrowHelper(force, new THREE.Vector3(0,0,0), len, 0xff00ff); let newmat = new THREE.MeshBasicMaterial({ transparent: true, opacity: 0.5, depthWrite: false, depthTest: false }); this.arrow.traverse(n => { if (n.material) { n.material = newmat; } }); var obj = new THREE.Object3D(); obj.add(this.arrow); return obj; } this.physics_force_apply = (function() { let forcevec = new THREE.Vector3(); return function() { var force = (this.force.absolute ? this.properties.body.worldToLocalDir(forcevec.copy(this.properties.force.force)) : forcevec.copy(this.properties.force.force)); var len = force.length(); force.divideScalar(len); this.arrow.setDirection(force); this.arrow.setLength(len / this.properties.body.mass * this.properties.forcescale); } })(); }, elation.engine.things.generic); elation.component.add("engine.things.physics_forces_gravity", function(args) { this.postinit = function() { this.defineProperties({ body: {type: 'object'}, force: {type: 'object'}, boundingbox: {type: 'object'}, forcescale: {type: 'float'} }); elation.events.add(this.properties.force, 'physics_force_apply', this); } this.createObject3D = function() { var grav = this.properties.body.worldToLocalDir(this.properties.force.gravsum.clone()); var len = grav.length(); grav.divideScalar(len); this.arrow = new THREE.ArrowHelper(grav, new THREE.Vector3(0,0,0), len, 0xff00ff); this.arrow.children[0].material.transparent = true; this.arrow.children[0].material.opacity = 0.5; this.arrow.children[0].material.depthWrite = this.arrow.children[0].material.depthTest = false; this.arrow.children[1].material.depthWrite = this.arrow.children[0].material.depthTest = false; var obj = new THREE.Object3D(); obj.add(this.arrow); /* var labeltext = elation.engine.materials.getTextureLabel('gravity'); //var labelgeo = new THREE.PlaneGeometry(labeltext.image.width / 100, labeltext.image.height / 100); //var label = new THREE.Mesh(labelgeo, new THREE.MeshBasicMaterial({map: labeltext, side: THREE.DoubleSide, transparent: true, depthWrite: false, depthTest: false})); var mapB = THREE.ImageUtils.loadTexture( "/media/space/textures/sprite1.png" ); var label = new THREE.Sprite(new THREE.SpriteMaterial({map: labeltext, useScreenCoordinates: false, sizeAttenuation: false, color: 0xffffff })); label.position.set(1.5, 1.5, 0) //label.scale.set(labeltext.image.width/100,labeltext.image.height/100,1); obj.add(label); */ return obj; } this.physics_force_apply = (function() { let gravforce = new THREE.Vector3(); return function() { var grav = this.properties.body.worldToLocalDir(gravforce.copy(this.properties.force.gravsum)); var len = grav.length(); grav.divideScalar(len); this.arrow.setDirection(grav); this.arrow.setLength(len / this.properties.body.mass * this.properties.forcescale); } })(); }, elation.engine.things.generic); elation.component.add("engine.things.physics_forces_buoyancy", function(args) { this.postinit = function() { this.defineProperties({ body: {type: 'object'}, force: {type: 'object'}, boundingbox: {type: 'object'}, forcescale: {type: 'float'} }); elation.events.add(this.properties.force, 'physics_force_apply', this); } this.createObject3D = function() { var obj = new THREE.Object3D(); var bbox = this.properties.boundingbox; var size = [bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y, bbox.max.z - bbox.min.z]; var offset = [(bbox.max.x + bbox.min.x) / 2, (bbox.max.y - bbox.min.y) / 2, (bbox.max.z + bbox.min.z) / 2]; var insidegeo = new THREE.BoxGeometry(size[0], size[1], size[2]); insidegeo.applyMatrix4(new THREE.Matrix4().makeTranslation(offset[0], size[1]/2 + offset[1], offset[2])); var insidemat_side = new THREE.MeshPhongMaterial({emissive: 0x006666, color: 0x00ffff, opacity: 0.8, transparent: true, depthWrite: false, depthTest: false}); var insidemat_top = new THREE.MeshPhongMaterial({emissive: 0x006666, color: 0x00ffff, opacity: 0.5, transparent: true, depthWrite: false, depthTest: false}); this.inside = new THREE.Mesh(insidegeo, insidemat_side); this.inside.scale.y = this.properties.force.submerged; this.inside.position.y = -size[1] / 2; obj.add(this.inside); this.arrow = new THREE.ArrowHelper(new THREE.Vector3(0,1,0), this.properties.force.position, 1, 0xff00ff); this.arrow.children[0].material.transparent = true; this.arrow.children[0].material.opacity = 0.5; this.arrow.children[0].material.depthWrite = this.arrow.children[0].material.depthTest = false; this.arrow.children[1].material.depthWrite = this.arrow.children[0].material.depthTest = false; obj.add(this.arrow); return obj; } this.physics_force_apply = function(ev) { this.inside.scale.y = Math.max(0.01, this.properties.force.submerged); var len = this.properties.force.force.length(); var grav = this.properties.body.worldToLocalDir(new THREE.Vector3(0,1,0)); if (len > .1) { if (!this.arrow.parent) { this.objects['3d'].add(this.arrow); } this.arrow.setLength(len / this.properties.body.mass * this.properties.forcescale); this.arrow.setDirection(grav); } else { this.objects['3d'].remove(this.arrow); } } }, elation.engine.things.generic); elation.component.add("engine.things.physics_forces_spring", function(args) { this.postinit = function() { this.defineProperties({ body: {type: 'object'}, force: {type: 'object'}, boundingbox: {type: 'object'}, forcescale: {type: 'float'} }); elation.events.add(this.properties.force, 'physics_force_apply', this); } this.createObject3D = function() { var obj = new THREE.Object3D(); this.arrow = new THREE.ArrowHelper(new THREE.Vector3(0,1,0), this.properties.force.connectionpoint, 1, 0xff00ff); this.arrow.children[0].material.transparent = true; this.arrow.children[0].material.opacity = 0.5; this.arrow.children[0].material.depthWrite = this.arrow.children[0].material.depthTest = false; this.arrow.children[1].material.depthWrite = this.arrow.children[0].material.depthTest = false; obj.add(this.arrow); return obj; } this.physics_force_apply = function(ev) { var force = this.properties.force.force.clone(); var length = force.length(); if (length > 0) { if (this.arrow.parent != this.objects['3d']) { this.objects['3d'].add(this.arrow); } force.divideScalar(length); this.arrow.setLength(length * this.properties.forcescale); this.arrow.setDirection(force); } else if (this.arrow.parent == this.objects['3d']) { this.objects['3d'].remove(this.arrow); } } }, elation.engine.things.generic); });