mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2024-12-15 02:59:55 +08:00
Refactor and cleanup
This commit is contained in:
parent
4c50b94f6a
commit
320f76f039
@ -1,5 +1,3 @@
|
|||||||
uniform vec3 u_fillColour;
|
|
||||||
uniform float u_opacity;
|
|
||||||
uniform vec3 u_lightWorldPos;
|
uniform vec3 u_lightWorldPos;
|
||||||
uniform mat4 u_worldViewProjection;
|
uniform mat4 u_worldViewProjection;
|
||||||
uniform mat4 u_worldInverseTranspose;
|
uniform mat4 u_worldInverseTranspose;
|
||||||
@ -10,14 +8,15 @@ attribute vec3 normal;
|
|||||||
varying vec4 v_colour;
|
varying vec4 v_colour;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 a_position = u_worldViewProjection * vec4(position.xyz * 0.1, 1.0);
|
vec4 a_position = u_worldViewProjection * vec4(position.xyz, 1.0);
|
||||||
|
|
||||||
vec3 v_normal = (u_worldInverseTranspose * vec4(normal, 0)).xyz;
|
vec3 v_normal = (u_worldInverseTranspose * vec4(normal, 0)).xyz;
|
||||||
vec3 v_lightDir = normalize(u_lightWorldPos);
|
vec3 v_lightDir = normalize(u_lightWorldPos);
|
||||||
|
|
||||||
float lighting = dot(v_normal, v_lightDir);
|
float lighting = abs(dot(v_normal, v_lightDir));
|
||||||
|
|
||||||
v_colour = vec4(u_fillColour * lighting, u_opacity);
|
//v_colour = vec4(normal *lighting), 1.0);
|
||||||
|
v_colour = vec4(vec3(lighting), 1.0);
|
||||||
|
|
||||||
gl_Position = a_position;
|
gl_Position = a_position;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
precision mediump float;
|
|
||||||
|
|
||||||
//uniform float time;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
//float alpha = (-cos(time * 0.05) + 1.0) / 4.0;
|
|
||||||
float alpha = 1.0;
|
|
||||||
gl_FragColor = vec4(1.0, 1.0, 1.0, 0.5);
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
uniform mat4 u_worldViewProjection;
|
|
||||||
|
|
||||||
attribute vec3 position;
|
|
||||||
attribute vec3 normal;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
// Extrude the vertices outwards slightly to avoid z-fighting
|
|
||||||
vec3 translated_position = position + normal * 0.0001;
|
|
||||||
|
|
||||||
gl_Position = u_worldViewProjection * vec4(translated_position, 1.0);
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
precision mediump float;
|
|
||||||
|
|
||||||
uniform vec3 u_fillColour;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_FragColor = vec4(u_fillColour, 1.0);
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
uniform mat4 u_worldViewProjection;
|
|
||||||
|
|
||||||
attribute vec3 position;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = u_worldViewProjection * vec4(position.xyz, 1.0);
|
|
||||||
}
|
|
@ -8,16 +8,10 @@ const renderer = new Renderer(voxelSize);
|
|||||||
const voxelManager = new VoxelManager(voxelSize);
|
const voxelManager = new VoxelManager(voxelSize);
|
||||||
const triangle = new Triangle(new Vector3(0, 0, 0), new Vector3(4, 3, 1), new Vector3(2, -3, -2));
|
const triangle = new Triangle(new Vector3(0, 0, 0), new Vector3(4, 3, 1), new Vector3(2, -3, -2));
|
||||||
const triangle2 = new Triangle(new Vector3(5, 2, -1), new Vector3(-2, 3, -1), new Vector3(0, 3, 2));
|
const triangle2 = new Triangle(new Vector3(5, 2, -1), new Vector3(-2, 3, -1), new Vector3(0, 3, 2));
|
||||||
console.log(triangle2);
|
|
||||||
|
|
||||||
renderer.setStroke(new Vector3(1.0, 0.0, 0.0));
|
|
||||||
renderer.registerTriangle(triangle);
|
|
||||||
renderer.registerTriangle(triangle2);
|
|
||||||
|
|
||||||
voxelManager.voxeliseTriangle(triangle);
|
|
||||||
voxelManager.voxeliseTriangle(triangle2);
|
voxelManager.voxeliseTriangle(triangle2);
|
||||||
|
voxelManager.voxeliseTriangle(triangle);
|
||||||
|
|
||||||
renderer.setStroke(new Vector3(1.0, 1.0, 1.0));
|
|
||||||
renderer.registerVoxels(voxelManager.voxels);
|
renderer.registerVoxels(voxelManager.voxels);
|
||||||
|
|
||||||
renderer.compileRegister();
|
renderer.compileRegister();
|
||||||
@ -25,7 +19,6 @@ renderer.compileRegister();
|
|||||||
|
|
||||||
function render(time) {
|
function render(time) {
|
||||||
renderer.begin();
|
renderer.begin();
|
||||||
|
|
||||||
renderer.end();
|
renderer.end();
|
||||||
|
|
||||||
requestAnimationFrame(render);
|
requestAnimationFrame(render);
|
||||||
|
231
src/renderer.js
231
src/renderer.js
@ -18,17 +18,24 @@ class Renderer {
|
|||||||
|
|
||||||
this._registerEvents();
|
this._registerEvents();
|
||||||
|
|
||||||
this._register = {
|
this._debugRegister = {
|
||||||
position: {numComponents: 3, data: []},
|
position: {numComponents: 3, data: []},
|
||||||
indices: {numComponents: 3, data: []},
|
indices: {numComponents: 3, data: []},
|
||||||
colour: {numComponents: 3, data: []}
|
colour: {numComponents: 3, data: []}
|
||||||
};
|
};
|
||||||
|
this._register = {
|
||||||
|
position: {numComponents: 3, data: []},
|
||||||
|
normal: {numComponents: 3, data: []},
|
||||||
|
indices: {numComponents: 3, data: []},
|
||||||
|
};
|
||||||
|
|
||||||
|
this._debugMaxIndex = 0;
|
||||||
this._maxIndex = 0;
|
this._maxIndex = 0;
|
||||||
|
|
||||||
//this._voxelSize = voxelSize;
|
this._voxelSize = voxelSize;
|
||||||
this._voxelSizeVector = new Vector3(voxelSize, voxelSize, voxelSize);
|
this._voxelSizeVector = new Vector3(voxelSize, voxelSize, voxelSize);
|
||||||
|
|
||||||
this._registerOpen = true;
|
this._registersOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_registerEvents() {
|
_registerEvents() {
|
||||||
@ -66,70 +73,124 @@ class Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileRegister() {
|
compileRegister() {
|
||||||
console.log(this._register);
|
this._debugRegisterBuffer = twgl.createBufferInfoFromArrays(this._gl, this._debugRegister);
|
||||||
|
|
||||||
this._registerBuffer = twgl.createBufferInfoFromArrays(this._gl, this._register);
|
this._registerBuffer = twgl.createBufferInfoFromArrays(this._gl, this._register);
|
||||||
this._registerOpen = false;
|
this._registersOpen = false;
|
||||||
}
|
|
||||||
|
|
||||||
end() {
|
|
||||||
if (this._registerOpen) {
|
|
||||||
console.error("Trying to draw register objects before register is closed. Call compileRegister() first.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw objects in register
|
|
||||||
const uniforms = {
|
|
||||||
u_worldViewProjection: this._camera.getWorldViewProjection()
|
|
||||||
};
|
|
||||||
|
|
||||||
const shader = shaderManager.unshadedRegisteredProgram;
|
|
||||||
this._gl.useProgram(shader.program);
|
|
||||||
twgl.setBuffersAndAttributes(this._gl, shader, this._registerBuffer);
|
|
||||||
twgl.setUniforms(shader, uniforms);
|
|
||||||
this._gl.drawElements(this._gl.LINES, this._registerBuffer.numElements, this._gl.UNSIGNED_SHORT, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setStroke(colour) {
|
setStroke(colour) {
|
||||||
this._strokeColour = colour;
|
this._strokeColour = colour;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getBoxData(centre, size) {
|
end() {
|
||||||
|
if (this._registersOpen) {
|
||||||
|
console.error("Trying to draw register objects before register is closed. Call compileRegister() first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._drawDebugRegister();
|
||||||
|
this._drawRegister();
|
||||||
|
}
|
||||||
|
|
||||||
|
_drawDebugRegister() {
|
||||||
|
const uniforms = {
|
||||||
|
u_worldViewProjection: this._camera.getWorldViewProjection()
|
||||||
|
};
|
||||||
|
|
||||||
|
this._drawBuffer(this._gl.LINES, this._debugRegisterBuffer, shaderManager.debugProgram, uniforms);
|
||||||
|
}
|
||||||
|
|
||||||
|
_drawRegister() {
|
||||||
|
const uniforms = {
|
||||||
|
//u_lightWorldPos: new Vector3(1, 2, 0.5).toArray(),
|
||||||
|
u_lightWorldPos: this._camera.getCameraPosition(0.5, 0.0),
|
||||||
|
u_worldViewProjection: this._camera.getWorldViewProjection(),
|
||||||
|
u_worldInverseTranspose: this._camera.getWorldInverseTranspose()
|
||||||
|
};
|
||||||
|
|
||||||
|
this._drawBuffer(this._gl.TRIANGLES, this._registerBuffer, shaderManager.shadedProgram, uniforms);
|
||||||
|
}
|
||||||
|
|
||||||
|
_drawBuffer(drawMode, buffer, shader, uniforms) {
|
||||||
|
this._gl.useProgram(shader.program);
|
||||||
|
twgl.setBuffersAndAttributes(this._gl, shader, buffer);
|
||||||
|
twgl.setUniforms(shader, uniforms);
|
||||||
|
this._gl.drawElements(drawMode, buffer.numElements, this._gl.UNSIGNED_SHORT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
_getBoxData(centre, size, debug) {
|
||||||
const a = Vector3.sub(centre, Vector3.mulScalar(size, 0.5));
|
const a = Vector3.sub(centre, Vector3.mulScalar(size, 0.5));
|
||||||
const b = Vector3.add(centre, Vector3.mulScalar(size, 0.5));
|
const b = Vector3.add(centre, Vector3.mulScalar(size, 0.5));
|
||||||
|
|
||||||
return {
|
if (debug) {
|
||||||
position: [
|
return {
|
||||||
a.x, a.y, a.z,
|
position: [
|
||||||
b.x, a.y, a.z,
|
a.x, a.y, a.z,
|
||||||
b.x, b.y, a.z,
|
b.x, a.y, a.z,
|
||||||
a.x, b.y, a.z,
|
b.x, b.y, a.z,
|
||||||
a.x, a.y, b.z,
|
a.x, b.y, a.z,
|
||||||
b.x, a.y, b.z,
|
a.x, a.y, b.z,
|
||||||
b.x, b.y, b.z,
|
b.x, a.y, b.z,
|
||||||
a.x, b.y, b.z
|
b.x, b.y, b.z,
|
||||||
],
|
a.x, b.y, b.z
|
||||||
indices: [
|
],
|
||||||
0, 1, 1, 2, 2, 3, 3, 0,
|
indices: [
|
||||||
4, 5, 5, 6, 6, 7, 7, 4,
|
0, 1, 1, 2, 2, 3, 3, 0,
|
||||||
0, 4, 1, 5, 2, 6, 3, 7
|
4, 5, 5, 6, 6, 7, 7, 4,
|
||||||
]
|
0, 4, 1, 5, 2, 6, 3, 7
|
||||||
};
|
]
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let cube = twgl.primitives.createCubeVertices(this._voxelSize);
|
||||||
|
|
||||||
|
for (let i = 0; i < 72; i += 3) {
|
||||||
|
cube.position[i + 0] += centre.x;
|
||||||
|
cube.position[i + 1] += centre.y;
|
||||||
|
cube.position[i + 2] += centre.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cube;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getTriangleData(a, b, c) {
|
_getTriangleData(triangle, debug) {
|
||||||
return {
|
|
||||||
position: [
|
const a = triangle.v0;
|
||||||
a.x, a.y, a.z,
|
const b = triangle.v1;
|
||||||
b.x, b.y, b.z,
|
const c = triangle.v2;
|
||||||
c.x, c.y, c.z,
|
const n = triangle.normal;
|
||||||
],
|
|
||||||
indices: [
|
if (debug) {
|
||||||
0, 1,
|
return {
|
||||||
1, 2,
|
position: [
|
||||||
2, 0
|
a.x, a.y, a.z,
|
||||||
]
|
b.x, b.y, b.z,
|
||||||
};
|
c.x, c.y, c.z,
|
||||||
|
],
|
||||||
|
indices: [
|
||||||
|
0, 1,
|
||||||
|
1, 2,
|
||||||
|
2, 0
|
||||||
|
]
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
position: [
|
||||||
|
a.x, a.y, a.z,
|
||||||
|
b.x, b.y, b.z,
|
||||||
|
c.x, c.y, c.z,
|
||||||
|
],
|
||||||
|
normal: [
|
||||||
|
n.x, n.y, n.z,
|
||||||
|
n.x, n.y, n.z,
|
||||||
|
n.x, n.y, n.z
|
||||||
|
],
|
||||||
|
indices: [
|
||||||
|
0, 1, 2
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -149,12 +210,8 @@ class Renderer {
|
|||||||
|
|
||||||
// Use when drawing the same thing each frame
|
// Use when drawing the same thing each frame
|
||||||
registerBox(centre, size, debug) {
|
registerBox(centre, size, debug) {
|
||||||
if (debug) {
|
const data = this._getBoxData(centre, size, debug);
|
||||||
const data = this._getBoxData(centre, size);
|
this._addDataToRegister(data, debug);
|
||||||
this._addDataToRegister(data);
|
|
||||||
} else {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use when drawing the same triangle each frame
|
// Use when drawing the same triangle each frame
|
||||||
@ -163,18 +220,15 @@ class Renderer {
|
|||||||
const data = this._getTriangleData(a, b, c);
|
const data = this._getTriangleData(a, b, c);
|
||||||
this._addDataToRegister(data);
|
this._addDataToRegister(data);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
registerTriangle(triangle, debug) {
|
registerTriangle(triangle, debug) {
|
||||||
if (debug) {
|
const data = this._getTriangleData(triangle, debug);
|
||||||
const data = this._getTriangleData(triangle.v0, triangle.v1, triangle.v2);
|
this._addDataToRegister(data, debug);
|
||||||
this._addDataToRegister(data);
|
|
||||||
} else {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerVoxel(centre) {
|
registerVoxel(centre) {
|
||||||
const data = this._getBoxData(centre, this._voxelSizeVector);
|
const data = this._getBoxData(centre, this._voxelSizeVector, false);
|
||||||
this._addDataToRegister(data);
|
this._addDataToRegister(data, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerVoxels(voxelCentres) {
|
registerVoxels(voxelCentres) {
|
||||||
@ -183,23 +237,43 @@ class Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_addDataToRegister(data) {
|
_cycleBuffers() {
|
||||||
if (!this._registerOpen) {
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_addDataToRegister(data, debug) {
|
||||||
|
if (!this._registersOpen) {
|
||||||
console.error("Trying to register object when register is closed. Register before calling compileRegister()");
|
console.error("Trying to register object when register is closed. Register before calling compileRegister()");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._register.position.data.push(...data.position);
|
if (debug) {
|
||||||
this._register.indices.data.push(...data.indices.map(x => x + this._maxIndex));
|
this._debugRegister.position.data.push(...data.position);
|
||||||
|
this._debugRegister.indices.data.push(...data.indices.map(x => x + this._debugMaxIndex));
|
||||||
|
|
||||||
|
const numVertices = data.position.length / 3;
|
||||||
|
const vertexColours = [].concat(...new Array(numVertices).fill(this._strokeColour.toArray()));
|
||||||
|
this._debugRegister.colour.data.push(...vertexColours);
|
||||||
|
|
||||||
|
this._debugMaxIndex += 1 + Math.max(...data.indices);
|
||||||
|
} else {
|
||||||
|
const newMaxIndex = this._maxIndex + 1 + Math.max(...data.indices);
|
||||||
|
if (newMaxIndex >= 65535) {
|
||||||
|
console.warn("Overloaded buffer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._register.position.data.push(...data.position);
|
||||||
|
this._register.normal.data.push(...data.normal);
|
||||||
|
this._register.indices.data.push(...data.indices.map(x => x + this._maxIndex));
|
||||||
|
|
||||||
|
this._maxIndex = newMaxIndex;
|
||||||
|
}
|
||||||
|
|
||||||
const numVertices = data.position.length / 3;
|
|
||||||
const vertexColours = [].concat(...new Array(numVertices).fill(this._strokeColour.toArray()));
|
|
||||||
this._register.colour.data.push(...vertexColours);
|
|
||||||
|
|
||||||
this._maxIndex += 1 + Math.max(...data.indices);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
_drawData(data) {
|
_drawData(data) {
|
||||||
const buffer = twgl.createBufferInfoFromArrays(this._gl, data);
|
const buffer = twgl.createBufferInfoFromArrays(this._gl, data);
|
||||||
|
|
||||||
@ -214,6 +288,7 @@ class Renderer {
|
|||||||
twgl.setUniforms(shader, uniforms);
|
twgl.setUniforms(shader, uniforms);
|
||||||
this._gl.drawElements(this._gl.LINES, buffer.numElements, this._gl.UNSIGNED_SHORT, 0);
|
this._gl.drawElements(this._gl.LINES, buffer.numElements, this._gl.UNSIGNED_SHORT, 0);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,26 +1,15 @@
|
|||||||
const twgl = require('twgl.js');
|
const twgl = require('twgl.js');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const gl = document.querySelector("#c").getContext("webgl");
|
const gl = document.querySelector("#c").getContext("webgl");
|
||||||
|
|
||||||
const shaded_vertex_shader = fs.readFileSync('./shaders/shaded_vertex.vs', 'utf8');
|
const shaded_vertex_shader = fs.readFileSync('./shaders/shaded_vertex.vs', 'utf8');
|
||||||
const shaded_fragment_shader = fs.readFileSync('./shaders/shaded_fragment.fs', 'utf8');
|
const shaded_fragment_shader = fs.readFileSync('./shaders/shaded_fragment.fs', 'utf8');
|
||||||
|
|
||||||
const unshaded_vertex_shader = fs.readFileSync('./shaders/unshaded_vertex.vs', 'utf8');
|
const debug_vertex_shader = fs.readFileSync('./shaders/debug_vertex.vs', 'utf8');
|
||||||
const unshaded_fragment_shader = fs.readFileSync('./shaders/unshaded_fragment.fs', 'utf8');
|
const debug_fragment_shader = fs.readFileSync('./shaders/debug_fragment.fs', 'utf8');
|
||||||
|
|
||||||
const unshaded_registered_vertex_shader = fs.readFileSync('./shaders/unshaded_registered_vertex.vs', 'utf8');
|
|
||||||
const unshaded_registered_fragment_shader = fs.readFileSync('./shaders/unshaded_registered_fragment.fs', 'utf8');
|
|
||||||
|
|
||||||
const test_vertex_shader = fs.readFileSync('./shaders/test_vertex.vs', 'utf8');
|
|
||||||
const test_fragment_shader = fs.readFileSync('./shaders/test_fragment.fs', 'utf8');
|
|
||||||
|
|
||||||
const shadedProgram = twgl.createProgramInfo(gl, [shaded_vertex_shader, shaded_fragment_shader]);
|
const shadedProgram = twgl.createProgramInfo(gl, [shaded_vertex_shader, shaded_fragment_shader]);
|
||||||
const unshadedProgram = twgl.createProgramInfo(gl, [unshaded_vertex_shader, unshaded_fragment_shader]);
|
const debugProgram = twgl.createProgramInfo(gl, [debug_vertex_shader, debug_fragment_shader]);
|
||||||
const unshadedRegisteredProgram = twgl.createProgramInfo(gl, [unshaded_registered_vertex_shader, unshaded_registered_fragment_shader]);
|
|
||||||
const testProgram = twgl.createProgramInfo(gl, [test_vertex_shader, test_fragment_shader]);
|
|
||||||
|
|
||||||
module.exports.shadedProgram = shadedProgram;
|
module.exports.shadedProgram = shadedProgram;
|
||||||
module.exports.unshadedProgram = unshadedProgram;
|
module.exports.debugProgram = debugProgram;
|
||||||
module.exports.unshadedRegisteredProgram = unshadedRegisteredProgram;
|
|
||||||
module.exports.testProgram = testProgram;
|
|
||||||
|
@ -9,6 +9,11 @@ class Triangle {
|
|||||||
this.v1 = v1;
|
this.v1 = v1;
|
||||||
this.v2 = v2;
|
this.v2 = v2;
|
||||||
|
|
||||||
|
const f0 = Vector3.sub(v1, v0);
|
||||||
|
const f1 = Vector3.sub(v0, v2);
|
||||||
|
this.normal = Vector3.cross(f0, f1);
|
||||||
|
this.normal.normalise();
|
||||||
|
|
||||||
this._calculateBoundingBox();
|
this._calculateBoundingBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +95,17 @@ class Vector3 {
|
|||||||
return this.x == vec.x && this.y == vec.y && this.z == vec.z;
|
return this.x == vec.x && this.y == vec.y && this.z == vec.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_magnitude() {
|
||||||
|
return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2) + Math.pow(this.z, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
normalise() {
|
||||||
|
const mag = this._magnitude();
|
||||||
|
this.x /= mag;
|
||||||
|
this.y /= mag;
|
||||||
|
this.z /= mag;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.Vector3 = Vector3;
|
module.exports.Vector3 = Vector3;
|
Loading…
Reference in New Issue
Block a user