├── .gitignore
├── README.md
├── index.html
├── js
├── engine
│ ├── Camera
│ │ ├── Camera.js
│ │ └── FpCamera.js
│ ├── Constants.js
│ ├── Geometry.js
│ ├── Light
│ │ └── SpotLight.js
│ ├── Matrix.js
│ ├── Object3D.js
│ ├── Point.js
│ ├── Polygon.js
│ ├── RenderedPolygon.js
│ ├── Renderer.js
│ ├── Scene.js
│ ├── Utils.js
│ └── reader
│ │ ├── Generate.js
│ │ └── Loader.js
├── geometries
│ ├── Cone.js
│ ├── Cube.js
│ ├── Plane.js
│ ├── PyramidSquare.js
│ ├── RegularPrism.js
│ └── Sphere.js
├── index.js
└── scenes
│ ├── CastleScene.js
│ ├── DefaultScene.js
│ ├── SpheresScene.js
│ ├── TestLoadScene.js
│ └── WavesScene.js
├── resources
└── obj
│ ├── cube.obj
│ ├── diamond.obj
│ ├── icosahedron.obj
│ ├── shuttle.obj
│ └── teapot.obj
├── screenshot.png
└── screenshot2.png
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .idea
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Javascript-3D-Engine
2 | Javascript 3D Engine - Based in canvas element
3 |
4 | Try it here:
5 | https://juangf.github.io/Javascript-3D-Engine/
6 |
7 |
8 |
9 | ## Camera controls
10 | - Key `W`: Front.
11 | - Key `A` or `←`: Left.
12 | - Key `D` or `→`: Right.
13 | - Key `S`: Back.
14 | - Key `↓`: Down.
15 | - Key `↑`: Up.
16 | - Key `C`: Restart camera position.
17 | - Key `1`: Default scene.
18 | - Key `2`: Pyramid scene.
19 | - Mouse Pointer: LookAt.
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Javascript 3D Engine
6 |
7 |
8 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/js/engine/Camera/Camera.js:
--------------------------------------------------------------------------------
1 | import Point from '../Point.js';
2 | import Matrix from '../Matrix.js';
3 |
4 | class Camera {
5 |
6 | constructor(config) {
7 | this.id = config.id;
8 | this.position = new Point(0, 190, -500);
9 | this.up = new Point(0, 1, 0);
10 | this.front = new Point(0, 0, 1);
11 | this.far = 5000;
12 | this.near = -650;
13 | this.right = -2500;
14 | this.left = 2500;
15 | this.top = 2500;
16 | this.bottom = -2500;
17 |
18 | if (config.position) {
19 | this.setPosition(config.position);
20 | }
21 | }
22 |
23 | getId() {
24 | return this.id;
25 | }
26 |
27 | getPosition() {
28 | return this.position;
29 | }
30 |
31 | setPosition(p) {
32 | this.position = p;
33 | return this;
34 | }
35 |
36 | getMatrix() {
37 | return new Matrix();
38 | }
39 |
40 | getFar() {
41 | return this.far;
42 | }
43 |
44 | getNear() {
45 | return this.near;
46 | }
47 |
48 | getLeft() {
49 | return this.left;
50 | }
51 |
52 | getRight() {
53 | return this.right;
54 | }
55 |
56 | getTop() {
57 | return this.top;
58 | }
59 |
60 | getBottom() {
61 | return this.bottom;
62 | }
63 |
64 | isPointInViewport(p) {
65 | return p.getZ() <= this.getFar() &&
66 | p.getZ() >= this.getNear() &&
67 | p.getX() >= this.getRight() &&
68 | p.getX() <= this.getLeft() &&
69 | p.getY() <= this.getTop() &&
70 | p.getY() >= this.getBottom();
71 | }
72 | }
73 |
74 | export default Camera;
--------------------------------------------------------------------------------
/js/engine/Camera/FpCamera.js:
--------------------------------------------------------------------------------
1 | import Camera from './Camera.js';
2 | import Matrix from '../Matrix.js';
3 | import Point from '../Point.js';
4 |
5 | class FpCamera extends Camera {
6 | constructor(config) {
7 | super(config);
8 | this.pitch = config.pitch ? config.pitch : 0;
9 | this.yaw = config.yaw ? config.yaw : 0;
10 | }
11 |
12 | setPitch(pitch) {
13 | this.pitch = pitch;
14 | }
15 |
16 | setYaw(yaw) {
17 | this.yaw = yaw;
18 | }
19 |
20 | getMatrix() {
21 | let cosPitch = Math.cos(this.pitch);
22 | let sinPitch = Math.sin(this.pitch);
23 | let cosYaw = Math.cos(this.yaw);
24 | let sinYaw = Math.sin(this.yaw);
25 |
26 | let xaxis = new Point(cosYaw, 0, -sinYaw);
27 | let yaxis = new Point(sinYaw * sinPitch, cosPitch, cosYaw * sinPitch);
28 | let zaxis = new Point(sinYaw * cosPitch, -sinPitch, cosPitch * cosYaw);
29 |
30 | let viewMatrix = new Matrix([
31 | [xaxis.x, yaxis.x, zaxis.x, 0],
32 | [xaxis.y, yaxis.y, zaxis.y, 0],
33 | [xaxis.z, yaxis.z, zaxis.z, 0],
34 | [-Point.dot(xaxis, this.position), -Point.dot(yaxis, this.position), -Point.dot(zaxis, this.position), 1]
35 | ]);
36 | //console.log(-Point.dot(xaxis, this.position), -Point.dot(yaxis, this.position), -Point.dot(zaxis, this.position));
37 |
38 | return viewMatrix;
39 | }
40 | }
41 |
42 | export default FpCamera;
--------------------------------------------------------------------------------
/js/engine/Constants.js:
--------------------------------------------------------------------------------
1 | // Engine.
2 | export const DEFAULT_MOVE_VELOCITY = 30;
3 |
4 | // Camera.
5 | export const DEFAULT_CAMERA_POINT = {x: 0, y: 540, z: -700};
6 |
7 | // Keys.
8 | export const KEY_W = 87;
9 | export const KEY_A = 65;
10 | export const KEY_S = 83;
11 | export const KEY_D = 68;
12 | export const KEY_C = 67;
13 | export const KEY_UP = 38;
14 | export const KEY_LEFT = 37;
15 | export const KEY_DOWN = 40;
16 | export const KEY_RIGHT = 39;
17 |
18 | // Scenes.
19 | export const KEY_1 = 49;
20 | export const KEY_2 = 50;
21 | export const KEY_3 = 51;
22 | export const KEY_4 = 52;
--------------------------------------------------------------------------------
/js/engine/Geometry.js:
--------------------------------------------------------------------------------
1 | import Point from './Point.js';
2 | import Utils from './Utils.js';
3 | class Geometry {
4 |
5 | constructor() {
6 | this.points = [];
7 | this.poligons = [];
8 | }
9 |
10 | addPoint(point) {
11 | this.points.push(point);
12 | return this;
13 | }
14 |
15 | getPoints() {
16 | return this.points;
17 | }
18 |
19 | addPolygon(polygon) {
20 | this.poligons.push(polygon);
21 | return this;
22 | }
23 |
24 | getPolygon() {
25 | return this.poligons;
26 | }
27 |
28 | calcNormals() {
29 | let pointsNormalsList = {};
30 |
31 | // Calc poligon normal
32 | for (let i = 0; i < this.poligons.length; i++) {
33 | let poly = this.poligons[i],
34 | indexs = poly.getIndexs(),
35 | normal = Utils.getNormal(this.points[indexs[0]], this.points[indexs[1]], this.points[indexs[2]]);
36 |
37 | poly.setNormal(normal);
38 |
39 | for (let j = 0; j < indexs.length; j++) {
40 | if (typeof pointsNormalsList[indexs[j]] === 'undefined') {
41 | pointsNormalsList[indexs[j]] = [normal];
42 | } else {
43 | pointsNormalsList[indexs[j]].push(normal);
44 | }
45 | }
46 | }
47 |
48 | // Calc vertex normals
49 | for (const index in pointsNormalsList) {
50 | let normals = pointsNormalsList[index],
51 | resultNormal = normals[0];
52 |
53 | for (let i = 1; i < normals.length; i++) {
54 | resultNormal = Point.add(resultNormal, normals[i]);
55 | }
56 | this.points[index].setNormal(Point.normalize(resultNormal));
57 | }
58 | }
59 | }
60 |
61 | export default Geometry;
--------------------------------------------------------------------------------
/js/engine/Light/SpotLight.js:
--------------------------------------------------------------------------------
1 | import Sphere from "../../geometries/Sphere.js";
2 | import Object3D from "../../engine/Object3D.js";
3 |
4 | class SpotLight {
5 | constructor(config) {
6 | this.id = config.id;
7 | this.position = config.position;
8 | this.options = Object.assign({
9 | rgbaColor: {
10 | r: 255,
11 | g: 255,
12 | b: 255,
13 | a: 0.1
14 | },
15 | }, config.options);
16 | this.object = new Object3D({
17 | id: 'spotLight_Sphere_' + this.id,
18 | position: this.position,
19 | geometry: new Sphere(12, 40),
20 | options: {
21 | rgbaColor: {
22 | r: 255,
23 | g: 255,
24 | b: 255,
25 | a: 1
26 | },
27 | }
28 | });
29 | }
30 |
31 | getObjects() {
32 | return [this.object];
33 | }
34 |
35 | getPosition() {
36 | return this.position;
37 | }
38 |
39 | setPosition(p) {
40 | this.position = p;
41 | if (this.object) {
42 | this.object.setPosition(p);
43 | }
44 | return this;
45 | }
46 |
47 | getOptions() {
48 | return this.options;
49 | }
50 | }
51 |
52 | export default SpotLight;
--------------------------------------------------------------------------------
/js/engine/Matrix.js:
--------------------------------------------------------------------------------
1 | class Matrix {
2 | /*
3 | 1, 0, 0, X
4 | 0, 1, 0, Y
5 | 0, 0, 1, Z
6 | 0, 0, 0, W
7 | */
8 | constructor(values = [
9 | [1, 0, 0, 0],
10 | [0, 1, 0, 0],
11 | [0, 0, 1, 0],
12 | [0, 0, 0, 1],
13 | ]) {
14 | // Create the identity matrix by default
15 | this.m = values;
16 | }
17 |
18 | getSize() {
19 | return this.m.length;
20 | }
21 |
22 | set(values) {
23 | this.m = values;
24 | }
25 |
26 | setRow(index, rowValues) {
27 | this.m[index] = rowValues;
28 | }
29 |
30 | get() {
31 | return this.m;
32 | }
33 |
34 | getValue(row, col) {
35 | return this.m[row][col];
36 | }
37 |
38 | setValue(row, col, value) {
39 | this.m[row][col] = value;
40 | return this;
41 | }
42 |
43 | toString() {
44 | console.log(`${this.m[0][0]}, ${this.m[0][1]}, ${this.m[0][2]}, ${this.m[0][3]}
45 | ${this.m[1][0]}, ${this.m[1][1]}, ${this.m[1][2]}, ${this.m[1][3]}
46 | ${this.m[2][0]}, ${this.m[2][1]}, ${this.m[2][2]}, ${this.m[2][3]}
47 | ${this.m[3][0]}, ${this.m[3][1]}, ${this.m[3][2]}, ${this.m[3][3]}`);
48 | }
49 |
50 | static multiply_iterative(m1, m2) {
51 | let m = new Matrix();
52 | let size = m1.getSize();
53 |
54 | for (let i = 0; i < size; i++) {
55 | for (let j = 0; j < size; j++) {
56 | let sum = 0;
57 | for (let k = 0; k < size; k++) {
58 | sum += m1.getValue(i, k) * m2.getValue(j, k);
59 | }
60 | m.setValue(i, j, sum);
61 | }
62 | }
63 | return m;
64 | }
65 |
66 | static multiply(m1, m2) {
67 | let m1v = m1.get();
68 | let m2v = m2.get();
69 |
70 | return new Matrix([
71 | [
72 | m1v[0][0] * m2v[0][0] + m1v[0][1] * m2v[0][1] + m1v[0][2] * m2v[0][2] + m1v[0][3] * m2v[0][3],
73 | m1v[0][0] * m2v[1][0] + m1v[0][1] * m2v[1][1] + m1v[0][2] * m2v[1][2] + m1v[0][3] * m2v[1][3],
74 | m1v[0][0] * m2v[2][0] + m1v[0][1] * m2v[2][1] + m1v[0][2] * m2v[2][2] + m1v[0][3] * m2v[2][3],
75 | m1v[0][0] * m2v[3][0] + m1v[0][1] * m2v[3][1] + m1v[0][2] * m2v[3][2] + m1v[0][3] * m2v[3][3]
76 | ],
77 | [
78 | m1v[1][0] * m2v[0][0] + m1v[1][1] * m2v[0][1] + m1v[1][2] * m2v[0][2] + m1v[1][3] * m2v[0][3],
79 | m1v[1][0] * m2v[1][0] + m1v[1][1] * m2v[1][1] + m1v[1][2] * m2v[1][2] + m1v[1][3] * m2v[1][3],
80 | m1v[1][0] * m2v[2][0] + m1v[1][1] * m2v[2][1] + m1v[1][2] * m2v[2][2] + m1v[1][3] * m2v[2][3],
81 | m1v[1][0] * m2v[3][0] + m1v[1][1] * m2v[3][1] + m1v[1][2] * m2v[3][2] + m1v[1][3] * m2v[3][3]
82 | ],
83 | [
84 | m1v[2][0] * m2v[0][0] + m1v[2][1] * m2v[0][1] + m1v[2][2] * m2v[0][2] + m1v[2][3] * m2v[0][3],
85 | m1v[2][0] * m2v[1][0] + m1v[2][1] * m2v[1][1] + m1v[2][2] * m2v[1][2] + m1v[2][3] * m2v[1][3],
86 | m1v[2][0] * m2v[2][0] + m1v[2][1] * m2v[2][1] + m1v[2][2] * m2v[2][2] + m1v[2][3] * m2v[2][3],
87 | m1v[2][0] * m2v[3][0] + m1v[2][1] * m2v[3][1] + m1v[2][2] * m2v[3][2] + m1v[2][3] * m2v[3][3]
88 | ],
89 | [
90 | m1v[3][0] * m2v[0][0] + m1v[3][1] * m2v[0][1] + m1v[3][2] * m2v[0][2] + m1v[3][3] * m2v[0][3],
91 | m1v[3][0] * m2v[1][0] + m1v[3][1] * m2v[1][1] + m1v[3][2] * m2v[1][2] + m1v[3][3] * m2v[1][3],
92 | m1v[3][0] * m2v[2][0] + m1v[3][1] * m2v[2][1] + m1v[3][2] * m2v[2][2] + m1v[3][3] * m2v[2][3],
93 | m1v[3][0] * m2v[3][0] + m1v[3][1] * m2v[3][1] + m1v[3][2] * m2v[3][2] + m1v[3][3] * m2v[3][3]
94 | ]
95 | ]);
96 | }
97 |
98 | /**
99 | * Based on:
100 | * https://github.com/jcoglan/sylvester/blob/master/src/matrix.js
101 | */
102 | static toRightTriangular(m) {
103 | let M = m, els;
104 | let n = 4, i, j, np = 8, p;
105 | for (i = 0; i < n; i++) {
106 | if (M.getValue(i, i) === 0) {
107 | for (j = i + 1; j < n; j++) {
108 | if (M.getValue(j, i) !== 0) {
109 | els = [];
110 | for (p = 0; p < np; p++) { els.push(M.getValue(i, p) + M.getValue(j, p)); }
111 | M.setRow(i, els);
112 | break;
113 | }
114 | }
115 | }
116 | if (M.getValue(i, i) !== 0) {
117 | for (j = i + 1; j < n; j++) {
118 | var multiplier = M.getValue(j, i) / M.getValue(i, i);
119 | els = [];
120 | for (p = 0; p < np; p++) {
121 | // Elements with column numbers up to an including the number of the
122 | // row that we're subtracting can safely be set straight to zero,
123 | // since that's the point of this routine and it avoids having to
124 | // loop over and correct rounding errors later
125 | els.push(p <= i ? 0 : M.getValue(j, p) - M.getValue(i, p) * multiplier);
126 | }
127 | M.setRow(j, els);
128 | }
129 | }
130 | }
131 | return M;
132 | };
133 |
134 | static duplicate(m) {
135 | return new Matrix(m.get());
136 | }
137 |
138 | static augment(m1, m2) {
139 | let M = m2;
140 | let T = m1, cols = 4;
141 | let i = 4, nj = 4, j;
142 | if (i !== 4) { return null; }
143 | while (i--) { j = nj;
144 | while (j--) {
145 | T.setValue(i, cols + j, M.getValue(i, j));
146 | }
147 | }
148 | return T;
149 | }
150 |
151 | /**
152 | * Based on:
153 | * https://github.com/jcoglan/sylvester/blob/master/src/matrix.js
154 | */
155 | static inverse(m) {
156 | let n = 4, i = n, j;
157 | let M = Matrix.toRightTriangular(Matrix.augment(m, new Matrix()));
158 | let np = 8, p, els, divisor;
159 | let inverse_elements = [];
160 | // Sylvester.Matrix is non-singular so there will be no zeros on the
161 | // diagonal. Cycle through rows from last to first.
162 | while (i--) {
163 | // First, normalise diagonal elements to 1
164 | els = [];
165 | inverse_elements[i] = [];
166 | divisor = M.getValue(i, i);
167 |
168 | for (p = 0; p < np; p++) {
169 | let new_element = M.getValue(i, p) / divisor;
170 | els.push(new_element);
171 | // Shuffle off the current row of the right hand side into the results
172 | // array as it will not be modified by later runs through this loop
173 | if (p >= n) {
174 | inverse_elements[i].push(new_element);
175 | }
176 | }
177 | M.setRow(i, els);
178 | // Then, subtract this row from those above it to give the identity matrix
179 | // on the left hand side
180 | j = i;
181 | while (j--) {
182 | els = [];
183 | for (p = 0; p < np; p++) {
184 | els.push(M.getValue(j, p) - M.getValue(i, p) * M.getValue(j, i));
185 | }
186 | M.setRow(j, els);
187 | }
188 | }
189 | return new Matrix(inverse_elements);
190 | }
191 |
192 | }
193 |
194 | export default Matrix;
--------------------------------------------------------------------------------
/js/engine/Object3D.js:
--------------------------------------------------------------------------------
1 | import Matrix from './Matrix.js';
2 | import Utils from './Utils.js';
3 |
4 | class Object3D {
5 |
6 | constructor(config) {
7 | this.id = config.id;
8 | this.geometry = config.geometry;
9 | this.position = config.position;
10 | this.options = Object.assign({
11 | drawPoints: false,
12 | backfaceCulling: true,
13 | drawNormals: false,
14 | drawVertexNormals: false,
15 | wireFrame: false,
16 | rgbaColor: {
17 | r: 180,
18 | g: 180,
19 | b: 180,
20 | a: 1
21 | }
22 | }, config.options);
23 | this.transforms = {
24 | translation: new Matrix(),
25 | scale: new Matrix(),
26 | rotations: {
27 | x: {
28 | MRotation: null,
29 | MInverse: null,
30 | MReverse: null
31 | },
32 | y: {
33 | MRotation: null,
34 | MInverse: null,
35 | MReverse: null
36 | },
37 | z: {
38 | MRotation: null,
39 | MInverse: null,
40 | MReverse: null
41 | }
42 | }
43 | };
44 | }
45 |
46 | getId() {
47 | return this.id;
48 | }
49 |
50 | getPosition() {
51 | return this.position;
52 | }
53 |
54 | setPosition(p) {
55 | this.position = p;
56 | return this;
57 | }
58 |
59 | getGeometry() {
60 | return this.geometry;
61 | }
62 |
63 | getOptions() {
64 | return this.options;
65 | }
66 |
67 | setOptions(options) {
68 | this.options = Object.assign(this.options, options);
69 | return this;
70 | }
71 |
72 | getTransforms() {
73 | return this.transforms;
74 | }
75 |
76 | scale(value) {
77 | this.transforms.scale = new Matrix(4, value);
78 | }
79 |
80 | rotate(axis, alpha, pointAround = this.position) {
81 | let axisTRot = this.transforms.rotations[axis];
82 |
83 | if (alpha !== 0) {
84 | alpha = Utils.degToRad(alpha);
85 |
86 | switch (axis) {
87 | case 'x':
88 | /*
89 | 1, 0, 0, 0
90 | 0, cos(a), -sin(a), 0
91 | 0, sin(a), cos(a), 0
92 | 0, 0, 0, 1
93 | */
94 | axisTRot.MRotation = new Matrix();
95 | axisTRot.MRotation.setValue(1, 1, Math.cos(alpha));
96 | axisTRot.MRotation.setValue(1, 2, -Math.sin(alpha));
97 | axisTRot.MRotation.setValue(2, 1, Math.sin(alpha));
98 | axisTRot.MRotation.setValue(2, 2, Math.cos(alpha));
99 | break;
100 |
101 | case 'y':
102 | /*
103 | cos(a), 0, sin(a), 0
104 | 0, 0, 0 , 0
105 | 0, 0, 0 , 0
106 | -sin(1), 0, cos(a), 1
107 | */
108 | axisTRot.MRotation = new Matrix();
109 | axisTRot.MRotation.setValue(0, 0, Math.cos(alpha));
110 | axisTRot.MRotation.setValue(2, 0, -Math.sin(alpha));
111 | axisTRot.MRotation.setValue(0, 2, Math.sin(alpha));
112 | axisTRot.MRotation.setValue(2, 2, Math.cos(alpha));
113 | break;
114 |
115 | case 'z':
116 | /*
117 | cos(a), -sin(a), 0, 0
118 | sin(a), cos(a), 0, 0
119 | 0, 0, 1, 0
120 | 0, 0, 0, 1
121 | */
122 | axisTRot.MRotation = new Matrix();
123 | axisTRot.MRotation.setValue(0, 0, Math.cos(alpha));
124 | axisTRot.MRotation.setValue(0, 1, -Math.sin(alpha));
125 | axisTRot.MRotation.setValue(1, 0, Math.sin(alpha));
126 | axisTRot.MRotation.setValue(1, 1, Math.cos(alpha));
127 | break;
128 | }
129 |
130 |
131 | axisTRot.MInverse = new Matrix();
132 | axisTRot.MInverse.setValue(0, 3, -pointAround.getX()),
133 | axisTRot.MInverse.setValue(1, 3, -pointAround.getY());
134 | axisTRot.MInverse.setValue(2, 3, -pointAround.getZ());
135 |
136 | axisTRot.MReverse = new Matrix();
137 | axisTRot.MReverse.setValue(0, 3, pointAround.getX()),
138 | axisTRot.MReverse.setValue(1, 3, pointAround.getY());
139 | axisTRot.MReverse.setValue(2, 3, pointAround.getZ());
140 | } else {
141 | axisTRot.MRotation = null;
142 | }
143 | return this;
144 | }
145 | }
146 |
147 | export default Object3D;
--------------------------------------------------------------------------------
/js/engine/Point.js:
--------------------------------------------------------------------------------
1 | class Point {
2 |
3 | constructor(x, y, z, w = 1) {
4 | this.x = x;
5 | this.y = y;
6 | this.z = z;
7 | this.w = w; // If the Vector is director, w = 0
8 | this.normal = null; // If it is a geometry vertex, it stores the calculated vertex normal
9 | }
10 |
11 | setCoords(x, y, z, w = 1) {
12 | this.x = x;
13 | this.y = y;
14 | this.z = z;
15 | this.w = w;
16 | return this;
17 | }
18 |
19 | getCoords() {
20 | return [this.x, this.y, this.z, this.w];
21 | }
22 |
23 | getX() {
24 | return this.x;
25 | }
26 |
27 | getY() {
28 | return this.y;
29 | }
30 |
31 | getZ() {
32 | return this.z;
33 | }
34 |
35 | setNormal(normal) {
36 | this.normal = normal;
37 | return this;
38 | }
39 |
40 | getNormal() {
41 | return this.normal;
42 | }
43 |
44 | static add(p1, p2) {
45 | return new Point(p2.getX() + p1.getX(), p2.getY() + p1.getY(), p2.getZ() + p1.getZ());
46 | }
47 |
48 | static substract(p1, p2) {
49 | return new Point(p2.getX() - p1.getX(), p2.getY() - p1.getY(), p2.getZ() - p1.getZ());
50 | }
51 |
52 | static multiply(p1, v) {
53 | return new Point(p1.getX() * v, p1.getY() * v, p1.getZ() * v);
54 | }
55 |
56 | static dotProduct(p1, p2) {
57 | return new Point(
58 | p1.getY() * p2.getZ() - p1.getZ() * p2.getY(),
59 | p1.getZ() * p2.getX() - p1.getX() * p2.getZ(),
60 | p1.getX() * p2.getY() - p1.getY() * p2.getX()
61 | );
62 | }
63 |
64 | static dot(p1, p2) {
65 | return p1.getX() * p2.getX() + p1.getY() * p2.getY() + p1.getZ() * p2.getZ();
66 | }
67 |
68 | static length(p) {
69 | return Math.sqrt(this.dot(p, p));
70 | }
71 |
72 | static normalize(p) {
73 | let m = this.length(p);
74 | if (m > 0) {
75 | return new Point(p.getX() / m, p.getY() / m, p.getZ() / m);
76 | } else {
77 | return p;
78 | }
79 | }
80 |
81 | static multiplyMatrix(p, m) {
82 | let size = m.getSize();
83 | let coords = p.getCoords();
84 | let res = [0, 0, 0];
85 |
86 | for (let i = 0; i < size; i++) {
87 | let sum = 0;
88 | for (let j = 0; j < size; j++) {
89 | sum += m.getValue(i, j) * coords[j];
90 | }
91 | res[i] = sum;
92 | }
93 |
94 | return new Point(res[0], res[1], res[2], res[3]);
95 | }
96 |
97 | static angleBetween(v1, v2) {
98 | var dot = this.dot(v1, v2);
99 | var mod1 = this.length(v1);
100 | var mod2 = this.length(v2);
101 |
102 | if (!mod2 || !mod1) {
103 | return 0;
104 | }
105 |
106 | var alpha = dot / (mod1 * mod2);
107 |
108 | if (alpha < -1) alpha = -1;
109 | else if (alpha > 1) alpha = 1;
110 |
111 | return Math.acos(alpha);
112 | }
113 | }
114 |
115 | export default Point;
--------------------------------------------------------------------------------
/js/engine/Polygon.js:
--------------------------------------------------------------------------------
1 | class Polygon {
2 |
3 | constructor(indexs = [], normal = null) {
4 | this.indexs = indexs;
5 | this.normal = normal; // Normal vector
6 | }
7 |
8 | getIndexs() {
9 | return this.indexs;
10 | }
11 |
12 | setNormal(normal) {
13 | this.normal = normal;
14 | return this;
15 | }
16 |
17 | getNormal() {
18 | return this.normal;
19 | }
20 |
21 | hasIndex(index) {
22 | return this.indexs.indexOf(index) > -1;
23 | }
24 | }
25 |
26 | export default Polygon;
--------------------------------------------------------------------------------
/js/engine/RenderedPolygon.js:
--------------------------------------------------------------------------------
1 | class RenderedPolygon {
2 | constructor(points = [], options = {}) {
3 | this.points = points;
4 | this.pointsIndexs = [];
5 | this.normal = null;
6 | this.options = Object.assign({
7 | drawPoints: false,
8 | drawNormals: false,
9 | drawVertexNormals: false,
10 | wireFrame: false,
11 | rgbaColor: ''
12 | }, options);
13 | }
14 |
15 | getOptions() {
16 | return this.options;
17 | }
18 |
19 | getPoints() {
20 | return this.points;
21 | }
22 |
23 | addPoint(point) {
24 | this.points.push(point);
25 | return this;
26 | }
27 |
28 | getPointIndexs() {
29 | return this.pointsIndexs;
30 | }
31 |
32 | addPointIndex(index) {
33 | this.pointsIndexs.push(index);
34 | return this;
35 | }
36 |
37 | setOptions(options) {
38 | this.options = Object.assign(this.options, options);
39 | return this;
40 | }
41 |
42 | setNormal(normal) {
43 | this.normal = normal;
44 | return this;
45 | }
46 |
47 | getNormal() {
48 | return this.normal;
49 | }
50 | }
51 |
52 | export default RenderedPolygon;
--------------------------------------------------------------------------------
/js/engine/Renderer.js:
--------------------------------------------------------------------------------
1 | import Point from './Point.js';
2 | import Matrix from './Matrix.js';
3 | import RenderedPolygon from './RenderedPolygon.js'
4 |
5 | class Renderer {
6 | constructor(canvas, camera, scene) {
7 | this.canvas = canvas;
8 | this.camera = camera;
9 | this.scene = scene
10 |
11 | this.ctx = this.canvas.getContext('2d');
12 | this.worldMatrix = new Matrix([
13 | [1, 0, 0, 0],
14 | [0, -1, 0, 0],
15 | [0, 0, 1, 0],
16 | [0, 0, 0, 1]
17 | ]);
18 |
19 | this.objectsLoaded = false;
20 | this.objects = {};
21 | this.lights = {};
22 | }
23 |
24 | setScene(scene) {
25 | this.scene = scene;
26 | this.objectsLoaded = false;
27 | return this;
28 | }
29 |
30 | isVisible(p1, p2, p3) {
31 | return (
32 | (this.projection('x', p2.getX(), p2.getZ()) - this.projection('x', p1.getX(), p1.getZ())) * (this.projection('y', p3.getY(), p3.getZ()) - this.projection('y', p1.getY(), p1.getZ())) <
33 | (this.projection('x', p3.getX(), p3.getZ()) - this.projection('x', p1.getX(), p1.getZ())) * (this.projection('y', p2.getY(), p2.getZ()) - this.projection('y', p1.getY(), p1.getZ()))
34 | );
35 | }
36 |
37 | projection(axis, value, z) {
38 | return (value * 700) / (z + 700) + (axis === 'x' ? this.canvas.width : this.canvas.height) / 2;
39 | }
40 |
41 | clearCanvas() {
42 | this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
43 | return this;
44 | }
45 |
46 | drawPoint(point, text = '') {
47 | let x = this.projection('x', point.getX(), point.getZ());
48 | let y = this.projection('y', point.getY(), point.getZ());
49 |
50 | this.ctx.beginPath();
51 | this.ctx.strokeStyle = '#000000';
52 | this.ctx.arc(x, y, 4, 0, Math.PI * 2, true);
53 | this.ctx.stroke();
54 |
55 | if (text !== '') {
56 | this.ctx.font = '10px Verdana';
57 | this.ctx.strokeText(text, x + 4, y - 4);
58 | }
59 | return this;
60 | }
61 |
62 | drawNormal(poly) {
63 | let pDir = Point.multiply(poly.getNormal(), 30);
64 | let points = poly.getPoints();
65 | let p = Point.add(Point.add(points[0], points[1]), points[2]);
66 |
67 | if (points.length === 4) {
68 | p = Point.multiply(Point.add(p, points[3]), 0.25);
69 | } else {
70 | p = Point.multiply(p, 0.33333);
71 | }
72 |
73 | this.ctx.beginPath();
74 | this.ctx.moveTo(
75 | this.projection('x', p.getX(), p.getZ()),
76 | this.projection('y', p.getY(), p.getZ())
77 | );
78 | this.ctx.strokeStyle = '#FF0000';
79 | this.ctx.lineTo(
80 | this.projection('x', p.getX() + pDir.getX(), p.getZ() + pDir.getZ()),
81 | this.projection('y', p.getY() + pDir.getY(), p.getZ() + pDir.getZ())
82 | );
83 | this.ctx.stroke();
84 | this.ctx.strokeStyle = '#000000';
85 | return this;
86 | }
87 |
88 | drawVertexNormal(p, normal) {
89 | let pDir = Point.multiply(normal, 30);
90 |
91 | this.ctx.beginPath();
92 | this.ctx.moveTo(
93 | this.projection('x', p.getX(), p.getZ()),
94 | this.projection('y', p.getY(), p.getZ())
95 | );
96 | this.ctx.strokeStyle = '#0000FF';
97 | this.ctx.lineTo(
98 | this.projection('x', p.getX() + pDir.getX(), p.getZ() + pDir.getZ()),
99 | this.projection('y', p.getY() + pDir.getY(), p.getZ() + pDir.getZ())
100 | );
101 | this.ctx.stroke();
102 | this.ctx.strokeStyle = '#000000';
103 | return this;
104 | }
105 |
106 | drawPolygons(renderedPolygons) {
107 | renderedPolygons.sort((poly1, poly2) => {
108 | let points1 = poly1.getPoints();
109 | let points2 = poly2.getPoints();
110 | return (points2[0].getZ() + points2[1].getZ() + points2[2].getZ()) / 3 - (points1[0].getZ() + points1[1].getZ() + points1[2].getZ()) / 3;
111 | });
112 |
113 | renderedPolygons.forEach(poly => {
114 | let options = poly.getOptions();
115 | let points = poly.getPoints();
116 |
117 | points.forEach((p, i) => {
118 | if (i === 0) {
119 | this.ctx.beginPath();
120 | this.ctx.moveTo(
121 | this.projection('x', p.getX(), p.getZ()),
122 | this.projection('y', p.getY(), p.getZ())
123 | );
124 | }
125 | this.ctx.lineTo(
126 | this.projection('x', p.getX(), p.getZ()),
127 | this.projection('y', p.getY(), p.getZ())
128 | );
129 | });
130 |
131 | this.ctx.closePath();
132 | this.ctx.strokeStyle = options.rgbaColor;
133 | this.ctx.stroke();
134 |
135 | if (!options.wireFrame) {
136 | this.ctx.fillStyle = options.rgbaColor;
137 | this.ctx.fill();
138 | }
139 |
140 | if (options.drawPoints) {
141 | let pointIndexs = poly.getPointIndexs();
142 | points.forEach((p, i) => {
143 | this.drawPoint(p, pointIndexs[i]);
144 | });
145 | }
146 |
147 | if (options.drawNormals) {
148 | this.drawNormal(poly);
149 | }
150 |
151 | if (options.drawVertexNormals) {
152 | points.forEach(p => {
153 | this.drawVertexNormal(p, p.getNormal());
154 | });
155 | }
156 | });
157 | return this;
158 | }
159 |
160 | renderPolygon(camera, polygon, position, points, transforms, transformsMatrix, options) {
161 | let indexs = polygon.getIndexs();
162 | let numindexs = indexs.length;
163 | let transformedPoints = {};
164 | let renderedPoly = new RenderedPolygon();
165 |
166 | /**
167 | * @todo refactorize the transformed points system
168 | */
169 | if (options.backfaceCulling) {
170 | for (let i = 0; i < 3; i++) {
171 | let p = Point.add(points[indexs[i]], position);
172 | transformedPoints[indexs[i]] = Point.multiplyMatrix(p, transformsMatrix);
173 | }
174 | if (!this.isVisible(transformedPoints[indexs[0]], transformedPoints[indexs[1]], transformedPoints[indexs[2]])) {
175 | return null;
176 | }
177 | }
178 |
179 | for (let i = 0; i < numindexs; i++) {
180 | let p = Point.add(points[indexs[i]], position);
181 | p = Point.multiplyMatrix(p, transformsMatrix);
182 |
183 | if (!camera.isPointInViewport(p)) {
184 | return null;
185 | }
186 |
187 | p.setNormal(Point.multiplyMatrix(Point.substract(points[indexs[i]].getNormal(), camera.getPosition()), transformsMatrix));
188 |
189 | renderedPoly.addPoint(p);
190 |
191 | if (options.drawPoints) {
192 | renderedPoly.addPointIndex(indexs[i]);
193 | }
194 | }
195 |
196 | renderedPoly.setNormal(Point.multiplyMatrix(Point.substract(polygon.getNormal(), camera.getPosition()), transformsMatrix));
197 |
198 | return renderedPoly;
199 | }
200 |
201 | lightFn(normal, p0, objPos, pL){
202 | let vOL = Point.substract(Point.add(p0, objPos), pL);
203 | let alpha = Point.angleBetween(vOL, normal) * 180 / Math.PI;
204 |
205 | return alpha * 255 / 90;
206 | }
207 |
208 | renderObjectPolygons(object, camera, lights) {
209 | let pos = object.getPosition();
210 | let options = object.getOptions();
211 | let geometry = object.getGeometry();
212 | let polygons = geometry.getPolygon();
213 | let points = geometry.getPoints();
214 | let transforms = object.getTransforms();
215 |
216 | // Note: TransformedPoint = TranslationMatrix * RotationMatrix * ScaleMatrix * OriginalPoint
217 | let transformsMatrix = Matrix.multiply(this.worldMatrix, camera.getMatrix());
218 | let renderedPolygons = [];
219 | let ambient = 190;
220 |
221 | polygons.forEach(p => {
222 | let renderedPoly = this.renderPolygon(camera, p, pos, points, transforms, transformsMatrix, options);
223 | if (renderedPoly) {
224 | if (options.drawNormals || options.drawVertexNormals || options.drawPoints) {
225 | renderedPoly.setOptions({
226 | drawNormals: options.drawNormals,
227 | drawVertexNormals: options.drawVertexNormals,
228 | drawPoints: options.drawPoints
229 | });
230 | }
231 |
232 | if (options.wireFrame) {
233 | renderedPoly.setOptions({wireFrame: options.wireFrame});
234 | }
235 |
236 | for (let [id, light] of Object.entries(lights)) {
237 | let indexs = p.getIndexs();
238 | let temp = this.lightFn(
239 | p.getNormal(),
240 | Point.multiply(Point.add(Point.add(points[indexs[0]], points[indexs[1]]), points[indexs[2]]), 0.33333),
241 | pos,
242 | light.getPosition()
243 | );
244 |
245 | if (temp < ambient) {
246 | temp = ambient;
247 | }
248 |
249 | let color = this.mixRgbaColors(options.rgbaColor, light.getOptions()['rgbaColor'], temp / 255);
250 |
251 | renderedPoly.setOptions({
252 | rgbaColor: `rgba(${color.r}, ${color.g}, ${color.b}, 1)`
253 | });
254 | }
255 |
256 | renderedPolygons.push(renderedPoly);
257 | }
258 | });
259 |
260 | return renderedPolygons;
261 | }
262 |
263 | // Fast and easy way to combine (additive mode) two RGBA colors with JavaScript.
264 | // https://gist.github.com/JordanDelcros/518396da1c13f75ee057
265 | mixRgbaColors(base, added, ratio = 1) {
266 | const alpha = 1 - (1 - added.a) * (1 - base.a);
267 | return {
268 | 'r' : Math.round((added.r * added.a / alpha) + (base.r * base.a * (1 - added.a) / alpha)) * ratio,
269 | 'g' : Math.round((added.g * added.a / alpha) + (base.g * base.a * (1 - added.a) / alpha)) * ratio,
270 | 'b' : Math.round((added.b * added.a / alpha) + (base.b * base.a * (1 - added.a) / alpha)) * ratio,
271 | 'a' : alpha
272 | };
273 | }
274 |
275 | render() {
276 | this.clearCanvas();
277 |
278 | // Call before render scene method
279 | this.scene.beforeRender();
280 |
281 | if (!this.objectsLoaded) {
282 | this.objects = this.scene.getObjects();
283 | this.lights = this.scene.getLights();
284 |
285 | let lightObjects = [];
286 | for (let [id, light] of Object.entries(this.lights)) {
287 | lightObjects = lightObjects.concat(light.getObjects());
288 | }
289 | this.objects = {...this.objects, ...lightObjects};
290 | this.objectsLoaded = true;
291 | }
292 |
293 | let renderedPolygons = [];
294 | for (let [id, object] of Object.entries(this.objects)) {
295 | renderedPolygons = renderedPolygons.concat(this.renderObjectPolygons(object, this.camera, this.lights));
296 | }
297 |
298 | this.drawPolygons(renderedPolygons);
299 | return this;
300 | }
301 | }
302 |
303 | export default Renderer;
--------------------------------------------------------------------------------
/js/engine/Scene.js:
--------------------------------------------------------------------------------
1 | class Scene {
2 |
3 | constructor(config) {
4 | this.id = config.id;
5 | this.name = config.name;
6 | this.objects = {};
7 | this.lights = {};
8 | }
9 |
10 | addObject(obj) {
11 | this.objects[obj.id] = obj;
12 | return this;
13 | }
14 |
15 | removeObject(objectId) {
16 | delete this.objects[objectId];
17 | return this;
18 | }
19 |
20 | getObject(objectId) {
21 | return this.objects[objectId];
22 | }
23 |
24 | getObjects() {
25 | return this.objects;
26 | }
27 |
28 | addLight(light) {
29 | this.lights[light.id] = light;
30 | return this;
31 | }
32 |
33 | removeLight(lightId) {
34 | delete this.lights[lightId];
35 | return this;
36 | }
37 |
38 | getLight(lightId) {
39 | return this.lights[lightId];
40 | }
41 |
42 | getLights() {
43 | return this.lights;
44 | }
45 |
46 | beforeRender() {
47 | return this;
48 | }
49 | }
50 |
51 | export default Scene;
--------------------------------------------------------------------------------
/js/engine/Utils.js:
--------------------------------------------------------------------------------
1 | import Point from './Point.js';
2 |
3 | class Utils {
4 | static degToRad(alpha) {
5 | return alpha * Math.PI / 180;
6 | }
7 | static radToDeg(radians) {
8 | return radians * 180 / Math.PI;
9 | }
10 | static getNormal(p0, p1, p2) {
11 | let ab1 = Point.substract(p1, p2);
12 | let ab2 = Point.substract(p1, p0);
13 | let ab1xab2 = Point.dotProduct(ab1, ab2);
14 | return Point.normalize(ab1xab2);
15 | }
16 | }
17 |
18 | export default Utils;
--------------------------------------------------------------------------------
/js/engine/reader/Generate.js:
--------------------------------------------------------------------------------
1 | import Loader from "./Loader.js";
2 | import Object3D from "../Object3D.js";
3 | import Point from "../Point.js";
4 | import Polygon from "../Polygon.js";
5 | import Geometry from "../../engine/Geometry.js";
6 |
7 | class Generate {
8 |
9 | /**
10 | *
11 | * @param {number} x
12 | * @param {number} y
13 | * @param {number} z
14 | * @param {string} file
15 | * @param {number} size
16 | * @returns {Object3D}
17 | */
18 | constructor(x, y, z, file, size = 100, options = {}) {
19 | this.point = {x: x, y: y, z: z};
20 | this.size = size;
21 | this.file = file;
22 |
23 | let loader = new Loader();
24 | this.raw = loader.load(this.file);
25 | this.options = Object.assign({
26 | drawPoints: false,
27 | drawNormals: false
28 | }, options);
29 |
30 | return this.parse();
31 | }
32 |
33 | parse() {
34 | let obj = {};
35 | let vertexMatches = this.raw.match(/^v( -?\d+(\.\d+)?){3}$/gm);
36 | if (vertexMatches) {
37 | obj.vertices = vertexMatches.map(function(vertex) {
38 | let vertices = vertex.split(" ");
39 | vertices.shift();
40 | return vertices;
41 | });
42 | }
43 |
44 | let facesMatches = this.raw.match(/^f(.*)([^\n]*\n+)/gm);
45 | if (facesMatches) {
46 | obj.faces = facesMatches.map(function(face) {
47 | let faces = face.split(" ");
48 | faces.shift();
49 | faces.forEach(function (e, i) {
50 | if (e.indexOf("/") !== -1) {
51 | faces[i] = (e.split('/')[0] - 1);
52 | } else {
53 | faces[i] = e - 1;
54 | }
55 | });
56 |
57 | return faces;
58 | });
59 | }
60 |
61 | let name = this.raw.match(/^o (\S+)/gm);
62 | if (name) {
63 | obj.name = name[0].split(" ")[1];
64 | }
65 |
66 | this.obj = obj;
67 | this.createObject();
68 | this.object.calcNormals();
69 |
70 | return new Object3D({
71 | id: this.obj.name,
72 | position: new Point(this.point.x, this.point.y, this.point.z),
73 | geometry: this.object,
74 | options: this.options
75 | });
76 | }
77 |
78 | createObject() {
79 | this.object = new Geometry();
80 | if (this.obj !== {}) {
81 | this.obj.vertices.forEach(function (element) {
82 | this.object.addPoint(new Point(element[0] * this.size, element[1] * this.size, element[2] * this.size));
83 | }, this);
84 | this.obj.faces.forEach(function (element) {
85 | this.object.addPolygon(new Polygon(element.reverse()));
86 | }, this);
87 | }
88 | }
89 | }
90 |
91 | export default Generate;
--------------------------------------------------------------------------------
/js/engine/reader/Loader.js:
--------------------------------------------------------------------------------
1 | class Loader {
2 | constructor() {}
3 |
4 | load(file) {
5 | let data = null;
6 |
7 | if (file !== null) {
8 | let rawFile = new XMLHttpRequest();
9 | rawFile.open("GET", file, false);
10 | rawFile.onreadystatechange = function () {
11 | if (rawFile.readyState === 4) {
12 | if (rawFile.status === 200 || rawFile.status === 0) {
13 | data = rawFile.responseText;
14 | }
15 | }
16 | };
17 | rawFile.send(null);
18 | }
19 |
20 | return data;
21 | }
22 | }
23 |
24 | export default Loader;
--------------------------------------------------------------------------------
/js/geometries/Cone.js:
--------------------------------------------------------------------------------
1 | import Point from '../engine/Point.js';
2 | import Polygon from '../engine/Polygon.js';
3 | import Geometry from '../engine/Geometry.js';
4 |
5 | class Cone extends Geometry {
6 | constructor(radius, height, space) {
7 | super();
8 | let index = 0;
9 |
10 | this.addPoint(new Point(0, 0, 0));
11 | this.addPoint(new Point(0, height, 0));
12 |
13 | for (let i = 0, b = space; i < 360 / space; i++, b += space) {
14 | this.addPoint(new Point(
15 | radius * Math.sin(b / 180 * Math.PI),
16 | 0,
17 | radius * Math.cos(b / 180 * Math.PI)
18 | ));
19 | if (index > 0) {
20 | this.addPolygon(new Polygon([0, index + 1, index + 2]));
21 | }
22 | this.addPolygon(new Polygon([1, index + 2, index + 1]));
23 | index++;
24 | }
25 |
26 | this.addPolygon(new Polygon([0, index + 1, 2]));
27 | this.addPolygon(new Polygon([1, 2, index + 1]));
28 |
29 | this.calcNormals();
30 | }
31 | }
32 |
33 | export default Cone;
--------------------------------------------------------------------------------
/js/geometries/Cube.js:
--------------------------------------------------------------------------------
1 | import RegularPrism from './RegularPrism.js';
2 |
3 | /**
4 | * Perfect regular prism.
5 | *
6 | * 5________4
7 | * / | /|
8 | * 0________1 |
9 | * | 6_____|_7
10 | * | / | /
11 | * 3________2/
12 | *
13 | */
14 | class Cube extends RegularPrism {
15 |
16 | /**
17 | * Constructor.
18 | * (All sides are the same size).
19 | *
20 | * @param {number} size
21 | */
22 | constructor(size, allTriangles = true) {
23 | super(size, size, size, allTriangles);
24 | }
25 | }
26 |
27 | export default Cube;
--------------------------------------------------------------------------------
/js/geometries/Plane.js:
--------------------------------------------------------------------------------
1 | import Point from '../engine/Point.js';
2 | import Polygon from '../engine/Polygon.js';
3 | import Geometry from '../engine/Geometry.js';
4 |
5 | class Plane extends Geometry {
6 | constructor(size, space = 1) {
7 | super();
8 | size = size / space;
9 |
10 | for (let i = 0, b = 0; i <= space; i++, b += space + 1) {
11 | for (let j = 0; j <= space; j++) {
12 | this.addPoint(new Point(j * size, 0, i * size));
13 | if (j < space && i < space) {
14 | let bj = b + j;
15 | this.addPolygon(new Polygon([bj + 1, bj + space + 2, bj + space + 1, bj]));
16 | }
17 | }
18 | }
19 |
20 | this.calcNormals();
21 | }
22 | }
23 |
24 | export default Plane;
--------------------------------------------------------------------------------
/js/geometries/PyramidSquare.js:
--------------------------------------------------------------------------------
1 | import Point from '../engine/Point.js';
2 | import Polygon from '../engine/Polygon.js';
3 | import Geometry from '../engine/Geometry.js';
4 |
5 | /**
6 | * Square base pyramid definition.
7 | */
8 | class PyramidSquare extends Geometry {
9 |
10 | /**
11 | * Constructor.
12 | *
13 | * @param {number} h high
14 | * @param {number} w width
15 | * @param {number} d dept
16 | */
17 | constructor(h, w, d) {
18 | super();
19 | this
20 | .addPoint(new Point(0, h / 2, 0))
21 | .addPoint(new Point(w / 2, -h / 2, d / 2))
22 | .addPoint(new Point(w / 2, -h / 2, -d / 2))
23 | .addPoint(new Point(-w / 2, -h / 2, -d / 2))
24 | .addPoint(new Point(-w / 2, -h / 2, d / 2));
25 |
26 | this
27 | .addPolygon(new Polygon([3, 4, 1, 2]))
28 | .addPolygon(new Polygon([0, 2, 1]))
29 | .addPolygon(new Polygon([0, 3, 2]))
30 | .addPolygon(new Polygon([0, 4, 3]))
31 | .addPolygon(new Polygon([0, 1, 4]));
32 |
33 | this.calcNormals();
34 | }
35 | }
36 |
37 | export default PyramidSquare;
--------------------------------------------------------------------------------
/js/geometries/RegularPrism.js:
--------------------------------------------------------------------------------
1 | import Point from '../engine/Point.js';
2 | import Polygon from '../engine/Polygon.js';
3 | import Geometry from '../engine/Geometry.js';
4 |
5 | /**
6 | * Object with 6 faces, 12 edges, and 8 vertices.
7 | */
8 | class RegularPrism extends Geometry {
9 |
10 | /**
11 | * Constructor.
12 | *
13 | * @param {number} w high
14 | * @param {number} h width
15 | * @param {number} d dept
16 | */
17 | constructor(w, h, d, allTriangles = true) {
18 | super();
19 | h = h / 2;
20 | w = w / 2;
21 | d = d / 2;
22 |
23 | this
24 | .addPoint(new Point(-h, -w, -d))
25 | .addPoint(new Point(h, -w, -d))
26 | .addPoint(new Point(h, w, -d))
27 | .addPoint(new Point(-h, w, -d))
28 | .addPoint(new Point(h, -w, d))
29 | .addPoint(new Point(-h, -w, d))
30 | .addPoint(new Point(-h, w, d))
31 | .addPoint(new Point(h, w, d));
32 |
33 | if (allTriangles) {
34 | this
35 | .addPolygon(new Polygon([0, 1, 2, 0]))
36 | .addPolygon(new Polygon([0, 2, 3, 0]))
37 | .addPolygon(new Polygon([3, 5, 0, 3]))
38 | .addPolygon(new Polygon([3, 6, 5, 3]))
39 | .addPolygon(new Polygon([4, 5, 6, 4]))
40 | .addPolygon(new Polygon([4, 6, 7, 4]))
41 | .addPolygon(new Polygon([1, 4, 7, 1]))
42 | .addPolygon(new Polygon([1, 7, 2, 1]))
43 | .addPolygon(new Polygon([5, 4, 1, 5]))
44 | .addPolygon(new Polygon([5, 1, 0, 5]))
45 | .addPolygon(new Polygon([3, 2, 7, 3]))
46 | .addPolygon(new Polygon([3, 7, 6, 3]));
47 | } else {
48 | this
49 | .addPolygon(new Polygon([0, 1, 2, 3]))
50 | .addPolygon(new Polygon([0, 3, 6, 5]))
51 | .addPolygon(new Polygon([4, 5, 6, 7]))
52 | .addPolygon(new Polygon([1, 4, 7, 2]))
53 | .addPolygon(new Polygon([5, 4, 1, 0]))
54 | .addPolygon(new Polygon([3, 2, 7, 6]));
55 | }
56 |
57 | this.calcNormals();
58 | }
59 | }
60 |
61 | export default RegularPrism;
--------------------------------------------------------------------------------
/js/geometries/Sphere.js:
--------------------------------------------------------------------------------
1 | import Point from '../engine/Point.js';
2 | import Polygon from '../engine/Polygon.js';
3 | import Geometry from '../engine/Geometry.js';
4 |
5 | class Sphere extends Geometry {
6 | constructor(radius, space, allTriangles = false) {
7 | super();
8 | let index = 0;
9 | let startInd = 0;
10 |
11 | this.addPoint(new Point(0, radius, 0));
12 |
13 | for (let j = 0; j < 360 / space - 1; j++) {
14 | this.addPolygon(new Polygon([0, index + 1, index + 2]));
15 | index++;
16 | }
17 |
18 | this.addPolygon(new Polygon([0, index + 1, 1]));
19 |
20 | index = 1;
21 |
22 | for (let i = 0, b = space; i < 180 / space - 1; i++, b += space) {
23 | //Assign our a loop to go through 360 degrees in intervals of our variable space
24 | for (let j = 0, a = 0; j < 360 / space; j++, a += space) {
25 | if (j === 0) {
26 | startInd = index;
27 | }
28 | this.addPoint(new Point(
29 | radius * Math.sin(a / 180 * Math.PI) * Math.sin(b / 180 * Math.PI),
30 | radius * Math.cos(b / 180 * Math.PI),
31 | -radius * Math.cos(a / 180 * Math.PI) * Math.sin(b / 180 * Math.PI)
32 | ));
33 |
34 | if (i > 0) {
35 | if (j < 360 / space - 1) {
36 | if (allTriangles) {
37 | this.addPolygon(new Polygon([
38 | index,
39 | index + 360 / space,
40 | index + 1
41 | ]));
42 | this.addPolygon(new Polygon([
43 | index + 360 / space,
44 | index + 360 / space + 1,
45 | index + 1
46 | ]));
47 | } else {
48 | this.addPolygon(new Polygon([
49 | index,
50 | index + 360 / space,
51 | index + 360 / space + 1,
52 | index + 1
53 | ]));
54 | }
55 | } else {
56 | if (allTriangles) {
57 | this.addPolygon(new Polygon([
58 | index,
59 | index + 360 / space,
60 | startInd
61 | ]));
62 | this.addPolygon(new Polygon([
63 | index + 360 / space,
64 | startInd + 360 / space,
65 | startInd
66 | ]));
67 | } else {
68 | this.addPolygon(new Polygon([
69 | index,
70 | index + 360 / space,
71 | startInd + 360 / space,
72 | startInd
73 | ]));
74 | }
75 | }
76 | index++;
77 | }
78 | }
79 | }
80 |
81 | this.addPoint(new Point(0, -radius, 0));
82 |
83 | index = this.getPoints().length - 1;
84 | startInd = index;
85 |
86 | for (let j = 0; j < 360 / space - 1; j++) {
87 | this.addPolygon(new Polygon([startInd, index - 1, index - 2]));
88 | index--;
89 | }
90 |
91 | this.addPolygon(new Polygon([startInd, index - 1, startInd - 1]));
92 |
93 | this.calcNormals();
94 | }
95 | }
96 |
97 | export default Sphere;
--------------------------------------------------------------------------------
/js/index.js:
--------------------------------------------------------------------------------
1 | // Core.
2 | import Renderer from './engine/Renderer.js';
3 | import Point from './engine/Point.js';
4 | import FpCamera from './engine/Camera/FpCamera.js';
5 | import Utils from './engine/Utils.js'
6 |
7 | // Constants.
8 | import {DEFAULT_MOVE_VELOCITY, DEFAULT_CAMERA_POINT} from './engine/Constants.js';
9 | import {KEY_W, KEY_A, KEY_S, KEY_D, KEY_UP, KEY_C, KEY_LEFT, KEY_DOWN, KEY_RIGHT} from './engine/Constants.js';
10 | import {KEY_1, KEY_2, KEY_3, KEY_4} from './engine/Constants.js';
11 |
12 | // Scenes.
13 | import DefaultScene from './scenes/DefaultScene.js';
14 | import CastleScene from './scenes/CastleScene.js';
15 | import WavesScene from './scenes/WavesScene.js';
16 | import TestLoadScene from './scenes/TestLoadScene.js';
17 |
18 | let canvas = document.getElementById('canvas');
19 | canvas.width = window.innerWidth;
20 | canvas.height = window.innerHeight;
21 |
22 | let homeScene = new DefaultScene();
23 | let castleScene = new CastleScene(0, 0, 0);
24 | let wavesScene = new WavesScene();
25 | let testLoadScene = new TestLoadScene();
26 |
27 | let cameraPosition = new Point(
28 | DEFAULT_CAMERA_POINT.x,
29 | DEFAULT_CAMERA_POINT.y,
30 | DEFAULT_CAMERA_POINT.z
31 | );
32 |
33 | let camera = new FpCamera({
34 | id: 'cam1',
35 | position: cameraPosition
36 | });
37 |
38 | let renderer = new Renderer(canvas, camera, homeScene);
39 |
40 | let renderFn = timestamp => {
41 | requestAnimationFrame(renderFn);
42 | renderer.render();
43 | };
44 | renderer.render();
45 |
46 | window.onkeydown = (e => {
47 | let updateScene = cameraPosition => {
48 | camera.setPosition(cameraPosition);
49 | renderer.render();
50 | };
51 |
52 | switch (e.keyCode) {
53 | case KEY_LEFT:
54 | case KEY_A:
55 | cameraPosition.setCoords(cameraPosition.getX() - DEFAULT_MOVE_VELOCITY, cameraPosition.getY(), cameraPosition.getZ());
56 | updateScene(cameraPosition);
57 | break;
58 | case KEY_RIGHT:
59 | case KEY_D:
60 | cameraPosition.setCoords(cameraPosition.getX() + DEFAULT_MOVE_VELOCITY, cameraPosition.getY(), cameraPosition.getZ());
61 | updateScene(cameraPosition);
62 | break;
63 | case KEY_DOWN:
64 | cameraPosition.setCoords(cameraPosition.getX(), cameraPosition.getY() + DEFAULT_MOVE_VELOCITY, cameraPosition.getZ());
65 | updateScene(cameraPosition);
66 | break;
67 | case KEY_UP:
68 | cameraPosition.setCoords(cameraPosition.getX(), cameraPosition.getY() - DEFAULT_MOVE_VELOCITY, cameraPosition.getZ());
69 | updateScene(cameraPosition);
70 | break;
71 | case KEY_W:
72 | cameraPosition.setCoords(cameraPosition.getX(), cameraPosition.getY(), cameraPosition.getZ() - DEFAULT_MOVE_VELOCITY);
73 | updateScene(cameraPosition);
74 | break;
75 | case KEY_S:
76 | cameraPosition.setCoords(cameraPosition.getX(), cameraPosition.getY(), cameraPosition.getZ() + DEFAULT_MOVE_VELOCITY);
77 | updateScene(cameraPosition);
78 | break;
79 | case KEY_C:
80 | cameraPosition.setCoords(DEFAULT_CAMERA_POINT.x, DEFAULT_CAMERA_POINT.y, DEFAULT_CAMERA_POINT.z);
81 | updateScene(cameraPosition);
82 | break;
83 | case KEY_1:
84 | cameraPosition.setCoords(DEFAULT_CAMERA_POINT.x, DEFAULT_CAMERA_POINT.y, DEFAULT_CAMERA_POINT.z);
85 | camera.setYaw(0);
86 | camera.setPitch(0);
87 | renderer.setScene(homeScene).render();
88 | break;
89 | case KEY_2:
90 | cameraPosition.setCoords(390, 300, -400);
91 | camera.setYaw(-2.18);
92 | camera.setPitch(0.5);
93 | renderer.setScene(wavesScene).render();
94 | break;
95 | case KEY_3:
96 | cameraPosition.setCoords(810, 1080, -1990);
97 | camera.setYaw(-0.37);
98 | camera.setPitch(0.38);
99 | renderer.setScene(castleScene).render();
100 | break;
101 | case KEY_4:
102 | cameraPosition.setCoords(0, 300, -300);
103 | camera.setYaw(0);
104 | camera.setPitch(0.38);
105 | renderer.setScene(testLoadScene).render();
106 | break;
107 | }
108 | });
109 |
110 | document.onmousemove =(e => {
111 | let posX = e.clientX;
112 | let posY = e.clientY;
113 | let percX = posX / window.innerWidth;
114 | let percY = posY / window.innerHeight;
115 |
116 | let alpha = Utils.degToRad((percX - 0.5) * 360);
117 | let beta = Utils.degToRad((percY - 0.5) * 360);
118 | camera.setYaw(alpha);
119 | camera.setPitch(beta);
120 | });
121 |
122 | renderFn();
--------------------------------------------------------------------------------
/js/scenes/CastleScene.js:
--------------------------------------------------------------------------------
1 | import Scene from "../engine/Scene.js";
2 | import Object3D from "../engine/Object3D.js";
3 | import Point from "../engine/Point.js";
4 | import PyramidSquare from "../geometries/PyramidSquare.js";
5 | import RegularPrism from "../geometries/RegularPrism.js";
6 | import SpotLight from "../engine/Light/SpotLight.js";
7 |
8 | /**
9 | * Scene with a castle.
10 | */
11 | class CastleScene extends Scene {
12 |
13 | /**
14 | * Constructor
15 | *
16 | * @param {number} x
17 | * @param {number} y
18 | * @param {number} z
19 | * @param {Camera} camera
20 | * @param {object} options
21 | */
22 | constructor(x, y, z, options = {}) {
23 | super({
24 | id: 'castle_scene',
25 | name: 'Castle'
26 | });
27 |
28 | let size = 1000;
29 | let high = size * 0.7;
30 |
31 | if (options !== null) {
32 | if ('size' in options && options.size > 0) {
33 | size = options.size;
34 | }
35 |
36 | if ('high' in options && options.high > 0) {
37 | high = options.high;
38 | }
39 | }
40 |
41 | let towerDimension = {
42 | h: high,
43 | w: size * 0.6,
44 | d: size * 0.6
45 | };
46 |
47 | let towerPositions = {
48 | tower_1: {
49 | id: 1,
50 | position: {
51 | x: - (x + size / 2),
52 | y: y,
53 | z: - (z + size / 2)
54 | },
55 | dimension: towerDimension
56 | },
57 | tower_2: {
58 | id: 2,
59 | position: {
60 | x: x + size / 2,
61 | y: y,
62 | z: - (z + size / 2)
63 | },
64 | dimension: towerDimension
65 | },
66 | tower_3: {
67 | id: 3,
68 | position: {
69 | x: - (x + size / 2),
70 | y: y,
71 | z: z + size / 2
72 | },
73 | dimension: towerDimension
74 | },
75 | tower_4: {
76 | id: 4,
77 | position: {
78 | x: x + size / 2,
79 | y: y,
80 | z: z + size / 2
81 | },
82 | dimension: towerDimension
83 | }
84 | };
85 | let wallDimension = {
86 | h: towerDimension.h - towerDimension.h * 0.4,
87 | w: size - towerDimension.w,
88 | d: towerDimension.d - towerDimension.d * 0.9
89 | };
90 |
91 | for (let key in towerPositions) {
92 | this.makeTower(towerPositions[key].id, towerPositions[key].position, towerPositions[key].dimension);
93 | }
94 |
95 | this
96 | .addObject(new Object3D({
97 | id: 'wall_tower_1_2',
98 | position: new Point(
99 | + towerPositions.tower_1.position.x / 2 + towerPositions.tower_2.position.x / 2,
100 | wallDimension.h / 2 - towerDimension.h / 2 + y,
101 | (towerPositions.tower_1.position.z) + (wallDimension.d / 2) - (towerDimension.w / 2)
102 | ),
103 | geometry: new RegularPrism(
104 | wallDimension.h,
105 | wallDimension.w + x * 2,
106 | wallDimension.d
107 | ),
108 | options: {
109 | drawPoints: false,
110 | drawNormals: false,
111 | rgbaColor: {
112 | r: 255,
113 | g: 175,
114 | b: 67,
115 | a: 1
116 | }
117 | }
118 | }))
119 | .addObject(new Object3D({
120 | id: 'wall_tower_1_3',
121 | position: new Point(
122 | (towerPositions.tower_1.position.x) + (wallDimension.d / 2) - (towerDimension.w / 2),
123 | wallDimension.h / 2 - towerDimension.h / 2 + y,
124 | 0,
125 | ),
126 | geometry: new RegularPrism(
127 | wallDimension.h,
128 | wallDimension.d,
129 | wallDimension.w + z * 2
130 | ),
131 | options: {
132 | drawPoints: false,
133 | drawNormals: false,
134 | rgbaColor: {
135 | r: 255,
136 | g: 175,
137 | b: 67,
138 | a: 1
139 | }
140 | }
141 | }))
142 | .addObject(new Object3D({
143 | id: 'wall_tower_3_4',
144 | position: new Point(
145 | + towerPositions.tower_3.position.x / 2 + towerPositions.tower_4.position.x / 2,
146 | wallDimension.h / 2 - towerDimension.h / 2 + y,
147 | (towerPositions.tower_3.position.z) - (wallDimension.d / 2) + (towerDimension.w / 2)
148 | ),
149 | geometry: new RegularPrism(
150 | wallDimension.h,
151 | wallDimension.w + x * 2,
152 | wallDimension.d
153 | ),
154 | options: {
155 | drawPoints: false,
156 | drawNormals: false,
157 | rgbaColor: {
158 | r: 255,
159 | g: 175,
160 | b: 67,
161 | a: 1
162 | }
163 | }
164 | }))
165 | .addObject(new Object3D({
166 | id: 'wall_tower_2_4',
167 | position: new Point(
168 | (towerPositions.tower_2.position.x) - (wallDimension.d / 2) + (towerDimension.w / 2),
169 | wallDimension.h / 2 - towerDimension.h / 2 + y,
170 | 0
171 | ),
172 | geometry: new RegularPrism(
173 | wallDimension.h,
174 | wallDimension.d,
175 | wallDimension.w + z * 2
176 | ),
177 | options: {
178 | drawPoints: false,
179 | drawNormals: false,
180 | rgbaColor: {
181 | r: 255,
182 | g: 175,
183 | b: 67,
184 | a: 1
185 | }
186 | }
187 | }))
188 | .addLight(new SpotLight({
189 | id: 'light1',
190 | position: new Point(0, 1500, -1400)
191 | }));
192 | }
193 |
194 | makeTower(id, position, dimension) {
195 | this
196 | .addObject(new Object3D({
197 | id: 'tower_' + id,
198 | position: new Point(position.x, position.y, position.z),
199 | geometry: new RegularPrism(dimension.h, dimension.w, dimension.d),
200 | options: {
201 | drawPoints: false,
202 | drawNormals: false,
203 | rgbaColor: {
204 | r: 255,
205 | g: 193,
206 | b: 7,
207 | a: 1
208 | }
209 | }
210 | }))
211 | .addObject(new Object3D({
212 | id: 'tower_roof_' + id,
213 | position: new Point(position.x, position.y + dimension.h, position.z),
214 | geometry: new PyramidSquare(dimension.h, dimension.w, dimension.d),
215 | options: {
216 | drawPoints: false,
217 | drawNormals: false,
218 | rgbaColor: {
219 | r: 244,
220 | g: 67,
221 | b: 54,
222 | a: 1
223 | }
224 | }
225 | }));
226 |
227 | return this;
228 | }
229 |
230 | }
231 |
232 | export default CastleScene;
--------------------------------------------------------------------------------
/js/scenes/DefaultScene.js:
--------------------------------------------------------------------------------
1 | import Scene from "../engine/Scene.js";
2 | import Object3D from "../engine/Object3D.js";
3 | import SpotLight from "../engine/Light/SpotLight.js";
4 | import Point from "../engine/Point.js";
5 | import Cube from "../geometries/Cube.js";
6 | import Plane from "../geometries/Plane.js";
7 | import Sphere from "../geometries/Sphere.js";
8 | import PyramidSquare from "../geometries/PyramidSquare.js";
9 | import Cone from "../geometries/Cone.js";
10 | import Generate from '../engine/reader/Generate.js';
11 |
12 | class DefaultScene extends Scene
13 | {
14 | constructor() {
15 | super({
16 | id: 'scn1',
17 | name: 'Testing Scene'
18 | });
19 |
20 | this
21 | .addObject(new Object3D({
22 | id: 'plane1',
23 | position: new Point(-1000, -50, -500),
24 | geometry: new Plane(2000, 20),
25 | options: {
26 | rgbaColor: {
27 | r: 100,
28 | g: 104,
29 | b: 139,
30 | a: 1
31 | },
32 | backfaceCulling: false
33 | }
34 | }))
35 | .addObject(new Object3D({
36 | id: 'cube1',
37 | position: new Point(0, 150, 300),
38 | geometry: new Cube(300, false),
39 | options: {
40 | rgbaColor: {
41 | r: 255,
42 | g: 131,
43 | b: 131,
44 | a: 1
45 | },
46 | drawPoints: false
47 | }
48 | }))
49 | .addObject(new Object3D({
50 | id: 'sphere1',
51 | position: new Point(440, 900, 500),
52 | geometry: new Sphere(200, 20, false),
53 | options: {
54 | rgbaColor: {
55 | r: 100,
56 | g: 10,
57 | b: 131,
58 | a: 1
59 | },
60 | drawNormals: false
61 | }
62 | }))
63 | .addObject(new Object3D({
64 | id: 'sphere2',
65 | position: new Point(-380, 400, 100),
66 | geometry: new Sphere(100, 20, false),
67 | options: {
68 | rgbaColor: {
69 | r: 255,
70 | g: 0,
71 | b: 0,
72 | a: 1
73 | }
74 | }
75 | }))
76 | .addObject(new Object3D({
77 | id: 'pyramid_square',
78 | position: new Point(450, 150, 190),
79 | geometry: new PyramidSquare(300, 300, 300),
80 | options: {
81 | rgbaColor: {
82 | r: 100,
83 | g: 100,
84 | b: 200,
85 | a: 1
86 | }
87 | }
88 | }))
89 | .addObject(new Object3D({
90 | id: 'cone1',
91 | position: new Point(400, 0, 700),
92 | geometry: new Cone(200, 600, 20)
93 | }))
94 | .addObject(new Object3D({
95 | id: 'cone2',
96 | position: new Point(400, 0, -100),
97 | geometry: new Cone(100, 200, 20)
98 | }))
99 | .addObject(new Generate(-450, 0, -100, 'resources/obj/teapot.obj', 100, {
100 | rgbaColor: {
101 | r: 255,
102 | g: 193,
103 | b: 7,
104 | a: 1
105 | }
106 | }))
107 | .addLight(new SpotLight({
108 | id: 'light1',
109 | position: new Point(0, 500, 0)
110 | }))
111 |
112 | this.alpha = 0;
113 | this.radius = 600;
114 | this.light = this.getLight('light1');
115 | }
116 | beforeRender() {
117 | this.alpha += 15;
118 | if (this.alpha > 360) {
119 | this.alpha -= 360;
120 | }
121 | this.light.setPosition(new Point(Math.cos(this.alpha * Math.PI / 180) * this.radius, 500, Math.sin(this.alpha * Math.PI / 180) * this.radius));
122 | return this;
123 | }
124 | }
125 |
126 | export default DefaultScene;
--------------------------------------------------------------------------------
/js/scenes/SpheresScene.js:
--------------------------------------------------------------------------------
1 | import Scene from "../engine/Scene.js";
2 | import Object3D from "../engine/Object3D.js";
3 | import Point from "../engine/Point.js";
4 | import Sphere from "../geometries/Sphere.js";
5 | import SpotLight from "../engine/Light/SpotLight.js";
6 |
7 | /**
8 | * Scene with spheres.
9 | */
10 | class SpheresScene extends Scene {
11 |
12 | /**
13 | * Constructor
14 | *
15 | * @param {number} x
16 | * @param {number} y
17 | * @param {number} z
18 | * @param {Camera} camera
19 | * @param {object} options
20 | */
21 | constructor() {
22 | super({
23 | id: 'spheres_scene',
24 | name: 'Spheres'
25 | });
26 |
27 | this
28 | .addObject(new Object3D({
29 | id: 'sphere1',
30 | position: new Point(250, 400, -200),
31 | geometry: new Sphere(200, 18),
32 | options: {
33 | rgbaColor: {
34 | r: 100,
35 | g: 10,
36 | b: 131,
37 | a: 1
38 | },
39 | drawNormals: true
40 | }
41 | }))
42 | .addObject(new Object3D({
43 | id: 'sphere2',
44 | position: new Point(-250, 400, -200),
45 | geometry: new Sphere(200, 30),
46 | options: {
47 | rgbaColor: {
48 | r: 100,
49 | g: 130,
50 | b: 1,
51 | a: 1
52 | },
53 | drawNormals: true
54 | }
55 | }))
56 | .addLight(new SpotLight({
57 | id: 'light1',
58 | position: new Point(0, 600, -500)
59 | }))
60 |
61 | return this;
62 | }
63 |
64 | }
65 |
66 | export default SpheresScene;
--------------------------------------------------------------------------------
/js/scenes/TestLoadScene.js:
--------------------------------------------------------------------------------
1 | import Scene from "../engine/Scene.js";
2 | import Point from "../engine/Point.js";
3 | import SpotLight from "../engine/Light/SpotLight.js";
4 |
5 | // Objects.
6 | import Generate from '../engine/reader/Generate.js';
7 |
8 | /**
9 | * Scene with a castle.
10 | */
11 | class TestLoadScene extends Scene {
12 | constructor() {
13 | super({
14 | id: 'test_load_obj',
15 | name: 'load_obj'
16 | });
17 | let cube = new Generate(0, 0, 0, 'resources/obj/cube.obj');
18 | let diamond = new Generate(150, 150, 150, 'resources/obj/diamond.obj');
19 | let icosahedron = new Generate(-150, -150, -150, 'resources/obj/icosahedron.obj');
20 | let teapot = new Generate(-200, 200, -200, 'resources/obj/teapot.obj', 50);
21 | let shuttle = new Generate(200, 200, -200, 'resources/obj/shuttle.obj', 10);
22 |
23 | this.addObject(cube);
24 | this.addObject(diamond);
25 | this.addObject(icosahedron);
26 | this.addObject(teapot);
27 | this.addObject(shuttle);
28 | this.addLight(new SpotLight({
29 | id: 'light1',
30 | position: new Point(0, 500, 0)
31 | }));
32 | }
33 | }
34 |
35 | export default TestLoadScene;
--------------------------------------------------------------------------------
/js/scenes/WavesScene.js:
--------------------------------------------------------------------------------
1 | import Scene from "../engine/Scene.js";
2 | import Object3D from "../engine/Object3D.js";
3 | import Point from "../engine/Point.js";
4 | import Utils from "../engine/Utils.js";
5 | import Plane from "../geometries/Plane.js";
6 | import SpotLight from "../engine/Light/SpotLight.js";
7 |
8 | /**
9 | * Scene with a castle.
10 | */
11 | class WavesScene extends Scene {
12 | constructor() {
13 | super({
14 | id: 'waves',
15 | name: 'Waves'
16 | });
17 |
18 | this.spaces = 30;
19 |
20 | let size = 1550;
21 |
22 | this.addObject(new Object3D({
23 | id: 'plane1',
24 | position: new Point(-size / 2, 200, -1500),
25 | geometry: new Plane(size, this.spaces),
26 | options: {
27 | rgbaColor: {
28 | r: 100,
29 | g: 100,
30 | b: 255,
31 | a: 1
32 | },
33 | drawPoints: false,
34 | drawNormals: false
35 | }
36 | }))
37 | .addLight(new SpotLight({
38 | id: 'light1',
39 | position: new Point(0, 300, -800),
40 | options: {
41 | rgbaColor: {
42 | r: 255,
43 | g: 255,
44 | b: 255,
45 | a: 0.3
46 | }
47 | }
48 | }));
49 |
50 | this.alpha = 0;
51 | let obj = this.getObject('plane1');
52 | this.points = obj.getGeometry().getPoints();
53 |
54 | obj.rotate('y', 90, new Point(0, 200, size / 2))
55 | }
56 | beforeRender() {
57 | if (this.alpha > 360) {
58 | this.alpha -= 360;
59 | } else {
60 | this.alpha += 4;
61 | }
62 |
63 | for (let i = 0; i <= this.spaces; i++) {
64 | for (let j = 0; j <= this.spaces; j++) {
65 | let p = this.points[i * (this.spaces + 1) + j];
66 | p.setCoords(p.getX(), Math.cos(Utils.degToRad(this.alpha - i * 20)) * 50, p.getZ());
67 | }
68 | }
69 | }
70 | }
71 | export default WavesScene;
--------------------------------------------------------------------------------
/resources/obj/cube.obj:
--------------------------------------------------------------------------------
1 |
2 | # cube.obj
3 | #
4 |
5 | o cube
6 |
7 | v -0.500000 -0.500000 0.500000
8 | v 0.500000 -0.500000 0.500000
9 | v -0.500000 0.500000 0.500000
10 | v 0.500000 0.500000 0.500000
11 | v -0.500000 0.500000 -0.500000
12 | v 0.500000 0.500000 -0.500000
13 | v -0.500000 -0.500000 -0.500000
14 | v 0.500000 -0.500000 -0.500000
15 |
16 | vt 0.000000 0.000000
17 | vt 1.000000 0.000000
18 | vt 0.000000 1.000000
19 | vt 1.000000 1.000000
20 |
21 | vn 0.000000 0.000000 1.000000
22 | vn 0.000000 1.000000 0.000000
23 | vn 0.000000 0.000000 -1.000000
24 | vn 0.000000 -1.000000 0.000000
25 | vn 1.000000 0.000000 0.000000
26 | vn -1.000000 0.000000 0.000000
27 |
28 | g cube
29 |
30 | s 1
31 | f 1/1/1 2/2/1 3/3/1
32 | f 3/3/1 2/2/1 4/4/1
33 | s 2
34 | f 3/1/2 4/2/2 5/3/2
35 | f 5/3/2 4/2/2 6/4/2
36 | s 3
37 | f 5/4/3 6/3/3 7/2/3
38 | f 7/2/3 6/3/3 8/1/3
39 | s 4
40 | f 7/1/4 8/2/4 1/3/4
41 | f 1/3/4 8/2/4 2/4/4
42 | s 5
43 | f 2/1/5 8/2/5 4/3/5
44 | f 4/3/5 8/2/5 6/4/5
45 | s 6
46 | f 7/1/6 1/2/6 5/3/6
47 | f 5/3/6 1/2/6 3/4/6
48 |
--------------------------------------------------------------------------------
/resources/obj/diamond.obj:
--------------------------------------------------------------------------------
1 | # diamond.obj
2 |
3 | o diamond
4 |
5 | v 0.000000 0.000000 0.780000
6 | v 0.450000 0.450000 0.000000
7 | v 0.450000 -0.450000 0.000000
8 | v -0.450000 -0.450000 0.000000
9 | v -0.450000 0.450000 0.000000
10 | v 0.000000 0.000000 -0.780000
11 |
12 | f 1 2 3
13 | f 1 3 4
14 | f 1 4 5
15 | f 1 5 2
16 | f 6 5 4
17 | f 6 4 3
18 | f 6 3 2
19 | f 6 2 1
20 | f 6 1 5
21 |
--------------------------------------------------------------------------------
/resources/obj/icosahedron.obj:
--------------------------------------------------------------------------------
1 |
2 | g icosahedron
3 |
4 | v 0 -0.525731 0.850651
5 | v 0.850651 0 0.525731
6 | v 0.850651 0 -0.525731
7 | v -0.850651 0 -0.525731
8 | v -0.850651 0 0.525731
9 | v -0.525731 0.850651 0
10 | v 0.525731 0.850651 0
11 | v 0.525731 -0.850651 0
12 | v -0.525731 -0.850651 0
13 | v 0 -0.525731 -0.850651
14 | v 0 0.525731 -0.850651
15 | v 0 0.525731 0.850651
16 |
17 | f 2 3 7
18 | f 2 8 3
19 | f 4 5 6
20 | f 5 4 9
21 | f 7 6 12
22 | f 6 7 11
23 | f 10 11 3
24 | f 11 10 4
25 | f 8 9 10
26 | f 9 8 1
27 | f 12 1 2
28 | f 1 12 5
29 | f 7 3 11
30 | f 2 7 12
31 | f 4 6 11
32 | f 6 5 12
33 | f 3 8 10
34 | f 8 2 1
35 | f 4 10 9
36 | f 5 9 1
37 |
--------------------------------------------------------------------------------
/resources/obj/shuttle.obj:
--------------------------------------------------------------------------------
1 | # Viewpoint Datalabs International, Inc. Copyright 1996
2 |
3 | o shuttle
4 |
5 | g
6 | v 3.070224 -0.119728 0.996443
7 | v 5.942016 -0.012019 4.157199
8 | v 6.614015 -0.063428 4.157199
9 | v 5.759114 0.000000 1.664500
10 | v 3.070224 -0.449143 0.929434
11 | v 5.000295 -0.539011 1.315104
12 | v 3.070224 -0.604752 0.872464
13 | v 3.070224 -0.866525 0.730690
14 | v 3.070224 -0.959007 0.650256
15 | v 3.070224 -1.053631 0.163277
16 | v 2.983248 -1.080021 -0.880639
17 | v 6.130317 -1.100022 -1.106943
18 | v 3.739287 -4.334102 -0.876958
19 | v 4.400283 -4.682100 -0.952940
20 | v 3.038248 -4.334102 -0.811319
21 | v 3.180259 -4.550090 -0.921939
22 | v 2.700250 -4.334102 -0.947940
23 | v 0.840214 -2.480049 -1.050312
24 | v 1.208789 -1.060728 0.203820
25 | v 1.208789 -1.054148 0.411073
26 | v 1.208789 -0.958092 0.610367
27 | v 1.208789 -0.875165 0.685964
28 | v 1.208789 -0.621528 0.854704
29 | v 1.208789 -0.467365 0.922276
30 | v -4.649089 -1.039587 0.209476
31 | v -4.649345 -0.922345 0.432259
32 | v -4.649708 -0.652575 0.753550
33 | v -4.999902 -1.012545 0.094530
34 | v -4.999240 -0.870266 0.347384
35 | v -4.999321 -0.802315 0.416133
36 | v -4.906714 -0.620194 0.686502
37 | v -4.999759 -0.491153 0.805206
38 | v -5.568033 -0.119200 0.568687
39 | v -5.349121 -0.814175 0.247113
40 | v -5.348800 -0.938377 -0.030175
41 | v -6.499984 -0.676000 -0.433500
42 | v -6.499984 -0.610000 -0.164800
43 | v -6.499984 -0.240000 0.109600
44 | v -7.649984 0.000000 -0.620000
45 | v 1.209237 -1.080021 -1.321617
46 | v 3.070224 0.119728 0.996443
47 | v 3.093016 0.040804 1.276300
48 | v 6.614015 0.063428 4.157199
49 | v 3.070224 0.449143 0.929434
50 | v 5.000295 0.539011 1.315104
51 | v 3.070224 0.604752 0.872464
52 | v 3.070224 0.866525 0.730690
53 | v 5.000295 1.149023 1.260104
54 | v 3.070224 0.959007 0.650256
55 | v 3.070224 1.053627 0.449897
56 | v 5.000295 1.428028 0.442095
57 | v 3.070224 1.053631 0.163277
58 | v 2.983248 1.080021 -0.880639
59 | v 5.000295 1.302926 -1.259946
60 | v 3.739287 4.334102 -0.876958
61 | v 4.400283 4.682100 -0.952940
62 | v 3.038248 4.334102 -0.811319
63 | v 3.180259 4.550090 -0.921939
64 | v 1.209237 1.080021 -0.636414
65 | v 2.700250 4.334102 -0.947940
66 | v 0.169216 1.990039 -1.063281
67 | v 1.208789 1.060728 0.203820
68 | v 1.208789 1.054148 0.411073
69 | v 1.208789 0.958092 0.610367
70 | v 1.208789 0.875165 0.685964
71 | v 1.208789 0.621528 0.854704
72 | v 1.208789 0.467365 0.922276
73 | v -4.649089 1.039587 0.209476
74 | v -4.649345 0.922345 0.432259
75 | v -4.649708 0.652575 0.753550
76 | v -4.649856 0.514670 0.885149
77 | v -4.649964 0.160748 0.994500
78 | v -4.999902 1.012545 0.094530
79 | v -4.999240 0.870266 0.347384
80 | v -4.999321 0.802315 0.416133
81 | v -4.999759 0.491153 0.805206
82 | v -4.999948 0.160720 0.980689
83 | v -5.299752 0.147914 0.811038
84 | v -5.349121 0.814175 0.247113
85 | v -5.348800 0.938377 -0.030175
86 | v -6.499984 0.676000 -0.433500
87 | v -6.499931 0.693962 -0.748535
88 | v -6.499984 0.610000 -0.164800
89 | v -6.499984 0.523000 -0.048800
90 | v -6.499984 0.240000 0.109600
91 | v 1.209237 1.080021 -1.321617
92 | v -5.568033 0.119200 0.568687
93 | v -5.299752 -0.147914 0.811038
94 | v -4.999948 -0.160720 0.980689
95 | v -4.649964 -0.160748 0.994500
96 | v 1.208789 -0.130179 0.996071
97 | v 1.208789 0.130179 0.996071
98 | v 3.093016 -0.040804 1.276300
99 | v 5.942016 0.012019 4.157199
100 | v 7.043714 0.000000 4.157199
101 | v 4.998233 -0.130896 1.193100
102 | v 5.171283 -1.310384 -1.055942
103 | v 6.130317 1.100022 -1.106943
104 | v 2.983248 -1.080021 -1.351649
105 | v 2.983248 1.080021 -1.351649
106 | v -6.499931 -0.693962 -0.748535
107 | v -4.999902 -1.000020 -0.943979
108 | v 0.169216 -1.990039 -1.063281
109 | v 5.000295 -1.510030 0.750093
110 | v 5.000295 -0.874017 1.399122
111 | v 5.000295 -1.149023 1.260104
112 | v 5.000295 0.874017 1.399122
113 | v -7.074984 -0.304058 -0.264426
114 | v -7.074984 0.139529 -0.169387
115 | v -7.074984 0.304058 -0.264426
116 | v -7.074957 0.403450 -0.684268
117 | v -7.074984 0.393008 -0.495246
118 | v -7.074984 0.354637 -0.334026
119 | v -7.074984 0.057454 -0.155083
120 | v -7.074984 -0.354637 -0.334026
121 | v -7.074984 -0.393008 -0.495246
122 | v -7.074957 -0.403450 -0.684268
123 | v -7.074984 -0.139529 -0.169387
124 | v -7.074984 -0.057454 -0.155083
125 | v 5.257180 -0.244260 -0.448877
126 | v 5.275361 -0.389797 -0.446328
127 | v 5.534085 -0.255527 -0.410058
128 | v 5.858724 -0.171973 -0.364548
129 | v 6.246687 -0.127423 -0.310161
130 | v 6.245811 -0.209802 -0.310283
131 | v 5.957494 -0.242908 -0.350702
132 | v 5.684797 -0.367023 -0.388930
133 | v 5.030259 -0.310424 -0.039389
134 | v 5.218888 -0.403501 -0.175729
135 | v 5.254566 -0.476272 -0.297997
136 | v 5.497149 -0.409135 -0.146573
137 | v 5.811742 -0.367356 -0.029404
138 | v 6.194348 -0.345081 0.063191
139 | v 6.203377 -0.386271 -0.007583
140 | v 5.919040 -0.402825 -0.076394
141 | v 5.661265 -0.464884 -0.221067
142 | v 5.030257 -0.815056 -0.039376
143 | v 5.218887 -0.721987 -0.175721
144 | v 5.254566 -0.649223 -0.297993
145 | v 5.497147 -0.716354 -0.146565
146 | v 5.811740 -0.758129 -0.029394
147 | v 6.194347 -0.780403 0.063202
148 | v 6.203376 -0.739216 -0.007574
149 | v 5.919039 -0.722663 -0.076386
150 | v 5.661264 -0.660610 -0.221062
151 | v 5.533661 -0.562752 -0.410117
152 | v 5.257178 -0.881243 -0.448860
153 | v 5.275359 -0.735706 -0.446319
154 | v 5.534083 -0.869976 -0.410042
155 | v 5.858722 -0.953530 -0.364528
156 | v 6.246684 -0.998080 -0.310138
157 | v 6.245809 -0.915701 -0.310265
158 | v 5.957492 -0.882595 -0.350685
159 | v 5.684796 -0.758480 -0.388920
160 | v 5.151601 -0.815102 -0.904963
161 | v 5.295470 -0.722016 -0.722016
162 | v 5.296154 -0.649239 -0.594654
163 | v 5.571022 -0.716382 -0.673535
164 | v 5.905705 -0.758165 -0.699682
165 | v 6.299025 -0.780442 -0.683500
166 | v 6.288245 -0.739248 -0.612975
167 | v 5.995947 -0.722692 -0.625000
168 | v 5.708329 -0.660628 -0.556788
169 | v 5.295474 -0.403530 -0.722041
170 | v 5.296155 -0.476288 -0.594668
171 | v 5.571025 -0.409163 -0.673559
172 | v 5.905710 -0.367392 -0.699712
173 | v 6.299029 -0.345120 -0.683534
174 | v 6.288249 -0.386303 -0.613002
175 | v 5.995951 -0.402854 -0.625025
176 | v 5.708331 -0.464902 -0.556803
177 | v 5.218888 0.403501 -0.175729
178 | v 5.257180 0.244260 -0.448877
179 | v 5.254566 0.476272 -0.297997
180 | v 5.275361 0.389797 -0.446328
181 | v 5.497149 0.409135 -0.146573
182 | v 5.534085 0.255527 -0.410058
183 | v 5.811742 0.367356 -0.029404
184 | v 5.858724 0.171973 -0.364548
185 | v 6.194348 0.345081 0.063191
186 | v 6.246687 0.127423 -0.310161
187 | v 6.203377 0.386271 -0.007583
188 | v 6.245811 0.209802 -0.310283
189 | v 5.919040 0.402825 -0.076394
190 | v 5.957494 0.242908 -0.350702
191 | v 5.661265 0.464884 -0.221067
192 | v 5.684797 0.367023 -0.388930
193 | v 5.218887 0.721987 -0.175721
194 | v 5.254566 0.649223 -0.297993
195 | v 5.497147 0.716354 -0.146565
196 | v 5.811740 0.758129 -0.029394
197 | v 6.194347 0.780403 0.063202
198 | v 6.203376 0.739216 -0.007574
199 | v 5.919039 0.722663 -0.076386
200 | v 5.661264 0.660610 -0.221062
201 | v 5.257178 0.881243 -0.448860
202 | v 5.275359 0.735706 -0.446319
203 | v 5.534083 0.869976 -0.410042
204 | v 5.858722 0.953530 -0.364528
205 | v 6.246684 0.998080 -0.310138
206 | v 6.245809 0.915701 -0.310265
207 | v 5.957492 0.882595 -0.350685
208 | v 5.684796 0.758480 -0.388920
209 | v 5.533661 0.562752 -0.410117
210 | v 5.295470 0.722016 -0.722016
211 | v 5.296154 0.649239 -0.594654
212 | v 5.571022 0.716382 -0.673535
213 | v 5.905705 0.758165 -0.699682
214 | v 6.299025 0.780442 -0.683500
215 | v 6.288245 0.739248 -0.612975
216 | v 5.995947 0.722692 -0.625000
217 | v 5.708329 0.660628 -0.556788
218 | v 5.295474 0.403530 -0.722041
219 | v 5.296155 0.476288 -0.594668
220 | v 5.571025 0.409163 -0.673559
221 | v 5.905710 0.367392 -0.699712
222 | v 6.299029 0.345120 -0.683534
223 | v 6.288249 0.386303 -0.613002
224 | v 5.995951 0.402854 -0.625025
225 | v 5.708331 0.464902 -0.556803
226 | v 5.165639 -0.318491 0.637328
227 | v 5.166101 -0.159250 0.913146
228 | v 4.998497 -0.252327 1.074635
229 | v 5.183997 -0.172954 0.637297
230 | v 5.184248 -0.086480 0.787078
231 | v 5.445252 -0.307224 0.636859
232 | v 5.445698 -0.153617 0.902920
233 | v 5.773065 -0.390779 0.636310
234 | v 5.773632 -0.195395 0.974730
235 | v 6.164821 -0.435329 0.635652
236 | v 6.165453 -0.217671 1.012654
237 | v 6.163937 -0.352950 0.635654
238 | v 6.164450 -0.176480 0.941314
239 | v 5.872800 -0.319843 0.636142
240 | v 5.873264 -0.159926 0.913131
241 | v 5.597437 -0.195729 0.636604
242 | v 5.597722 -0.097867 0.806108
243 | v 5.444824 0.000000 0.636860
244 | v 5.166102 0.159236 0.913155
245 | v 5.184248 0.086472 0.787083
246 | v 5.445698 0.153603 0.902928
247 | v 5.773632 0.195378 0.974740
248 | v 6.165453 0.217651 1.012665
249 | v 6.164450 0.176464 0.941323
250 | v 5.873265 0.159912 0.913140
251 | v 5.597722 0.097858 0.806113
252 | v 5.165639 0.318491 0.637345
253 | v 4.997765 0.504639 0.637636
254 | v 5.183997 0.172954 0.637307
255 | v 5.445252 0.307224 0.636875
256 | v 5.773065 0.390779 0.636330
257 | v 6.164821 0.435329 0.635675
258 | v 6.163937 0.352950 0.635673
259 | v 5.872800 0.319843 0.636159
260 | v 5.597437 0.195729 0.636614
261 | v 5.165176 0.159265 0.361518
262 | v 4.997031 0.252350 0.200598
263 | v 5.183746 0.086488 0.487521
264 | v 5.444806 0.153631 0.370806
265 | v 5.772497 0.195413 0.297899
266 | v 6.164188 0.217691 0.258662
267 | v 6.163424 0.176496 0.330003
268 | v 5.872335 0.159941 0.359162
269 | v 5.597153 0.097876 0.467105
270 | v 5.165176 -0.159221 0.361493
271 | v 4.997031 -0.252281 0.200558
272 | v 5.183746 -0.086464 0.487507
273 | v 5.444806 -0.153589 0.370782
274 | v 5.772497 -0.195360 0.297868
275 | v 6.164188 -0.217631 0.258628
276 | v 6.163424 -0.176448 0.329975
277 | v 5.872335 -0.159897 0.359136
278 | v 5.597153 -0.097850 0.467090
279 | v 5.090927 -1.067391 -0.472156
280 | v 5.171283 1.310384 -1.055942
281 | v 5.151606 0.310470 -0.905003
282 | v 5.151606 -0.310470 -0.905003
283 | v 5.030257 0.815056 -0.039376
284 | v 5.030259 0.310424 -0.039389
285 | v 5.090930 -0.058113 -0.472183
286 | v 5.090930 0.058113 -0.472183
287 | v 5.000295 -1.210004 0.173074
288 | v 5.000295 1.210004 0.173074
289 | v 5.000295 -1.428028 0.442095
290 | v 4.997764 -0.504639 0.637610
291 | v 4.998497 0.252304 1.074648
292 | v 4.998233 0.130896 1.193100
293 | v 5.000295 1.510030 0.750093
294 | v 5.151601 0.815102 -0.904963
295 | v 5.090927 1.067391 -0.472156
296 | v 3.070224 -1.053627 0.449897
297 | v -5.349205 0.737229 0.323968
298 | v -5.349205 -0.737229 0.323968
299 | v -5.349476 -0.470935 0.566062
300 | v -6.499984 -0.098825 0.133439
301 | v -6.499984 0.098825 0.133439
302 | v -6.499984 -0.523000 -0.048800
303 | v -5.349476 0.470935 0.566062
304 | v -4.999902 1.000020 -0.943979
305 | v 0.840214 2.480049 -1.050312
306 | v 1.209237 -1.080021 -0.636414
307 | v 3.804262 4.682100 -0.938960
308 | v 5.000295 -1.302926 -1.259946
309 | v 3.804262 -4.682100 -0.938960
310 | v -4.649856 -0.514670 0.885149
311 | v -4.999492 0.681710 0.569242
312 | v -4.649417 0.860391 0.497003
313 | v -4.906714 0.620194 0.686502
314 | v -4.649417 -0.860391 0.497003
315 | v -4.999492 -0.681710 0.569242
316 | # 310 vertices
317 |
318 | # 0 vertex parms
319 |
320 | # 0 texture vertices
321 |
322 | # 0 normals
323 |
324 | g windows
325 | usemtl glass
326 | s 1
327 | f 310 32 294
328 | f 76 308 306
329 | f 294 88 33
330 | f 310 31 32
331 | f 88 294 32
332 | f 87 33 88 78
333 | f 87 78 298
334 | f 298 76 306
335 | f 298 78 76
336 | g tail
337 | usemtl bone
338 | s 4
339 | f 95 3 96 4
340 | f 4 287 43 95
341 | f 94 2 3 43
342 | f 3 2 93 96
343 | f 41 1 93 42
344 | f 41 42 287
345 | f 43 3 95
346 | f 287 42 94 43
347 | f 42 93 2 94
348 | f 96 93 1
349 | g rearbody
350 | s 6
351 | f 275 98 54
352 | f 96 223 286 287
353 | f 97 277 155
354 | f 276 281 280 277
355 | f 276 275 289
356 | f 283 282 128 279
357 | f 283 290 275
358 | f 257 51 248
359 | f 282 303 97
360 | f 96 6 106
361 | f 303 12 97
362 | f 104 285 223
363 | f 97 155 274
364 | f 284 282 266
365 | f 286 288 287
366 | f 137 128 282
367 | f 283 279 278
368 | f 248 288 286
369 | f 6 105 106
370 | f 275 54 283
371 | f 284 266 285
372 | f 96 287 4
373 | f 284 285 104
374 | f 248 51 288
375 | f 283 278 290
376 | f 274 137 282
377 | f 289 275 290
378 | f 97 12 98 275
379 | f 48 107 45
380 | f 96 106 104
381 | f 282 283 257 266
382 | f 97 275 276 277
383 | f 104 223 96
384 | f 257 283 51
385 | f 97 274 282
386 | f 128 280 281 279
387 | f 287 288 48
388 | f 287 48 45
389 | g body
390 | s 7
391 | f 309 31 310
392 | f 294 33 295
393 | f 108 118 39
394 | f 80 79 74 73
395 | f 49 47 48
396 | f 5 1 91 24
397 | f 10 291 20 19
398 | f 294 295 38
399 | f 78 77 76
400 | f 81 82 111 112
401 | f 65 66 46 47
402 | f 30 309 310
403 | f 5 105 6
404 | f 30 29 26 309
405 | f 68 62 59 299
406 | f 78 88 89 77
407 | f 118 38 295 119
408 | f 83 81 112 113
409 | f 64 65 47 49
410 | f 35 37 36
411 | f 23 8 7
412 | f 24 91 90 305
413 | f 62 52 53 59
414 | f 296 85 109 114
415 | f 79 292 75 74
416 | f 50 49 288
417 | f 22 23 27
418 | f 282 10 11 303
419 | f 293 294 297
420 | f 71 72 92 67
421 | f 112 39 113
422 | f 310 294 293
423 | f 305 90 89
424 | f 308 70 307
425 | f 296 87 298
426 | f 114 39 119
427 | f 71 77 72
428 | f 45 107 44
429 | f 8 23 22
430 | f 7 5 24 23
431 | f 287 44 41
432 | f 307 69 74 75
433 | f 92 91 1 41
434 | f 63 62 68
435 | f 28 29 34 35
436 | f 105 7 8 106
437 | f 32 89 88
438 | f 49 48 288
439 | f 82 81 299
440 | f 115 37 297 108
441 | f 113 39 110
442 | f 73 74 69 68
443 | f 29 30 293 34
444 | f 291 104 9
445 | f 22 27 309
446 | f 54 53 52 283
447 | f 83 79 80
448 | f 83 80 81
449 | f 48 47 46 107
450 | f 25 20 21 26
451 | f 301 11 10 19
452 | f 39 115 108
453 | f 306 307 75
454 | f 110 39 109
455 | f 292 298 306
456 | f 306 308 307
457 | f 70 66 65
458 | f 294 38 297
459 | f 5 6 96
460 | f 85 84 110 109
461 | f 62 63 50 52
462 | f 102 25 28
463 | f 9 106 8
464 | f 310 293 30
465 | f 70 71 66
466 | f 77 89 90 72
467 | f 66 71 67
468 | f 297 37 34 293
469 | f 106 9 104
470 | f 25 19 20
471 | f 44 107 46
472 | f 85 296 298
473 | f 117 101 36 116
474 | f 111 39 112
475 | f 307 70 65
476 | f 35 34 37
477 | f 23 305 27
478 | f 102 301 19 25
479 | f 50 288 51
480 | f 80 73 299
481 | f 84 298 292
482 | f 49 50 63 64
483 | f 32 305 89
484 | f 1 5 96
485 | f 32 31 27 305
486 | f 66 67 44 46
487 | f 296 295 33 87
488 | f 291 10 282
489 | f 81 80 299
490 | f 309 27 31
491 | f 84 85 298
492 | f 116 36 37 115
493 | f 292 79 83 84
494 | f 283 52 51
495 | f 309 26 21 22
496 | f 284 291 282
497 | f 102 36 101
498 | f 65 64 69 307
499 | f 295 296 114 119
500 | f 73 68 299
501 | f 39 116 115
502 | f 105 5 7
503 | f 23 24 305
504 | f 39 117 116
505 | f 77 71 76
506 | f 109 39 114
507 | f 297 38 118 108
508 | f 75 292 306
509 | f 39 118 119
510 | f 21 20 291 9
511 | f 9 8 22 21
512 | f 287 45 44
513 | f 71 70 308 76
514 | f 84 83 113 110
515 | f 67 92 41 44
516 | f 25 26 29 28
517 | f 104 291 284
518 | f 102 28 35
519 | f 69 64 63 68
520 | f 72 90 91 92
521 | f 52 50 51
522 | f 102 35 36
523 | g wings
524 | s 5
525 | f 16 15 17
526 | f 304 15 16
527 | f 300 57 60
528 | f 14 13 304
529 | f 59 53 55 57
530 | f 60 57 58
531 | f 18 301 103
532 | f 300 59 57
533 | f 304 13 15
534 | f 56 55 53 54
535 | f 15 13 11 301
536 | f 61 59 300
537 | f 57 55 302
538 | f 103 301 102
539 | f 17 15 301
540 | f 303 11 13 14
541 | f 58 57 302
542 | f 302 55 56
543 | f 17 301 18
544 | f 299 59 61
545 | g tiles
546 | usemtl fldkdkgrey
547 | s 3
548 | f 302 56 54
549 | f 18 103 40
550 | f 16 17 99
551 | f 86 61 300
552 | f 99 304 16
553 | f 303 14 304
554 | f 99 303 304
555 | f 17 18 99
556 | f 302 54 100
557 | f 58 302 100
558 | f 100 86 300
559 | f 18 40 99
560 | f 100 60 58
561 | f 100 300 60
562 | f 101 117 111 82
563 | f 102 101 82 299
564 | f 117 39 111
565 | f 99 100 54 303
566 | f 303 54 98 12
567 | f 86 100 99 40
568 | f 40 103 61 86
569 | f 299 61 103 102
570 | g enginside
571 | usemtl redbrick
572 | s 9
573 | f 238 255 246
574 | f 194 202 201 193
575 | f 153 162 163 154
576 | f 144 153 154 145
577 | f 184 194 193 182
578 | f 238 246 237
579 | f 272 234 232 271
580 | f 236 237 235 234
581 | f 204 195 186
582 | f 134 143 144 135
583 | f 143 152 153 144
584 | f 204 203 195
585 | f 237 246 245 235
586 | f 273 236 234 272
587 | f 238 237 236
588 | f 185 184 182 183
589 | f 135 144 145 136
590 | f 154 163 146
591 | f 195 203 202 194
592 | f 235 245 244 233
593 | f 264 273 272 263
594 | f 219 185 183 218
595 | f 187 186 184 185
596 | f 136 145 146
597 | f 161 169 170 162
598 | f 204 220 212
599 | f 255 264 263 254
600 | f 234 235 233 232
601 | f 186 195 194 184
602 | f 145 154 146
603 | f 152 161 162 153
604 | f 204 212 203
605 | f 246 255 254 245
606 | f 238 236 273
607 | f 204 187 220
608 | f 169 125 126 170
609 | f 126 135 136 127
610 | f 163 171 146
611 | f 203 212 211 202
612 | f 245 254 253 244
613 | f 238 273 264
614 | f 211 219 218 210
615 | f 170 126 127 171
616 | f 127 136 146
617 | f 162 170 171 163
618 | f 202 211 210 201
619 | f 238 264 255
620 | f 254 263 262 253
621 | f 212 220 219 211
622 | f 171 127 146
623 | f 125 134 135 126
624 | f 204 186 187
625 | f 220 187 185 219
626 | f 263 272 271 262
627 | g engout
628 | usemtl black
629 | f 251 260 259 250
630 | f 209 217 216 208
631 | f 157 165 166 158
632 | f 132 141 142 133
633 | f 179 178 176 177
634 | f 215 177 175 214
635 | f 270 230 228 269
636 | f 227 241 240 225
637 | f 191 199 198 190
638 | f 150 159 160 151
639 | f 131 140 141 132
640 | f 177 176 174 175
641 | f 230 231 229 228
642 | f 269 228 226 268
643 | f 229 242 241 227
644 | f 192 200 199 191
645 | f 139 148 149 140
646 | f 130 139 140 131
647 | f 180 192 191 178
648 | f 228 229 227 226
649 | f 268 226 224 267
650 | f 231 243 242 229
651 | f 176 190 189 174
652 | f 140 149 150 141
653 | f 149 158 159 150
654 | f 190 198 197 189
655 | f 243 252 251 242
656 | f 259 268 267 258
657 | f 216 179 177 215
658 | f 181 180 178 179
659 | f 121 130 131 122
660 | f 167 123 124 168
661 | f 208 216 215 207
662 | f 250 259 258 249
663 | f 252 261 260 251
664 | f 198 207 206 197
665 | f 158 166 167 159
666 | f 123 132 133 124
667 | f 166 122 123 167
668 | f 207 215 214 206
669 | f 261 270 269 260
670 | f 241 250 249 240
671 | f 199 208 207 198
672 | f 159 167 168 160
673 | f 122 131 132 123
674 | f 165 121 122 166
675 | f 217 181 179 216
676 | f 260 269 268 259
677 | f 242 251 250 241
678 | f 200 209 208 199
679 | f 148 157 158 149
680 | f 141 150 151 142
681 | f 178 191 190 176
682 | f 226 227 225 224
683 | g engmount
684 | usemtl brass
685 | s 11
686 | f 225 240 239 222
687 | f 164 120 121 165
688 | f 128 137 138 129
689 | f 196 205 289 290
690 | f 265 221 285 266
691 | f 206 214 213 205
692 | f 138 147 148 139
693 | f 174 189 188 172
694 | f 249 258 256 247
695 | f 221 222 223 285
696 | f 155 277 164 156
697 | f 274 155 156 147
698 | f 213 173 281 276
699 | f 258 267 265 256
700 | f 189 197 196 188
701 | f 120 129 130 121
702 | f 173 172 279 281
703 | f 239 247 248 286
704 | f 205 213 276 289
705 | f 137 274 147 138
706 | f 156 164 165 157
707 | f 224 225 222 221
708 | f 247 256 257 248
709 | f 172 188 278 279
710 | f 280 128 129 120
711 | f 188 196 290 278
712 | f 256 265 266 257
713 | f 214 175 173 213
714 | f 147 156 157 148
715 | f 175 174 172 173
716 | f 240 249 247 239
717 | f 222 239 286 223
718 | f 277 280 120 164
719 | f 129 138 139 130
720 | f 197 206 205 196
721 | f 267 224 221 265
722 | g engrim
723 | usemtl dkdkgrey
724 | s off
725 | f 233 244 243 231
726 | f 124 133 134 125
727 | f 262 271 270 261
728 | f 142 151 152 143
729 | f 253 262 261 252
730 | f 151 160 161 152
731 | f 244 253 252 243
732 | f 160 168 169 161
733 | f 201 210 209 200
734 | f 271 232 230 270
735 | f 133 142 143 134
736 | f 232 233 231 230
737 | f 183 182 180 181
738 | f 218 183 181 217
739 | f 182 193 192 180
740 | f 210 218 217 209
741 | f 193 201 200 192
742 | f 168 124 125 169
743 | # 393 elements
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangf/Javascript-3D-Engine/a93e377448440a1e726db3fb2e27ed962ffd8859/screenshot.png
--------------------------------------------------------------------------------
/screenshot2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juangf/Javascript-3D-Engine/a93e377448440a1e726db3fb2e27ed962ffd8859/screenshot2.png
--------------------------------------------------------------------------------