Created
April 16, 2024 21:44
-
-
Save PhoenixIllusion/791fc945e83a0ce3b3379b7c1596c69f to your computer and use it in GitHub Desktop.
Crude port of example.js in Jolt Physics Example folder to Typescript for typescript coding
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as THREE from 'three'; | |
import Stats from 'stats.js'; | |
import JOLT_NS from 'jolt-physics/wasm-compat'; | |
export var Jolt: typeof JOLT_NS; | |
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' | |
import { WebGL } from './webgl'; | |
// Graphics variables | |
let container: HTMLDivElement; | |
export function setContaienr(_: HTMLDivElement) { | |
container = _; | |
} | |
export var stats: Stats; | |
export var camera: THREE.PerspectiveCamera; | |
export var controls: OrbitControls; | |
export var scene: THREE.Scene; | |
export var renderer: THREE.WebGLRenderer; | |
// Timers | |
export var clock = new THREE.Clock(); | |
export var time = 0; | |
// Physics variables | |
export var jolt: JOLT_NS.JoltInterface; | |
export var physicsSystem: JOLT_NS.PhysicsSystem; | |
export var bodyInterface: JOLT_NS.BodyInterface; | |
// List of objects spawned | |
export var dynamicObjects: THREE.Mesh[] = []; | |
declare type UpdateFunction = (time: number, deltaTime: number)=>void; | |
export var onExampleUpdate: UpdateFunction | null | undefined; | |
export const DegreesToRadians = (deg: number) => deg * (Math.PI / 180.0); | |
export const wrapVec3 = (v: JOLT_NS.Vec3) => new THREE.Vector3(v.GetX(), v.GetY(), v.GetZ()); | |
export const unwrapVec3 = (v: THREE.Vector3) => new Jolt.Vec3(v.x, v.y, v.z); | |
export const wrapRVec3 = (v: JOLT_NS.RVec3) => new THREE.Vector3(v.GetX(), v.GetY(), v.GetZ());; | |
export const unwrapRVec3 = (v: THREE.Vector3) => new Jolt.RVec3(v.x, v.y, v.z); | |
export const wrapQuat = (q: JOLT_NS.Quat) => new THREE.Quaternion(q.GetX(), q.GetY(), q.GetZ(), q.GetW()); | |
export const unwrapQuat = (q: THREE.Quaternion) => new Jolt.Quat(q.x, q.y, q.z, q.w); | |
// Object layers | |
declare type LAYER = typeof LAYER_NON_MOVING | typeof LAYER_MOVING; | |
export const LAYER_NON_MOVING = 0; | |
export const LAYER_MOVING = 1; | |
export const NUM_OBJECT_LAYERS = 2; | |
export function getRandomQuat() { | |
let vec = new Jolt.Vec3(0.001 + Math.random(), Math.random(), Math.random()); | |
let quat = Jolt.Quat.prototype.sRotation(vec.Normalized(), 2 * Math.PI * Math.random()); | |
Jolt.destroy(vec); | |
return quat; | |
} | |
function onWindowResize() { | |
camera.aspect = window.innerWidth / window.innerHeight; | |
camera.updateProjectionMatrix(); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
} | |
export function initGraphics() { | |
renderer = new THREE.WebGLRenderer(); | |
renderer.setClearColor(0xbfd1e5); | |
renderer.setPixelRatio(window.devicePixelRatio); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.2, 2000); | |
camera.position.set(0, 15, 30); | |
camera.lookAt(new THREE.Vector3(0, 0, 0)); | |
scene = new THREE.Scene(); | |
var dirLight = new THREE.DirectionalLight(0xffffff, 1); | |
dirLight.position.set(10, 10, 5); | |
scene.add(dirLight); | |
controls = new OrbitControls(camera, container); | |
container.appendChild(renderer.domElement); | |
stats = new Stats(); | |
stats.dom.style.position = 'absolute'; | |
stats.dom.style.top = '0px'; | |
container.appendChild(stats.dom); | |
window.addEventListener('resize', onWindowResize, false); | |
} | |
let setupCollisionFiltering = function (settings: JOLT_NS.JoltSettings) { | |
// Layer that objects can be in, determines which other objects it can collide with | |
// Typically you at least want to have 1 layer for moving bodies and 1 layer for static bodies, but you can have more | |
// layers if you want. E.g. you could have a layer for high detail collision (which is not used by the physics simulation | |
// but only if you do collision testing). | |
let objectFilter = new Jolt.ObjectLayerPairFilterTable(NUM_OBJECT_LAYERS); | |
objectFilter.EnableCollision(LAYER_NON_MOVING, LAYER_MOVING); | |
objectFilter.EnableCollision(LAYER_MOVING, LAYER_MOVING); | |
// Each broadphase layer results in a separate bounding volume tree in the broad phase. You at least want to have | |
// a layer for non-moving and moving objects to avoid having to update a tree full of static objects every frame. | |
// You can have a 1-on-1 mapping between object layers and broadphase layers (like in this case) but if you have | |
// many object layers you'll be creating many broad phase trees, which is not efficient. | |
const BP_LAYER_NON_MOVING = new Jolt.BroadPhaseLayer(0); | |
const BP_LAYER_MOVING = new Jolt.BroadPhaseLayer(1); | |
const NUM_BROAD_PHASE_LAYERS = 2; | |
let bpInterface = new Jolt.BroadPhaseLayerInterfaceTable(NUM_OBJECT_LAYERS, NUM_BROAD_PHASE_LAYERS); | |
bpInterface.MapObjectToBroadPhaseLayer(LAYER_NON_MOVING, BP_LAYER_NON_MOVING); | |
bpInterface.MapObjectToBroadPhaseLayer(LAYER_MOVING, BP_LAYER_MOVING); | |
settings.mObjectLayerPairFilter = objectFilter; | |
settings.mBroadPhaseLayerInterface = bpInterface; | |
settings.mObjectVsBroadPhaseLayerFilter = new Jolt.ObjectVsBroadPhaseLayerFilterTable(settings.mBroadPhaseLayerInterface, NUM_BROAD_PHASE_LAYERS, settings.mObjectLayerPairFilter, NUM_OBJECT_LAYERS); | |
}; | |
function initPhysics() { | |
// Initialize Jolt | |
const settings = new Jolt.JoltSettings(); | |
setupCollisionFiltering(settings); | |
jolt = new Jolt.JoltInterface(settings); | |
Jolt.destroy(settings); | |
physicsSystem = jolt.GetPhysicsSystem(); | |
bodyInterface = physicsSystem.GetBodyInterface(); | |
// Helper functions | |
/*Jolt.Vec3.prototype.ToString = function () { return `(${this.GetX()}, ${this.GetY()}, ${this.GetZ()})` }; | |
Jolt.Vec3.prototype.Clone = function () { return new Jolt.Vec3(this.GetX(), this.GetY(), this.GetZ()); }; | |
Jolt.RVec3.prototype.ToString = function () { return `(${this.GetX()}, ${this.GetY()}, ${this.GetZ()})` }; | |
Jolt.RVec3.prototype.Clone = function () { return new Jolt.RVec3(this.GetX(), this.GetY(), this.GetZ()); }; | |
Jolt.Quat.prototype.ToString = function () { return `(${this.GetX()}, ${this.GetY()}, ${this.GetZ()}, ${this.GetW()})` }; | |
Jolt.Quat.prototype.Clone = function () { return new Jolt.Quat(this.GetX(), this.GetY(), this.GetZ(), this.GetW()); }; | |
Jolt.AABox.prototype.ToString = function () { return `[${this.mMax.ToString()}, ${this.mMin.ToString()}]`; };*/ | |
} | |
function updatePhysics(deltaTime: number) { | |
// When running below 55 Hz, do 2 steps instead of 1 | |
var numSteps = deltaTime > 1.0 / 55.0 ? 2 : 1; | |
// Step the physics world | |
jolt.Step(deltaTime, numSteps); | |
} | |
export function initExample(_Jolt: typeof Jolt, updateFunction?: UpdateFunction|null) { | |
Jolt = _Jolt; | |
container.innerHTML = ""; | |
if (WebGL.isWebGLAvailable()) { | |
onExampleUpdate = updateFunction; | |
initGraphics(); | |
initPhysics(); | |
renderExample(); | |
} else { | |
const warning = WebGL.getWebGLErrorMessage(); | |
container.appendChild(warning); | |
} | |
// The memory profiler doesn't have an ID so we can't mess with it in css, set an ID here | |
let memoryprofilerCanvas = document.getElementById("memoryprofiler_canvas")!; | |
if (memoryprofilerCanvas) | |
memoryprofilerCanvas.parentElement!.id = "memoryprofiler"; | |
} | |
function renderExample() { | |
requestAnimationFrame(renderExample); | |
// Don't go below 30 Hz to prevent spiral of death | |
var deltaTime = clock.getDelta(); | |
deltaTime = Math.min(deltaTime, 1.0 / 30.0); | |
if (onExampleUpdate != null) | |
onExampleUpdate(time, deltaTime); | |
// Update object transforms | |
for (let i = 0, il = dynamicObjects.length; i < il; i++) { | |
let objThree = dynamicObjects[i]; | |
let body = objThree.userData['body']; | |
objThree.position.copy(wrapVec3(body.GetPosition())); | |
objThree.quaternion.copy(wrapQuat(body.GetRotation())); | |
if (body.GetBodyType() == Jolt.EBodyType_SoftBody) { | |
const updateVertex: ()=>void = objThree.userData['updateVertex']; | |
if (updateVertex) { | |
updateVertex(); | |
} else { | |
objThree.geometry = createMeshForShape(body.GetShape()); | |
} | |
} | |
} | |
time += deltaTime; | |
updatePhysics(deltaTime); | |
controls.update(deltaTime); | |
renderer.render(scene, camera); | |
stats.update(); | |
} | |
export function addToThreeScene(body: JOLT_NS.Body, color: number): void { | |
let threeObject = getThreeObjectForBody(body, color); | |
threeObject.userData['body'] = body; | |
scene.add(threeObject); | |
dynamicObjects.push(threeObject); | |
} | |
export function addToScene(body: JOLT_NS.Body, color: number): void{ | |
bodyInterface.AddBody(body.GetID(), Jolt.EActivation_Activate); | |
addToThreeScene(body, color); | |
} | |
export function removeFromScene(threeObject: THREE.Mesh) { | |
let id = threeObject.userData['body'].GetID(); | |
bodyInterface.RemoveBody(id); | |
bodyInterface.DestroyBody(id); | |
delete threeObject.userData['body']; | |
scene.remove(threeObject); | |
let idx = dynamicObjects.indexOf(threeObject); | |
dynamicObjects.splice(idx, 1); | |
} | |
export function createFloor(size = 50): JOLT_NS.Body { | |
var shape = new Jolt.BoxShape(new Jolt.Vec3(size, 0.5, size), 0.05); | |
var creationSettings = new Jolt.BodyCreationSettings(shape, new Jolt.RVec3(0, -0.5, 0), new Jolt.Quat(0, 0, 0, 1), Jolt.EMotionType_Static, LAYER_NON_MOVING); | |
let body = bodyInterface.CreateBody(creationSettings); | |
Jolt.destroy(creationSettings); | |
addToScene(body, 0xc7c7c7); | |
return body; | |
} | |
export function createBox(position: JOLT_NS.RVec3, rotation: JOLT_NS.Quat, halfExtent: JOLT_NS.Vec3, motionType: JOLT_NS.EMotionType, layer: LAYER, color: number = 0xffffff): JOLT_NS.Body { | |
let shape = new Jolt.BoxShape(halfExtent, 0.05); | |
let creationSettings = new Jolt.BodyCreationSettings(shape, position, rotation, motionType, layer); | |
let body = bodyInterface.CreateBody(creationSettings); | |
Jolt.destroy(creationSettings); | |
addToScene(body, color); | |
return body; | |
} | |
export function createSphere(position: JOLT_NS.RVec3, radius: number, motionType: JOLT_NS.EMotionType, layer: LAYER, color = 0xffffff): JOLT_NS.Body { | |
let shape = new Jolt.SphereShape(radius); | |
let creationSettings = new Jolt.BodyCreationSettings(shape, position, Jolt.Quat.prototype.sIdentity(), motionType, layer); | |
let body = bodyInterface.CreateBody(creationSettings); | |
Jolt.destroy(creationSettings); | |
addToScene(body, color); | |
return body; | |
} | |
export function createMeshForShape(shape: JOLT_NS.Shape): THREE.BufferGeometry { | |
// Get triangle data | |
let scale = new Jolt.Vec3(1, 1, 1); | |
let triContext = new Jolt.ShapeGetTriangles(shape, Jolt.AABox.prototype.sBiggest(), shape.GetCenterOfMass(), Jolt.Quat.prototype.sIdentity(), scale); | |
Jolt.destroy(scale); | |
// Get a view on the triangle data (does not make a copy) | |
let vertices = new Float32Array(Jolt.HEAPF32.buffer, triContext.GetVerticesData(), triContext.GetVerticesSize() / Float32Array.BYTES_PER_ELEMENT); | |
// Now move the triangle data to a buffer and clone it so that we can free the memory from the C++ heap (which could be limited in size) | |
let buffer = new THREE.BufferAttribute(vertices, 3).clone(); | |
Jolt.destroy(triContext); | |
// Create a three mesh | |
let geometry = new THREE.BufferGeometry(); | |
geometry.setAttribute('position', buffer); | |
geometry.computeVertexNormals(); | |
return geometry; | |
} | |
function getSoftBodyMesh(body: JOLT_NS.Body, material: THREE.Material) { | |
const motionProperties = Jolt.castObject(body.GetMotionProperties(), Jolt.SoftBodyMotionProperties); | |
const vertexSettings = motionProperties.GetVertices(); | |
const settings = motionProperties.GetSettings(); | |
const positionOffset = Jolt.prototype.SoftBodyVertexTraits.prototype.mPositionOffset; | |
const faceData = settings.mFaces; | |
const softVertex: Float32Array[] = []; | |
// WARNING: The code uses direct memory mapping of properties in Jolt and makes assumptions about the memory layout. | |
function memoryMapVertex(i: number) { | |
const offset = Jolt.getPointer(vertexSettings.at(i)); | |
return new Float32Array(Jolt.HEAPF32.buffer, offset + positionOffset, 3); | |
} | |
for (let i = 0; i < faceData.size(); i++) { | |
const [v0, v1, v2] = new Uint32Array(Jolt.HEAP32.buffer, Jolt.getPointer(faceData.at(i)), 3); | |
softVertex.push(memoryMapVertex(v0)) | |
softVertex.push(memoryMapVertex(v1)) | |
softVertex.push(memoryMapVertex(v2)) | |
} | |
// Get a view on the triangle data (does not make a copy) | |
let vertices = new Float32Array(settings.mFaces.size() * 9); | |
for (let i = 0; i < softVertex.length; i++) { | |
vertices.set(softVertex[i], i * 3); | |
} | |
let buffer = new THREE.BufferAttribute(vertices, 3); | |
// Create a three mesh | |
let geometry = new THREE.BufferGeometry(); | |
geometry.setAttribute('position', buffer); | |
geometry.computeVertexNormals(); | |
material.side = THREE.DoubleSide; | |
const threeObject = new THREE.Mesh(geometry, material); | |
threeObject.userData['updateVertex'] = () => { | |
for (let i = 0; i < softVertex.length; i++) { | |
vertices.set(softVertex[i], i * 3); | |
} | |
geometry.computeVertexNormals(); | |
geometry.getAttribute('position').needsUpdate = true; | |
geometry.getAttribute('normal').needsUpdate = true; | |
} | |
return threeObject; | |
} | |
function getThreeObjectForBody(body: JOLT_NS.Body, color: number) { | |
let material = new THREE.MeshPhongMaterial({ color: color }); | |
let threeObject; | |
let shape = body.GetShape(); | |
switch (shape.GetSubType()) { | |
case Jolt.EShapeSubType_Box: | |
let boxShape = Jolt.castObject(shape, Jolt.BoxShape); | |
let extent = wrapVec3(boxShape.GetHalfExtent()).multiplyScalar(2); | |
threeObject = new THREE.Mesh(new THREE.BoxGeometry(extent.x, extent.y, extent.z, 1, 1, 1), material); | |
break; | |
case Jolt.EShapeSubType_Sphere: | |
let sphereShape = Jolt.castObject(shape, Jolt.SphereShape); | |
threeObject = new THREE.Mesh(new THREE.SphereGeometry(sphereShape.GetRadius(), 32, 32), material); | |
break; | |
case Jolt.EShapeSubType_Capsule: | |
let capsuleShape = Jolt.castObject(shape, Jolt.CapsuleShape); | |
threeObject = new THREE.Mesh(new THREE.CapsuleGeometry(capsuleShape.GetRadius(), 2 * capsuleShape.GetHalfHeightOfCylinder(), 20, 10), material); | |
break; | |
case Jolt.EShapeSubType_Cylinder: | |
let cylinderShape = Jolt.castObject(shape, Jolt.CylinderShape); | |
threeObject = new THREE.Mesh(new THREE.CylinderGeometry(cylinderShape.GetRadius(), cylinderShape.GetRadius(), 2 * cylinderShape.GetHalfHeight(), 20, 1), material); | |
break; | |
default: | |
if (body.GetBodyType() == Jolt.EBodyType_SoftBody) | |
threeObject = getSoftBodyMesh(body, material); | |
else | |
threeObject = new THREE.Mesh(createMeshForShape(shape), material); | |
break; | |
} | |
threeObject.position.copy(wrapRVec3(body.GetPosition())); | |
threeObject.quaternion.copy(wrapQuat(body.GetRotation())); | |
return threeObject; | |
} | |
export function createMeshFloor(n: number, cellSize: number, maxHeight: number, posX: number, posY: number, posZ: number) { | |
// Create regular grid of triangles | |
let height = function (x: number, y: number) { return Math.sin(x / 2) * Math.cos(y / 3); }; | |
let triangles = new Jolt.TriangleList; | |
triangles.resize(n * n * 2); | |
for (let x = 0; x < n; ++x) | |
for (let z = 0; z < n; ++z) { | |
let center = n * cellSize / 2; | |
let x1 = cellSize * x - center; | |
let z1 = cellSize * z - center; | |
let x2 = x1 + cellSize; | |
let z2 = z1 + cellSize; | |
{ | |
let t = triangles.at((x * n + z) * 2); | |
let v1 = t.get_mV(0), v2 = t.get_mV(1), v3 = t.get_mV(2); | |
v1.x = x1, v1.y = height(x, z), v1.z = z1; | |
v2.x = x1, v2.y = height(x, z + 1), v2.z = z2; | |
v3.x = x2, v3.y = height(x + 1, z + 1), v3.z = z2; | |
} | |
{ | |
let t = triangles.at((x * n + z) * 2 + 1); | |
let v1 = t.get_mV(0), v2 = t.get_mV(1), v3 = t.get_mV(2); | |
v1.x = x1, v1.y = height(x, z), v1.z = z1; | |
v2.x = x2, v2.y = height(x + 1, z + 1), v2.z = z2; | |
v3.x = x2, v3.y = height(x + 1, z), v3.z = z1; | |
} | |
} | |
let materials = new Jolt.PhysicsMaterialList; | |
let shape = new Jolt.MeshShapeSettings(triangles, materials).Create().Get(); | |
Jolt.destroy(triangles); | |
Jolt.destroy(materials); | |
// Create body | |
let creationSettings = new Jolt.BodyCreationSettings(shape, new Jolt.RVec3(posX, posY, posZ), new Jolt.Quat(0, 0, 0, 1), Jolt.EMotionType_Static, LAYER_NON_MOVING); | |
let body = bodyInterface.CreateBody(creationSettings); | |
Jolt.destroy(creationSettings); | |
addToScene(body, 0xc7c7c7); | |
} | |
export function createVehicleTrack() { | |
const track = [ | |
[[[38, 64, -14], [38, 64, -16], [38, -64, -16], [38, -64, -14], [64, -64, -16], [64, -64, -14], [64, 64, -16], [64, 64, -14]], [[-16, 64, -14], [-16, 64, -16], [-16, -64, -16], [-16, -64, -14], [10, -64, -16], [10, -64, -14], [10, 64, -16], [10, 64, -14]], [[10, -48, -14], [10, -48, -16], [10, -64, -16], [10, -64, -14], [38, -64, -16], [38, -64, -14], [38, -48, -16], [38, -48, -14]], [[10, 64, -14], [10, 64, -16], [10, 48, -16], [10, 48, -14], [38, 48, -16], [38, 48, -14], [38, 64, -16], [38, 64, -14]]], | |
[[[38, 48, -10], [38, 48, -14], [38, -48, -14], [38, -48, -10], [40, -48, -14], [40, -48, -10], [40, 48, -14], [40, 48, -10]], [[62, 62, -10], [62, 62, -14], [62, -64, -14], [62, -64, -10], [64, -64, -14], [64, -64, -10], [64, 62, -14], [64, 62, -10]], [[8, 48, -10], [8, 48, -14], [8, -48, -14], [8, -48, -10], [10, -48, -14], [10, -48, -10], [10, 48, -14], [10, 48, -10]], [[-16, 62, -10], [-16, 62, -14], [-16, -64, -14], [-16, -64, -10], [-14, -64, -14], [-14, -64, -10], [-14, 62, -14], [-14, 62, -10]], [[-14, -62, -10], [-14, -62, -14], [-14, -64, -14], [-14, -64, -10], [62, -64, -14], [62, -64, -10], [62, -62, -14], [62, -62, -10]], [[8, -48, -10], [8, -48, -14], [8, -50, -14], [8, -50, -10], [40, -50, -14], [40, -50, -10], [40, -48, -14], [40, -48, -10]], [[8, 50, -10], [8, 50, -14], [8, 48, -14], [8, 48, -10], [40, 48, -14], [40, 48, -10], [40, 50, -14], [40, 50, -10]], [[-16, 64, -10], [-16, 64, -14], [-16, 62, -14], [-16, 62, -10], [64, 62, -14], [64, 62, -10], [64, 64, -14], [64, 64, -10]]], | |
[[[-4, 22, -14], [-4, -14, -14], [-4, -14, -10], [4, -14, -14], [4, -14, -10], [4, 22, -14]], [[-4, -27, -14], [-4, -48, -14], [-4, -48, -11], [4, -48, -14], [4, -48, -11], [4, -27, -14]], [[-4, 50, -14], [-4, 30, -14], [-4, 30, -12], [4, 30, -14], [4, 30, -12], [4, 50, -14]], [[46, 50, -14], [46, 31, -14], [46, 50, -12], [54, 31, -14], [54, 50, -12], [54, 50, -14]], [[46, 16, -14], [46, -19, -14], [46, 16, -10], [54, -19, -14], [54, 16, -10], [54, 16, -14]], [[46, -28, -14], [46, -48, -14], [46, -28, -11], [54, -48, -14], [54, -28, -11], [54, -28, -14]]] | |
]; | |
const mapColors = [0x666666, 0x006600, 0x000066]; | |
let tempVec = new Jolt.Vec3(0, 1, 0); | |
const mapRot = Jolt.Quat.prototype.sRotation(tempVec, 0.5 * Math.PI); | |
let tempRVec = new Jolt.RVec3(0, 0, 0); | |
track.forEach((type, tIdx) => { | |
type.forEach(block => { | |
const hull = new Jolt.ConvexHullShapeSettings; | |
block.forEach(v => { | |
tempVec.Set(-v[1], v[2], v[0]); | |
hull.mPoints.push_back(tempVec); | |
}); | |
const shape = hull.Create().Get(); | |
tempRVec.Set(0, 10, 0); | |
const creationSettings = new Jolt.BodyCreationSettings(shape, tempRVec, mapRot, Jolt.EMotionType_Static, LAYER_NON_MOVING); | |
Jolt.destroy(hull); | |
const body = bodyInterface.CreateBody(creationSettings); | |
Jolt.destroy(creationSettings); | |
body.SetFriction(1.0); | |
addToScene(body, mapColors[tIdx]); | |
}); | |
}); | |
Jolt.destroy(tempVec); | |
Jolt.destroy(tempRVec); | |
} | |
export function addLine(from: JOLT_NS.RVec3, to: JOLT_NS.RVec3, color: number): void { | |
const material = new THREE.LineBasicMaterial({ color: color }); | |
const points = []; | |
points.push(wrapRVec3(from)); | |
points.push(wrapRVec3(to)); | |
const geometry = new THREE.BufferGeometry().setFromPoints(points); | |
const line = new THREE.Line(geometry, material); | |
scene.add(line); | |
} | |
export function addMarker(location: JOLT_NS.Vec3, size: number, color: number): void { | |
const material = new THREE.LineBasicMaterial({ color: color }); | |
const points = []; | |
const center = wrapVec3(location); | |
points.push(center.clone().add(new THREE.Vector3(-size, 0, 0))); | |
points.push(center.clone().add(new THREE.Vector3(size, 0, 0))); | |
points.push(center.clone().add(new THREE.Vector3(0, -size, 0))); | |
points.push(center.clone().add(new THREE.Vector3(0, size, 0))); | |
points.push(center.clone().add(new THREE.Vector3(0, 0, -size))); | |
points.push(center.clone().add(new THREE.Vector3(0, 0, size))); | |
const geometry = new THREE.BufferGeometry().setFromPoints(points); | |
const line = new THREE.LineSegments(geometry, material); | |
scene.add(line); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class WebGL { | |
static isWebGLAvailable() { | |
try { | |
const canvas = document.createElement( 'canvas' ); | |
return !! ( window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ) ); | |
} catch ( e ) { | |
return false; | |
} | |
} | |
static isWebGL2Available() { | |
try { | |
const canvas = document.createElement( 'canvas' ); | |
return !! ( window.WebGL2RenderingContext && canvas.getContext( 'webgl2' ) ); | |
} catch ( e ) { | |
return false; | |
} | |
} | |
static getWebGLErrorMessage() { | |
return this.getErrorMessage( 1 ); | |
} | |
static getWebGL2ErrorMessage() { | |
return this.getErrorMessage( 2 ); | |
} | |
static getErrorMessage( version: 1|2 ) { | |
const names = { | |
1: 'WebGL', | |
2: 'WebGL 2' | |
}; | |
const contexts = { | |
1: window.WebGLRenderingContext, | |
2: window.WebGL2RenderingContext | |
}; | |
let message = 'Your $0 does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation" style="color:#000">$1</a>'; | |
const element = document.createElement( 'div' ); | |
element.id = 'webglmessage'; | |
element.style.fontFamily = 'monospace'; | |
element.style.fontSize = '13px'; | |
element.style.fontWeight = 'normal'; | |
element.style.textAlign = 'center'; | |
element.style.background = '#fff'; | |
element.style.color = '#000'; | |
element.style.padding = '1.5em'; | |
element.style.width = '400px'; | |
element.style.margin = '5em auto 0'; | |
if ( contexts[ version ] ) { | |
message = message.replace( '$0', 'graphics card' ); | |
} else { | |
message = message.replace( '$0', 'browser' ); | |
} | |
message = message.replace( '$1', names[ version ] ); | |
element.innerHTML = message; | |
return element; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment