UNPKG

noa-engine

Version:

Experimental voxel game engine

117 lines (88 loc) 3.44 kB
import vec3 from 'gl-vec3' import { Color3 } from '@babylonjs/core/Maths/math.color' import { CreateDisc } from '@babylonjs/core/Meshes/Builders/discBuilder' import '@babylonjs/core/Meshes/instancedMesh' /** @param {import('../index').Engine} noa */ export default function (noa, distance = 10) { var shadowDist = distance // create a mesh to re-use for shadows var scene = noa.rendering.getScene() var disc = CreateDisc('shadow', { radius: 0.75, tessellation: 30 }, scene) disc.rotation.x = Math.PI / 2 var mat = noa.rendering.makeStandardMaterial('shadow_component_mat') mat.diffuseColor.set(0, 0, 0) mat.ambientColor.set(0, 0, 0) mat.alpha = 0.5 disc.material = mat mat.freeze() // source mesh needn't be in the scene graph noa.rendering.setMeshVisibility(disc, false) return { name: 'shadow', order: 80, state: { size: 0.5, _mesh: null, }, onAdd: function (eid, state) { var mesh = disc.createInstance('shadow_instance') noa.rendering.addMeshToScene(mesh) mesh.setEnabled(false) state._mesh = mesh }, onRemove: function (eid, state) { state._mesh.dispose() state._mesh = null }, system: function shadowSystem(dt, states) { var cpos = noa.camera._localGetPosition() var dist = shadowDist for (var i = 0; i < states.length; i++) { var state = states[i] var posState = noa.ents.getPositionData(state.__id) var physState = noa.ents.getPhysics(state.__id) updateShadowHeight(noa, posState, physState, state._mesh, state.size, dist, cpos) } }, renderSystem: function (dt, states) { // before render adjust shadow x/z to render positions for (var i = 0; i < states.length; i++) { var state = states[i] var rpos = noa.ents.getPositionData(state.__id)._renderPosition var spos = state._mesh.position spos.x = rpos[0] spos.z = rpos[2] } } } } var shadowPos = vec3.fromValues(0, 0, 0) var down = vec3.fromValues(0, -1, 0) function updateShadowHeight(noa, posDat, physDat, mesh, size, shadowDist, camPos) { // local Y ground position - from physics or raycast var localY if (physDat && physDat.body.resting[1] < 0) { localY = posDat._localPosition[1] } else { var res = noa._localPick(posDat._localPosition, down, shadowDist) if (!res) { mesh.setEnabled(false) return } localY = res.position[1] - noa.worldOriginOffset[1] } // round Y pos and offset upwards slightly to avoid z-fighting localY = Math.round(localY) vec3.copy(shadowPos, posDat._localPosition) shadowPos[1] = localY var sqdist = vec3.squaredDistance(camPos, shadowPos) // offset ~ 0.01 for nearby shadows, up to 0.1 at distance of ~40 var offset = 0.01 + 0.1 * (sqdist / 1600) if (offset > 0.1) offset = 0.1 mesh.position.y = localY + offset // set shadow scale var dist = posDat._localPosition[1] - localY var scale = size * 0.7 * (1 - dist / shadowDist) mesh.scaling.copyFromFloats(scale, scale, scale) mesh.setEnabled(true) }