773 lines
20 KiB
JavaScript
773 lines
20 KiB
JavaScript
importScripts("../lib/improved-noise.js");
|
|
importScripts("../lib/cannon-es.js");
|
|
importScripts("../lib/ammo.js");
|
|
|
|
var neighborData = [
|
|
{
|
|
"position": [-1, 0, 0],
|
|
"corners": [
|
|
[0, 0, 0, 0, 0],
|
|
[0, 0, 1, 1, 0],
|
|
[0, 1, 0, 0, 1],
|
|
[0, 1, 1, 1, 1]
|
|
]
|
|
},
|
|
{
|
|
"position": [1, 0, 0],
|
|
"corners": [
|
|
[1, 0, 1, 0, 0],
|
|
[1, 0, 0, 1, 0],
|
|
[1, 1, 1, 0, 1],
|
|
[1, 1, 0, 1, 1]
|
|
]
|
|
},
|
|
{
|
|
"position": [0, -1, 0],
|
|
"corners": [
|
|
[0, 0, 0, 0, 1],
|
|
[1, 0, 0, 1, 1],
|
|
[0, 0, 1, 0, 0],
|
|
[1, 0, 1, 1, 0]
|
|
]
|
|
},
|
|
{
|
|
"position": [0, 1, 0],
|
|
"corners": [
|
|
[0, 1, 1, 0, 0],
|
|
[1, 1, 1, 1, 0],
|
|
[0, 1, 0, 0, 1],
|
|
[1, 1, 0, 1, 1]
|
|
]
|
|
},
|
|
{
|
|
"position": [0, 0, -1],
|
|
"corners": [
|
|
[1, 0, 0, 0, 0],
|
|
[0, 0, 0, 1, 0],
|
|
[1, 1, 0, 0, 1],
|
|
[0, 1, 0, 1, 1]
|
|
]
|
|
},
|
|
{
|
|
"position": [0, 0, 1],
|
|
"corners": [
|
|
[0, 0, 1, 0, 0],
|
|
[1, 0, 1, 1, 0],
|
|
[0, 1, 1, 0, 1],
|
|
[1, 1, 1, 1, 1]
|
|
]
|
|
}
|
|
];
|
|
|
|
let worldConfig = {
|
|
"chunkSize": [16, 16, 16]
|
|
};
|
|
|
|
let textureData = {
|
|
"resolution": [16, 16],
|
|
"size": [256, 256],
|
|
"blocks": {
|
|
|
|
}
|
|
};
|
|
|
|
var tasks = {
|
|
"physics": {
|
|
"running": false,
|
|
"interval": setInterval(async function () {
|
|
if (!tasks["physics"].running && (tasks["physics"].queue.length > 0)) {
|
|
tasks["physics"].running = true;
|
|
while (tasks["physics"].queue.length > 0) {
|
|
tasks["physics"].queue[0].run(...tasks["physics"].queue[0].arguments);
|
|
tasks["physics"].queue.splice(0, 1);
|
|
}
|
|
tasks["physics"].running = false;
|
|
}
|
|
}, 256),
|
|
"queue": []
|
|
}
|
|
};
|
|
|
|
var ammoIsReady = false;
|
|
|
|
let world;
|
|
|
|
var worlds = {};
|
|
|
|
var bodies = {};
|
|
|
|
var onmessage = function (event) {
|
|
var command = event.data["command"];
|
|
var arguments = event.data["arguments"];
|
|
|
|
var callbackID = arguments["callbackID"];
|
|
|
|
switch (command) {
|
|
case "config":
|
|
if (arguments["world"]) worldConfig = arguments["world"];
|
|
textureData = arguments["textureData"];
|
|
break;
|
|
case "generate":
|
|
var generator = self.generators[arguments["generator"]];
|
|
var data = arguments["data"];
|
|
|
|
if (!generator) return postMessage({
|
|
"event": "generated",
|
|
"data": {
|
|
"callbackID": callbackID,
|
|
"blocks": []
|
|
}
|
|
});
|
|
|
|
var position = arguments["position"];
|
|
|
|
var blocks = [];
|
|
|
|
var chunkWidth = worldConfig.chunkSize[0];
|
|
var chunkHeight = worldConfig.chunkSize[1];
|
|
var chunkDepth = worldConfig.chunkSize[2];
|
|
|
|
switch (generator.type) {
|
|
case "stepped":
|
|
for (var x = 0; x < chunkWidth; x++) {
|
|
for (var z = 0; z < chunkDepth; z++) {
|
|
for (var y = 0; y < chunkHeight; y++) {
|
|
var block = generator.run([
|
|
(position[0] * chunkWidth) + x,
|
|
(position[1] * chunkHeight) + y,
|
|
(position[2] * chunkDepth) + z
|
|
], data, worldConfig.chunkSize);
|
|
var blockIndex = computeBlockIndex(
|
|
worldConfig.chunkSize,
|
|
[
|
|
x,
|
|
y,
|
|
z
|
|
]
|
|
);
|
|
|
|
if (block) blocks[blockIndex] = block;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case "full":
|
|
var blocks = generator.run(position, data, worldConfig.chunkSize);
|
|
break;
|
|
default:
|
|
|
|
}
|
|
|
|
postMessage({
|
|
"event": "generated",
|
|
"data": {
|
|
"callbackID": callbackID,
|
|
"blocks": blocks
|
|
}
|
|
});
|
|
break;
|
|
case "construct":
|
|
var chunk = arguments["chunk"];
|
|
var neighbors = arguments["neighbors"];
|
|
|
|
postMessage({
|
|
"event": "constructed",
|
|
"data": {
|
|
"callbackID": callbackID,
|
|
"properties": construct(chunk, neighbors)
|
|
}
|
|
});
|
|
break;
|
|
case "create-world":
|
|
// worlds[arguments["uuid"]] = new CANNON.World({
|
|
// "allowSleep": arguments["allowSleep"] || true,
|
|
// "gravity": ("gravity" in arguments) ? new CANNON.Vec3(
|
|
// arguments["gravity"][0],
|
|
// arguments["gravity"][1],
|
|
// arguments["gravity"][2]
|
|
// ) : new CANNON.Vec3(0, -9.82, 0),
|
|
// "solver": new CANNON.GSSolver()
|
|
// });
|
|
|
|
var collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
|
|
var dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
|
|
var overlappingPairCache = new Ammo.btDbvtBroadphase();
|
|
var solver = new Ammo.btSequentialImpulseConstraintSolver();
|
|
|
|
world = new Ammo.btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
|
|
world.setGravity(new Ammo.btVector3(0, -9.82, 0));
|
|
|
|
postMessage({
|
|
"event": "world-created",
|
|
"data": {
|
|
"callbackID": callbackID
|
|
}
|
|
});
|
|
break;
|
|
case "create-body":
|
|
let bodyType;
|
|
|
|
switch (arguments["type"]) {
|
|
case "dynamic":
|
|
bodyType = CANNON.Body.DYNAMIC;
|
|
break;
|
|
case "static":
|
|
bodyType = CANNON.Body.STATIC;
|
|
break;
|
|
case "kinematic":
|
|
bodyType = CANNON.Body.KINEMATIC;
|
|
break;
|
|
default:
|
|
bodyType = CANNON.Body.DYNAMIC;
|
|
}
|
|
|
|
var bodyOptions = {
|
|
"allowSleep": arguments["allowSleep"] || true,
|
|
"fixedRotation": arguments["fixedRotation"] || false,
|
|
"mass": arguments["mass"] || 0,
|
|
"position": ("position" in arguments) ? new CANNON.Vec3(
|
|
arguments["position"][0],
|
|
arguments["position"][1],
|
|
arguments["position"][2]
|
|
) : new CANNON.Vec3(0, 0, 0),
|
|
"quaternion": ("quaternion" in arguments) ? new CANNON.Quaternion(
|
|
arguments["quaternion"][0],
|
|
arguments["quaternion"][1],
|
|
arguments["quaternion"][2],
|
|
arguments["quaternion"][3]
|
|
) : new CANNON.Quaternion(0, 0, 0, 1),
|
|
"type": bodyType,
|
|
"material": new CANNON.Material()
|
|
/*"shape": ("shape" in arguments) ? new CANNON.ConvexPolyhedron({
|
|
"vertices": (function (positions) {
|
|
var vertices = [];
|
|
|
|
for (var v = 0; v < (positions.length / 3); v++) {
|
|
var vertexOffset = v * 3;
|
|
|
|
vertices[v] = new CANNON.Vec3(
|
|
positions[vertexOffset + 0],
|
|
positions[vertexOffset + 1],
|
|
positions[vertexOffset + 2]
|
|
);
|
|
}
|
|
|
|
return vertices;
|
|
})(arguments["shape"].vertices),
|
|
"faces": (function (indices) {
|
|
var faces = [];
|
|
|
|
for (var f = 0; f < (indices.length / 3); f++) {
|
|
var faceOffset = f * 3;
|
|
|
|
faces[f] = indices.slice(faceOffset, faceOffset + 3);
|
|
}
|
|
|
|
return faces;
|
|
})(arguments["shape"].indices)
|
|
}) : new CANNON.Box(new CANNON.Vec3(1, 1, 1))*/
|
|
};
|
|
|
|
var shape = ("shape" in arguments) ? new CANNON.Trimesh(
|
|
arguments["shape"].vertices,
|
|
arguments["shape"].indices
|
|
) : new CANNON.Box(new CANNON.Vec3(1, 1, 1));
|
|
|
|
postMessage({
|
|
"event": "body-created",
|
|
"data": {
|
|
"callbackID": callbackID
|
|
}
|
|
});
|
|
|
|
bodies[arguments["uuid"]] = (shape instanceof CANNON.Trimesh) ? trimeshToPolyhedron(bodyOptions, shape) : new CANNON.Body(bodyOptions);
|
|
|
|
if (shape instanceof CANNON.Box) bodies[arguments["uuid"]].addShape(shape);
|
|
|
|
bodies[arguments["uuid"]].uuid = arguments["uuid"];
|
|
|
|
break;
|
|
case "set-body":
|
|
if ("shape" in arguments) {
|
|
var convexPolyhedronBody = trimeshToPolyhedron({}, new CANNON.Trimesh(
|
|
arguments["shape"].vertices,
|
|
arguments["shape"].indices
|
|
));
|
|
|
|
for (var s = bodies[arguments["uuid"]].shapes.length - 1; s >= 0; s--) {
|
|
bodies[arguments["uuid"]].removeShape(bodies[arguments["uuid"]].shapes[s]);
|
|
}
|
|
|
|
for (var s = 0; s < convexPolyhedronBody.shapes.length; s++) {
|
|
bodies[arguments["uuid"]].addShape(convexPolyhedronBody.shapes[s]);
|
|
}
|
|
|
|
bodies[arguments["uuid"]].updateBoundingRadius();
|
|
bodies[arguments["uuid"]].updateAABB();
|
|
/*bodies[arguments["uuid"]].shapes[0] = new CANNON.ConvexPolyhedron({
|
|
"vertices": (function (positions) {
|
|
var vertices = [];
|
|
|
|
for (var v = 0; v < (positions.length / 3); v++) {
|
|
var vertexOffset = v * 3;
|
|
|
|
vertices[v] = new CANNON.Vec3(
|
|
positions[vertexOffset + 0],
|
|
positions[vertexOffset + 1],
|
|
positions[vertexOffset + 2]
|
|
);
|
|
}
|
|
|
|
return vertices;
|
|
})(arguments["shape"].vertices),
|
|
"faces": (function (indices) {
|
|
var faces = [];
|
|
|
|
for (var f = 0; f < (indices.length / 3); f++) {
|
|
var faceOffset = f * 3;
|
|
|
|
faces[f] = indices.slice(faceOffset, faceOffset + 3);
|
|
}
|
|
|
|
return faces;
|
|
})(arguments["shape"].indices)
|
|
});*/
|
|
}
|
|
|
|
if ("position" in arguments) {
|
|
bodies[arguments["uuid"]].position.set(
|
|
arguments["position"][0],
|
|
arguments["position"][1],
|
|
arguments["position"][2]
|
|
);
|
|
bodies[arguments["uuid"]].aabbNeedsUpdate = true;
|
|
}
|
|
|
|
if ("quaternion" in arguments) {
|
|
bodies[arguments["uuid"]].quaternion.set(
|
|
arguments["quaternion"][0],
|
|
arguments["quaternion"][1],
|
|
arguments["quaternion"][2],
|
|
arguments["quaternion"][3]
|
|
);
|
|
}
|
|
break;
|
|
case "add-body":
|
|
worlds[arguments["worldUUID"]].addBody(bodies[arguments["bodyUUID"]]);
|
|
|
|
postMessage({
|
|
"event": "body-added",
|
|
"data": {
|
|
"callbackID": callbackID
|
|
}
|
|
});
|
|
break;
|
|
case "step-simulation":
|
|
worlds[arguments["uuid"]].step(arguments["delta"]);
|
|
|
|
var updates = {};
|
|
|
|
for (var b = 0; b < worlds[arguments["uuid"]].bodies.length; b++) {
|
|
var body = worlds[arguments["uuid"]].bodies[b];
|
|
|
|
updates[body.uuid] = {
|
|
"position": body.position.toArray(),
|
|
"quaternion": body.quaternion.toArray()
|
|
}
|
|
}
|
|
|
|
postMessage({
|
|
"event": "simulation-stepped",
|
|
"data": {
|
|
"callbackID": callbackID,
|
|
"updates": updates
|
|
}
|
|
});
|
|
break;
|
|
case "remove-body":
|
|
worlds[arguments["worldUUID"]].removeBody(bodies[arguments["bodyUUID"]]);
|
|
|
|
postMessage({
|
|
"event": "body-removed",
|
|
"data": {
|
|
"callbackID": callbackID
|
|
}
|
|
});
|
|
break;
|
|
case "destroy-body":
|
|
delete bodies[arguments["uuid"]];
|
|
|
|
postMessage({
|
|
"event": "body-destroyed",
|
|
"data": {
|
|
"callbackID": callbackID
|
|
}
|
|
});
|
|
break;
|
|
case "put-voxels":
|
|
tasks["physics"].queue.push({
|
|
"run": function (args) {
|
|
var worldUUID = args["worldUUID"];
|
|
var chunkUUID = args["chunkUUID"];
|
|
var chunkPosition = args["chunkPosition"];
|
|
var voxelIndices = args["indices"];
|
|
|
|
var bodyOptions = {
|
|
"mass": 0,
|
|
"type": CANNON.Body.STATIC,
|
|
"material": new CANNON.Material(),
|
|
"position": new CANNON.Vec3(
|
|
chunkPosition[0] * worldConfig.chunkSize[0],
|
|
chunkPosition[1] * worldConfig.chunkSize[1],
|
|
chunkPosition[2] * worldConfig.chunkSize[2]
|
|
)
|
|
};
|
|
|
|
var chunk = bodies[chunkUUID] = new CANNON.Body(bodyOptions);
|
|
|
|
var shapes = [];
|
|
|
|
for (var i = 0; i < voxelIndices.length; i++) {
|
|
var shape = shapes[i] = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5));
|
|
var shapePosition = computeBlockPosition(worldConfig.chunkSize, voxelIndices[i]);
|
|
|
|
chunk.addShape(shape, new CANNON.Vec3(
|
|
shapePosition[0],
|
|
shapePosition[1],
|
|
shapePosition[2]
|
|
));
|
|
}
|
|
|
|
worlds[worldUUID].addBody(chunk);
|
|
},
|
|
"arguments": [arguments]
|
|
});
|
|
break;
|
|
case "remove-voxels":
|
|
tasks["physics"].queue.push({
|
|
"run": function (args) {
|
|
var worldUUID = args["worldUUID"];
|
|
var chunkUUID = args["chunkUUID"];
|
|
|
|
worlds[worldUUID].removeBody(bodies[chunkUUID]);
|
|
|
|
delete bodies[chunkUUID];
|
|
},
|
|
"arguments": [arguments]
|
|
});
|
|
break;
|
|
case "destroy-world":
|
|
delete worlds[arguments["uuid"]];
|
|
|
|
postMessage({
|
|
"event": "world-destroyed",
|
|
"data": {
|
|
"callbackID": callbackID
|
|
}
|
|
});
|
|
break;
|
|
default:
|
|
|
|
}
|
|
};
|
|
|
|
self.generators = {
|
|
"flat": {
|
|
"type": "stepped",
|
|
"run": function (position, data = {}) {
|
|
if (position[1] >= 0 && position[1] <= 5) return ["glitch", {}];
|
|
if (position[1] >= 6 && position[1] <= 57) return ["stone", {}];
|
|
if (position[1] >= 58 && position[1] <= 62) return ["dirt", {}];
|
|
if (position[1] == 63) return ["grass", {}];
|
|
}
|
|
},
|
|
"sphere": {
|
|
"type": "stepped",
|
|
"run": function (position, data = {}) {
|
|
var mustGenerate = ((position[0] ** 2) + (position[1] ** 2) + (position[2] ** 2)) <= 16 * 16;
|
|
|
|
if (mustGenerate) return ["stone", {}];
|
|
}
|
|
},
|
|
"xr-flat": {
|
|
"type": "stepped",
|
|
"run": function (position, data = {}) {
|
|
if (position[1] >= 0 && position[1] <= 1) return ["glitch", {}];
|
|
if (position[1] >= 2 && position[1] <= 4) return ["stone", {}];
|
|
if (position[1] >= 5 && position[1] <= 6) return ["dirt", {}];
|
|
if (position[1] == 7) return ["grass", {}];
|
|
}
|
|
},
|
|
"terrain": {
|
|
"type": "full",
|
|
"run": function (position, data = {}, chunkSize) {
|
|
var chunkWidth = chunkSize[0];
|
|
var chunkHeight = chunkSize[1];
|
|
var chunkDepth = chunkSize[2];
|
|
|
|
var blocks = new Array(chunkSize[0] * chunkSize[1] * chunkSize[2]),
|
|
perlin = new ImprovedNoise(),
|
|
averageY = data["averageY"] || 64;
|
|
|
|
let quality = 25;
|
|
|
|
for (var x = 0; x < chunkWidth; x++) {
|
|
for (var z = 0; z < chunkDepth; z++) {
|
|
var absoluteCoords = [
|
|
(position[0] * chunkWidth) + x,
|
|
(position[2] * chunkDepth) + z
|
|
];
|
|
|
|
var maxY = Math.round(
|
|
perlin.noise(
|
|
absoluteCoords[0] / quality,
|
|
averageY / quality,
|
|
absoluteCoords[1] / quality
|
|
) * quality
|
|
) + averageY;
|
|
|
|
for (var y = 0; y < chunkHeight; y++) {
|
|
var absoluteY = (position[1] * chunkHeight) + y;
|
|
|
|
if (absoluteY >= (position[1] * chunkHeight)
|
|
&& absoluteY <= ((position[1] * chunkHeight) + chunkHeight)) {
|
|
if (absoluteY >= 0 && absoluteY <= 3) {
|
|
blocks[computeBlockIndex(worldConfig.chunkSize, [
|
|
x, y, z
|
|
])] = ["glitch", {}];
|
|
} else if (absoluteY >= 4 && absoluteY <= (maxY - 4)) {
|
|
blocks[computeBlockIndex(worldConfig.chunkSize, [
|
|
x, y, z
|
|
])] = ["stone", {}];
|
|
} else if (absoluteY >= (maxY - 3) && absoluteY <= (maxY - 1)) {
|
|
blocks[computeBlockIndex(worldConfig.chunkSize, [
|
|
x, y, z
|
|
])] = ["dirt", {}];
|
|
} else if (absoluteY == maxY) {
|
|
blocks[computeBlockIndex(worldConfig.chunkSize, [
|
|
x, y, z
|
|
])] = ["grass", {}];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return blocks;
|
|
}
|
|
}
|
|
};
|
|
|
|
var construct = function (chunk, neighbors, chunkSize) {
|
|
var positions = [];
|
|
var normals = [];
|
|
var uvs = [];
|
|
var indexes = [];
|
|
let texUV = [];
|
|
|
|
for (var b = 0; b < chunk.blocks.length; b++) {
|
|
var blockPosition = computeBlockPosition(worldConfig.chunkSize, b);
|
|
|
|
if (chunk.blocks[b]) {
|
|
var type = chunk.blocks[b][0];
|
|
|
|
var blockTexData = textureData.textures[type];
|
|
|
|
for (var n = 0; n < neighborData.length; n++) {
|
|
var neighbor = neighborData[n];
|
|
|
|
var adjacentPosition = addPositions(blockPosition, neighbor.position);
|
|
|
|
let adjacentInNeighbor = false;
|
|
if (
|
|
adjacentPosition[0] < 0 || adjacentPosition[0] >= worldConfig.chunkSize[0] ||
|
|
adjacentPosition[1] < 0 || adjacentPosition[1] >= worldConfig.chunkSize[1] ||
|
|
adjacentPosition[2] < 0 || adjacentPosition[2] >= worldConfig.chunkSize[2]
|
|
) adjacentInNeighbor = true;
|
|
|
|
let hasAdjacent = true;
|
|
if (adjacentInNeighbor) {
|
|
if (!neighbors[n]) hasAdjacent = false;
|
|
|
|
var positionInNeighbor = neighborPosition(adjacentPosition, worldConfig.chunkSize);
|
|
var indexInNeighbor = computeBlockIndex(worldConfig.chunkSize, positionInNeighbor);
|
|
|
|
if (!neighbors[n] || !neighbors[n].blocks[indexInNeighbor]) hasAdjacent = false;
|
|
} else {
|
|
var blockIndex = computeBlockIndex(worldConfig.chunkSize, adjacentPosition);
|
|
if (!chunk.blocks[blockIndex]) hasAdjacent = false;
|
|
}
|
|
|
|
if (!hasAdjacent) {
|
|
var index = positions.length / 3;
|
|
|
|
if (blockTexData.sided) {
|
|
let sideMap;
|
|
|
|
for (var sm = 0; sm < blockTexData.sideMaps.length; sm++) {
|
|
if (blockTexData.sideMaps[sm].includes(n)) sideMap = sm;
|
|
}
|
|
|
|
texUV[0] = blockTexData.positions[sideMap][0] / blockTexData.sizes[sideMap][0];
|
|
texUV[1] = blockTexData.positions[sideMap][1] / blockTexData.sizes[sideMap][1];
|
|
} else {
|
|
texUV[0] = blockTexData.position[0] / blockTexData.size[0];
|
|
texUV[1] = blockTexData.position[1] / blockTexData.size[1];
|
|
}
|
|
var sideTexData = blockTexData[n];
|
|
|
|
// texUV[0] = sideTexData.end[0] - sideTexData.start[0];
|
|
// texUV[1] = sideTexData.end[1] - sideTexData.start[1];
|
|
|
|
for (var c = 0; c < neighbor.corners.length; c++) {
|
|
var corner = neighbor.corners[c];
|
|
|
|
var position = addPositions(blockPosition, corner);
|
|
|
|
positions.push(
|
|
position[0],
|
|
position[1],
|
|
position[2]
|
|
);
|
|
normals.push(
|
|
neighbor.position[0],
|
|
neighbor.position[1],
|
|
neighbor.position[2]
|
|
);
|
|
|
|
var uv = corner.slice(3, 5);
|
|
|
|
uvs.push(
|
|
(texUV[0] + uv[0]) *
|
|
textureData.resolution[0] /
|
|
textureData.size[0],
|
|
1 - (texUV[1] + 1 - uv[1]) *
|
|
textureData.resolution[1] /
|
|
textureData.size[1]
|
|
);
|
|
}
|
|
|
|
indexes.push(
|
|
index + 0, index + 1, index + 2,
|
|
index + 2, index + 1, index + 3
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return {positions, normals, uvs, indexes};
|
|
};
|
|
|
|
var computeBlockIndex = function (chunkSize, blockPosition) {
|
|
var x = blockPosition[0] % chunkSize[0];
|
|
var z = blockPosition[2] % chunkSize[2];
|
|
var y = blockPosition[1] % chunkSize[1];
|
|
|
|
return (x * (chunkSize[1] * chunkSize[2])) + y + (z * chunkSize[1]);
|
|
};
|
|
|
|
var computeBlockPosition = function (chunkSize, blockIndex) {
|
|
var blockPosition = [0, 0, 0];
|
|
var subtractedIndex = 0 + blockIndex;
|
|
|
|
blockPosition[1] = subtractedIndex % chunkSize[1];
|
|
subtractedIndex -= blockPosition[1];
|
|
|
|
blockPosition[2] = (subtractedIndex / chunkSize[1]) % chunkSize[2];
|
|
subtractedIndex -= blockPosition[2] * chunkSize[1];
|
|
|
|
blockPosition[0] = (subtractedIndex / (chunkSize[1] * chunkSize[2])) % chunkSize[0];
|
|
subtractedIndex -= blockPosition[0] * chunkSize[0];
|
|
|
|
return blockPosition;
|
|
};
|
|
|
|
var neighborPosition = function (position, chunkSize) {
|
|
var chunkPosition = position.slice(0, 3);
|
|
|
|
if (position[0] < 0) chunkPosition[0] = chunkSize[0] + position[0];
|
|
if (position[0] >= chunkSize[0]) chunkPosition[0] = position[0] - chunkSize[0];
|
|
if (position[1] < 0) chunkPosition[1] = chunkSize[1] + position[1];
|
|
if (position[1] >= chunkSize[1]) chunkPosition[1] = position[1] - chunkSize[1];
|
|
if (position[2] < 0) chunkPosition[2] = chunkSize[2] + position[2];
|
|
if (position[2] >= chunkSize[2]) chunkPosition[2] = position[2] - chunkSize[2];
|
|
|
|
return chunkPosition;
|
|
};
|
|
|
|
var addPositions = function (positionA, positionB) {
|
|
var position = new Array(positionA.length);
|
|
|
|
for (var p = 0; p < position.length; p++) {
|
|
position[p] = positionA[p] + positionB[p];
|
|
}
|
|
|
|
return position;
|
|
};
|
|
|
|
// Thanks to https://github.com/pmndrs/cannon-es/issues/21 (I slightly modified it)
|
|
var trimeshToPolyhedron = function (bodyOptions, trimesh, upvector) {
|
|
let p1 = new CANNON.Vec3(),
|
|
p2 = new CANNON.Vec3(),
|
|
p3 = new CANNON.Vec3(),
|
|
p4 = new CANNON.Vec3(),
|
|
mp = new CANNON.Vec3(),
|
|
tmp = new CANNON.Vec3(),
|
|
e1 = new CANNON.Vec3(),
|
|
e2 = new CANNON.Vec3();
|
|
|
|
const body = new CANNON.Body(bodyOptions);
|
|
|
|
for (let i = 0; i < trimesh.indices.length / 3; i++) {
|
|
mp.set(0, 0, 0);
|
|
trimesh.getTriangleVertices(i, p1, p2, p3);
|
|
trimesh.getNormal(i, p4);
|
|
// if (upvector && p4.dot(upvector) < 0) p4.scale(-1, p4);
|
|
p4.normalize();
|
|
mp = mp.vadd(p1).vadd(p2).vadd(p3).scale(1/3);
|
|
const vertices = [
|
|
new CANNON.Vec3().copy(p1),
|
|
new CANNON.Vec3().copy(p2),
|
|
new CANNON.Vec3().copy(p3),
|
|
mp.vadd(p4.scale(-1 / 2))
|
|
];
|
|
const faces = [
|
|
[0, 1, 2],
|
|
[0, 3, 1],
|
|
[1, 3, 2],
|
|
[2, 3, 0]
|
|
];
|
|
const normals = [
|
|
new CANNON.Vec3().copy(p4)
|
|
];
|
|
for (let j = 0; j < 3; j++) {
|
|
vertices[j].vsub(vertices[(j + 1) % 3], e1);
|
|
vertices[(j + 1) % 3].vsub(p4, e2);
|
|
tmp.set(0, 0, 0);
|
|
const points = faces[j + 1];
|
|
for (let p = 0; p < points.length; p++) {
|
|
tmp.vadd(vertices[points[p]], tmp);
|
|
}
|
|
tmp.scale(1 / points.length, tmp);
|
|
const normal = e1.cross(e2);
|
|
normal.normalize();
|
|
normal.scale(-1, normal);
|
|
const angle = normal.dot(tmp);
|
|
|
|
if(angle < 0) normal.scale(-1, normal);
|
|
normals.push(normal);
|
|
}
|
|
const polyhedron = new CANNON.ConvexPolyhedron({vertices, faces, normals});
|
|
body.addShape(polyhedron);
|
|
}
|
|
|
|
return body;
|
|
};
|
|
|
|
Ammo().then(function () {
|
|
ammoIsReady = true;
|
|
}).catch(function (error) {
|
|
|
|
});
|