noa-engine
Version:
Experimental voxel game engine
123 lines (90 loc) • 3.66 kB
JavaScript
/**
* @module
* @internal
*/
import vec3 from 'gl-vec3'
export class PhysicsState {
constructor() {
/** @type {import('voxel-physics-engine').RigidBody} */
this.body = null
}
}
/**
* Physics component, stores an entity's physics engbody.
* @param {import('..').Engine} noa
*/
export default function (noa) {
return {
name: 'physics',
order: 40,
state: new PhysicsState,
onAdd: function (entID, state) {
state.body = noa.physics.addBody()
// implicitly assume body has a position component, to get size
var posDat = noa.ents.getPositionData(state.__id)
setPhysicsFromPosition(state, posDat)
},
onRemove: function (entID, state) {
// update position before removing
// this lets entity wind up at e.g. the result of a collision
// even if physics component is removed in collision handler
if (noa.ents.hasPosition(state.__id)) {
var pdat = noa.ents.getPositionData(state.__id)
setPositionFromPhysics(state, pdat)
backtrackRenderPos(state, pdat, 0, false)
}
noa.physics.removeBody(state.body)
},
system: function (dt, states) {
for (var i = 0; i < states.length; i++) {
var state = states[i]
var pdat = noa.ents.getPositionData(state.__id)
setPositionFromPhysics(state, pdat)
}
},
renderSystem: function (dt, states) {
var tickPos = noa.positionInCurrentTick
var tickTime = 1000 / noa.container._shell.tickRate
tickTime *= noa.timeScale
var tickMS = tickPos * tickTime
// tickMS is time since last physics engine tick
// to avoid temporal aliasing, render the state as if lerping between
// the last position and the next one
// since the entity data is the "next" position this amounts to
// offsetting each entity into the past by tickRate - dt
// http://gafferongames.com/game-physics/fix-your-timestep/
var backtrackAmt = (tickMS - tickTime) / 1000
for (var i = 0; i < states.length; i++) {
var state = states[i]
var id = state.__id
var pdat = noa.ents.getPositionData(id)
var smoothed = noa.ents.cameraSmoothed(id)
backtrackRenderPos(state, pdat, backtrackAmt, smoothed)
}
}
}
}
// var offset = vec3.create()
var local = vec3.create()
export function setPhysicsFromPosition(physState, posState) {
var box = physState.body.aabb
var ext = posState._extents
vec3.copy(box.base, ext)
vec3.set(box.vec, posState.width, posState.height, posState.width)
vec3.add(box.max, box.base, box.vec)
}
function setPositionFromPhysics(physState, posState) {
var base = physState.body.aabb.base
var hw = posState.width / 2
vec3.set(posState._localPosition, base[0] + hw, base[1], base[2] + hw)
}
function backtrackRenderPos(physState, posState, backtrackAmt, smoothed) {
// pos = pos + backtrack * body.velocity
var vel = physState.body.velocity
vec3.scaleAndAdd(local, posState._localPosition, vel, backtrackAmt)
// smooth out update if component is present
// (this is set after sudden movements like auto-stepping)
if (smoothed) vec3.lerp(local, posState._renderPosition, local, 0.3)
// copy values over to renderPosition,
vec3.copy(posState._renderPosition, local)
}