├── robots.txt
├── .gitignore
├── .prettierrc
├── _redirects
├── src
├── app
│ ├── src
│ │ ├── utils
│ │ │ ├── lerp.ts
│ │ │ ├── constants.ts
│ │ │ ├── updateDebug.ts
│ │ │ ├── debounce.ts
│ │ │ ├── declarations.d.ts
│ │ │ ├── globalState.ts
│ │ │ ├── printMat4.ts
│ │ │ ├── EventDispatcher.ts
│ │ │ └── MouseMove.ts
│ │ ├── shaders
│ │ │ ├── post
│ │ │ │ ├── vertex.glsl
│ │ │ │ └── fragment.glsl
│ │ │ ├── background
│ │ │ │ ├── vertex.glsl
│ │ │ │ └── fragment.glsl
│ │ │ └── default
│ │ │ │ ├── vertex.glsl
│ │ │ │ └── fragment.glsl
│ │ ├── lib
│ │ │ ├── GeometriesManager.ts
│ │ │ ├── Util.ts
│ │ │ ├── parseOBJ.ts
│ │ │ ├── Camera.ts
│ │ │ ├── Mesh.ts
│ │ │ ├── ShaderProgram.ts
│ │ │ └── TexturesManager.ts
│ │ ├── App.ts
│ │ ├── Components
│ │ │ └── Objects3D.ts
│ │ └── Scene.ts
│ └── index.ts
├── eleventy
│ ├── index.md
│ ├── public
│ │ └── assets
│ │ │ └── models
│ │ │ ├── efa
│ │ │ ├── efa.avif
│ │ │ ├── efa.png
│ │ │ ├── efa.webp
│ │ │ └── efa.obj
│ │ │ ├── f22
│ │ │ ├── f22.avif
│ │ │ ├── f22.png
│ │ │ ├── f22.webp
│ │ │ └── f22.obj
│ │ │ ├── crab
│ │ │ └── crab.png
│ │ │ ├── cube
│ │ │ ├── cube.png
│ │ │ └── cube.obj
│ │ │ └── f117
│ │ │ ├── f117.png
│ │ │ └── f117.obj
│ └── _includes
│ │ ├── layouts
│ │ └── base.njk
│ │ └── components
│ │ └── footer.njk
└── styles
│ ├── utils
│ ├── responsive.scss
│ └── variables.scss
│ ├── index.scss
│ ├── pages
│ └── landing.scss
│ ├── components
│ ├── containers.scss
│ └── footer.scss
│ └── base
│ ├── global.scss
│ └── reset.scss
├── tsconfig.json
├── .eleventy.js
├── README.md
├── rollup.config.js
├── scss.js
└── package.json
/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .env
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 120,
3 | "semi": true,
4 | "singleQuote": true
5 | }
6 |
--------------------------------------------------------------------------------
/_redirects:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/src/app/src/utils/lerp.ts:
--------------------------------------------------------------------------------
1 | export const lerp = (p1: number, p2: number, t: number) => {
2 | return p1 + (p2 - p1) * t;
3 | };
4 |
--------------------------------------------------------------------------------
/src/app/src/utils/constants.ts:
--------------------------------------------------------------------------------
1 | const DEFAULT_FPS = 60;
2 |
3 | export const constants = {
4 | DT_FPS: 1000 / DEFAULT_FPS,
5 | };
6 |
--------------------------------------------------------------------------------
/src/eleventy/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: 'layouts/base.njk'
3 | ---
4 |
5 |
6 | {% include "components/footer.njk" %}
7 |
8 |
--------------------------------------------------------------------------------
/src/eleventy/public/assets/models/efa/efa.avif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michalzalobny/webgl-3d-engine/HEAD/src/eleventy/public/assets/models/efa/efa.avif
--------------------------------------------------------------------------------
/src/eleventy/public/assets/models/efa/efa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michalzalobny/webgl-3d-engine/HEAD/src/eleventy/public/assets/models/efa/efa.png
--------------------------------------------------------------------------------
/src/eleventy/public/assets/models/efa/efa.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michalzalobny/webgl-3d-engine/HEAD/src/eleventy/public/assets/models/efa/efa.webp
--------------------------------------------------------------------------------
/src/eleventy/public/assets/models/f22/f22.avif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michalzalobny/webgl-3d-engine/HEAD/src/eleventy/public/assets/models/f22/f22.avif
--------------------------------------------------------------------------------
/src/eleventy/public/assets/models/f22/f22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michalzalobny/webgl-3d-engine/HEAD/src/eleventy/public/assets/models/f22/f22.png
--------------------------------------------------------------------------------
/src/eleventy/public/assets/models/f22/f22.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michalzalobny/webgl-3d-engine/HEAD/src/eleventy/public/assets/models/f22/f22.webp
--------------------------------------------------------------------------------
/src/eleventy/public/assets/models/crab/crab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michalzalobny/webgl-3d-engine/HEAD/src/eleventy/public/assets/models/crab/crab.png
--------------------------------------------------------------------------------
/src/eleventy/public/assets/models/cube/cube.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michalzalobny/webgl-3d-engine/HEAD/src/eleventy/public/assets/models/cube/cube.png
--------------------------------------------------------------------------------
/src/eleventy/public/assets/models/f117/f117.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michalzalobny/webgl-3d-engine/HEAD/src/eleventy/public/assets/models/f117/f117.png
--------------------------------------------------------------------------------
/src/styles/utils/responsive.scss:
--------------------------------------------------------------------------------
1 | @media only screen and (min-width: 768px) {
2 | }
3 |
4 | @media only screen and (min-width: 1025px) {
5 | }
6 |
7 | @media only screen and (min-width: 1921px) {
8 | }
9 |
--------------------------------------------------------------------------------
/src/app/src/utils/updateDebug.ts:
--------------------------------------------------------------------------------
1 | import { globalState } from "./globalState";
2 |
3 | export const updateDebug = (text: string) => {
4 | if (!globalState.debugHolderEl) return;
5 | globalState.debugHolderEl.innerHTML = text;
6 | };
7 |
--------------------------------------------------------------------------------
/src/styles/utils/variables.scss:
--------------------------------------------------------------------------------
1 | :root {
2 | --easing-1: cubic-bezier(0.64, 0.02, 0.16, 0.97);
3 |
4 | --font-size-1: 12px;
5 | --font-size-2: 13px;
6 |
7 | --t-1: 0.45s;
8 | --t-2: 0.7s;
9 |
10 | --s-1: 10px;
11 | --s-2: 20px;
12 | }
13 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2020",
4 | "module": "commonjs",
5 | "strict": true,
6 | "skipLibCheck": true,
7 | "esModuleInterop": true
8 | },
9 | "include": ["src/app/**/*"],
10 | "exclude": ["node_modules"]
11 | }
12 |
--------------------------------------------------------------------------------
/src/styles/index.scss:
--------------------------------------------------------------------------------
1 | @import './utils/variables.scss';
2 | @import './utils/responsive.scss';
3 |
4 | @import './base/reset.scss';
5 | @import './base/global.scss';
6 |
7 | @import './pages/landing.scss';
8 |
9 | @import './components/containers.scss';
10 | @import './components/footer.scss';
11 |
--------------------------------------------------------------------------------
/src/app/src/utils/debounce.ts:
--------------------------------------------------------------------------------
1 | export const debounce = (func: Function, wait: number) => {
2 | let timeout: ReturnType | null = null;
3 |
4 | const debounced = (...args: any) => {
5 | timeout && clearTimeout(timeout);
6 | timeout = setTimeout(() => func(...args), wait);
7 | };
8 |
9 | return debounced;
10 | };
11 |
--------------------------------------------------------------------------------
/src/app/src/utils/declarations.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.glsl" {
2 | const value: string;
3 | export default value;
4 | }
5 |
6 | declare module "*.glb" {
7 | const value: string;
8 | export default value;
9 | }
10 |
11 | declare module "*.mp4" {
12 | const src: string;
13 | export default src;
14 | }
15 |
16 | declare module "*.mp3" {
17 | const src: string;
18 | export default src;
19 | }
20 |
--------------------------------------------------------------------------------
/src/styles/pages/landing.scss:
--------------------------------------------------------------------------------
1 | .debug-holder {
2 | position: fixed;
3 | z-index: 100;
4 | top: 50px;
5 | right: 50px;
6 | color: rgb(0, 0, 0);
7 | background-color: rgb(206, 206, 206);
8 | border: 1px solid rgb(0, 0, 0);
9 | font-size: 13px;
10 | padding: 5px 10px;
11 | display: none;
12 | }
13 |
14 | #canvas {
15 | position: absolute;
16 | z-index: -1;
17 | top: 0;
18 | left: 0;
19 | width: 100%;
20 | height: 100%;
21 | }
22 |
--------------------------------------------------------------------------------
/src/app/src/utils/globalState.ts:
--------------------------------------------------------------------------------
1 | import { App } from '../App';
2 |
3 | export const globalState = {
4 | stageSize: {
5 | value: [0, 0],
6 | },
7 | pixelRatio: {
8 | value: 1,
9 | },
10 | slowDownFactor: {
11 | value: 1,
12 | },
13 | uTime: {
14 | value: 0,
15 | },
16 | mouse2DTarget: {
17 | value: [0, 0],
18 | },
19 | mouse2DCurrent: {
20 | value: [0, 0],
21 | },
22 | app: null as App | null,
23 | debugHolderEl: null as HTMLDivElement | null,
24 | canvasEl: null as HTMLCanvasElement | null,
25 | };
26 |
--------------------------------------------------------------------------------
/src/styles/components/containers.scss:
--------------------------------------------------------------------------------
1 | .c-large {
2 | position: relative;
3 | max-width: 1920px;
4 | margin: 0 auto;
5 | padding: 0 var(--s-1);
6 |
7 | @media only screen and (min-width: 768px) {
8 | padding: 0 var(--s-2);
9 | }
10 | }
11 |
12 | .c-big {
13 | max-width: 1450px;
14 | margin: 0 auto;
15 | padding: 0 var(--s-1);
16 | }
17 |
18 | .c-medium {
19 | max-width: 955px;
20 | margin: 0 auto;
21 | padding: 0 var(--s-1);
22 | }
23 |
24 | .c-small {
25 | max-width: 480px;
26 | margin: 0 auto;
27 | padding: 0 var(--s-1);
28 | }
29 |
--------------------------------------------------------------------------------
/src/app/src/shaders/post/vertex.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | in vec3 a_position;
4 | in vec3 a_normal;
5 | in vec2 a_uv;
6 |
7 | uniform mat4 u_projectionMatrix;
8 | uniform mat4 u_modelMatrix;
9 | uniform mat4 u_viewMatrix;
10 |
11 | out vec2 v_uv;
12 |
13 | void main() {
14 | vec3 pos = a_position;
15 | // Clip space (GL_Position) is from -1 to 1.
16 | // Your square is a 1x1, so it doesn't fill the complete space.
17 | // Multiplying it by 2 makes it fill the whole range from -1 to 1
18 | // ~ Daniel Velasquez
19 | pos.xy *= 2.0;
20 |
21 | gl_Position = vec4(pos, 1.0);
22 |
23 | v_uv = a_uv;
24 | }
--------------------------------------------------------------------------------
/src/app/src/shaders/background/vertex.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | in vec3 a_position;
4 | in vec3 a_normal;
5 | in vec2 a_uv;
6 |
7 | uniform mat4 u_projectionMatrix;
8 | uniform mat4 u_modelMatrix;
9 | uniform mat4 u_viewMatrix;
10 |
11 | out vec2 v_uv;
12 |
13 | void main() {
14 | vec3 pos = a_position;
15 | // Clip space (GL_Position) is from -1 to 1.
16 | // Your square is a 1x1, so it doesn't fill the complete space.
17 | // Multiplying it by 2 makes it fill the whole range from -1 to 1
18 | // ~ Daniel Velasquez
19 | pos.xy *= 2.0;
20 |
21 | gl_Position = vec4(pos, 1.0);
22 |
23 | v_uv = a_uv;
24 | }
--------------------------------------------------------------------------------
/src/app/src/utils/printMat4.ts:
--------------------------------------------------------------------------------
1 | import { mat4 } from "gl-matrix";
2 |
3 | export const printMat4 = (m: mat4) => {
4 | //Copy the matrix so we don't modify the original
5 | const mCopy = mat4.clone(m);
6 |
7 | // Example:
8 | // | 1 0 0 0 |
9 | // | 0 1 0 0 |
10 | // | 0 0 1 0 |
11 | // | 0 0 0 1 |
12 |
13 | const arr = Array.from(mCopy).map((n) => n.toFixed(2));
14 |
15 | console.log(
16 | `| ${arr[0]} ${arr[4]} ${arr[8]} ${arr[12]} |\n` +
17 | `| ${arr[1]} ${arr[5]} ${arr[9]} ${arr[13]} |\n` +
18 | `| ${arr[2]} ${arr[6]} ${arr[10]} ${arr[14]} |\n` +
19 | `| ${arr[3]} ${arr[7]} ${arr[11]} ${arr[15]} |`
20 | );
21 | };
22 |
--------------------------------------------------------------------------------
/.eleventy.js:
--------------------------------------------------------------------------------
1 | const htmlmin = require("html-minifier").minify;
2 |
3 | module.exports = function (eleventyConfig) {
4 | eleventyConfig.addPassthroughCopy("src/eleventy/public");
5 |
6 | //Minify HTML
7 | eleventyConfig.addTransform("htmlmin", function (content, outputPath) {
8 | if (outputPath && outputPath.endsWith(".html")) {
9 | let minified = htmlmin(content, {
10 | useShortDoctype: true,
11 | removeComments: true,
12 | collapseWhitespace: true,
13 | });
14 | return minified;
15 | }
16 | return content;
17 | });
18 |
19 | return {
20 | dir: {
21 | input: "src/eleventy",
22 | output: "dist",
23 | },
24 | };
25 | };
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WebGL2 3D Engine built from scratch
2 |
3 | The goal is to build a custom 3D engine without any libraries - just using WebGL2 API and what I have learnt while creating a [3D renderer in C](https://github.com/michalzalobny/3d-renderer-in-c). Guided by [WebGL2 Fundamentals](https://webgl2fundamentals.org/).
4 |
5 | I'm using here Right-Handed Coordinate System (positive Z axis points out of the screen). I'm also using column-major matrices layout - so I will be using post-multiplication -> `M * v` to transform a vector `v` by a matrix `M`.
6 |
7 | ## Lighthouse Performance
8 |
9 | - Application without any WebGL2 context scores 4x 100% on lighthouse, with 0ms of Total Blocking Time, 0.8s First and Largest Contentful Paint. Speed index is 0.8s.
10 | - After adding the WebGL2 context, the Total Blocking Time increases to 40ms.
11 | - After adding 4 textures to load into GPU and a model to parse, the Total Blocking Time increases to 60ms. The rest of the metrics are the same.
12 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | const resolve = require("@rollup/plugin-node-resolve");
2 | const swc = require("rollup-plugin-swc").default;
3 | const commonjs = require("@rollup/plugin-commonjs");
4 | const glslify = require("rollup-plugin-glslify");
5 |
6 | const isProduction = process.env.NODE_ENV === "production";
7 |
8 | // Configuration for your main application
9 | const mainConfig = {
10 | input: "src/app/index.ts",
11 | output: {
12 | dir: "dist/js",
13 | format: "esm",
14 | sourcemap: !isProduction,
15 | },
16 | plugins: [
17 | resolve({ extensions: [".js", ".ts"] }),
18 | commonjs(),
19 | glslify({
20 | include: ["**/*.vs", "**/*.fs", "**/*.vert", "**/*.frag", "**/*.glsl"],
21 | exclude: "node_modules/**",
22 | compress: true,
23 | }),
24 | swc({
25 | jsc: {
26 | target: "es2020",
27 | parser: {
28 | syntax: "typescript",
29 | },
30 | },
31 | sourceMaps: !isProduction,
32 | minify: isProduction,
33 | }),
34 | ],
35 | };
36 |
37 | module.exports = [mainConfig];
38 |
--------------------------------------------------------------------------------
/src/eleventy/public/assets/models/cube/cube.obj:
--------------------------------------------------------------------------------
1 |
2 | # cube.obj
3 | #
4 |
5 | mtllib cube.mtl
6 | o cube
7 |
8 | v -1.000000 -1.000000 1.000000
9 | v 1.000000 -1.000000 1.000000
10 | v -1.000000 1.000000 1.000000
11 | v 1.000000 1.000000 1.000000
12 | v -1.000000 1.000000 -1.000000
13 | v 1.000000 1.000000 -1.000000
14 | v -1.000000 -1.000000 -1.000000
15 | v 1.000000 -1.000000 -1.000000
16 |
17 | vt 1.000000 0.000000
18 | vt 0.000000 0.000000
19 | vt 1.000000 1.000000
20 | vt 0.000000 1.000000
21 |
22 | vn 0.000000 0.000000 1.000000
23 | vn 0.000000 1.000000 0.000000
24 | vn 0.000000 0.000000 -1.000000
25 | vn 0.000000 -1.000000 0.000000
26 | vn 1.000000 0.000000 0.000000
27 | vn -1.000000 0.000000 0.000000
28 |
29 | g cube
30 | usemtl cube
31 | s 1
32 | f 1/1/1 2/2/1 3/3/1
33 | f 3/3/1 2/2/1 4/4/1
34 | s 2
35 | f 3/1/2 4/2/2 5/3/2
36 | f 5/3/2 4/2/2 6/4/2
37 | s 3
38 | f 5/4/3 6/3/3 7/2/3
39 | f 7/2/3 6/3/3 8/1/3
40 | s 4
41 | f 7/1/4 8/2/4 1/3/4
42 | f 1/3/4 8/2/4 2/4/4
43 | s 5
44 | f 2/1/5 8/2/5 4/3/5
45 | f 4/3/5 8/2/5 6/4/5
46 | s 6
47 | f 7/1/6 1/2/6 5/3/6
48 | f 5/3/6 1/2/6 3/4/6
49 |
--------------------------------------------------------------------------------
/src/app/src/shaders/default/vertex.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | in vec3 a_position;
4 | in vec3 a_normal;
5 | in vec2 a_uv;
6 |
7 | uniform mat4 u_projectionMatrix;
8 | uniform mat4 u_modelMatrix;
9 | uniform mat4 u_viewMatrix;
10 |
11 | out vec2 v_uv;
12 | out vec3 v_fragNormal;
13 | out vec3 v_fragPosition;
14 |
15 | void main() {
16 | vec4 worldPosition = u_modelMatrix * vec4(a_position, 1.0);
17 | vec4 viewPosition = u_viewMatrix * worldPosition;
18 |
19 | gl_Position = u_projectionMatrix * viewPosition;
20 | // Used only in Point rendering mode
21 | gl_PointSize = 8.0;
22 | gl_PointSize = gl_PointSize / gl_Position.w;
23 |
24 | // Transform normal to world space (using the inverse transpose for normals)
25 | vec4 normal = vec4(a_normal, 0.0);
26 | mat4 normalMatrix = transpose(inverse(u_modelMatrix));
27 | normal = normalize(normalMatrix * normal);
28 | // Transform normal to view space
29 | normal = normalize(u_viewMatrix * normal);
30 |
31 | v_fragPosition = vec3(viewPosition);
32 | v_fragNormal = normal.xyz;
33 | v_uv = a_uv;
34 | }
--------------------------------------------------------------------------------
/src/app/index.ts:
--------------------------------------------------------------------------------
1 | import { globalState } from './src/utils/globalState';
2 | import { App } from './src/App';
3 | import { MouseMove } from './src/utils/MouseMove';
4 |
5 | const onMouseMove = (e: any) => {
6 | const mouseX = (e.target as MouseMove).mouse.x;
7 | const mouseY = (e.target as MouseMove).mouse.y;
8 |
9 | const stageX = globalState.stageSize.value[0];
10 | const stageY = globalState.stageSize.value[1];
11 |
12 | globalState.mouse2DTarget.value = [(mouseX / stageX) * 2 - 1, -(mouseY / stageY) * 2 + 1];
13 | };
14 |
15 | document.addEventListener('DOMContentLoaded', async () => {
16 | globalState.debugHolderEl = document.querySelector('.debug-holder') as HTMLDivElement;
17 |
18 | globalState.canvasEl = document.getElementById('canvas') as HTMLCanvasElement;
19 |
20 | const mouseMove = MouseMove.getInstance();
21 |
22 | mouseMove.addEventListener('mousemove', onMouseMove);
23 |
24 | // const { App } = await import("./App");
25 | globalState.app = new App();
26 | });
27 |
28 | // Don't allow to zoom
29 | document.addEventListener(
30 | 'touchstart',
31 | function (event) {
32 | if (event.touches.length > 1) {
33 | event.preventDefault();
34 | }
35 | },
36 | { passive: false }
37 | );
38 |
39 | let lastTouchEnd = 0;
40 | document.addEventListener(
41 | 'touchend',
42 | function (event) {
43 | const now = window.performance.now();
44 | if (now - lastTouchEnd <= 300) {
45 | event.preventDefault();
46 | }
47 | lastTouchEnd = now;
48 | },
49 | false
50 | );
51 |
--------------------------------------------------------------------------------
/scss.js:
--------------------------------------------------------------------------------
1 | const sass = require("node-sass");
2 | const postcss = require("postcss");
3 | const autoprefixer = require("autoprefixer");
4 | const CleanCSS = require("clean-css");
5 | const fs = require("fs-extra");
6 | const path = require("path");
7 |
8 | const srcDir = path.join(__dirname, "src", "styles");
9 | const outDir = path.join(__dirname, "dist", "css");
10 |
11 | const isProduction = process.env.NODE_ENV === "production";
12 |
13 | // Empty the output directory
14 | fs.emptyDirSync(outDir);
15 |
16 | fs.readdir(srcDir, (err, files) => {
17 | if (err) throw err;
18 |
19 | files.forEach((file) => {
20 | if (path.extname(file) === ".scss") {
21 | const srcFile = path.join(srcDir, file);
22 | const outFile = path.join(outDir, file.replace(".scss", ".css"));
23 |
24 | sass.render(
25 | {
26 | file: srcFile,
27 | outputStyle: isProduction ? "compressed" : "expanded",
28 | },
29 | (err, result) => {
30 | if (err) throw err;
31 |
32 | postcss([autoprefixer])
33 | .process(result.css, { from: srcFile, to: outFile })
34 | .then((result) => {
35 | let outputCss = result.css;
36 |
37 | if (isProduction) {
38 | outputCss = new CleanCSS({}).minify(result.css).styles;
39 | }
40 |
41 | fs.writeFile(outFile, outputCss, (err) => {
42 | if (err) throw err;
43 | });
44 | });
45 | }
46 | );
47 | }
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webgl-3d-engine",
3 | "version": "1.0.1",
4 | "description": "",
5 | "scripts": {
6 | "build": "npm run build:pre-eleventy && eleventy && cp _redirects dist/ && cp robots.txt dist/",
7 | "build:pre-eleventy": "npm run scss && rollup -c",
8 | "scss": "node scss.js",
9 | "watch:scss": "nodemon -e scss -x \"npm run scss\"",
10 | "rollup-compile-app": "rollup -c",
11 | "watch:rollup-compile-app": "rollup -cw",
12 | "dev": "npm run build:pre-eleventy && concurrently \"npm run watch:scss\" \"npm run watch:rollup-compile-app\" \"eleventy --serve\""
13 | },
14 | "keywords": [],
15 | "author": "",
16 | "license": "ISC",
17 | "dependencies": {
18 | "@11ty/eleventy": "2.0.1",
19 | "@rollup/plugin-commonjs": "25.0.4",
20 | "@rollup/plugin-node-resolve": "15.2.1",
21 | "@swc/core": "1.3.78",
22 | "@types/node": "20.4.2",
23 | "autoprefixer": "10.4.14",
24 | "chokidar-cli": "3.0.0",
25 | "clean-css": "5.3.2",
26 | "concurrently": "8.2.0",
27 | "dotenv": "16.3.1",
28 | "fs-extra": "11.1.1",
29 | "gl-matrix": "^3.4.3",
30 | "glob": "10.3.3",
31 | "glsl-noise": "0.0.0",
32 | "glslify": "7.1.1",
33 | "html-minifier": "4.0.0",
34 | "isomorphic-fetch": "3.0.0",
35 | "lil-gui": "0.17.0",
36 | "node-sass": "9.0.0",
37 | "nodemon": "3.0.1",
38 | "postcss": "8.4.26",
39 | "rollup": "3.28.1",
40 | "rollup-plugin-glslify": "1.3.1",
41 | "rollup-plugin-swc": "0.2.1"
42 | },
43 | "devDependencies": {
44 | "typescript": "5.1.6"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/app/src/lib/GeometriesManager.ts:
--------------------------------------------------------------------------------
1 | import { parseOBJ, GeometryObject } from '../lib/parseOBJ';
2 |
3 | interface AddGeometry {
4 | geometryUrl: string;
5 | geometryObject: GeometryObject;
6 | }
7 |
8 | export class GeometriesManager {
9 | private isReady = false;
10 | private loadedGeometries: Map = new Map();
11 |
12 | constructor() {}
13 |
14 | public getGeometry(geometryUrl: string) {
15 | const geometryObject = this.loadedGeometries.get(geometryUrl);
16 | if (!this.isReady) return null;
17 | if (!geometryObject) {
18 | console.error(`Geometry not found. ${geometryUrl} `);
19 | return null;
20 | }
21 | return geometryObject;
22 | }
23 |
24 | private async loadGeometry(elUrl: string) {
25 | const response = await fetch(elUrl);
26 | const text = await response.text();
27 | const objData = parseOBJ(text);
28 |
29 | this.addGeometry({ geometryUrl: elUrl, geometryObject: objData });
30 |
31 | return Promise.resolve();
32 | }
33 |
34 | public addGeometry({ geometryUrl, geometryObject }: AddGeometry) {
35 | if (this.loadedGeometries.has(geometryUrl)) {
36 | console.error(`Geometry already loaded. ${geometryUrl} `);
37 | return;
38 | }
39 | this.loadedGeometries.set(geometryUrl, geometryObject);
40 | }
41 |
42 | public async addObjectsToLoad(objsToLoad: string[]) {
43 | const promises = objsToLoad.map((geometryUrl) => {
44 | return this.loadGeometry(geometryUrl);
45 | });
46 | return Promise.allSettled(promises).then(() => {
47 | this.isReady = true;
48 | Promise.resolve();
49 | });
50 | }
51 |
52 | public destroy() {
53 | this.loadedGeometries.clear();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/app/src/shaders/background/fragment.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | precision highp float;
4 |
5 | in vec2 v_uv;
6 |
7 | uniform float u_time;
8 |
9 | uniform vec2 u_resolution;
10 |
11 | #define PI 3.14159265359
12 |
13 | float N21 (vec2 p){
14 | return fract(sin(p.x * 100.0 + p.y * 657.0) * 5647.0);
15 | }
16 |
17 | float SmoothNoise(vec2 uv){
18 | vec2 lv = fract(uv);
19 | vec2 id = floor(uv);
20 |
21 | lv = lv * lv * (3.0 - 2.0 * lv); //3x^2 - 2x^3
22 |
23 | float bl = N21(id);
24 | float br = N21(id + vec2(1.0, 0.0));
25 | float b = mix(bl, br, lv.x);
26 |
27 | float tl = N21(id + vec2(0.0, 1.0));
28 | float tr = N21(id + vec2(1.0, 1.0));
29 | float t = mix(tl, tr, lv.x);
30 |
31 | return mix(b, t, lv.y);
32 | }
33 |
34 | float SmoothNoise2(vec2 uv){
35 | float c = SmoothNoise(uv * 4.0);
36 | c += SmoothNoise(uv * 8.0) * 0.5;
37 | c += SmoothNoise(uv * 16.0) * 0.25;
38 | c += SmoothNoise(uv * 32.0) * 0.125;
39 | c += SmoothNoise(uv * 65.0) * 0.0625;
40 |
41 | return c /= 1.9375; // (sum of all possible maximum values)so that it's always between 0 - 1
42 | }
43 |
44 | // we need to declare an output for the fragment shader
45 | out vec4 outColor;
46 |
47 |
48 | void main() {
49 | vec2 uv = v_uv;
50 |
51 | //fix uv aspect ratio
52 | float aspect = u_resolution.x / u_resolution.y;
53 | uv.x *= aspect * 0.5;
54 |
55 | float t = u_time * 0.1;
56 |
57 | vec3 color1 = vec3(0.12, 0.12, 0.12);
58 | vec3 color2 = vec3(0.7, 0.7, 0.7);
59 | vec3 colorMixed = mix(color1, color2, uv.y);
60 |
61 | float c = SmoothNoise2(vec2(uv.x + t * 1.3, uv.y + t *0.1));
62 | float c2 = SmoothNoise2(vec2(uv.x + t * 8.0, uv.y));
63 | c = mix(c, c2, 0.2);
64 |
65 | colorMixed = mix(colorMixed, vec3(c), 0.31);
66 |
67 | outColor = vec4(colorMixed, 1.0);
68 | }
69 |
--------------------------------------------------------------------------------
/src/app/src/lib/Util.ts:
--------------------------------------------------------------------------------
1 | interface CreateAndInitBuffer {
2 | target: number;
3 | data: Float32Array | Uint16Array;
4 | gl: WebGL2RenderingContext;
5 | }
6 |
7 | export const createAndInitBuffer = (props: CreateAndInitBuffer) => {
8 | const { target, data, gl } = props;
9 | const buffer = gl.createBuffer();
10 | gl.bindBuffer(target, buffer);
11 | gl.bufferData(target, data, gl.STATIC_DRAW);
12 | gl.bindBuffer(target, null);
13 | return buffer;
14 | };
15 |
16 | interface SetupVertexAttribute {
17 | name: string;
18 | program: WebGLProgram | null;
19 | buffer: WebGLBuffer | null;
20 | size: number;
21 | gl: WebGL2RenderingContext;
22 | }
23 |
24 | export const setupVertexAttribute = (props: SetupVertexAttribute) => {
25 | const { name, program, buffer, size, gl } = props;
26 | if (!program) throw new Error("Could not create VAO, no program");
27 | const location = gl.getAttribLocation(program, name);
28 | if (location === -1) {
29 | console.warn(
30 | `Could not find attribute location for ${name}. Either the attribute is not used in the vertex shader or the name is misspelled.`
31 | );
32 | return null;
33 | }
34 | gl.enableVertexAttribArray(location);
35 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
36 | gl.vertexAttribPointer(location, size, gl.FLOAT, false, 0, 0);
37 | gl.bindBuffer(gl.ARRAY_BUFFER, null);
38 | return location;
39 | };
40 |
41 | interface UseTexture {
42 | gl: WebGL2RenderingContext | null;
43 | shaderProgram: WebGLProgram | null;
44 | uniformLocation: WebGLUniformLocation | undefined;
45 | textureIndex: number;
46 | texture: WebGLTexture;
47 | }
48 |
49 | export const useTexture = (props: UseTexture) => {
50 | const { gl, shaderProgram, texture, textureIndex, uniformLocation } = props;
51 |
52 | if (!gl || !shaderProgram) {
53 | throw new Error(
54 | "Cannot use texture. WebGL context or shader program is not available. "
55 | );
56 | }
57 |
58 | gl.activeTexture(gl.TEXTURE0 + textureIndex);
59 | gl.bindTexture(gl.TEXTURE_2D, texture);
60 |
61 | if (!uniformLocation) return;
62 |
63 | gl.uniform1i(uniformLocation, textureIndex);
64 | };
65 |
--------------------------------------------------------------------------------
/src/styles/components/footer.scss:
--------------------------------------------------------------------------------
1 | .footerIcons {
2 | position: fixed;
3 | bottom: 15px;
4 | right: 15px;
5 | z-index: 10;
6 |
7 | @media only screen and (min-width: 768px) {
8 | bottom: 25px;
9 | right: 25px;
10 | }
11 | }
12 |
13 | .footerName {
14 | position: fixed;
15 | bottom: 15px;
16 | left: 15px;
17 | z-index: 10;
18 |
19 | @media only screen and (min-width: 768px) {
20 | bottom: 25px;
21 | left: 25px;
22 | }
23 | }
24 |
25 | .nameWrapper {
26 | font-size: 13px;
27 | color: rgba(255, 255, 255, 0.6);
28 | line-height: 1.5;
29 | }
30 |
31 | .socials {
32 | display: flex;
33 | align-items: center;
34 | flex-direction: column;
35 |
36 | @media only screen and (min-width: 768px) {
37 | flex-direction: row;
38 | }
39 | }
40 |
41 | .iconWrapper {
42 | width: 18px;
43 |
44 | &:not(:last-child) {
45 | margin-bottom: 10px;
46 | padding-bottom: 10px;
47 | border-bottom: 1px solid rgba(255, 255, 255, 0.2);
48 | }
49 |
50 | @media only screen and (min-width: 768px) {
51 | &:not(:last-child) {
52 | margin-bottom: 0;
53 | padding-bottom: 0;
54 |
55 | margin-right: 15px;
56 | padding-right: 15px;
57 |
58 | width: calc(18px + 15px);
59 | border-bottom: 0;
60 | border-right: 1px solid rgba(255, 255, 255, 0.2);
61 | }
62 | }
63 |
64 | svg {
65 | fill: rgb(255, 255, 255);
66 | width: 100%;
67 | opacity: 0.6;
68 | transition: opacity 0.35s;
69 |
70 | &:hover {
71 | opacity: 0.85;
72 | }
73 | }
74 | }
75 |
76 | .underline {
77 | cursor: pointer;
78 | position: relative;
79 | display: inline-block;
80 | color: rgba(255, 255, 255, 0.9);
81 |
82 | &:before {
83 | content: '';
84 | position: absolute;
85 | top: 80%;
86 | width: 100%;
87 | height: 1px;
88 | background-color: currentColor;
89 | transform-origin: left;
90 | transform: scaleX(0);
91 | transition: transform var(--t-1) var(--easing-1);
92 | }
93 |
94 | &:hover {
95 | &:before {
96 | transform: scaleX(1);
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/app/src/utils/EventDispatcher.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * https://github.com/mrdoob/eventdispatcher.js/
3 | */
4 |
5 | interface BaseEvent {
6 | type: string;
7 | target?: EventDispatcher | null;
8 | [key: string]: any; // Allows for additional properties
9 | }
10 |
11 | interface Listener {
12 | (event: any): void;
13 | }
14 |
15 | interface EventMap {
16 | [type: string]: Listener[];
17 | }
18 |
19 | class EventDispatcher {
20 | _listeners?: EventMap;
21 |
22 | addEventListener(type: string, listener: Listener) {
23 | if (this._listeners === undefined) this._listeners = {};
24 |
25 | const listeners = this._listeners;
26 |
27 | if (listeners[type] === undefined) {
28 | listeners[type] = [];
29 | }
30 |
31 | if (listeners[type].indexOf(listener) === -1) {
32 | listeners[type].push(listener);
33 | }
34 | }
35 |
36 | hasEventListener(type: string, listener: Listener): boolean {
37 | if (this._listeners === undefined) return false;
38 |
39 | const listeners = this._listeners;
40 |
41 | return (
42 | listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1
43 | );
44 | }
45 |
46 | removeEventListener(type: string, listener: Listener) {
47 | if (this._listeners === undefined) return;
48 |
49 | const listeners = this._listeners;
50 | const listenerArray = listeners[type];
51 |
52 | if (listenerArray !== undefined) {
53 | const index = listenerArray.indexOf(listener);
54 |
55 | if (index !== -1) {
56 | listenerArray.splice(index, 1);
57 | }
58 | }
59 | }
60 |
61 | dispatchEvent(event: BaseEvent) {
62 | if (this._listeners === undefined) return;
63 |
64 | const listeners = this._listeners;
65 | const listenerArray = listeners[event.type];
66 |
67 | if (listenerArray !== undefined) {
68 | event.target = this;
69 |
70 | // Make a copy, in case listeners are removed while iterating.
71 | const array = listenerArray.slice(0);
72 |
73 | for (let i = 0, l = array.length; i < l; i++) {
74 | array[i].call(this, event);
75 | }
76 |
77 | event.target = null;
78 | }
79 | }
80 | }
81 |
82 | export { EventDispatcher };
83 |
--------------------------------------------------------------------------------
/src/app/src/App.ts:
--------------------------------------------------------------------------------
1 | import { globalState } from './utils/globalState';
2 | import { constants } from './utils/constants';
3 | import { debounce } from './utils/debounce';
4 | import { Scene } from './Scene';
5 |
6 | export class App {
7 | private rafId: number | null = null;
8 | private isResumed = true;
9 | private lastFrameTime: number | null = null;
10 | private scene = new Scene();
11 |
12 | constructor() {
13 | this.setPixelRatio(Math.min(window.devicePixelRatio, 2));
14 |
15 | this.addListeners();
16 | this.resumeAppFrame();
17 | this.onResize();
18 | }
19 |
20 | private onResizeDebounced = debounce(() => this.onResize(), 300);
21 |
22 | private onResize() {
23 | if (!globalState.canvasEl) return;
24 | const bounds = globalState.canvasEl.getBoundingClientRect();
25 | const stageX = bounds.width;
26 | const stageY = bounds.height;
27 | globalState.stageSize.value = [stageX, stageY];
28 |
29 | this.scene.onResize();
30 | }
31 |
32 | private setPixelRatio(pixelRatio: number) {
33 | globalState.pixelRatio.value = pixelRatio;
34 | }
35 |
36 | private onVisibilityChange = () => {
37 | if (document.hidden) {
38 | this.stopAppFrame();
39 | } else {
40 | this.resumeAppFrame();
41 | }
42 | };
43 |
44 | private addListeners() {
45 | window.addEventListener('resize', this.onResizeDebounced);
46 | window.addEventListener('visibilitychange', this.onVisibilityChange);
47 | }
48 |
49 | private resumeAppFrame() {
50 | this.isResumed = true;
51 | if (!this.rafId) {
52 | this.rafId = window.requestAnimationFrame(this.renderOnFrame);
53 | }
54 | }
55 |
56 | private renderOnFrame = (time: number) => {
57 | this.rafId = window.requestAnimationFrame(this.renderOnFrame);
58 |
59 | if (this.isResumed || !this.lastFrameTime) {
60 | this.lastFrameTime = window.performance.now();
61 | this.isResumed = false;
62 | return;
63 | }
64 |
65 | const delta = time - this.lastFrameTime;
66 | const slowDownFactor = delta / constants.DT_FPS;
67 | globalState.slowDownFactor.value = slowDownFactor;
68 | globalState.uTime.value = time * 0.001;
69 |
70 | this.lastFrameTime = time;
71 |
72 | this.scene.update();
73 | };
74 |
75 | private stopAppFrame() {
76 | if (this.rafId) {
77 | window.cancelAnimationFrame(this.rafId);
78 | this.rafId = null;
79 | }
80 | }
81 |
82 | public destroy() {
83 | this.stopAppFrame();
84 | window.removeEventListener('resize', this.onResizeDebounced);
85 | window.removeEventListener('visibilitychange', this.onVisibilityChange);
86 | this.scene.destroy();
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/eleventy/_includes/layouts/base.njk:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {# Meta Tags #}
5 |
6 |
7 |
8 |
9 |
10 |
11 | {# Title #}
12 | {{ title | default('WebGL 3D Engine') }}
13 |
14 | {# Favicon and Apple Touch Icons #}
15 | {# #}
16 | {# #}
17 |
18 | {# Canonical Tag #}
19 | {# #}
20 |
21 | {# Open Graph Tags #}
22 |
23 |
24 |
25 |
26 |
27 | {# Twitter Card Tags #}
28 |
29 |
30 |
31 |
32 |
33 | {# Stylesheet with Preload #}
34 |
35 |
36 |
37 |
38 |
39 |
40 | {# Main Content #}
41 |
42 |
43 |
44 | {{content | safe}}
45 |
46 |
47 |
48 |
49 | debug
50 |
51 |
52 | {# Scripts #}
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/styles/base/global.scss:
--------------------------------------------------------------------------------
1 | *,
2 | *:before,
3 | *:after {
4 | box-sizing: inherit;
5 | margin: 0;
6 | padding: 0;
7 | -webkit-touch-callout: none !important;
8 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important;
9 | -webkit-tap-highlight-color: transparent !important;
10 | }
11 |
12 | //Disable on touch devices
13 | @media (hover: hover) {
14 | [data-js-focus-visible] :focus:not([data-focus-visible-added]) {
15 | outline: none;
16 | }
17 | }
18 |
19 | html {
20 | box-sizing: border-box;
21 | -moz-osx-font-smoothing: grayscale;
22 | -webkit-font-smoothing: antialiased;
23 |
24 | font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Ubuntu, 'Helvetica Neue', sans-serif;
25 | }
26 |
27 | body {
28 | overflow-y: scroll;
29 | color: var(--foreground-rgb);
30 | background: var(--foreground-rgb);
31 | font-size: var(--font-size-1);
32 | line-height: 1.6;
33 |
34 | // &:before {
35 | // content: "";
36 | // position: absolute;
37 | // top: 50%;
38 | // left: 0;
39 | // height: 1px;
40 | // width: 100%;
41 | // background-color: red;
42 | // z-index: 1;
43 | // }
44 | }
45 |
46 | html {
47 | background: var(--background-rgb);
48 | }
49 |
50 | a {
51 | color: inherit;
52 | pointer-events: auto;
53 | text-decoration: none;
54 | display: inline-block;
55 | }
56 |
57 | a[href^='tel'] {
58 | color: inherit;
59 | text-decoration: none;
60 | }
61 |
62 | button {
63 | background: none;
64 | border: none;
65 | border-radius: none;
66 | color: inherit;
67 | font: inherit;
68 | pointer-events: auto;
69 | }
70 |
71 | input,
72 | textarea {
73 | appearance: none;
74 | background: none;
75 | border: none;
76 | border-radius: 0;
77 | pointer-events: auto;
78 | }
79 |
80 | img,
81 | svg {
82 | vertical-align: center;
83 | }
84 |
85 | canvas {
86 | position: fixed;
87 | z-index: -1;
88 | top: 0;
89 | left: 0;
90 | width: 100%;
91 | height: 100%;
92 | pointer-events: none;
93 | user-select: none;
94 | }
95 |
96 | //Creative
97 | body {
98 | position: fixed;
99 | top: 0;
100 | left: 0;
101 | width: 100%;
102 | height: 99.98%; //99.95%
103 | overflow: hidden;
104 | overscroll-behavior: none;
105 | background-color: black;
106 | }
107 |
108 | html {
109 | position: fixed;
110 | top: 0;
111 | left: 0;
112 | width: 100%;
113 | height: 100%;
114 | overscroll-behavior: none;
115 | }
116 |
117 | img {
118 | user-select: none;
119 | pointer-events: none;
120 | }
121 |
122 | *,
123 | *:before,
124 | *:after {
125 | user-select: none;
126 | }
127 |
128 | .sr-only {
129 | position: absolute;
130 | width: 1px;
131 | height: 1px;
132 | padding: 0;
133 | margin: -1px;
134 | overflow: hidden;
135 | clip: rect(0, 0, 0, 0);
136 | border: 0;
137 | }
138 |
--------------------------------------------------------------------------------
/src/app/src/shaders/default/fragment.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | precision highp float;
4 |
5 | in vec2 v_uv;
6 | in vec3 v_fragNormal;
7 | in vec3 v_fragPosition;
8 |
9 | uniform sampler2D u_image;
10 | uniform float u_time;
11 | uniform vec3 u_cameraPositionWorld;
12 | uniform mat4 u_viewMatrix;
13 |
14 | // we need to declare an output for the fragment shader
15 | out vec4 outColor;
16 |
17 | #define PI 3.14159265359
18 |
19 | float S(float a, float b, float t){
20 | return clamp((t - a) / (b - a), 0.0, 1.0);
21 | }
22 |
23 | vec2 Rain(vec2 uv, float t){
24 | t *= 40.0;
25 | vec2 a = vec2(3.0, 1.0);//aspect ratio
26 | vec2 st = uv * a;
27 | vec2 id = floor(st);
28 | st.y += t * 0.22;
29 | float n = fract(sin(id.x * 716.34) * 768.32);
30 | st.y += n; //offsets by random number in the given field
31 | uv.y +=n;
32 | id = floor(st); //recalculate id because of shifting
33 | st = fract(st) - 0.5;
34 | t += fract(sin(id.x * 76.34 + id.y * 1453.7) * 768.32) * 2.0 * PI;
35 |
36 | float y = -sin(t + sin(t + sin(t) * 0.5)) * 0.43;
37 | vec2 p1 = vec2(0.0, y);
38 |
39 | vec2 o1 = (st - p1)/a;
40 | float d = length(o1);
41 | float m1 = S(0.07, 0.0, d);
42 |
43 | vec2 o2 = (fract(uv * a.x * vec2(1.0, 2.0)) - 0.5) / vec2(1.0, 2.0);
44 | d = length(o2);
45 |
46 | float m2 = S(0.3 * (0.5 - st.y), 0.0, d) * S( -.1, .1, st.y - p1.y);
47 |
48 | // if(st.x> .48 || st.y > .49) m1 = 1.0;
49 | return vec2(m1 * o1 * 30.0 + m2 * o2 * 10.0);
50 | }
51 |
52 | void main() {
53 | vec2 uv = v_uv;
54 | uv.y = 1.0 - uv.y;
55 |
56 | // Rain effect
57 | float t = u_time * 0.1;
58 | vec2 rainDistort = Rain(vec2(uv.y, 1.0-uv.x) * 5.0, t) * 0.5;
59 | rainDistort += Rain(vec2(uv.y, 1.0-uv.x) * 7.0, t) * 0.5;
60 | uv += rainDistort;
61 |
62 | // Texture
63 | vec4 color = texture(u_image, uv);
64 |
65 | // Lighting
66 | vec3 lightPositionWorld = vec3(1.0, 3.0, 3.0);
67 |
68 | // Normal vector in view space
69 | vec3 normal = normalize(v_fragNormal);
70 |
71 | // Ambient lighting
72 | float ambient = 0.37;
73 |
74 | // Diffuse lighting
75 | vec3 lightPosition_view = (u_viewMatrix * vec4(lightPositionWorld, 1.0)).xyz;
76 | vec3 lightDirection = normalize(lightPosition_view - v_fragPosition);
77 | float diffuse = max(dot(normal, lightDirection), 0.0);
78 |
79 | // View direction in view space
80 | vec3 viewPosition = (u_viewMatrix * vec4(u_cameraPositionWorld, 1.0)).xyz;
81 | vec3 viewDirection = normalize(v_fragPosition - viewPosition);
82 |
83 | // Specular lighting
84 | vec3 reflectDirection = reflect(lightDirection, normal);
85 | float specularStrength = 0.5;
86 | float shininess = 32.0;
87 | float specular = pow(max(dot(viewDirection, reflectDirection), 0.0), shininess) * specularStrength;
88 |
89 | // Apply lighting
90 | vec3 lighting = vec3(ambient + diffuse + specular);
91 | color.rgb *= lighting;
92 |
93 | outColor = color;
94 | }
95 |
--------------------------------------------------------------------------------
/src/app/src/lib/parseOBJ.ts:
--------------------------------------------------------------------------------
1 | export interface GeometryObject {
2 | vertices: number[];
3 | normals: number[];
4 | texcoords: number[];
5 | }
6 |
7 | export const parseOBJ = (text: string): GeometryObject => {
8 | const vertices_lookup: number[][] = [];
9 | const normals_lookup: number[][] = [];
10 | const texcoords_lookup: number[][] = [];
11 |
12 | const finalVertices: number[][] = [];
13 | const finalNormals: number[][] = [];
14 | const finalTexcoords: number[][] = [];
15 |
16 | const lines = text.split('\n');
17 |
18 | for (let lineNo = 0; lineNo < lines.length; ++lineNo) {
19 | // Remove whitespace from both sides of a string
20 | const line = lines[lineNo].trim();
21 |
22 | // Skip comments and empty lines
23 | if (line === '' || line.startsWith('#')) {
24 | continue;
25 | }
26 |
27 | // Split a string into an array of substrings - after each space or tab
28 | const lineParts = line.split(/\s+/);
29 | const keyword = lineParts[0];
30 |
31 | // Load vertices_lookup
32 | if (keyword === 'v') {
33 | const x = parseFloat(lineParts[1]);
34 | const y = parseFloat(lineParts[2]);
35 | const z = parseFloat(lineParts[3]);
36 | vertices_lookup.push([x, y, z]);
37 | }
38 |
39 | // Load normals_lookup
40 | if (keyword === 'vn') {
41 | const x = parseFloat(lineParts[1]);
42 | const y = parseFloat(lineParts[2]);
43 | const z = parseFloat(lineParts[3]);
44 | normals_lookup.push([x, y, z]);
45 | }
46 |
47 | // Load texture coordinates lookup
48 | if (keyword === 'vt') {
49 | const u = parseFloat(lineParts[1]);
50 | const v = parseFloat(lineParts[2]);
51 | texcoords_lookup.push([u, v]);
52 | }
53 |
54 | // Load face data. It's in the format of "f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3"
55 | if (keyword === 'f') {
56 | const faceVertices: number[][] = [];
57 | const faceNormals: number[][] = [];
58 | const faceTexcoords: number[][] = [];
59 |
60 | for (let i = 1; i < lineParts.length; ++i) {
61 | const facePart = lineParts[i].split('/');
62 |
63 | const vertexIndex = parseInt(facePart[0]);
64 | const vertex = vertices_lookup[vertexIndex - 1];
65 | faceVertices.push(vertex);
66 |
67 | const texcoordIndex = parseInt(facePart[1]);
68 | const texcoord = texcoords_lookup[texcoordIndex - 1];
69 | faceTexcoords.push(texcoord);
70 |
71 | const normalIndex = parseInt(facePart[2]);
72 | const normal = normals_lookup[normalIndex - 1];
73 | faceNormals.push(normal);
74 | }
75 |
76 | // Add vertices, normals and texture coordinates to the final arrays
77 | finalVertices.push(...faceVertices);
78 | finalNormals.push(...faceNormals);
79 | finalTexcoords.push(...faceTexcoords);
80 | }
81 | }
82 |
83 | return {
84 | vertices: finalVertices.flat().map((v) => v * 0.04),
85 | normals: finalNormals.flat(),
86 | texcoords: finalTexcoords.flat(),
87 | };
88 | };
89 |
--------------------------------------------------------------------------------
/src/app/src/shaders/post/fragment.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | precision highp float;
4 |
5 | in vec2 v_uv;
6 |
7 | uniform float u_time;
8 | uniform sampler2D u_image;
9 | uniform vec2 u_resolution;
10 |
11 | // we need to declare an output for the fragment shader
12 | out vec4 outColor;
13 |
14 | #define PI 3.14159265359
15 |
16 | float N21 (vec2 p){
17 | return fract(sin(p.x * 100.0 + p.y * 657.0) * 5647.0);
18 | }
19 |
20 | float SmoothNoise(vec2 uv){
21 | vec2 lv = fract(uv);
22 | vec2 id = floor(uv);
23 |
24 | lv = lv * lv * (3.0 - 2.0 * lv); //3x^2 - 2x^3
25 |
26 | float bl = N21(id);
27 | float br = N21(id + vec2(1.0, 0.0));
28 | float b = mix(bl, br, lv.x);
29 |
30 | float tl = N21(id + vec2(0.0, 1.0));
31 | float tr = N21(id + vec2(1.0, 1.0));
32 | float t = mix(tl, tr, lv.x);
33 |
34 | return mix(b, t, lv.y);
35 | }
36 |
37 | float SmoothNoise2(vec2 uv){
38 | float c = SmoothNoise(uv * 4.0);
39 | c += SmoothNoise(uv * 8.0) * 0.5;
40 | c += SmoothNoise(uv * 16.0) * 0.25;
41 | c += SmoothNoise(uv * 32.0) * 0.125;
42 | c += SmoothNoise(uv * 65.0) * 0.0625;
43 |
44 | return c /= 1.9375; // (sum of all possible maximum values)so that it's always between 0 - 1
45 | }
46 |
47 | float S(float a, float b, float t){
48 | return clamp((t - a) / (b - a), 0.0, 1.0);
49 | }
50 |
51 | vec2 Rain(vec2 uv, float t){
52 | t *= 40.0;
53 | vec2 a = vec2(3.0, 1.0);//aspect ratio
54 | vec2 st = uv * a;
55 | vec2 id = floor(st);
56 | st.y += t * 0.22;
57 | float n = fract(sin(id.x * 716.34) * 768.32);
58 | st.y += n; //offsets by random number in the given field
59 | uv.y +=n;
60 | id = floor(st); //recalculate id because of shifting
61 | st = fract(st) - 0.5;
62 | t += fract(sin(id.x * 76.34 + id.y * 1453.7) * 768.32) * 2.0 * PI;
63 |
64 | float y = -sin(t + sin(t + sin(t) * 0.5)) * 0.43;
65 | vec2 p1 = vec2(0.0, y);
66 |
67 | vec2 o1 = (st - p1)/a;
68 | float d = length(o1);
69 | float m1 = S(0.07, 0.0, d);
70 |
71 | vec2 o2 = (fract(uv * a.x * vec2(1.0, 2.0)) - 0.5) / vec2(1.0, 2.0);
72 | d = length(o2);
73 |
74 | float m2 = S(0.3 * (0.5 - st.y), 0.0, d) * S( -.1, .1, st.y - p1.y);
75 |
76 | // if(st.x> .48 || st.y > .49) m1 = 1.0;
77 | return vec2(m1 * o1 * 10.0 + m2 * o2 * 2.0);
78 | }
79 |
80 | void main() {
81 | vec2 uv = v_uv;
82 |
83 | //fix uv aspect ratio
84 | vec2 uv_aspect = uv;
85 |
86 | float aspect = u_resolution.x / u_resolution.y;
87 | uv_aspect.x *= aspect;
88 |
89 | // Rain effect
90 | float t = u_time * 0.1;
91 | vec2 rainDistort = Rain(uv_aspect * 15.0, t) * 0.1;
92 | rainDistort += Rain(uv_aspect * 9.0, t) * 0.4;
93 | uv += rainDistort *0.4;
94 |
95 | float c = SmoothNoise2(vec2(uv_aspect.x + t * 1.3, uv_aspect.y + t *0.1));
96 |
97 | // Texture
98 | vec4 color = texture(u_image, uv);
99 |
100 | color = mix(color, vec4(c), 0.02);
101 |
102 | outColor = color;
103 | }
104 |
--------------------------------------------------------------------------------
/src/styles/base/reset.scss:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/
2 | v2.0 | 20110126
3 | License: none (public domain)
4 | */
5 |
6 | html,
7 | body,
8 | div,
9 | span,
10 | applet,
11 | object,
12 | iframe,
13 | h1,
14 | h2,
15 | h3,
16 | h4,
17 | h5,
18 | h6,
19 | p,
20 | blockquote,
21 | pre,
22 | a,
23 | abbr,
24 | acronym,
25 | address,
26 | big,
27 | cite,
28 | code,
29 | del,
30 | dfn,
31 | em,
32 | img,
33 | ins,
34 | kbd,
35 | q,
36 | s,
37 | samp,
38 | small,
39 | strike,
40 | strong,
41 | sub,
42 | sup,
43 | tt,
44 | var,
45 | b,
46 | u,
47 | i,
48 | center,
49 | dl,
50 | dt,
51 | dd,
52 | ol,
53 | ul,
54 | li,
55 | fieldset,
56 | form,
57 | label,
58 | legend,
59 | table,
60 | caption,
61 | tbody,
62 | tfoot,
63 | thead,
64 | tr,
65 | th,
66 | td,
67 | article,
68 | aside,
69 | canvas,
70 | details,
71 | embed,
72 | figure,
73 | figcaption,
74 | footer,
75 | header,
76 | hgroup,
77 | menu,
78 | nav,
79 | output,
80 | ruby,
81 | section,
82 | summary,
83 | time,
84 | mark,
85 | audio,
86 | video {
87 | margin: 0;
88 | padding: 0;
89 | border: 0;
90 | font-size: 100%;
91 | font: inherit;
92 | vertical-align: baseline;
93 | }
94 |
95 | article,
96 | aside,
97 | details,
98 | figcaption,
99 | figure,
100 | footer,
101 | header,
102 | hgroup,
103 | menu,
104 | nav,
105 | section {
106 | display: block;
107 | }
108 | body {
109 | line-height: 1;
110 | }
111 | ol,
112 | ul,
113 | li {
114 | list-style: none;
115 | }
116 | blockquote,
117 | q {
118 | quotes: none;
119 | }
120 | blockquote:before,
121 | blockquote:after,
122 | q:before,
123 | q:after {
124 | content: "";
125 | content: none;
126 | }
127 | table {
128 | border-collapse: collapse;
129 | border-spacing: 0;
130 | }
131 |
132 | // rverts css from reset.scss
133 | .revertCss {
134 | html,
135 | body,
136 | div,
137 | span,
138 | applet,
139 | object,
140 | iframe,
141 | h1,
142 | h2,
143 | h3,
144 | h4,
145 | h5,
146 | h6,
147 | p,
148 | blockquote,
149 | pre,
150 | a,
151 | abbr,
152 | acronym,
153 | address,
154 | big,
155 | cite,
156 | code,
157 | del,
158 | dfn,
159 | em,
160 | img,
161 | ins,
162 | kbd,
163 | q,
164 | s,
165 | samp,
166 | small,
167 | strike,
168 | strong,
169 | sub,
170 | sup,
171 | tt,
172 | var,
173 | b,
174 | u,
175 | i,
176 | center,
177 | dl,
178 | dt,
179 | dd,
180 | ol,
181 | ul,
182 | li,
183 | fieldset,
184 | form,
185 | label,
186 | legend,
187 | table,
188 | caption,
189 | tbody,
190 | tfoot,
191 | thead,
192 | tr,
193 | th,
194 | td,
195 | article,
196 | aside,
197 | canvas,
198 | details,
199 | embed,
200 | figure,
201 | figcaption,
202 | footer,
203 | header,
204 | hgroup,
205 | menu,
206 | nav,
207 | output,
208 | ruby,
209 | section,
210 | summary,
211 | time,
212 | mark,
213 | audio,
214 | video {
215 | margin: revert;
216 | padding: revert;
217 | border: revert;
218 | font-size: revert;
219 | font: revert;
220 | vertical-align: revert;
221 | }
222 |
223 | article,
224 | aside,
225 | details,
226 | figcaption,
227 | figure,
228 | footer,
229 | header,
230 | hgroup,
231 | menu,
232 | nav,
233 | section {
234 | display: revert;
235 | }
236 | body {
237 | line-height: revert;
238 | }
239 | ol,
240 | ul,
241 | li {
242 | list-style: revert;
243 | }
244 | blockquote,
245 | q {
246 | quotes: revert;
247 | }
248 | blockquote:before,
249 | blockquote:after,
250 | q:before,
251 | q:after {
252 | content: revert;
253 | content: revert;
254 | }
255 | table {
256 | border-collapse: revert;
257 | border-spacing: revert;
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/src/app/src/utils/MouseMove.ts:
--------------------------------------------------------------------------------
1 | import { EventDispatcher } from "./EventDispatcher";
2 |
3 | interface Mouse {
4 | x: number;
5 | y: number;
6 | }
7 |
8 | export class MouseMove extends EventDispatcher {
9 | _mouseLast: Mouse = { x: 0, y: 0 };
10 | _isTouching = false;
11 | _clickStart: Mouse = { x: 0, y: 0 };
12 | mouse: Mouse = { x: 0, y: 0 };
13 | strength = 0;
14 | _isInit = false;
15 |
16 | static _instance: MouseMove | null;
17 | static _canCreate = false;
18 | static getInstance() {
19 | if (!MouseMove._instance) {
20 | MouseMove._canCreate = true;
21 | MouseMove._instance = new MouseMove();
22 | MouseMove._canCreate = false;
23 | }
24 |
25 | return MouseMove._instance;
26 | }
27 |
28 | constructor() {
29 | super();
30 |
31 | if (MouseMove._instance || !MouseMove._canCreate) {
32 | throw new Error("Use MouseMove.getInstance()");
33 | }
34 |
35 | this._addEvents();
36 |
37 | MouseMove._instance = this;
38 | }
39 |
40 | _onTouchDown = (event: TouchEvent | MouseEvent) => {
41 | this._isInit = true;
42 | this._isTouching = true;
43 | this._mouseLast.x =
44 | "touches" in event ? event.touches[0].clientX : event.clientX;
45 | this._mouseLast.y =
46 | "touches" in event ? event.touches[0].clientY : event.clientY;
47 |
48 | this.mouse.x = this._mouseLast.x;
49 | this.mouse.y = this._mouseLast.y;
50 |
51 | this._clickStart.x = this.mouse.x;
52 | this._clickStart.y = this.mouse.y;
53 | this.dispatchEvent({ type: "down" });
54 | };
55 |
56 | _onTouchMove = (event: TouchEvent | MouseEvent) => {
57 | this._isInit = true;
58 | const touchX =
59 | "touches" in event ? event.touches[0].clientX : event.clientX;
60 | const touchY =
61 | "touches" in event ? event.touches[0].clientY : event.clientY;
62 |
63 | const deltaX = touchX - this._mouseLast.x;
64 | const deltaY = touchY - this._mouseLast.y;
65 |
66 | this.strength = deltaX * deltaX + deltaY * deltaY;
67 |
68 | this._mouseLast.x = touchX;
69 | this._mouseLast.y = touchY;
70 |
71 | this.mouse.x += deltaX;
72 | this.mouse.y += deltaY;
73 |
74 | this.dispatchEvent({ type: "mousemove" });
75 | this._mouseLast.x = this.mouse.x;
76 | this._mouseLast.y = this.mouse.y;
77 | };
78 |
79 | _onTouchUp = () => {
80 | this._isTouching = false;
81 | this.dispatchEvent({ type: "up" });
82 | };
83 |
84 | _onMouseLeave = () => {
85 | this.dispatchEvent({ type: "left" });
86 | };
87 |
88 | _onClick = (e: any) => {
89 | // Dont react if the user clicked on a button or a link
90 | if (
91 | e.target instanceof HTMLButtonElement ||
92 | e.target instanceof HTMLAnchorElement
93 | ) {
94 | // console.warn("The clicked element is not a canvas");
95 | return;
96 | }
97 | this._isInit = true;
98 | const clickBounds = 10;
99 | const xDiff = Math.abs(this._clickStart.x - this.mouse.x);
100 | const yDiff = Math.abs(this._clickStart.y - this.mouse.y);
101 |
102 | //Make sure that the user's click is held between certain boundaries
103 | if (xDiff <= clickBounds && yDiff <= clickBounds) {
104 | this.dispatchEvent({ type: "click" });
105 | }
106 | };
107 |
108 | _addEvents() {
109 | window.addEventListener("pointerdown", this._onTouchDown);
110 |
111 | window.addEventListener("mousemove", this._onTouchMove, { passive: true });
112 | window.addEventListener("touchmove", this._onTouchMove, { passive: true });
113 |
114 | window.addEventListener("pointerup", this._onTouchUp);
115 |
116 | window.addEventListener("click", this._onClick);
117 | window.addEventListener("mouseout", this._onMouseLeave);
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/eleventy/_includes/components/footer.njk:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/app/src/lib/Camera.ts:
--------------------------------------------------------------------------------
1 | import { mat4, vec3 } from 'gl-matrix';
2 |
3 | interface MakeProjectionMatrix {
4 | fov: number;
5 | aspect_ratio: number;
6 | near: number;
7 | far: number;
8 | }
9 |
10 | interface MakeLookAtMatrix {
11 | eye?: vec3;
12 | target?: vec3;
13 | up?: vec3;
14 | }
15 |
16 | export class Camera {
17 | public perspectiveProjectionMatrix = mat4.create();
18 | public orthoProjectionMatrix = mat4.create();
19 | public viewMatrix = mat4.create();
20 |
21 | public position = vec3.fromValues(0, 0, 0.36);
22 | private target = vec3.fromValues(0, 0, -1);
23 | private up = vec3.fromValues(0, 1, 0);
24 |
25 | constructor() {
26 | this.viewMatrix = this.makeLookAtMatrix({
27 | eye: this.position,
28 | target: this.target,
29 | up: this.up,
30 | });
31 | }
32 |
33 | private makePerspectiveProjMatrix(props: MakeProjectionMatrix) {
34 | const { fov, aspect_ratio, near, far } = props;
35 |
36 | const r = near * Math.tan(fov / 2.0) * aspect_ratio; // aspect_ratio = width / height
37 | const t = near * Math.tan(fov / 2.0);
38 | const f = far;
39 |
40 | const l = -r;
41 | const b = -t;
42 | const n = near;
43 |
44 | const out = mat4.create();
45 |
46 | //Perspective projection: https://www.songho.ca/opengl/gl_projectionmatrix.html
47 | out[0] = (2 * n) / (r - l); // m[0][0]
48 | out[5] = (2 * n) / (t - b); // m[1][1]
49 | out[8] = (r + l) / (r - l); // m[2][0]
50 | out[9] = (t + b) / (t - b); // m[2][1]
51 | out[10] = -(f + n) / (f - n); // m[2][2]
52 | out[11] = -1.0; // m[2][3]
53 | out[14] = -(2 * f * n) / (f - n); // m[3][2]
54 | out[15] = 0.0; // m[3][3]
55 |
56 | return out;
57 | }
58 |
59 | private makeOrthoProjMatrix(props: MakeProjectionMatrix) {
60 | const { fov, aspect_ratio, near, far } = props;
61 |
62 | const r = near * Math.tan(fov / 2.0) * aspect_ratio; // aspect_ratio = width / height
63 | const t = near * Math.tan(fov / 2.0);
64 | const f = far;
65 |
66 | const l = -r;
67 | const b = -t;
68 | const n = near;
69 |
70 | const out = mat4.create();
71 |
72 | //Orthographic projection: https://www.songho.ca/opengl/gl_projectionmatrix.html
73 | out[0] = 2 / (r - l); // m[0][0]
74 | out[5] = 2 / (t - b); // m[1][1]
75 | out[10] = -2 / (f - n); // m[2][2]
76 | out[12] = -(r + l) / (r - l); // m[0][3]
77 | out[13] = -(t + b) / (t - b); // m[1][3]
78 | out[14] = -(f + n) / (f - n); // m[2][3]
79 | out[15] = 1.0; // m[3][3]
80 |
81 | return out;
82 | }
83 |
84 | private makeLookAtMatrix(props: MakeLookAtMatrix) {
85 | const { eye = this.position, target = this.target, up = this.up } = props;
86 |
87 | // LookAt matrix: https://www.songho.ca/opengl/gl_camera.html
88 | const forward = vec3.create();
89 | vec3.subtract(forward, eye, target);
90 | vec3.normalize(forward, forward);
91 |
92 | const left = vec3.create();
93 | vec3.cross(left, up, forward);
94 | vec3.normalize(left, left);
95 |
96 | const newUp = vec3.create();
97 | vec3.cross(newUp, forward, left);
98 |
99 | const out = mat4.create();
100 |
101 | out[0] = left[0];
102 | out[1] = newUp[0];
103 | out[2] = forward[0];
104 | out[3] = 0;
105 |
106 | out[4] = left[1];
107 | out[5] = newUp[1];
108 | out[6] = forward[1];
109 | out[7] = 0;
110 |
111 | out[8] = left[2];
112 | out[9] = newUp[2];
113 | out[10] = forward[2];
114 | out[11] = 0;
115 |
116 | out[12] = -left[0] * eye[0] - left[1] * eye[1] - left[2] * eye[2];
117 | out[13] = -newUp[0] * eye[0] - newUp[1] * eye[1] - newUp[2] * eye[2];
118 | out[14] = -forward[0] * eye[0] - forward[1] * eye[1] - forward[2] * eye[2];
119 | out[15] = 1;
120 |
121 | return out;
122 | }
123 |
124 | public updateProjectionMatrix(props: MakeProjectionMatrix) {
125 | this.perspectiveProjectionMatrix = this.makePerspectiveProjMatrix(props);
126 | this.orthoProjectionMatrix = this.makeOrthoProjMatrix(props);
127 | }
128 |
129 | public updateViewMatrix({ eye, target, up }: MakeLookAtMatrix) {
130 | this.viewMatrix = this.makeLookAtMatrix({ eye, target, up });
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/app/src/lib/Mesh.ts:
--------------------------------------------------------------------------------
1 | import { mat4, vec3 } from 'gl-matrix';
2 |
3 | import { ShaderProgram } from './ShaderProgram';
4 | import { createAndInitBuffer, setupVertexAttribute } from './Util';
5 | import { Camera } from './Camera';
6 | import { GeometryObject } from './parseOBJ';
7 |
8 | interface Constructor {
9 | geometry: GeometryObject | null;
10 | shaderProgram: ShaderProgram;
11 | gl: WebGL2RenderingContext;
12 | }
13 |
14 | interface Render {
15 | camera: Camera;
16 | }
17 |
18 | export class Mesh {
19 | private gl: WebGL2RenderingContext;
20 | private shaderProgram: ShaderProgram;
21 | private vertices: number[];
22 | private normals: number[];
23 | private texcoords: number[];
24 | private VAO: WebGLVertexArrayObject | null = null;
25 |
26 | private positionBuffer: WebGLBuffer | null = null;
27 | private normalBuffer: WebGLBuffer | null = null;
28 | private uvBuffer: WebGLBuffer | null = null;
29 |
30 | private modelMatrix = mat4.create();
31 |
32 | public position = vec3.fromValues(0, 0, 0);
33 | public scale = vec3.fromValues(1, 1, 1);
34 | public rotation = vec3.fromValues(0, 0, 0);
35 |
36 | constructor(props: Constructor) {
37 | const { gl, shaderProgram, geometry } = props;
38 |
39 | if (!geometry) throw new Error('No geometry provided for the Mesh');
40 |
41 | this.gl = gl;
42 | this.shaderProgram = shaderProgram;
43 | this.vertices = geometry.vertices;
44 | this.normals = geometry.normals;
45 | this.texcoords = geometry.texcoords;
46 |
47 | this.init();
48 | }
49 |
50 | private init() {
51 | // Create VAO for buffer bindings
52 | this.VAO = this.gl.createVertexArray();
53 | this.gl.bindVertexArray(this.VAO);
54 |
55 | // Position buffer
56 | this.positionBuffer = createAndInitBuffer({
57 | gl: this.gl,
58 | target: this.gl.ARRAY_BUFFER,
59 | data: new Float32Array(this.vertices),
60 | });
61 |
62 | setupVertexAttribute({
63 | gl: this.gl,
64 | name: 'a_position',
65 | program: this.shaderProgram.program,
66 | buffer: this.positionBuffer,
67 | size: 3,
68 | });
69 |
70 | // Normal buffer
71 | if (this.normals.length > 0) {
72 | this.normalBuffer = createAndInitBuffer({
73 | gl: this.gl,
74 | target: this.gl.ARRAY_BUFFER,
75 | data: new Float32Array(this.normals),
76 | });
77 |
78 | setupVertexAttribute({
79 | gl: this.gl,
80 | name: 'a_normal',
81 | program: this.shaderProgram.program,
82 | buffer: this.normalBuffer,
83 | size: 3,
84 | });
85 | }
86 |
87 | if (this.texcoords.length > 0) {
88 | // UV buffer
89 | this.uvBuffer = createAndInitBuffer({
90 | gl: this.gl,
91 | target: this.gl.ARRAY_BUFFER,
92 | data: new Float32Array(this.texcoords),
93 | });
94 |
95 | setupVertexAttribute({
96 | gl: this.gl,
97 | name: 'a_uv',
98 | program: this.shaderProgram.program,
99 | buffer: this.uvBuffer,
100 | size: 2,
101 | });
102 | }
103 |
104 | // Unbind VAO
105 | this.gl.bindVertexArray(null);
106 | }
107 |
108 | public render(props: Render) {
109 | const { camera } = props;
110 |
111 | this.shaderProgram.use();
112 | this.gl.bindVertexArray(this.VAO);
113 |
114 | // Construct model matrix
115 | mat4.identity(this.modelMatrix);
116 | mat4.translate(this.modelMatrix, this.modelMatrix, this.position);
117 | mat4.scale(this.modelMatrix, this.modelMatrix, this.scale);
118 | mat4.rotateX(this.modelMatrix, this.modelMatrix, this.rotation[0]);
119 | mat4.rotateY(this.modelMatrix, this.modelMatrix, this.rotation[1]);
120 | mat4.rotateZ(this.modelMatrix, this.modelMatrix, this.rotation[2]);
121 |
122 | // Load model matrix, view matrix and projection matrix to shader
123 | this.shaderProgram.setUniformMatrix4fv('u_modelMatrix', this.modelMatrix);
124 | this.shaderProgram.setUniformMatrix4fv('u_viewMatrix', camera.viewMatrix);
125 | this.shaderProgram.setUniform3f('u_cameraPositionWorld', camera.position);
126 | this.shaderProgram.setUniformMatrix4fv('u_projectionMatrix', camera.perspectiveProjectionMatrix);
127 |
128 | this.gl.drawArrays(this.gl.TRIANGLES, 0, this.vertices.length / 3);
129 |
130 | // Unbind VAO
131 | this.gl.bindVertexArray(null);
132 |
133 | // Unbind textures - doing it here due to asynchronous nature of WebGL handling
134 | this.gl.bindTexture(this.gl.TEXTURE_2D, null);
135 | this.gl.activeTexture(this.gl.TEXTURE0);
136 | }
137 |
138 | public destroy() {
139 | this.gl.deleteBuffer(this.positionBuffer);
140 | this.gl.deleteBuffer(this.normalBuffer);
141 | this.gl.deleteBuffer(this.uvBuffer);
142 | this.gl.deleteVertexArray(this.VAO);
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/src/app/src/Components/Objects3D.ts:
--------------------------------------------------------------------------------
1 | import { vec3 } from 'gl-matrix';
2 |
3 | import { ShaderProgram } from '../lib/ShaderProgram';
4 | import { Mesh } from '../lib/Mesh';
5 | import { TexturesManager } from '../lib/TexturesManager';
6 | import { globalState } from '../utils/globalState';
7 | import { GeometriesManager } from '../lib/GeometriesManager';
8 | import { Camera } from '../lib/Camera';
9 |
10 | import fragmentShaderSource from '../shaders/default/fragment.glsl';
11 | import vertexShaderSource from '../shaders/default/vertex.glsl';
12 |
13 | import bgFragment from '../shaders/background/fragment.glsl';
14 | import bgVertex from '../shaders/background/vertex.glsl';
15 |
16 | interface Constructor {
17 | gl: WebGL2RenderingContext | null;
18 | texturesManager: TexturesManager;
19 | geometriesManager: GeometriesManager;
20 | camera: Camera;
21 | }
22 |
23 | export class Objects3D {
24 | private gl: WebGL2RenderingContext;
25 |
26 | private jet1: Mesh | null = null;
27 | private jet2: Mesh | null = null;
28 | private bgMesh: Mesh | null = null;
29 |
30 | private jetProgram1: ShaderProgram | null = null;
31 | private jetProgram2: ShaderProgram | null = null;
32 | private bgProgram: ShaderProgram | null = null;
33 |
34 | private texturesManager: TexturesManager;
35 | private geometriesManager: GeometriesManager;
36 | private camera: Camera;
37 |
38 | constructor(props: Constructor) {
39 | const { gl, texturesManager, geometriesManager, camera } = props;
40 | if (!gl) throw new Error('WebGL2 not supported, cannot create Objects3D');
41 |
42 | this.gl = gl;
43 | this.texturesManager = texturesManager;
44 | this.geometriesManager = geometriesManager;
45 | this.camera = camera;
46 |
47 | this.init();
48 | }
49 |
50 | private init() {
51 | this.jetProgram1 = new ShaderProgram({
52 | gl: this.gl,
53 | fragmentCode: fragmentShaderSource,
54 | vertexCode: vertexShaderSource,
55 | texturesManager: this.texturesManager,
56 | texturesToUse: [
57 | {
58 | textureSrc: '/public/assets/models/efa/efa.webp',
59 | uniformName: 'u_image',
60 | },
61 | ],
62 | uniforms: {
63 | u_time: globalState.uTime,
64 | },
65 | });
66 |
67 | this.jetProgram2 = new ShaderProgram({
68 | gl: this.gl,
69 | fragmentCode: fragmentShaderSource,
70 | vertexCode: vertexShaderSource,
71 | texturesManager: this.texturesManager,
72 | texturesToUse: [
73 | {
74 | textureSrc: '/public/assets/models/f22/f22.webp',
75 | uniformName: 'u_image',
76 | },
77 | ],
78 | uniforms: {
79 | u_time: globalState.uTime,
80 | },
81 | });
82 |
83 | this.jet1 = new Mesh({
84 | gl: this.gl,
85 | shaderProgram: this.jetProgram1,
86 | geometry: this.geometriesManager.getGeometry('/public/assets/models/efa/efa.obj'),
87 | });
88 |
89 | this.jet2 = new Mesh({
90 | gl: this.gl,
91 | shaderProgram: this.jetProgram2,
92 | geometry: this.geometriesManager.getGeometry('/public/assets/models/f22/f22.obj'),
93 | });
94 |
95 | this.bgProgram = new ShaderProgram({
96 | gl: this.gl,
97 | fragmentCode: bgFragment,
98 | vertexCode: bgVertex,
99 | texturesManager: this.texturesManager,
100 | uniforms: {
101 | u_time: globalState.uTime,
102 | u_resolution: globalState.stageSize,
103 | },
104 | });
105 |
106 | this.bgMesh = new Mesh({
107 | gl: this.gl,
108 | shaderProgram: this.bgProgram,
109 | geometry: this.geometriesManager.getGeometry('plane'),
110 | });
111 | }
112 |
113 | public update() {
114 | const mouse2DCurrent = globalState.mouse2DCurrent.value;
115 |
116 | if (this.bgMesh) {
117 | // Disable depth test when rendering the background - it will be behind everything
118 | this.gl.disable(this.gl.DEPTH_TEST);
119 | this.bgMesh.render({ camera: this.camera });
120 | this.gl.enable(this.gl.DEPTH_TEST);
121 | }
122 |
123 | if (this.jet1) {
124 | this.jet1.rotation[2] = -mouse2DCurrent[0] * 0.4;
125 | this.jet1.rotation[0] = mouse2DCurrent[1] * 3.2;
126 |
127 | const floatY = Math.sin(globalState.uTime.value * 1.2) * 0.035;
128 |
129 | this.jet1.position = vec3.fromValues(
130 | mouse2DCurrent[0] * -0.5,
131 | mouse2DCurrent[1] * -0.1 + floatY - 0.03,
132 | -mouse2DCurrent[1] * 0.25 - 0.02
133 | );
134 |
135 | this.jet1.render({ camera: this.camera });
136 | }
137 |
138 | if (this.jet2) {
139 | this.jet2.rotation[2] = mouse2DCurrent[0] * 0.8;
140 | this.jet2.rotation[0] = -mouse2DCurrent[1] * 1.2;
141 |
142 | const floatY = Math.sin(globalState.uTime.value * 1.6) * 0.02;
143 |
144 | this.jet2.position = vec3.fromValues(
145 | mouse2DCurrent[0] * 0.5,
146 | mouse2DCurrent[1] * 0.1 + floatY + 0.03,
147 | mouse2DCurrent[1] * 0.2 - 0.05
148 | );
149 |
150 | this.jet2.render({ camera: this.camera });
151 | }
152 | }
153 |
154 | public destroy() {
155 | if (this.jet1) this.jet1.destroy();
156 | if (this.jet2) this.jet2.destroy();
157 | if (this.bgMesh) this.bgMesh.destroy();
158 | if (this.jetProgram1) this.jetProgram1.destroy();
159 | if (this.jetProgram2) this.jetProgram2.destroy();
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/app/src/Scene.ts:
--------------------------------------------------------------------------------
1 | import { vec3 } from 'gl-matrix';
2 |
3 | import fragmentShaderPostSource from './shaders/post/fragment.glsl';
4 | import vertexShaderPostSource from './shaders/post/vertex.glsl';
5 |
6 | import { globalState } from './utils/globalState';
7 | import { ShaderProgram } from './lib/ShaderProgram';
8 | import { Mesh } from './lib/Mesh';
9 | import { Camera } from './lib/Camera';
10 | import { lerp } from './utils/lerp';
11 | import { TexturesManager } from './lib/TexturesManager';
12 | import { GeometriesManager } from './lib/GeometriesManager';
13 |
14 | import { Objects3D } from './Components/Objects3D';
15 |
16 | export class Scene {
17 | private gl: WebGL2RenderingContext | null = null;
18 | private camera = new Camera();
19 |
20 | private texturesManager;
21 | private geometriesManager;
22 |
23 | private objects3D: Objects3D | null = null;
24 |
25 | private postProcessShaderProgram: ShaderProgram | null = null;
26 | private postProcessMesh: Mesh | null = null;
27 |
28 | constructor() {
29 | if (globalState.canvasEl) {
30 | this.gl = globalState.canvasEl.getContext('webgl2');
31 | }
32 | if (!this.gl) throw new Error('WebGL2 not supported');
33 |
34 | this.texturesManager = new TexturesManager({ gl: this.gl });
35 | this.geometriesManager = new GeometriesManager();
36 |
37 | this.init();
38 | }
39 |
40 | private postProcess() {
41 | const { gl } = this;
42 | if (!gl) return;
43 | const { width, height } = gl.canvas;
44 |
45 | // Create a texture to render to
46 | this.texturesManager.createFrameBufferTexture(width, height, 'postProcessTexture');
47 |
48 | this.postProcessShaderProgram = new ShaderProgram({
49 | gl: this.gl,
50 | fragmentCode: fragmentShaderPostSource,
51 | vertexCode: vertexShaderPostSource,
52 | texturesManager: this.texturesManager,
53 | texturesToUse: [
54 | {
55 | textureSrc: 'postProcessTexture',
56 | uniformName: 'u_image',
57 | },
58 | ],
59 | uniforms: {
60 | u_time: globalState.uTime,
61 | u_resolution: globalState.stageSize,
62 | },
63 | });
64 |
65 | this.postProcessMesh = new Mesh({
66 | gl,
67 | shaderProgram: this.postProcessShaderProgram,
68 | geometry: this.geometriesManager.getGeometry('plane'),
69 | });
70 | }
71 |
72 | private async init() {
73 | if (!this.gl) return;
74 |
75 | await this.geometriesManager.addObjectsToLoad([
76 | '/public/assets/models/f22/f22.obj',
77 | '/public/assets/models/efa/efa.obj',
78 | ]);
79 |
80 | // Plane made out of two triangles
81 | const planeVertices = [-0.5, 0.5, 0, -0.5, -0.5, 0, 0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0];
82 | const planeTexcoords = [0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1];
83 |
84 | this.geometriesManager.addGeometry({
85 | geometryUrl: 'plane',
86 | geometryObject: { vertices: planeVertices, texcoords: planeTexcoords, normals: [] },
87 | });
88 |
89 | await this.texturesManager.addTexturesToLoad([
90 | '/public/assets/models/f22/f22.webp',
91 | '/public/assets/models/efa/efa.webp',
92 | ]);
93 |
94 | this.objects3D = new Objects3D({
95 | gl: this.gl,
96 | texturesManager: this.texturesManager,
97 | camera: this.camera,
98 | geometriesManager: this.geometriesManager,
99 | });
100 |
101 | this.postProcess();
102 | }
103 |
104 | private render() {
105 | const gl = this.gl;
106 | if (!gl) return;
107 |
108 | // Render to post process texture
109 | const textureObj = this.texturesManager.getTextureObj('postProcessTexture');
110 | if (textureObj && textureObj.frameBuffer) {
111 | gl.bindFramebuffer(gl.FRAMEBUFFER, textureObj.frameBuffer);
112 | }
113 |
114 | // Clear the canvas and depth buffer
115 | gl.clearColor(0, 0, 0, 0);
116 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
117 | gl.enable(gl.DEPTH_TEST);
118 |
119 | // Lerp mouse position
120 | const mouse2DTarget = globalState.mouse2DTarget.value;
121 | const mouse2DCurrent = globalState.mouse2DCurrent.value;
122 | mouse2DCurrent[0] = lerp(mouse2DCurrent[0], mouse2DTarget[0], 0.053 * globalState.slowDownFactor.value);
123 | mouse2DCurrent[1] = lerp(mouse2DCurrent[1], mouse2DTarget[1], 0.053 * globalState.slowDownFactor.value);
124 |
125 | // Update camera
126 | this.camera.updateViewMatrix({
127 | target: vec3.fromValues(mouse2DCurrent[0] * -0.25, mouse2DCurrent[1] * 0.05, -1),
128 | });
129 |
130 | this.objects3D?.update();
131 |
132 | // Post process 2nd part
133 | gl.bindFramebuffer(gl.FRAMEBUFFER, null);
134 | // Render plane where post process texture is applied
135 | this.postProcessShaderProgram?.use();
136 | this.postProcessMesh?.render({
137 | camera: this.camera,
138 | });
139 | }
140 |
141 | public update() {
142 | this.render();
143 | }
144 |
145 | public onResize() {
146 | let w = globalState.stageSize.value[0];
147 | let h = globalState.stageSize.value[1];
148 |
149 | // updateDebug(`Window size: ${w.toFixed(1)} X ${h.toFixed(1)}`);
150 |
151 | const ratio = globalState.pixelRatio.value;
152 |
153 | // Possibly need to Math.round() w and h here, but will leave for now
154 | w = w * ratio;
155 | h = h * ratio;
156 |
157 | const canvas = globalState.canvasEl;
158 | if (!canvas || !this.gl) return;
159 |
160 | if (canvas.width !== w && canvas.height !== h) {
161 | // Sets only the resolution of the canvas
162 | canvas.width = w;
163 | canvas.height = h;
164 | }
165 |
166 | this.gl.viewport(0, 0, w, h);
167 |
168 | this.camera.updateProjectionMatrix({
169 | fov: Math.PI / 3,
170 | aspect_ratio: w / h,
171 | near: 0.1,
172 | far: 20,
173 | });
174 |
175 | this.texturesManager.resize();
176 | }
177 |
178 | public destroy() {
179 | this.geometriesManager?.destroy();
180 | this.texturesManager?.destroy();
181 |
182 | this.objects3D?.destroy();
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/app/src/lib/ShaderProgram.ts:
--------------------------------------------------------------------------------
1 | import { useTexture } from './Util';
2 | import { TexturesManager } from './TexturesManager';
3 |
4 | interface TextureToUse {
5 | textureSrc: string;
6 | uniformName: string;
7 | }
8 |
9 | interface UniformValue {
10 | value: number | number[] | Float32Array;
11 | }
12 | type Uniforms = Record;
13 |
14 | interface Props {
15 | vertexCode: string;
16 | fragmentCode: string;
17 | gl: WebGL2RenderingContext | null;
18 | texturesToUse?: TextureToUse[];
19 | uniforms?: Uniforms;
20 | texturesManager: TexturesManager;
21 | }
22 |
23 | export class ShaderProgram {
24 | private vertexCode: string;
25 | private fragmentCode: string;
26 | private gl: WebGL2RenderingContext;
27 | public program: WebGLProgram | null = null;
28 | private texturesManager;
29 |
30 | private uniformLocations = new Map();
31 |
32 | public texturesToUse: TextureToUse[] = [];
33 | private uniforms: Uniforms = {};
34 |
35 | constructor(props: Props) {
36 | const { vertexCode, fragmentCode, gl, texturesToUse, texturesManager, uniforms } = props;
37 |
38 | if (!gl) {
39 | throw new Error('No gl context provided to ShaderProgram constructor');
40 | }
41 | this.gl = gl;
42 |
43 | this.vertexCode = vertexCode;
44 | this.fragmentCode = fragmentCode;
45 |
46 | this.texturesManager = texturesManager;
47 |
48 | if (texturesToUse) this.texturesToUse = texturesToUse;
49 | if (uniforms) this.uniforms = uniforms;
50 |
51 | this.init(this.gl);
52 | }
53 |
54 | private createShader(gl: WebGL2RenderingContext, type: number, source: string) {
55 | const shader = gl.createShader(type);
56 | if (!shader) throw new Error(`Shader not created for type ${type}`);
57 | gl.shaderSource(shader, source);
58 | gl.compileShader(shader);
59 | const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
60 | if (success) return shader;
61 | gl.deleteShader(shader);
62 | }
63 |
64 | private createProgram(gl: WebGL2RenderingContext, vertexShader: WebGLShader, fragmentShader: WebGLShader) {
65 | const program = gl.createProgram();
66 | if (!program) throw new Error('Program not created');
67 | gl.attachShader(program, vertexShader);
68 | gl.attachShader(program, fragmentShader);
69 | gl.linkProgram(program);
70 | const success = gl.getProgramParameter(program, gl.LINK_STATUS);
71 |
72 | // Clean up
73 | gl.deleteShader(vertexShader);
74 | gl.deleteShader(fragmentShader);
75 |
76 | if (success) return program;
77 | gl.deleteProgram(program);
78 | }
79 |
80 | private init(gl: WebGL2RenderingContext) {
81 | const vertexShader = this.createShader(gl, gl.VERTEX_SHADER, this.vertexCode);
82 | if (!vertexShader) throw new Error('Could not create vertex shader');
83 |
84 | const fragmentShader = this.createShader(gl, gl.FRAGMENT_SHADER, this.fragmentCode);
85 | if (!fragmentShader) throw new Error('Could not create fragment shader');
86 |
87 | const program = this.createProgram(gl, vertexShader, fragmentShader);
88 | if (!program) throw new Error('Could not create program');
89 | this.program = program;
90 | }
91 |
92 | private getUniformLocation(name: string) {
93 | if (!this.program) {
94 | throw new Error('Cannot get uniform location, program not initialized');
95 | }
96 |
97 | // If uniform location is not cached, get it from the GPU
98 | if (!this.uniformLocations.has(name)) {
99 | const location = this.gl.getUniformLocation(this.program, name);
100 | if (!location) {
101 | return undefined;
102 | }
103 | this.uniformLocations.set(name, location);
104 | }
105 | const cachedLocation = this.uniformLocations.get(name);
106 | return cachedLocation;
107 | }
108 |
109 | public setUniform1f(name: string, value: number) {
110 | const location = this.getUniformLocation(name);
111 | if (!location) {
112 | return undefined;
113 | }
114 | this.gl.uniform1f(location, value);
115 | }
116 |
117 | public setUniform4f(name: string, value: [number, number, number, number]) {
118 | const location = this.getUniformLocation(name);
119 | if (!location) {
120 | return undefined;
121 | }
122 | this.gl.uniform4f(location, value[0], value[1], value[2], value[3]);
123 | }
124 |
125 | public setUniform2f(name: string, value: [number, number] | Float32Array) {
126 | const location = this.getUniformLocation(name);
127 | if (!location) {
128 | return undefined;
129 | }
130 | this.gl.uniform2f(location, value[0], value[1]);
131 | }
132 |
133 | public setUniform3f(name: string, value: [number, number, number] | Float32Array) {
134 | const location = this.getUniformLocation(name);
135 | if (!location) {
136 | return undefined;
137 | }
138 | this.gl.uniform3f(location, value[0], value[1], value[2]);
139 | }
140 |
141 | public setUniformMatrix4fv(name: string, value: Float32Array | number[]) {
142 | const location = this.getUniformLocation(name);
143 | if (!location) {
144 | return undefined;
145 | }
146 | this.gl.uniformMatrix4fv(location, false, value);
147 | }
148 |
149 | public use() {
150 | if (!this.program) {
151 | throw new Error('Cannot use program, program is not set');
152 | }
153 | this.gl.useProgram(this.program);
154 |
155 | // Bind textures
156 | this.texturesToUse.forEach((el) => {
157 | const textureObj = this.texturesManager.getTextureObj(el.textureSrc);
158 | if (!textureObj) return;
159 |
160 | useTexture({
161 | gl: this.gl,
162 | shaderProgram: this.program,
163 | uniformLocation: this.getUniformLocation(el.uniformName),
164 | texture: textureObj.texture,
165 | textureIndex: textureObj.textureIndex,
166 | });
167 | });
168 |
169 | // Set uniforms
170 | Object.entries(this.uniforms).forEach(([objKey, objValue]) => {
171 | const value = objValue.value;
172 | if (typeof value === 'number') {
173 | this.setUniform1f(objKey, value);
174 | } else if (value instanceof Array) {
175 | if (value.length === 4) {
176 | this.setUniform4f(objKey, [value[0], value[1], value[2], value[3]]);
177 | }
178 | if (value.length === 2) {
179 | this.setUniform2f(objKey, [value[0], value[1]]);
180 | }
181 | } else if (value instanceof Float32Array) {
182 | this.setUniformMatrix4fv(objKey, value);
183 | }
184 | });
185 | }
186 |
187 | public destroy() {
188 | this.gl.deleteProgram(this.program);
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/src/app/src/lib/TexturesManager.ts:
--------------------------------------------------------------------------------
1 | interface Constructor {
2 | gl: WebGL2RenderingContext | null;
3 | }
4 |
5 | interface TextureObject {
6 | width: number;
7 | height: number;
8 | textureIndex: number;
9 | frameBuffer?: WebGLFramebuffer;
10 | texture: WebGLTexture;
11 | depthBuffer?: WebGLRenderbuffer;
12 | }
13 |
14 | interface LoadTexture {
15 | gl: WebGL2RenderingContext | null;
16 | url: string;
17 | textureIndex: number;
18 | }
19 |
20 | export class TexturesManager {
21 | private gl: WebGL2RenderingContext | null = null;
22 | private isReady = false;
23 | private startedLoading = false;
24 | private loadedTextures: Map = new Map();
25 |
26 | constructor(props: Constructor) {
27 | const { gl } = props;
28 |
29 | this.gl = gl;
30 | if (!this.gl) return;
31 | }
32 |
33 | public getTextureObj(textureUrl: string) {
34 | const textureObject = this.loadedTextures.get(textureUrl);
35 | if (!this.isReady) return null;
36 | if (!textureObject) {
37 | console.error(`Texture not found. ${textureUrl} `);
38 | return null;
39 | }
40 | return textureObject;
41 | }
42 |
43 | private loadImage(url: string): Promise {
44 | return new Promise((resolve, reject) => {
45 | const image = new Image();
46 | image.crossOrigin = 'anonymous';
47 | image.src = url;
48 |
49 | const onLoaded = () => {
50 | resolve(image);
51 | };
52 |
53 | if (image.complete) {
54 | onLoaded();
55 | } else {
56 | image.onload = onLoaded;
57 | image.onerror = function () {
58 | console.error('Failed to load texture image:', url);
59 | reject();
60 | };
61 | }
62 | });
63 | }
64 |
65 | private async loadTexture(props: LoadTexture) {
66 | const { gl, url, textureIndex } = props;
67 |
68 | const image = await this.loadImage(url);
69 |
70 | if (!gl) {
71 | return console.error('WebGL context or shader program is not available.');
72 | }
73 |
74 | // Create and bind the texture
75 | const texture = gl.createTexture();
76 | if (!texture) {
77 | return console.error('Unable to create texture.');
78 | }
79 | gl.bindTexture(gl.TEXTURE_2D, texture);
80 |
81 | const mode = gl.REPEAT; // or gl.CLAMP_TO_EDGE
82 |
83 | // Set the texture parameters
84 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, mode);
85 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, mode);
86 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
87 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
88 |
89 | // Upload the image to the texture
90 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
91 |
92 | // Generate mipmaps
93 | const isPowerOfTwo = (value: number) => {
94 | return (value & (value - 1)) == 0;
95 | };
96 | if (isPowerOfTwo(image.width) && isPowerOfTwo(image.height)) {
97 | gl.generateMipmap(gl.TEXTURE_2D);
98 | }
99 |
100 | // Add the texture to the loaded textures
101 | this.loadedTextures.set(url, {
102 | texture,
103 | height: image.height,
104 | width: image.width,
105 | textureIndex,
106 | });
107 |
108 | // Unbind the texture
109 | gl.bindTexture(gl.TEXTURE_2D, null);
110 |
111 | return this.loadedTextures.get(url);
112 | }
113 |
114 | public createFrameBufferTexture(width: number, height: number, name: string) {
115 | const gl = this.gl;
116 | if (!gl) return console.error('Cannot create frame buffer texture, WebGL context not available.');
117 |
118 | const frameBuffer = gl.createFramebuffer();
119 | if (!frameBuffer) return console.error('Cannot create frame buffer for frame buffer texture.');
120 |
121 | gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
122 |
123 | // Create and set up the color texture
124 | const texture = gl.createTexture();
125 | if (!texture) return console.error('Cannot create frame buffer texture.');
126 | gl.bindTexture(gl.TEXTURE_2D, texture);
127 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
128 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
129 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
130 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
131 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
132 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
133 |
134 | // Create and set up the depth renderbuffer
135 | const depthBuffer = gl.createRenderbuffer();
136 | if (!depthBuffer) return console.error('Cannot create depth renderbuffer.');
137 | gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
138 | gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
139 | gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
140 |
141 | // Check if the framebuffer is complete
142 | const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
143 | if (status !== gl.FRAMEBUFFER_COMPLETE) {
144 | console.error('Framebuffer is not complete. Status:', status);
145 |
146 | // Remove everything in case of error
147 | gl.bindTexture(gl.TEXTURE_2D, null);
148 | gl.bindRenderbuffer(gl.RENDERBUFFER, null);
149 | gl.bindFramebuffer(gl.FRAMEBUFFER, null);
150 |
151 | texture && gl.deleteTexture(texture);
152 | depthBuffer && gl.deleteRenderbuffer(depthBuffer);
153 | frameBuffer && gl.deleteFramebuffer(frameBuffer);
154 | }
155 |
156 | // Add the texture and renderbuffer to the loaded textures
157 | this.loadedTextures.set(name, {
158 | width,
159 | height,
160 | textureIndex: 0,
161 | frameBuffer,
162 | texture,
163 | depthBuffer,
164 | });
165 |
166 | // Unbind texture, renderbuffer, and framebuffer
167 | gl.bindTexture(gl.TEXTURE_2D, null);
168 | gl.bindRenderbuffer(gl.RENDERBUFFER, null);
169 | gl.bindFramebuffer(gl.FRAMEBUFFER, null);
170 |
171 | return this.loadedTextures.get(name);
172 | }
173 |
174 | private resizeFrameBufferTextures(width: number, height: number) {
175 | this.loadedTextures.forEach((textureObject) => {
176 | // Don't do anything if the texture does not have a frame buffer etc.
177 | if (!textureObject.frameBuffer || !textureObject.depthBuffer) return;
178 |
179 | const gl = this.gl;
180 | if (!gl) return console.error('Cannot resize frame buffer texture.');
181 |
182 | gl.bindFramebuffer(gl.FRAMEBUFFER, textureObject.frameBuffer);
183 |
184 | // Resize the color texture
185 | gl.bindTexture(gl.TEXTURE_2D, textureObject.texture);
186 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
187 |
188 | // Resize the depth renderbuffer
189 | gl.bindRenderbuffer(gl.RENDERBUFFER, textureObject.depthBuffer);
190 | gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
191 |
192 | // Unbind texture, renderbuffer, and framebuffer
193 | gl.bindTexture(gl.TEXTURE_2D, null);
194 | gl.bindRenderbuffer(gl.RENDERBUFFER, null);
195 | gl.bindFramebuffer(gl.FRAMEBUFFER, null);
196 | });
197 | }
198 |
199 | public async addTexturesToLoad(texturesToLoad: string[]) {
200 | if (!this.gl) {
201 | return console.error('WebGL context not available for TexturesManager.');
202 | }
203 |
204 | if (this.startedLoading) {
205 | console.error('TexturesManager already started loading textures. Cannot add more textures.');
206 | return;
207 | }
208 |
209 | this.startedLoading = true;
210 |
211 | const promises = texturesToLoad.map((textureUrl, key) => {
212 | return this.loadTexture({
213 | gl: this.gl,
214 | url: textureUrl,
215 | textureIndex: key,
216 | });
217 | });
218 |
219 | return Promise.allSettled(promises).then(() => {
220 | this.isReady = true;
221 | Promise.resolve();
222 | });
223 | }
224 |
225 | public destroy() {
226 | this.loadedTextures.forEach((textureObject) => {
227 | if (textureObject.texture) {
228 | this.gl?.deleteTexture(textureObject.texture);
229 | }
230 |
231 | if (textureObject.frameBuffer) {
232 | this.gl?.deleteFramebuffer(textureObject.frameBuffer);
233 | }
234 |
235 | if (textureObject.depthBuffer) {
236 | this.gl?.deleteRenderbuffer(textureObject.depthBuffer);
237 | }
238 | });
239 | this.loadedTextures.clear();
240 | }
241 |
242 | public resize() {
243 | if (!this.gl) return;
244 | const width = this.gl.drawingBufferWidth || 0;
245 | const height = this.gl.drawingBufferHeight || 0;
246 | this.resizeFrameBufferTextures(width, height);
247 | }
248 | }
249 |
--------------------------------------------------------------------------------
/src/eleventy/public/assets/models/f117/f117.obj:
--------------------------------------------------------------------------------
1 | # Blender v2.79 (sub 0) OBJ File: 'fighters_0.blend'
2 | # www.blender.org
3 | mtllib f-117.mtl
4 | o F-117
5 | v 1.431311 -0.080746 -0.100098
6 | v 0.959029 -0.063769 -0.243942
7 | v 1.197672 -0.080746 -0.196029
8 | v 0.720659 -0.080746 -0.395345
9 | v 0.783476 0.073457 -0.183297
10 | v 0.547609 0.073457 -0.329773
11 | v 0.089260 -0.042764 -0.490725
12 | v 0.210262 0.118042 -0.349212
13 | v 0.063131 0.163098 -0.239188
14 | v -1.561768 -0.080746 -1.097042
15 | v -0.858154 -0.080746 -0.468403
16 | v -0.709112 -0.046530 -0.454687
17 | v -0.405576 0.091581 -0.327342
18 | v -0.996863 -0.080746 -0.432693
19 | v -1.433718 -0.080746 -1.217256
20 | v -1.271797 -0.080746 -1.217256
21 | v -0.924093 0.042619 -0.061599
22 | v -1.223704 -0.029585 -0.057042
23 | v -1.699272 0.295954 -0.300594
24 | v -2.006486 0.295954 -0.300594
25 | v 0.816673 0.229178 -0.049913
26 | v 0.800470 0.153175 -0.115014
27 | v 1.059639 0.139678 -0.044752
28 | v 0.899962 0.237001 -0.018498
29 | v 1.003989 0.102522 -0.101096
30 | v 0.876546 0.229086 -0.033912
31 | v 0.895185 0.231746 -0.025417
32 | v 1.017887 0.109624 -0.090248
33 | v 0.902870 0.242351 -0.006867
34 | v 1.065382 0.142992 -0.032976
35 | v 1.431311 -0.080746 0.100098
36 | v 0.959029 -0.063769 0.243942
37 | v 1.197672 -0.080746 0.196029
38 | v 0.720659 -0.080746 0.395345
39 | v 0.783476 0.073457 0.183297
40 | v 0.547609 0.073457 0.329773
41 | v 0.089260 -0.042764 0.490725
42 | v 0.210262 0.118042 0.349212
43 | v 0.063131 0.163098 0.239188
44 | v -1.561768 -0.080746 1.097042
45 | v -0.858154 -0.080746 0.468403
46 | v -0.709112 -0.046530 0.454687
47 | v -0.405576 0.091581 0.327342
48 | v -0.996863 -0.080746 0.432693
49 | v -1.433718 -0.080746 1.217256
50 | v -1.271797 -0.080746 1.217256
51 | v -0.924093 0.042619 0.061599
52 | v -1.223704 -0.029585 0.057042
53 | v -1.699272 0.295954 0.300594
54 | v -2.006486 0.295954 0.300594
55 | v 0.816673 0.229178 0.049913
56 | v 0.800470 0.153175 0.115014
57 | v 1.059639 0.139678 0.044752
58 | v 0.899962 0.237001 0.018498
59 | v 1.003989 0.102522 0.101096
60 | v 0.876546 0.229086 0.033912
61 | v 0.895185 0.231746 0.025417
62 | v 1.017887 0.109624 0.090248
63 | v 0.902870 0.242351 0.006867
64 | v 1.065382 0.142992 0.032976
65 | v 1.669928 -0.080746 0.000000
66 | v 1.210881 0.054104 0.000000
67 | v -1.500610 -0.080746 0.000000
68 | v -0.796050 0.091437 0.000000
69 | v 0.799601 -0.137449 0.000000
70 | v -1.220287 -0.012240 0.000000
71 | v 0.829096 0.287450 0.000000
72 | v 1.065449 0.142992 0.000000
73 | v 0.902884 0.242351 0.000000
74 | vt 0.077619 0.790742
75 | vt 0.148382 0.821431
76 | vt 0.004460 0.821431
77 | vt 0.222417 0.746640
78 | vt 0.149251 0.761330
79 | vt 0.295500 0.700221
80 | vt 0.011719 0.652344
81 | vt 0.085938 0.600830
82 | vt 0.085938 0.652344
83 | vt 0.236807 0.919792
84 | vt 0.231596 0.944233
85 | vt 0.169199 0.928703
86 | vt 0.309122 0.919792
87 | vt 0.449649 0.884160
88 | vt 0.412550 0.933462
89 | vt 0.497094 0.748098
90 | vt 0.348556 0.720325
91 | vt 0.451984 0.714365
92 | vt 0.457660 0.947276
93 | vt 0.226643 0.967465
94 | vt 0.601362 0.925349
95 | vt 0.640796 0.721070
96 | vt 0.489083 0.670978
97 | vt 0.906374 0.448228
98 | vt 0.956018 0.448228
99 | vt 0.779553 0.677822
100 | vt 0.733858 0.682027
101 | vt 0.995277 0.485085
102 | vt 0.077142 0.081292
103 | vt 0.003984 0.050603
104 | vt 0.270820 0.050603
105 | vt 0.976050 0.050603
106 | vt 0.821604 0.183264
107 | vt 0.779077 0.194212
108 | vt 0.295023 0.171813
109 | vt 0.148774 0.110704
110 | vt 0.782647 0.872515
111 | vt 0.694424 0.883005
112 | vt 0.740119 0.872515
113 | vt 0.905897 0.423806
114 | vt 0.955541 0.423806
115 | vt 0.760513 0.821431
116 | vt 0.799770 0.802545
117 | vt 0.890581 0.821431
118 | vt 0.262254 0.821431
119 | vt 0.822081 0.688770
120 | vt 0.463799 0.548321
121 | vt 0.369609 0.548321
122 | vt 0.610653 0.453831
123 | vt 0.701464 0.470650
124 | vt 0.037753 0.470652
125 | vt 0.129611 0.448514
126 | vt 0.275417 0.548323
127 | vt 0.976526 0.821431
128 | vt 0.891629 0.803942
129 | vt 0.214508 0.432829
130 | vt 0.524708 0.432827
131 | vt 0.109817 0.872515
132 | vt 0.152137 0.940095
133 | vt 0.038185 0.872515
134 | vt 0.164938 0.930881
135 | vt 0.222820 0.985401
136 | vt 0.202557 0.968322
137 | vt 0.029547 0.682016
138 | vt 0.079968 0.710244
139 | vt 0.078503 0.711855
140 | vt 0.208272 0.967505
141 | vt 0.201093 0.969934
142 | vt 0.101849 0.712312
143 | vt 0.125173 0.689040
144 | vt 0.120220 0.712272
145 | vt 0.017677 0.941196
146 | vt 0.007567 0.913944
147 | vt 0.038256 0.872600
148 | vt 0.011442 0.681877
149 | vt 0.013548 0.712340
150 | vt 0.011442 0.712340
151 | vt 0.007567 0.985486
152 | vt 0.007567 0.971659
153 | vt 0.009672 0.971659
154 | vt 0.011719 0.605469
155 | vt 0.021552 0.681877
156 | vt 0.007567 0.941196
157 | vt 0.062775 0.673510
158 | vt 0.042348 0.672802
159 | vt 0.994800 0.386949
160 | vt 0.276241 0.765234
161 | vt 0.182983 0.877720
162 | vt 0.256066 0.872515
163 | vt 0.077619 0.790742
164 | vt 0.222417 0.746640
165 | vt 0.295500 0.700221
166 | vt 0.149251 0.761330
167 | vt 0.011719 0.652344
168 | vt 0.085938 0.652344
169 | vt 0.085938 0.600830
170 | vt 0.236807 0.919792
171 | vt 0.169199 0.928703
172 | vt 0.231596 0.944233
173 | vt 0.309122 0.919792
174 | vt 0.412550 0.933462
175 | vt 0.449649 0.884160
176 | vt 0.497094 0.748098
177 | vt 0.451984 0.714365
178 | vt 0.348556 0.720325
179 | vt 0.457660 0.947276
180 | vt 0.226643 0.967465
181 | vt 0.601362 0.925349
182 | vt 0.640796 0.721070
183 | vt 0.489083 0.670978
184 | vt 0.956018 0.448228
185 | vt 0.906374 0.448228
186 | vt 0.779553 0.677822
187 | vt 0.995277 0.485085
188 | vt 0.733858 0.682027
189 | vt 0.077142 0.081292
190 | vt 0.821604 0.183264
191 | vt 0.779077 0.194212
192 | vt 0.295023 0.171813
193 | vt 0.148774 0.110704
194 | vt 0.782647 0.872515
195 | vt 0.740119 0.872515
196 | vt 0.694424 0.883005
197 | vt 0.905897 0.423806
198 | vt 0.955541 0.423806
199 | vt 0.799770 0.802545
200 | vt 0.822081 0.688770
201 | vt 0.463799 0.548321
202 | vt 0.369609 0.548321
203 | vt 0.701464 0.470650
204 | vt 0.037753 0.470652
205 | vt 0.275417 0.548323
206 | vt 0.129611 0.448514
207 | vt 0.891629 0.803942
208 | vt 0.109817 0.872515
209 | vt 0.038185 0.872515
210 | vt 0.152137 0.940095
211 | vt 0.164938 0.930881
212 | vt 0.202557 0.968322
213 | vt 0.029547 0.682016
214 | vt 0.078503 0.711855
215 | vt 0.079968 0.710244
216 | vt 0.208272 0.967505
217 | vt 0.201093 0.969934
218 | vt 0.101849 0.712312
219 | vt 0.120220 0.712272
220 | vt 0.125173 0.689040
221 | vt 0.017677 0.941196
222 | vt 0.038256 0.872600
223 | vt 0.013548 0.712340
224 | vt 0.009672 0.971659
225 | vt 0.011719 0.605469
226 | vt 0.021552 0.681877
227 | vt 0.062775 0.673510
228 | vt 0.042348 0.672802
229 | vt 0.994800 0.386949
230 | vt 0.276241 0.765234
231 | vt 0.182983 0.877720
232 | vt 0.256066 0.872515
233 | vn 0.2339 0.7964 -0.5577
234 | vn 0.1290 0.9423 -0.3088
235 | vn 0.3819 0.6900 -0.6149
236 | vn 0.2044 0.6113 -0.7645
237 | vn 0.1249 0.6008 -0.7896
238 | vn 0.1400 0.9678 -0.2092
239 | vn 0.1375 0.6273 -0.7666
240 | vn -0.0552 0.6827 -0.7286
241 | vn -0.0541 0.8966 -0.4396
242 | vn 0.0000 0.9986 -0.0522
243 | vn -0.2389 0.9335 0.2674
244 | vn 0.0642 -0.9861 -0.1532
245 | vn -0.0246 -0.9993 -0.0287
246 | vn -0.0163 -0.9979 -0.0632
247 | vn 0.0645 -0.9859 -0.1543
248 | vn -0.0223 0.9995 -0.0237
249 | vn 0.0638 -0.9858 -0.1555
250 | vn -0.1400 0.8276 -0.5436
251 | vn 0.0636 -0.9860 -0.1541
252 | vn -0.0436 -0.9985 0.0335
253 | vn -0.2288 0.9363 -0.2664
254 | vn -0.1187 0.9842 -0.1315
255 | vn -0.2268 0.9357 -0.2701
256 | vn -0.2254 0.9369 -0.2672
257 | vn 0.0000 0.6982 0.7159
258 | vn 0.0149 0.7099 0.7041
259 | vn 0.1055 -0.4923 -0.8640
260 | vn -0.2277 0.9369 -0.2651
261 | vn -0.2297 0.9362 -0.2662
262 | vn 0.0000 -0.5991 -0.8007
263 | vn -0.0355 -0.6521 -0.7573
264 | vn -0.1249 0.5110 0.8505
265 | vn 0.2862 0.6575 -0.6970
266 | vn 0.2053 0.6111 -0.7645
267 | vn 0.1984 0.6079 -0.7688
268 | vn 0.5215 0.8532 -0.0011
269 | vn 0.5215 0.8532 -0.0010
270 | vn 0.3413 0.7092 -0.6169
271 | vn 0.1989 0.6082 -0.7684
272 | vn 0.2045 0.6116 -0.7643
273 | vn -0.1224 0.9817 -0.1456
274 | vn 0.0000 -1.0000 0.0000
275 | vn 0.0000 -0.9989 -0.0465
276 | vn -0.1399 0.8197 -0.5555
277 | vn -0.0065 0.9992 -0.0387
278 | vn 0.0921 0.9704 -0.2233
279 | vn -0.0348 0.7177 -0.6955
280 | vn 0.1373 0.9655 -0.2211
281 | vn 0.1977 0.6075 -0.7694
282 | vn 0.1577 0.5310 -0.8326
283 | vn 0.2070 0.6047 -0.7691
284 | vn 0.2339 0.7964 0.5577
285 | vn 0.1290 0.9423 0.3088
286 | vn 0.3819 0.6900 0.6149
287 | vn 0.2044 0.6113 0.7645
288 | vn 0.1249 0.6008 0.7896
289 | vn 0.1400 0.9678 0.2092
290 | vn 0.1375 0.6273 0.7666
291 | vn -0.0552 0.6827 0.7286
292 | vn -0.0541 0.8966 0.4396
293 | vn 0.0000 0.9986 0.0522
294 | vn -0.2389 0.9335 -0.2674
295 | vn 0.0642 -0.9861 0.1532
296 | vn -0.0246 -0.9993 0.0287
297 | vn -0.0163 -0.9979 0.0632
298 | vn 0.0645 -0.9859 0.1543
299 | vn -0.0223 0.9995 0.0237
300 | vn 0.0638 -0.9858 0.1555
301 | vn -0.1400 0.8276 0.5436
302 | vn 0.0636 -0.9860 0.1541
303 | vn -0.0436 -0.9985 -0.0335
304 | vn -0.2288 0.9363 0.2664
305 | vn -0.1187 0.9842 0.1315
306 | vn -0.2268 0.9357 0.2701
307 | vn -0.2254 0.9369 0.2672
308 | vn 0.0000 0.6982 -0.7159
309 | vn 0.0149 0.7099 -0.7041
310 | vn 0.1055 -0.4923 0.8640
311 | vn -0.2277 0.9369 0.2651
312 | vn -0.2297 0.9362 0.2662
313 | vn 0.0000 -0.5991 0.8007
314 | vn -0.0355 -0.6521 0.7573
315 | vn -0.1249 0.5110 -0.8505
316 | vn 0.2862 0.6575 0.6970
317 | vn 0.2053 0.6111 0.7645
318 | vn 0.1984 0.6079 0.7688
319 | vn 0.5215 0.8532 0.0011
320 | vn 0.5215 0.8532 0.0010
321 | vn 0.3413 0.7092 0.6169
322 | vn 0.1989 0.6082 0.7684
323 | vn 0.2045 0.6116 0.7643
324 | vn -0.1224 0.9817 0.1456
325 | vn 0.0000 -0.9989 0.0465
326 | vn -0.1399 0.8197 0.5555
327 | vn -0.0065 0.9992 0.0387
328 | vn 0.0921 0.9704 0.2233
329 | vn -0.0348 0.7177 0.6955
330 | vn 0.1373 0.9655 0.2211
331 | vn 0.1977 0.6075 0.7694
332 | vn 0.1577 0.5310 0.8326
333 | vn 0.2070 0.6047 0.7691
334 | usemtl Material
335 | s off
336 | f 1/1/1 62/2/1 61/3/1
337 | f 2/4/2 3/5/2 4/6/2
338 | f 5/7/3 4/8/3 6/9/3
339 | f 5/10/4 22/11/4 25/12/4
340 | f 6/13/5 7/14/5 8/15/5
341 | f 9/16/6 6/17/6 8/18/6
342 | f 9/19/7 21/20/7 22/11/7
343 | f 7/14/8 13/21/8 8/15/8
344 | f 8/18/9 13/22/9 9/16/9
345 | f 7/23/10 16/24/10 15/25/10
346 | f 11/26/11 12/27/11 10/28/11
347 | f 1/29/12 61/30/12 65/31/12
348 | f 63/32/13 14/33/13 65/31/13
349 | f 14/33/14 11/34/14 65/31/14
350 | f 4/35/15 3/36/15 65/31/15
351 | f 12/27/16 15/25/16 10/28/16
352 | f 3/36/17 1/29/17 65/31/17
353 | f 14/37/18 12/38/18 11/39/18
354 | f 65/31/19 16/40/19 4/35/19
355 | f 11/34/20 15/41/20 65/31/20
356 | f 64/42/21 17/43/21 66/44/21
357 | f 9/16/22 64/42/22 67/45/22
358 | f 17/43/23 64/42/23 13/22/23
359 | f 17/43/24 13/22/24 14/46/24
360 | f 19/47/25 20/48/25 66/49/25
361 | f 66/49/26 17/50/26 19/47/26
362 | f 17/51/27 18/52/27 19/53/27
363 | f 14/46/28 63/54/28 18/55/28
364 | f 14/46/29 18/55/29 17/43/29
365 | f 19/53/30 18/52/30 20/48/30
366 | f 18/52/31 63/56/31 20/48/31
367 | f 20/48/32 63/57/32 66/49/32
368 | f 3/58/33 23/59/33 1/60/33
369 | f 28/61/33 67/62/33 27/63/33
370 | f 23/64/33 27/65/33 24/66/33
371 | f 21/20/34 67/62/34 26/67/34
372 | f 67/62/33 24/68/33 27/63/33
373 | f 23/59/33 67/62/33 1/60/33
374 | f 26/69/34 22/70/34 21/71/34
375 | f 25/12/35 67/62/35 3/58/35
376 | f 30/72/36 62/73/36 1/74/36
377 | f 68/75/37 29/76/37 69/77/37
378 | f 67/78/36 69/79/36 29/80/36
379 | f 30/72/36 67/78/36 29/80/36
380 | f 5/7/38 2/81/38 4/8/38
381 | f 30/72/36 1/74/36 67/78/36
382 | f 68/75/36 30/82/36 29/76/36
383 | f 30/72/36 68/83/36 62/73/36
384 | f 25/12/39 26/67/39 67/62/39
385 | f 26/69/40 25/84/40 22/70/40
386 | f 23/59/33 24/68/33 67/62/33
387 | f 23/64/33 28/85/33 27/65/33
388 | f 28/61/33 3/58/33 67/62/33
389 | f 3/58/33 28/61/33 23/59/33
390 | f 9/16/41 13/22/41 64/42/41
391 | f 11/34/42 10/86/42 15/41/42
392 | f 65/31/43 15/41/43 16/40/43
393 | f 14/37/44 13/21/44 12/38/44
394 | f 12/27/45 7/23/45 15/25/45
395 | f 7/23/46 4/6/46 16/24/46
396 | f 7/14/47 12/38/47 13/21/47
397 | f 9/19/7 67/62/7 21/20/7
398 | f 22/11/7 5/10/7 9/19/7
399 | f 9/16/48 5/87/48 6/17/48
400 | f 25/12/49 3/58/49 2/88/49
401 | f 6/13/50 4/89/50 7/14/50
402 | f 2/88/51 5/10/51 25/12/51
403 | f 31/90/52 61/3/52 62/2/52
404 | f 32/91/53 34/92/53 33/93/53
405 | f 35/94/54 36/95/54 34/96/54
406 | f 35/97/55 55/98/55 52/99/55
407 | f 36/100/56 38/101/56 37/102/56
408 | f 39/103/57 38/104/57 36/105/57
409 | f 39/106/58 52/99/58 51/107/58
410 | f 37/102/59 38/101/59 43/108/59
411 | f 38/104/60 39/103/60 43/109/60
412 | f 37/110/61 45/111/61 46/112/61
413 | f 41/113/62 40/114/62 42/115/62
414 | f 31/116/63 65/31/63 61/30/63
415 | f 63/32/64 65/31/64 44/117/64
416 | f 44/117/65 65/31/65 41/118/65
417 | f 34/119/66 65/31/66 33/120/66
418 | f 42/115/67 40/114/67 45/111/67
419 | f 33/120/68 65/31/68 31/116/68
420 | f 44/121/69 41/122/69 42/123/69
421 | f 65/31/70 34/119/70 46/124/70
422 | f 41/118/71 65/31/71 45/125/71
423 | f 64/42/72 66/44/72 47/126/72
424 | f 39/103/73 67/45/73 64/42/73
425 | f 47/126/74 43/109/74 64/42/74
426 | f 47/126/75 44/127/75 43/109/75
427 | f 49/128/76 66/49/76 50/129/76
428 | f 66/49/77 49/128/77 47/130/77
429 | f 47/131/78 49/132/78 48/133/78
430 | f 44/127/79 48/134/79 63/54/79
431 | f 44/127/80 47/126/80 48/134/80
432 | f 49/132/81 50/129/81 48/133/81
433 | f 48/133/82 50/129/82 63/56/82
434 | f 50/129/83 66/49/83 63/57/83
435 | f 33/135/84 31/136/84 53/137/84
436 | f 58/138/84 57/139/84 67/62/84
437 | f 53/140/84 54/141/84 57/142/84
438 | f 51/107/85 56/143/85 67/62/85
439 | f 67/62/84 57/139/84 54/144/84
440 | f 53/137/84 31/136/84 67/62/84
441 | f 56/145/85 51/146/85 52/147/85
442 | f 55/98/86 33/135/86 67/62/86
443 | f 60/148/87 31/149/87 62/73/87
444 | f 68/75/88 69/77/88 59/150/88
445 | f 67/78/87 59/151/87 69/79/87
446 | f 60/148/87 59/151/87 67/78/87
447 | f 35/94/89 34/96/89 32/152/89
448 | f 60/148/87 67/78/87 31/149/87
449 | f 68/75/87 59/150/87 60/153/87
450 | f 60/148/87 62/73/87 68/83/87
451 | f 55/98/90 67/62/90 56/143/90
452 | f 56/145/91 52/147/91 55/154/91
453 | f 53/137/84 67/62/84 54/144/84
454 | f 53/140/84 57/142/84 58/155/84
455 | f 58/138/84 67/62/84 33/135/84
456 | f 33/135/84 53/137/84 58/138/84
457 | f 39/103/92 64/42/92 43/109/92
458 | f 41/118/42 45/125/42 40/156/42
459 | f 65/31/93 46/124/93 45/125/93
460 | f 44/121/94 42/123/94 43/108/94
461 | f 42/115/95 45/111/95 37/110/95
462 | f 37/110/96 46/112/96 34/92/96
463 | f 37/102/97 43/108/97 42/123/97
464 | f 39/106/58 51/107/58 67/62/58
465 | f 52/99/58 39/106/58 35/97/58
466 | f 39/103/98 36/105/98 35/157/98
467 | f 55/98/99 32/158/99 33/135/99
468 | f 36/100/100 37/102/100 34/159/100
469 | f 32/158/101 55/98/101 35/97/101
470 |
--------------------------------------------------------------------------------
/src/eleventy/public/assets/models/f22/f22.obj:
--------------------------------------------------------------------------------
1 | # Blender v2.79 (sub 0) OBJ File: 'fighters_0.blend'
2 | # www.blender.org
3 | mtllib f-22.mtl
4 | o F-22
5 | v 0.885739 0.001910 -0.380334
6 | v 0.322026 0.035470 -0.433102
7 | v -0.907053 0.017201 -0.352922
8 | v 1.047128 0.048117 -0.183264
9 | v 0.362277 0.114239 -0.142520
10 | v 1.125941 0.131116 -0.101088
11 | v 1.199510 0.242528 -0.056765
12 | v 1.529867 0.098373 -0.067674
13 | v 1.566378 0.011323 -0.146393
14 | v 1.762624 0.050007 -0.049581
15 | v 1.789931 -0.007335 -0.103633
16 | v -1.226703 0.083896 -0.231401
17 | v -1.226703 0.083896 -0.043034
18 | v -1.304689 0.083896 -0.142520
19 | v -0.560223 0.071221 -0.299735
20 | v 0.779234 -0.185506 -0.231401
21 | v -0.408812 -0.168000 -0.231401
22 | v -1.226703 -0.023036 -0.043034
23 | v -1.226703 -0.023036 -0.231464
24 | v -1.304556 -0.023036 -0.142520
25 | v -0.442164 -0.009523 -0.406435
26 | v -0.875984 0.035470 -1.161559
27 | v -0.727243 0.035470 -1.316844
28 | v -0.494141 0.035470 -1.316844
29 | v -1.376274 0.035470 -0.874979
30 | v -1.417826 0.035470 -0.043034
31 | v 1.016889 0.247035 -0.041089
32 | v -0.910201 0.071705 -0.260759
33 | v -0.910201 0.114239 -0.142520
34 | v -1.347072 0.053931 -0.291132
35 | v -0.949411 0.058716 -0.324121
36 | v -1.086356 0.618995 -0.582384
37 | v -0.945513 0.618995 -0.582384
38 | v -0.820765 0.618995 -0.582384
39 | v -1.074394 0.035470 -0.526090
40 | v -1.347072 0.017010 -0.291132
41 | v -1.420792 0.035470 -0.231401
42 | v -1.704863 0.035470 -0.554642
43 | v -1.607890 0.035470 -0.874979
44 | v 1.142508 0.156204 -0.091107
45 | v 0.821684 0.218761 -0.028011
46 | v 1.453805 0.131564 -0.065163
47 | v 0.835981 0.180467 -0.042103
48 | v 0.885739 0.001910 0.380334
49 | v 0.322026 0.035470 0.433102
50 | v -0.907053 0.017201 0.352922
51 | v 1.047128 0.048117 0.183264
52 | v 0.362277 0.114239 0.142520
53 | v 1.125941 0.131116 0.101088
54 | v 1.199510 0.242528 0.056765
55 | v 1.529867 0.098373 0.067674
56 | v 1.566378 0.011323 0.146393
57 | v 1.762624 0.050007 0.049581
58 | v 1.789931 -0.007335 0.103633
59 | v -1.226703 0.083896 0.231401
60 | v -1.226703 0.083896 0.043034
61 | v -1.304689 0.083896 0.142520
62 | v -0.560223 0.071221 0.299735
63 | v 0.779234 -0.185506 0.231401
64 | v -0.408812 -0.168000 0.231401
65 | v -1.226703 -0.023036 0.043034
66 | v -1.226703 -0.023036 0.231464
67 | v -1.304556 -0.023036 0.142520
68 | v -0.442164 -0.009523 0.406435
69 | v -0.875984 0.035470 1.161559
70 | v -0.727243 0.035470 1.316844
71 | v -0.494141 0.035470 1.316844
72 | v -1.376274 0.035470 0.874979
73 | v -1.417826 0.035470 0.043034
74 | v 1.016889 0.247035 0.041089
75 | v -0.910201 0.071705 0.260759
76 | v -0.910201 0.114239 0.142520
77 | v -1.347072 0.053931 0.291132
78 | v -0.949411 0.058716 0.324121
79 | v -1.086356 0.618995 0.582384
80 | v -0.945513 0.618995 0.582384
81 | v -0.820765 0.618995 0.582384
82 | v -1.074394 0.035470 0.526090
83 | v -1.347072 0.017010 0.291132
84 | v -1.420792 0.035470 0.231401
85 | v -1.704863 0.035470 0.554642
86 | v -1.607890 0.035470 0.874979
87 | v 1.142508 0.156204 0.091107
88 | v 0.821684 0.218761 0.028011
89 | v 1.453805 0.131564 0.065163
90 | v 0.835981 0.180467 0.042103
91 | v -0.910201 0.094558 0.000000
92 | v 0.403590 0.158204 0.000000
93 | v 1.236021 0.268974 0.000000
94 | v 1.568094 0.113851 0.000000
95 | v 1.791648 0.062518 0.000000
96 | v 2.014232 -0.030625 0.000000
97 | v 1.566378 -0.120168 0.000000
98 | v 1.789931 -0.082458 0.000000
99 | v 0.919486 -0.169810 0.000000
100 | v -0.408812 -0.167592 0.000000
101 | v -1.226703 0.078200 0.000000
102 | v -1.226703 -0.023036 0.000000
103 | v -1.442453 0.035470 0.000000
104 | v 1.016889 0.266178 0.000000
105 | v 1.487051 0.151710 0.000000
106 | v 0.817113 0.231006 0.000000
107 | vt 0.302940 0.699066
108 | vt 0.442081 0.762279
109 | vt 0.260042 0.751449
110 | vt 0.780315 0.800162
111 | vt 0.431099 0.800162
112 | vt 0.239093 0.773292
113 | vt 0.316239 0.628907
114 | vt 0.320312 0.648438
115 | vt 0.269531 0.648438
116 | vt 0.268080 0.668033
117 | vt 0.209833 0.668033
118 | vt 0.152344 0.648438
119 | vt 0.218750 0.648438
120 | vt 0.234686 0.622186
121 | vt 0.122021 0.761249
122 | vt 0.069858 0.786983
123 | vt 0.062599 0.772615
124 | vt 0.121565 0.800162
125 | vt 0.131726 0.782173
126 | vt 0.002978 0.800162
127 | vt 0.062143 0.800162
128 | vt 0.687288 0.720490
129 | vt 0.780315 0.762279
130 | vt 0.117476 0.888175
131 | vt 0.176898 0.858183
132 | vt 0.176898 0.893135
133 | vt 0.057855 0.881984
134 | vt 0.117476 0.868207
135 | vt 0.314919 0.902915
136 | vt 0.348847 0.844988
137 | vt 0.350939 0.448650
138 | vt 0.338144 0.510748
139 | vt 0.289431 0.452822
140 | vt 0.329981 0.132766
141 | vt 0.645773 0.071258
142 | vt 0.645773 0.132766
143 | vt 0.452780 0.685040
144 | vt 0.669723 0.450134
145 | vt 0.731683 0.450134
146 | vt 0.771220 0.491410
147 | vt 0.823959 0.660323
148 | vt 0.507656 0.899553
149 | vt 0.357817 0.890633
150 | vt 0.386127 0.840816
151 | vt 0.701919 0.845469
152 | vt 0.896439 0.722777
153 | vt 0.916034 0.738294
154 | vt 0.864444 0.738294
155 | vt 0.445843 0.497454
156 | vt 0.469468 0.469031
157 | vt 0.469468 0.497454
158 | vt 0.495912 0.497454
159 | vt 0.654638 0.182832
160 | vt 0.668454 0.424826
161 | vt 0.451511 0.189920
162 | vt 0.654638 0.179292
163 | vt 0.778209 0.165068
164 | vt 0.822690 0.211097
165 | vt 0.864444 0.469031
166 | vt 0.864444 0.497454
167 | vt 0.916034 0.484582
168 | vt 0.863174 0.082019
169 | vt 0.863174 0.071258
170 | vt 0.913977 0.082019
171 | vt 0.864444 0.800162
172 | vt 0.864444 0.789083
173 | vt 0.809306 0.497454
174 | vt 0.809306 0.469031
175 | vt 0.860108 0.484582
176 | vt 0.915246 0.789083
177 | vt 0.921792 0.800162
178 | vt 0.822690 0.214637
179 | vt 0.769951 0.383550
180 | vt 0.730414 0.424826
181 | vt 0.834355 0.894697
182 | vt 0.919320 0.884002
183 | vt 0.141653 0.276624
184 | vt 0.107458 0.425551
185 | vt 0.038204 0.279948
186 | vt 0.178054 0.425551
187 | vt 0.140617 0.425551
188 | vt 0.883868 0.109141
189 | vt 0.863174 0.132783
190 | vt 0.780315 0.730850
191 | vt 0.904201 0.567586
192 | vt 0.790737 0.714008
193 | vt 0.991542 0.652734
194 | vt 0.385137 0.280077
195 | vt 0.361364 0.425551
196 | vt 0.394523 0.425551
197 | vt 0.431960 0.425551
198 | vt 0.501261 0.275352
199 | vt 0.895170 0.148643
200 | vt 0.914765 0.132766
201 | vt 0.902932 0.303834
202 | vt 0.990273 0.218686
203 | vt 0.964497 0.303834
204 | vt 0.965766 0.567586
205 | vt 0.885173 0.762279
206 | vt 0.151941 0.583698
207 | vt 0.239090 0.583579
208 | vt 0.234686 0.590248
209 | vt 0.316239 0.596969
210 | vt 0.431096 0.590779
211 | vt 0.321182 0.614322
212 | vt 0.319967 0.606876
213 | vt 0.431099 0.614322
214 | vt 0.143107 0.601016
215 | vt 0.131726 0.583028
216 | vt 0.321182 0.668033
217 | vt 0.121565 0.601016
218 | vt 0.131723 0.574876
219 | vt 0.292110 0.279948
220 | vt 0.247354 0.275352
221 | vt 0.710784 0.887594
222 | vt 0.920523 0.071258
223 | vt 0.495912 0.469031
224 | vt 0.445826 0.469031
225 | vt 0.292701 0.071258
226 | vt 0.390527 0.498466
227 | vt 0.143107 0.668033
228 | vt 0.302940 0.699066
229 | vt 0.260042 0.751449
230 | vt 0.442081 0.762279
231 | vt 0.239093 0.773292
232 | vt 0.316239 0.628907
233 | vt 0.269531 0.648438
234 | vt 0.320312 0.648438
235 | vt 0.218750 0.648438
236 | vt 0.152344 0.648438
237 | vt 0.234686 0.622186
238 | vt 0.122021 0.761249
239 | vt 0.062599 0.772615
240 | vt 0.069858 0.786983
241 | vt 0.131726 0.782173
242 | vt 0.687288 0.720490
243 | vt 0.780315 0.762279
244 | vt 0.117476 0.888175
245 | vt 0.176898 0.893135
246 | vt 0.314919 0.902915
247 | vt 0.350939 0.448650
248 | vt 0.338144 0.510748
249 | vt 0.329981 0.132766
250 | vt 0.645773 0.132766
251 | vt 0.452780 0.685040
252 | vt 0.731683 0.450134
253 | vt 0.669723 0.450134
254 | vt 0.823959 0.660323
255 | vt 0.771220 0.491410
256 | vt 0.507656 0.899553
257 | vt 0.386127 0.840816
258 | vt 0.357817 0.890633
259 | vt 0.701919 0.845469
260 | vt 0.896439 0.722777
261 | vt 0.864444 0.738294
262 | vt 0.916034 0.738294
263 | vt 0.445843 0.497454
264 | vt 0.469468 0.497454
265 | vt 0.469468 0.469031
266 | vt 0.495912 0.497454
267 | vt 0.654638 0.182832
268 | vt 0.451511 0.189920
269 | vt 0.668454 0.424826
270 | vt 0.654638 0.179292
271 | vt 0.822690 0.211097
272 | vt 0.778209 0.165068
273 | vt 0.864444 0.469031
274 | vt 0.916034 0.484582
275 | vt 0.864444 0.497454
276 | vt 0.863174 0.082019
277 | vt 0.913977 0.082019
278 | vt 0.864444 0.789083
279 | vt 0.809306 0.497454
280 | vt 0.860108 0.484582
281 | vt 0.809306 0.469031
282 | vt 0.915246 0.789083
283 | vt 0.822690 0.214637
284 | vt 0.769951 0.383550
285 | vt 0.730414 0.424826
286 | vt 0.834355 0.894697
287 | vt 0.919320 0.884002
288 | vt 0.141653 0.276624
289 | vt 0.038204 0.279948
290 | vt 0.107458 0.425551
291 | vt 0.140617 0.425551
292 | vt 0.178054 0.425551
293 | vt 0.883868 0.109141
294 | vt 0.863174 0.132783
295 | vt 0.780315 0.730850
296 | vt 0.904201 0.567586
297 | vt 0.790737 0.714008
298 | vt 0.991542 0.652734
299 | vt 0.385137 0.280077
300 | vt 0.394523 0.425551
301 | vt 0.361364 0.425551
302 | vt 0.501261 0.275352
303 | vt 0.431960 0.425551
304 | vt 0.895170 0.148643
305 | vt 0.914765 0.132766
306 | vt 0.902932 0.303834
307 | vt 0.964497 0.303834
308 | vt 0.990273 0.218686
309 | vt 0.965766 0.567586
310 | vt 0.885173 0.762279
311 | vt 0.151941 0.583698
312 | vt 0.234686 0.590248
313 | vt 0.239090 0.583579
314 | vt 0.316239 0.596969
315 | vt 0.319967 0.606876
316 | vt 0.131726 0.583028
317 | vt 0.131723 0.574876
318 | vt 0.292110 0.279948
319 | vt 0.247354 0.275352
320 | vt 0.710784 0.887594
321 | vt 0.495912 0.469031
322 | vt 0.445826 0.469031
323 | vt 0.390527 0.498466
324 | vn 0.0752 0.9554 -0.2856
325 | vn -0.0464 0.9582 -0.2821
326 | vn -0.0672 0.7335 -0.6764
327 | vn -0.1079 0.3076 -0.9454
328 | vn -0.1576 0.8951 -0.4170
329 | vn 0.3158 0.7660 -0.5600
330 | vn 0.1032 0.3081 -0.9457
331 | vn 0.0996 0.6507 -0.7527
332 | vn 0.1879 0.7196 -0.6685
333 | vn 0.2166 0.9180 -0.3323
334 | vn 0.3493 0.7254 -0.5931
335 | vn 0.3509 0.8385 -0.4170
336 | vn 0.0000 0.9645 -0.2639
337 | vn 0.0656 -0.7424 -0.6668
338 | vn 0.1839 -0.7958 -0.5769
339 | vn 0.0506 -0.6600 -0.7496
340 | vn 0.6508 -0.6744 -0.3487
341 | vn -0.0147 -0.9999 0.0018
342 | vn -0.0004 0.9652 -0.2616
343 | vn 0.0000 0.9994 -0.0351
344 | vn -0.0610 0.9980 -0.0190
345 | vn 0.0346 -0.6338 -0.7728
346 | vn -0.0101 -0.6854 -0.7281
347 | vn -0.2421 0.9703 0.0011
348 | vn -0.7517 -0.0009 -0.6595
349 | vn -0.7870 -0.0010 0.6169
350 | vn 0.0569 -0.9970 -0.0525
351 | vn -0.0625 -0.9970 -0.0448
352 | vn 0.0001 -0.0006 1.0000
353 | vn -0.0313 0.9991 -0.0300
354 | vn -0.1740 -0.9847 0.0000
355 | vn -0.2927 -0.9562 0.0000
356 | vn -0.0512 0.9901 0.1310
357 | vn 0.0000 0.0000 -1.0000
358 | vn -0.1931 0.9749 -0.1105
359 | vn -0.0670 -0.9975 -0.0209
360 | vn -0.0388 -0.9986 -0.0372
361 | vn -0.1372 -0.7744 -0.6176
362 | vn 0.0702 -0.4308 -0.8997
363 | vn 0.0000 -0.4186 -0.9082
364 | vn -0.1725 -0.9734 -0.1510
365 | vn -0.1596 -0.9872 0.0017
366 | vn -0.1795 -0.9737 0.1405
367 | vn 0.0048 0.9410 -0.3385
368 | vn 0.0000 0.9906 0.1368
369 | vn 0.0387 0.9987 -0.0335
370 | vn -0.0258 0.9948 -0.0985
371 | vn -0.0061 0.8987 -0.4386
372 | vn -0.0204 0.9946 -0.1018
373 | vn -0.1436 0.9816 0.1262
374 | vn 0.0000 0.5067 0.8621
375 | vn -0.0803 0.4857 0.8704
376 | vn -0.2881 -0.9557 -0.0602
377 | vn 0.0387 -0.9987 -0.0335
378 | vn -0.0421 -0.9990 -0.0128
379 | vn -0.0126 -0.9956 -0.0929
380 | vn -0.0646 -0.8840 -0.4629
381 | vn -0.0000 0.9995 -0.0316
382 | vn 0.0000 -0.9995 -0.0316
383 | vn -0.0766 0.9953 0.0600
384 | vn -0.0765 0.9948 -0.0671
385 | vn -0.0519 0.9893 0.1366
386 | vn -0.1094 0.4290 -0.8967
387 | vn 0.3481 0.8307 -0.4344
388 | vn -0.0681 0.4073 -0.9107
389 | vn -0.1215 0.3445 -0.9309
390 | vn -0.0116 0.9064 -0.4223
391 | vn -0.0132 0.9096 -0.4153
392 | vn 0.3865 0.8274 -0.4075
393 | vn -0.1436 -0.9816 0.1262
394 | vn 0.0970 0.4924 0.8649
395 | vn -0.0421 0.9990 -0.0128
396 | vn -0.0694 -0.4316 -0.8994
397 | vn -0.1182 -0.7472 -0.6540
398 | vn -0.2436 0.9615 0.1273
399 | vn -0.2588 -0.9545 -0.1481
400 | vn 0.0000 -0.9988 -0.0494
401 | vn -0.7875 0.0000 0.6163
402 | vn -0.7525 0.0004 -0.6586
403 | vn 0.0200 -0.7392 -0.6731
404 | vn 0.0355 0.9988 -0.0328
405 | vn -0.0017 -0.9976 0.0687
406 | vn 0.6468 -0.6647 -0.3739
407 | vn -0.0053 -0.7439 -0.6682
408 | vn 0.1353 -0.8022 -0.5815
409 | vn -0.0364 0.9403 -0.3383
410 | vn 0.2094 0.9120 -0.3527
411 | vn 0.1990 0.7019 -0.6839
412 | vn 0.1150 0.6924 -0.7123
413 | vn 0.3792 0.8117 -0.4444
414 | vn 0.0728 0.9469 -0.3132
415 | vn 0.0825 0.9589 -0.2714
416 | vn 0.0752 0.9554 0.2856
417 | vn -0.0464 0.9582 0.2821
418 | vn -0.0672 0.7335 0.6764
419 | vn -0.1079 0.3076 0.9454
420 | vn -0.1576 0.8951 0.4170
421 | vn 0.3158 0.7660 0.5600
422 | vn 0.1032 0.3081 0.9457
423 | vn 0.0996 0.6507 0.7527
424 | vn 0.1879 0.7196 0.6685
425 | vn 0.2166 0.9180 0.3323
426 | vn 0.3493 0.7254 0.5931
427 | vn 0.3509 0.8385 0.4170
428 | vn 0.0000 0.9645 0.2639
429 | vn 0.0656 -0.7424 0.6668
430 | vn 0.1839 -0.7958 0.5769
431 | vn 0.0506 -0.6600 0.7496
432 | vn 0.6508 -0.6744 0.3487
433 | vn -0.0147 -0.9999 -0.0018
434 | vn -0.0004 0.9652 0.2616
435 | vn 0.0000 0.9994 0.0351
436 | vn -0.0610 0.9980 0.0190
437 | vn 0.0346 -0.6338 0.7728
438 | vn -0.0101 -0.6854 0.7281
439 | vn -0.2421 0.9703 -0.0011
440 | vn -0.7517 -0.0009 0.6595
441 | vn -0.7870 -0.0010 -0.6169
442 | vn 0.0569 -0.9970 0.0525
443 | vn -0.0625 -0.9970 0.0448
444 | vn 0.0001 -0.0006 -1.0000
445 | vn -0.0313 0.9991 0.0300
446 | vn -0.0512 0.9901 -0.1310
447 | vn 0.0000 0.0000 1.0000
448 | vn -0.1931 0.9749 0.1105
449 | vn -0.0670 -0.9975 0.0209
450 | vn -0.0388 -0.9986 0.0372
451 | vn -0.1372 -0.7744 0.6176
452 | vn 0.0702 -0.4308 0.8997
453 | vn 0.0000 -0.4186 0.9082
454 | vn -0.1725 -0.9734 0.1510
455 | vn -0.1596 -0.9872 -0.0017
456 | vn -0.1795 -0.9737 -0.1405
457 | vn 0.0048 0.9410 0.3385
458 | vn 0.0000 0.9906 -0.1368
459 | vn 0.0387 0.9987 0.0335
460 | vn -0.0258 0.9948 0.0985
461 | vn -0.0061 0.8987 0.4386
462 | vn -0.0204 0.9946 0.1018
463 | vn -0.1436 0.9816 -0.1262
464 | vn 0.0000 0.5067 -0.8621
465 | vn -0.0803 0.4857 -0.8704
466 | vn -0.2881 -0.9557 0.0602
467 | vn 0.0387 -0.9987 0.0335
468 | vn -0.0421 -0.9990 0.0128
469 | vn -0.0126 -0.9956 0.0929
470 | vn -0.0646 -0.8840 0.4629
471 | vn -0.0000 0.9995 0.0316
472 | vn 0.0000 -0.9995 0.0316
473 | vn -0.0766 0.9953 -0.0600
474 | vn -0.0765 0.9948 0.0671
475 | vn -0.0519 0.9893 -0.1366
476 | vn -0.1094 0.4290 0.8967
477 | vn 0.3481 0.8307 0.4344
478 | vn -0.0681 0.4073 0.9107
479 | vn -0.1215 0.3445 0.9309
480 | vn -0.0116 0.9064 0.4223
481 | vn -0.0132 0.9096 0.4153
482 | vn 0.3865 0.8274 0.4075
483 | vn -0.1436 -0.9816 -0.1262
484 | vn 0.0970 0.4924 -0.8649
485 | vn -0.0421 0.9990 0.0128
486 | vn -0.0694 -0.4316 0.8994
487 | vn -0.1182 -0.7472 0.6540
488 | vn -0.2436 0.9615 -0.1273
489 | vn -0.2588 -0.9545 0.1481
490 | vn 0.0000 -0.9988 0.0494
491 | vn -0.7875 0.0000 -0.6163
492 | vn -0.7525 0.0004 0.6586
493 | vn 0.0200 -0.7392 0.6731
494 | vn 0.0355 0.9988 0.0328
495 | vn -0.0017 -0.9976 -0.0687
496 | vn 0.6468 -0.6647 0.3739
497 | vn -0.0053 -0.7439 0.6682
498 | vn 0.1353 -0.8022 0.5815
499 | vn -0.0364 0.9403 0.3383
500 | vn 0.2094 0.9120 0.3527
501 | vn 0.1990 0.7019 0.6839
502 | vn 0.1150 0.6924 0.7123
503 | vn 0.3792 0.8117 0.4444
504 | vn 0.0728 0.9469 0.3132
505 | vn 0.0825 0.9589 0.2714
506 | usemtl Material.001
507 | s off
508 | f 1/1/1 5/2/1 4/3/1
509 | f 5/2/2 87/4/2 88/5/2
510 | f 4/3/3 88/5/3 6/6/3
511 | f 43/7/4 41/8/4 27/9/4
512 | f 41/8/5 100/10/5 27/9/5
513 | f 89/11/6 42/12/6 7/13/6
514 | f 7/13/7 42/12/7 40/14/7
515 | f 6/6/8 9/15/8 4/3/8
516 | f 9/15/9 10/16/9 11/17/9
517 | f 90/18/10 10/16/10 8/19/10
518 | f 11/17/11 10/16/11 92/20/11
519 | f 10/16/12 91/21/12 92/20/12
520 | f 15/22/13 29/23/13 5/2/13
521 | f 11/24/14 93/25/14 9/26/14
522 | f 11/24/15 92/27/15 94/28/15
523 | f 4/29/16 93/25/16 95/30/16
524 | f 16/31/17 4/32/17 95/33/17
525 | f 16/34/18 96/35/18 17/36/18
526 | f 5/2/19 2/37/19 15/22/19
527 | f 15/22/20 24/38/20 23/39/20
528 | f 15/22/21 22/40/21 35/41/21
529 | f 2/42/22 1/43/22 16/44/22
530 | f 17/45/23 2/42/23 16/44/23
531 | f 30/46/24 37/47/24 12/48/24
532 | f 12/49/25 20/50/25 14/51/25
533 | f 20/50/26 13/52/26 14/51/26
534 | f 21/53/27 24/54/27 2/55/27
535 | f 21/56/28 3/57/28 35/58/28
536 | f 19/59/29 12/60/29 37/61/29
537 | f 15/22/30 23/39/30 22/40/30
538 | f 18/62/31 96/35/31 98/63/31
539 | f 98/63/32 26/64/32 18/62/32
540 | f 97/65/33 87/4/33 13/66/33
541 | f 13/67/34 18/68/34 26/69/34
542 | f 97/65/35 26/70/35 99/71/35
543 | f 35/72/36 22/73/36 21/53/36
544 | f 22/73/37 23/74/37 21/53/37
545 | f 3/75/38 17/45/38 19/76/38
546 | f 31/77/39 34/78/39 15/79/39
547 | f 31/77/40 32/80/40 33/81/40
548 | f 20/82/41 19/83/41 17/36/41
549 | f 20/82/42 17/36/42 96/35/42
550 | f 18/62/43 20/82/43 96/35/43
551 | f 29/23/44 28/84/44 12/48/44
552 | f 87/4/45 5/2/45 29/23/45
553 | f 25/85/46 30/46/46 35/41/46
554 | f 31/86/47 15/22/47 35/41/47
555 | f 30/46/48 12/48/48 28/84/48
556 | f 30/46/49 31/86/49 35/41/49
557 | f 30/46/50 38/87/50 37/47/50
558 | f 28/88/51 34/89/51 33/90/51
559 | f 28/88/52 32/91/52 30/92/52
560 | f 36/93/53 19/83/53 37/94/53
561 | f 36/93/54 25/95/54 35/58/54
562 | f 36/93/55 38/96/55 39/97/55
563 | f 36/93/56 35/58/56 3/57/56
564 | f 3/57/57 19/83/57 36/93/57
565 | f 25/85/58 39/98/58 30/46/58
566 | f 36/93/59 39/97/59 25/95/59
567 | f 29/23/60 14/99/60 13/66/60
568 | f 14/99/61 29/23/61 12/48/61
569 | f 87/4/62 29/23/62 13/66/62
570 | f 42/100/7 6/101/7 40/102/7
571 | f 43/103/63 6/101/63 88/104/63
572 | f 102/105/5 41/106/5 88/107/5
573 | f 101/108/64 8/109/64 42/100/64
574 | f 27/9/65 7/13/65 40/14/65
575 | f 40/14/66 43/7/66 27/9/66
576 | f 41/8/5 102/110/5 100/10/5
577 | f 100/10/67 89/11/67 27/9/67
578 | f 89/11/68 7/13/68 27/9/68
579 | f 101/108/69 90/111/69 8/109/69
580 | f 43/103/63 40/102/63 6/101/63
581 | f 88/104/4 41/106/4 43/103/4
582 | f 42/100/7 8/112/7 6/101/7
583 | f 36/93/70 37/94/70 38/96/70
584 | f 28/88/51 33/90/51 32/91/51
585 | f 28/88/71 15/113/71 34/89/71
586 | f 30/46/72 39/98/72 38/87/72
587 | f 31/77/73 30/114/73 32/80/73
588 | f 31/77/40 33/81/40 34/78/40
589 | f 3/75/74 21/115/74 17/45/74
590 | f 97/65/75 13/66/75 26/70/75
591 | f 98/63/76 99/116/76 26/64/76
592 | f 21/53/77 23/74/77 24/54/77
593 | f 20/50/78 18/117/78 13/52/78
594 | f 12/49/79 19/118/79 20/50/79
595 | f 17/45/80 21/115/80 2/42/80
596 | f 15/22/81 2/37/81 24/38/81
597 | f 16/34/82 95/119/82 96/35/82
598 | f 16/31/83 1/120/83 4/32/83
599 | f 4/29/84 9/26/84 93/25/84
600 | f 11/24/85 94/28/85 93/25/85
601 | f 15/22/86 28/84/86 29/23/86
602 | f 90/18/87 91/21/87 10/16/87
603 | f 9/15/88 8/19/88 10/16/88
604 | f 6/6/89 8/19/89 9/15/89
605 | f 89/11/90 101/121/90 42/12/90
606 | f 4/3/91 5/2/91 88/5/91
607 | f 1/1/92 2/37/92 5/2/92
608 | f 44/122/93 47/123/93 48/124/93
609 | f 48/124/94 88/5/94 87/4/94
610 | f 47/123/95 49/125/95 88/5/95
611 | f 86/126/96 70/127/96 84/128/96
612 | f 84/128/97 70/127/97 100/10/97
613 | f 89/11/98 50/129/98 85/130/98
614 | f 50/129/99 83/131/99 85/130/99
615 | f 49/125/100 47/123/100 52/132/100
616 | f 52/132/101 54/133/101 53/134/101
617 | f 90/18/102 51/135/102 53/134/102
618 | f 54/133/103 92/20/103 53/134/103
619 | f 53/134/104 92/20/104 91/21/104
620 | f 58/136/105 48/124/105 72/137/105
621 | f 54/138/106 52/139/106 93/25/106
622 | f 54/138/107 94/28/107 92/27/107
623 | f 47/140/108 95/30/108 93/25/108
624 | f 59/141/109 95/33/109 47/142/109
625 | f 59/143/110 60/144/110 96/35/110
626 | f 48/124/111 58/136/111 45/145/111
627 | f 58/136/112 66/146/112 67/147/112
628 | f 58/136/113 78/148/113 65/149/113
629 | f 45/150/114 59/151/114 44/152/114
630 | f 60/153/115 59/151/115 45/150/115
631 | f 73/154/116 55/155/116 80/156/116
632 | f 55/157/117 57/158/117 63/159/117
633 | f 63/159/118 57/158/118 56/160/118
634 | f 64/161/119 45/162/119 67/163/119
635 | f 64/164/120 78/165/120 46/166/120
636 | f 62/167/121 80/168/121 55/169/121
637 | f 58/136/122 65/149/122 66/146/122
638 | f 61/170/31 98/63/31 96/35/31
639 | f 98/63/32 61/170/32 69/171/32
640 | f 97/65/123 56/172/123 87/4/123
641 | f 56/173/124 69/174/124 61/175/124
642 | f 97/65/125 99/71/125 69/176/125
643 | f 78/177/126 64/161/126 65/178/126
644 | f 65/178/127 64/161/127 66/179/127
645 | f 46/180/128 62/181/128 60/153/128
646 | f 74/182/129 58/183/129 77/184/129
647 | f 74/182/130 76/185/130 75/186/130
648 | f 63/187/131 60/144/131 62/188/131
649 | f 63/187/132 96/35/132 60/144/132
650 | f 61/170/133 96/35/133 63/187/133
651 | f 72/137/134 55/155/134 71/189/134
652 | f 87/4/135 72/137/135 48/124/135
653 | f 68/190/136 78/148/136 73/154/136
654 | f 74/191/137 78/148/137 58/136/137
655 | f 73/154/138 71/189/138 55/155/138
656 | f 73/154/139 78/148/139 74/191/139
657 | f 73/154/140 80/156/140 81/192/140
658 | f 71/193/141 76/194/141 77/195/141
659 | f 71/193/142 73/196/142 75/197/142
660 | f 79/198/143 80/199/143 62/188/143
661 | f 79/198/144 78/165/144 68/200/144
662 | f 79/198/145 82/201/145 81/202/145
663 | f 79/198/146 46/166/146 78/165/146
664 | f 46/166/147 79/198/147 62/188/147
665 | f 68/190/148 73/154/148 82/203/148
666 | f 79/198/149 68/200/149 82/201/149
667 | f 72/137/150 56/172/150 57/204/150
668 | f 57/204/151 55/155/151 72/137/151
669 | f 87/4/152 56/172/152 72/137/152
670 | f 85/205/99 83/206/99 49/207/99
671 | f 86/208/153 88/104/153 49/207/153
672 | f 102/105/97 88/107/97 84/209/97
673 | f 101/108/154 85/205/154 51/210/154
674 | f 70/127/155 83/131/155 50/129/155
675 | f 83/131/156 70/127/156 86/126/156
676 | f 84/128/97 100/10/97 102/110/97
677 | f 100/10/157 70/127/157 89/11/157
678 | f 89/11/158 70/127/158 50/129/158
679 | f 101/108/159 51/210/159 90/111/159
680 | f 86/208/153 49/207/153 83/206/153
681 | f 88/104/96 86/208/96 84/209/96
682 | f 85/205/99 49/207/99 51/211/99
683 | f 79/198/160 81/202/160 80/199/160
684 | f 71/193/141 75/197/141 76/194/141
685 | f 71/193/161 77/195/161 58/212/161
686 | f 73/154/162 81/192/162 82/203/162
687 | f 74/182/163 75/186/163 73/213/163
688 | f 74/182/130 77/184/130 76/185/130
689 | f 46/180/164 60/153/164 64/214/164
690 | f 97/65/165 69/176/165 56/172/165
691 | f 98/63/166 69/171/166 99/116/166
692 | f 64/161/167 67/163/167 66/179/167
693 | f 63/159/168 56/160/168 61/215/168
694 | f 55/157/169 63/159/169 62/216/169
695 | f 60/153/170 45/150/170 64/214/170
696 | f 58/136/171 67/147/171 45/145/171
697 | f 59/143/172 96/35/172 95/119/172
698 | f 59/141/173 47/142/173 44/217/173
699 | f 47/140/174 93/25/174 52/139/174
700 | f 54/138/175 93/25/175 94/28/175
701 | f 58/136/176 72/137/176 71/189/176
702 | f 90/18/177 53/134/177 91/21/177
703 | f 52/132/178 53/134/178 51/135/178
704 | f 49/125/179 52/132/179 51/135/179
705 | f 89/11/180 85/130/180 101/121/180
706 | f 47/123/181 88/5/181 48/124/181
707 | f 44/122/182 48/124/182 45/145/182
708 |
--------------------------------------------------------------------------------
/src/eleventy/public/assets/models/efa/efa.obj:
--------------------------------------------------------------------------------
1 | # Blender v2.79 (sub 0) OBJ File: 'fighters_0.blend'
2 | # www.blender.org
3 | mtllib ef-2000.mtl
4 | o EF-2000
5 | v -0.637137 -0.058235 -1.212867
6 | v 1.408317 0.129410 -0.096909
7 | v -0.470713 0.060266 -0.225620
8 | v 0.697766 0.065191 -0.184658
9 | v -1.227013 0.048801 -0.208787
10 | v 0.806605 0.177697 -0.113658
11 | v -0.499514 0.146273 -0.124903
12 | v -1.227013 0.107477 -0.111575
13 | v -1.010604 -0.053182 -0.223190
14 | v 1.430488 0.019222 -0.116861
15 | v -0.470713 -0.026554 -0.239685
16 | v 0.627798 -0.180045 -0.213781
17 | v -0.470713 -0.139104 -0.225865
18 | v -0.470713 -0.087806 -0.239685
19 | v -1.181829 0.179699 -0.024453
20 | v 0.635645 -0.060923 -0.202913
21 | v 1.478628 -0.073220 -0.093295
22 | v 0.878495 -0.072815 -0.195265
23 | v 0.878495 -0.186329 -0.207954
24 | v -1.227013 -0.127982 -0.112402
25 | v 1.064405 0.042134 -0.156845
26 | v 1.260287 0.049293 -0.136943
27 | v 1.260287 0.017903 -0.135526
28 | v 1.097918 -0.013106 -0.445226
29 | v 0.989527 -0.013106 -0.445226
30 | v -0.963396 -0.027673 -1.243429
31 | v -0.599211 -0.058235 -1.243429
32 | v -1.001322 -0.058235 -1.243429
33 | v -0.637137 -0.027673 -1.243429
34 | v -0.963396 -0.058235 -1.212867
35 | v -0.637137 -0.088797 -1.243429
36 | v -0.963396 -0.088797 -1.243429
37 | v -0.963396 -0.058235 -1.273991
38 | v -0.637137 -0.058235 -1.273991
39 | v -1.227013 -0.072252 -0.208787
40 | v -1.405837 0.038651 -0.188607
41 | v -1.405837 0.086090 -0.106440
42 | v -1.405837 -0.103667 -0.106440
43 | v -1.405837 -0.056228 -0.024272
44 | v -1.405837 -0.056228 -0.188607
45 | v -1.405837 0.038651 -0.024272
46 | v -1.227013 0.107477 -0.022448
47 | v -1.359631 0.107477 -0.022448
48 | v -1.358574 0.179699 -0.022448
49 | v -0.507611 0.150293 -0.050429
50 | v 1.157646 0.273375 -0.071246
51 | v 1.006628 0.306839 -0.072911
52 | v -0.637137 -0.058235 1.212867
53 | v 1.408317 0.129410 0.096909
54 | v -0.470713 0.060266 0.225620
55 | v 0.697766 0.065191 0.184658
56 | v -1.227013 0.048801 0.208787
57 | v 0.806605 0.177697 0.113658
58 | v -0.499514 0.146273 0.124903
59 | v -1.227013 0.107477 0.111575
60 | v -1.010604 -0.053182 0.223190
61 | v 1.430488 0.019222 0.116861
62 | v -0.470713 -0.026554 0.239685
63 | v 0.627798 -0.180045 0.213781
64 | v -0.470713 -0.139104 0.225865
65 | v -0.470713 -0.087806 0.239685
66 | v -1.181829 0.179699 0.024453
67 | v 0.635645 -0.060923 0.202913
68 | v 1.478628 -0.073220 0.093295
69 | v 0.878495 -0.072815 0.195265
70 | v 0.878495 -0.186329 0.207954
71 | v -1.227013 -0.127982 0.112402
72 | v 1.064405 0.042134 0.156845
73 | v 1.260287 0.049293 0.136943
74 | v 1.260287 0.017903 0.135526
75 | v 1.097918 -0.013106 0.445226
76 | v 0.989527 -0.013106 0.445226
77 | v -0.963396 -0.027673 1.243429
78 | v -0.599211 -0.058235 1.243429
79 | v -1.001322 -0.058235 1.243429
80 | v -0.637137 -0.027673 1.243429
81 | v -0.963396 -0.058235 1.212867
82 | v -0.637137 -0.088797 1.243429
83 | v -0.963396 -0.088797 1.243429
84 | v -0.963396 -0.058235 1.273991
85 | v -0.637137 -0.058235 1.273991
86 | v -1.227013 -0.072252 0.208787
87 | v -1.405837 0.038651 0.188607
88 | v -1.405837 0.086090 0.106440
89 | v -1.405837 -0.103667 0.106440
90 | v -1.405837 -0.056228 0.024272
91 | v -1.405837 -0.056228 0.188607
92 | v -1.405837 0.038651 0.024272
93 | v -1.227013 0.107477 0.022448
94 | v -1.359631 0.107477 0.022448
95 | v -1.358574 0.179699 0.022448
96 | v -0.507611 0.150293 0.050429
97 | v 1.157646 0.273375 0.071246
98 | v 1.006628 0.306839 0.072911
99 | v 1.995160 -0.027199 0.000000
100 | v -0.579312 0.185891 0.000000
101 | v -1.359309 0.179699 0.000000
102 | v 1.555716 -0.089928 0.000000
103 | v 0.877271 -0.072852 0.000000
104 | v 0.627798 -0.221217 0.000000
105 | v -0.470713 -0.191763 0.000000
106 | v -1.426984 0.810342 0.000000
107 | v -1.655949 0.816006 0.000000
108 | v -1.570942 0.836300 0.000000
109 | v 0.878495 -0.075334 0.000000
110 | v 0.878495 -0.226005 0.000000
111 | v -1.227013 -0.062495 0.000000
112 | v -1.227013 0.048801 0.000000
113 | v -1.227013 0.092461 0.000000
114 | v -1.359631 0.092461 0.000000
115 | v 0.385472 0.241012 0.000000
116 | v 1.475018 0.145701 0.000000
117 | v 1.179164 0.297095 0.000000
118 | v 1.000319 0.334393 0.000000
119 | vt 0.375195 0.740672
120 | vt 0.768365 0.737287
121 | vt 0.770803 0.759569
122 | vt 0.394253 0.496202
123 | vt 0.501980 0.535095
124 | vt 0.314999 0.535077
125 | vt 0.193993 0.535077
126 | vt 0.263030 0.557061
127 | vt 0.173898 0.557061
128 | vt 0.502144 0.535095
129 | vt 0.316910 0.557061
130 | vt 0.194067 0.745714
131 | vt 0.173988 0.774886
132 | vt 0.017414 0.774886
133 | vt 0.768380 0.885180
134 | vt 0.375210 0.894639
135 | vt 0.759710 0.859290
136 | vt 0.194082 0.880104
137 | vt 0.017429 0.832961
138 | vt 0.187408 0.846935
139 | vt 0.922229 0.825140
140 | vt 0.759710 0.799275
141 | vt 0.987373 0.819399
142 | vt 0.017402 0.089990
143 | vt 0.149684 0.089990
144 | vt 0.172889 0.122500
145 | vt 0.353910 0.089990
146 | vt 0.426645 0.151071
147 | vt 0.426658 0.729094
148 | vt 0.809793 0.425076
149 | vt 0.759695 0.718025
150 | vt 0.922214 0.722990
151 | vt 0.908004 0.425076
152 | vt 0.172917 0.819108
153 | vt 0.429007 0.071038
154 | vt 0.759683 0.006685
155 | vt 0.759683 0.074675
156 | vt 0.783089 0.963505
157 | vt 0.845687 0.930093
158 | vt 0.841868 0.964263
159 | vt 0.987373 0.855839
160 | vt 0.238642 0.855987
161 | vt 0.297607 0.853832
162 | vt 0.429007 0.006685
163 | vt 0.353542 0.069284
164 | vt 0.353542 0.006685
165 | vt 0.809780 0.387409
166 | vt 0.426645 0.083391
167 | vt 0.759683 0.094460
168 | vt 0.545142 0.304303
169 | vt 0.634437 0.495844
170 | vt 0.491717 0.304303
171 | vt 0.608848 0.501953
172 | vt 0.565514 0.494140
173 | vt 0.922201 0.089495
174 | vt 0.907991 0.387409
175 | vt 0.269539 0.535077
176 | vt 0.194082 0.535077
177 | vt 0.429035 0.786951
178 | vt 0.353570 0.819230
179 | vt 0.353570 0.785060
180 | vt 0.302464 0.801759
181 | vt 0.302096 0.742980
182 | vt 0.375199 0.740678
183 | vt 0.426673 0.822810
184 | vt 0.759710 0.833155
185 | vt 0.759710 0.814717
186 | vt 0.187380 0.125167
187 | vt 0.238614 0.130786
188 | vt 0.287491 0.224012
189 | vt 0.297579 0.137203
190 | vt 0.320119 0.224012
191 | vt 0.238627 0.733663
192 | vt 0.287504 0.640863
193 | vt 0.320132 0.640863
194 | vt 0.187393 0.739708
195 | vt 0.919420 0.415876
196 | vt 0.908004 0.415876
197 | vt 0.809780 0.396609
198 | vt 0.809780 0.405809
199 | vt 0.798363 0.396609
200 | vt 0.919407 0.396609
201 | vt 0.907991 0.396609
202 | vt 0.809793 0.415876
203 | vt 0.798376 0.415876
204 | vt 0.809793 0.406676
205 | vt 0.908004 0.406676
206 | vt 0.907991 0.405809
207 | vt 0.987345 0.040521
208 | vt 0.987345 0.006685
209 | vt 0.987345 0.069535
210 | vt 0.034834 0.555807
211 | vt 0.096727 0.590650
212 | vt 0.036535 0.598064
213 | vt 0.097571 0.525905
214 | vt 0.037680 0.517820
215 | vt 0.103915 0.493972
216 | vt 0.045488 0.477967
217 | vt 0.102283 0.619496
218 | vt 0.057775 0.676010
219 | vt 0.043652 0.634717
220 | vt 0.057675 0.442660
221 | vt 0.755353 0.951618
222 | vt 0.748595 0.969280
223 | vt 0.721766 0.969280
224 | vt 0.924062 0.923877
225 | vt 0.923747 0.950235
226 | vt 0.897195 0.965302
227 | vt 0.505318 0.275807
228 | vt 0.545142 0.282565
229 | vt 0.505318 0.282565
230 | vt 0.545142 0.304303
231 | vt 0.551897 0.304303
232 | vt 0.551897 0.278042
233 | vt 0.310347 0.306166
234 | vt 0.297592 0.727672
235 | vt 0.987358 0.741299
236 | vt 0.501965 0.774886
237 | vt 0.792386 0.774886
238 | vt 0.987358 0.768129
239 | vt 0.987358 0.774886
240 | vt 0.238642 0.846538
241 | vt 0.407973 0.860773
242 | vt 0.987373 0.873502
243 | vt 0.783089 0.918150
244 | vt 0.302096 0.801759
245 | vt 0.095237 0.556659
246 | vt 0.113514 0.652331
247 | vt 0.113514 0.466100
248 | vt 0.755353 0.964760
249 | vt 0.873640 0.950775
250 | vt 0.874330 0.921570
251 | vt 0.897155 0.908205
252 | vt 0.545139 0.275807
253 | vt 0.375195 0.740672
254 | vt 0.770803 0.759569
255 | vt 0.768365 0.737287
256 | vt 0.394253 0.496202
257 | vt 0.314999 0.535077
258 | vt 0.193993 0.535077
259 | vt 0.194067 0.745714
260 | vt 0.768380 0.885180
261 | vt 0.759710 0.859290
262 | vt 0.375210 0.894639
263 | vt 0.194082 0.880104
264 | vt 0.187408 0.846935
265 | vt 0.922229 0.825140
266 | vt 0.987373 0.819399
267 | vt 0.759710 0.799275
268 | vt 0.172889 0.122500
269 | vt 0.426645 0.151071
270 | vt 0.426658 0.729094
271 | vt 0.759695 0.718025
272 | vt 0.809793 0.425076
273 | vt 0.922214 0.722990
274 | vt 0.908004 0.425076
275 | vt 0.172917 0.819108
276 | vt 0.429007 0.071038
277 | vt 0.759683 0.074675
278 | vt 0.841868 0.964263
279 | vt 0.845687 0.930093
280 | vt 0.987373 0.855839
281 | vt 0.238642 0.855987
282 | vt 0.297607 0.853832
283 | vt 0.353542 0.069284
284 | vt 0.809780 0.387409
285 | vt 0.759683 0.094460
286 | vt 0.426645 0.083391
287 | vt 0.491717 0.304303
288 | vt 0.922201 0.089495
289 | vt 0.907991 0.387409
290 | vt 0.194082 0.535077
291 | vt 0.269539 0.535077
292 | vt 0.429035 0.786951
293 | vt 0.353570 0.785060
294 | vt 0.353570 0.819230
295 | vt 0.375199 0.740678
296 | vt 0.302096 0.742980
297 | vt 0.759710 0.833155
298 | vt 0.426673 0.822810
299 | vt 0.759710 0.814717
300 | vt 0.187380 0.125167
301 | vt 0.287491 0.224012
302 | vt 0.238614 0.130786
303 | vt 0.320119 0.224012
304 | vt 0.297579 0.137203
305 | vt 0.238627 0.733663
306 | vt 0.320132 0.640863
307 | vt 0.287504 0.640863
308 | vt 0.187393 0.739708
309 | vt 0.919420 0.415876
310 | vt 0.908004 0.415876
311 | vt 0.809780 0.396609
312 | vt 0.798363 0.396609
313 | vt 0.809780 0.405809
314 | vt 0.919407 0.396609
315 | vt 0.907991 0.396609
316 | vt 0.809793 0.415876
317 | vt 0.809793 0.406676
318 | vt 0.798376 0.415876
319 | vt 0.908004 0.406676
320 | vt 0.907991 0.405809
321 | vt 0.987345 0.040521
322 | vt 0.987345 0.069535
323 | vt 0.034834 0.555807
324 | vt 0.096727 0.590650
325 | vt 0.097571 0.525905
326 | vt 0.037680 0.517820
327 | vt 0.103915 0.493972
328 | vt 0.045488 0.477967
329 | vt 0.102283 0.619496
330 | vt 0.057775 0.676010
331 | vt 0.057675 0.442660
332 | vt 0.721766 0.969280
333 | vt 0.748595 0.969280
334 | vt 0.924062 0.923877
335 | vt 0.897195 0.965302
336 | vt 0.923747 0.950235
337 | vt 0.505318 0.282565
338 | vt 0.545142 0.282565
339 | vt 0.545142 0.304303
340 | vt 0.297592 0.727672
341 | vt 0.987358 0.741299
342 | vt 0.987358 0.774886
343 | vt 0.987358 0.768129
344 | vt 0.238642 0.846538
345 | vt 0.407973 0.860773
346 | vt 0.987373 0.873502
347 | vt 0.095237 0.556659
348 | vt 0.113514 0.652331
349 | vt 0.113514 0.466100
350 | vt 0.874330 0.921570
351 | vt 0.873640 0.950775
352 | vt 0.897155 0.908205
353 | vn -0.0235 0.9981 -0.0564
354 | vn -0.1532 0.5053 -0.8492
355 | vn 0.4134 0.8078 -0.4203
356 | vn -0.1401 0.9221 -0.3606
357 | vn 0.2952 0.8880 -0.3525
358 | vn -0.0126 0.7586 -0.6514
359 | vn 0.2149 0.2157 -0.9525
360 | vn -0.0432 -0.2415 -0.9694
361 | vn 0.1356 -0.9498 -0.2821
362 | vn -0.0082 -0.9988 -0.0489
363 | vn 0.0323 0.9988 -0.0380
364 | vn -0.0495 0.9987 -0.0075
365 | vn -0.0000 0.9995 -0.0325
366 | vn 0.1886 -0.1492 -0.9706
367 | vn -0.0338 -0.9733 -0.2269
368 | vn 1.0000 0.0000 0.0000
369 | vn -0.0260 0.7565 -0.6535
370 | vn 0.0845 0.3161 -0.9449
371 | vn -0.0188 -0.9821 -0.1874
372 | vn 0.0254 -0.9991 -0.0347
373 | vn -0.1362 -0.0635 -0.9886
374 | vn 0.0075 0.0417 -0.9991
375 | vn -0.0639 -0.9980 0.0020
376 | vn 0.0000 -0.9995 -0.0304
377 | vn 0.0473 0.2546 -0.9659
378 | vn 0.0259 0.1110 -0.9935
379 | vn 0.0489 0.9988 0.0005
380 | vn 0.0377 0.1598 -0.9864
381 | vn -0.0380 0.1598 -0.9864
382 | vn 0.0309 0.0888 -0.9956
383 | vn -0.0034 -0.9948 0.1014
384 | vn -0.1435 -0.9644 0.2220
385 | vn 0.0000 0.9801 -0.1984
386 | vn 0.1999 0.9345 -0.2944
387 | vn -0.4951 0.6144 0.6144
388 | vn 0.4951 -0.6144 -0.6144
389 | vn -0.4951 -0.6144 0.6144
390 | vn 0.4951 0.6144 -0.6144
391 | vn -0.4951 0.6144 -0.6144
392 | vn 0.0000 0.7071 -0.7071
393 | vn 0.4951 -0.6144 0.6144
394 | vn 0.0000 -0.7071 0.7071
395 | vn -0.4951 -0.6144 -0.6144
396 | vn 0.0000 -0.7071 -0.7071
397 | vn 0.4951 0.6144 0.6144
398 | vn 0.0000 0.7071 0.7071
399 | vn -0.0664 -0.0000 -0.9978
400 | vn -0.1461 -0.8548 0.4980
401 | vn -0.0875 -0.8624 -0.4986
402 | vn -0.1127 0.8794 0.4625
403 | vn -0.1063 0.8513 -0.5138
404 | vn -0.1121 -0.0000 -0.9937
405 | vn -0.0981 -0.8599 0.5010
406 | vn -0.1345 0.0000 0.9909
407 | vn -0.1329 -0.8580 -0.4961
408 | vn -1.0000 -0.0000 0.0000
409 | vn 0.0000 -0.8312 -0.5560
410 | vn 0.0000 1.0000 0.0000
411 | vn -0.0113 -0.0207 -0.9997
412 | vn -1.0000 0.0037 0.0025
413 | vn -0.0483 -0.9727 -0.2268
414 | vn 0.0399 0.0542 -0.9977
415 | vn 0.1504 0.2054 -0.9671
416 | vn -0.0172 0.9828 -0.1838
417 | vn 0.0000 -0.9950 0.0996
418 | vn -0.0542 0.9967 -0.0597
419 | vn -0.0448 0.7850 -0.6179
420 | vn 0.0410 -0.0534 -0.9977
421 | vn 0.0009 -0.2601 -0.9656
422 | vn 0.1085 -0.1922 -0.9754
423 | vn 0.0702 -0.2761 -0.9586
424 | vn 0.0817 0.1032 -0.9913
425 | vn 0.0960 0.4632 -0.8811
426 | vn 0.0576 0.4009 -0.9143
427 | vn 0.1272 -0.0875 -0.9880
428 | vn -0.0704 0.7650 -0.6402
429 | vn 0.0279 0.5141 -0.8573
430 | vn 0.1904 0.9128 -0.3614
431 | vn -0.0550 0.8548 -0.5160
432 | vn 0.0587 0.2165 -0.9745
433 | vn 0.4256 0.8122 -0.3990
434 | vn -0.0461 -0.2598 -0.9645
435 | vn -0.0249 -0.9874 -0.1563
436 | vn -0.0263 -0.9816 -0.1890
437 | vn -0.0202 -0.9818 -0.1891
438 | vn 0.0357 0.0885 -0.9954
439 | vn 0.8969 0.4422 0.0057
440 | vn 0.0342 0.1266 -0.9914
441 | vn -0.0235 0.0907 -0.9956
442 | vn -0.0889 0.8626 0.4980
443 | vn -0.1171 0.8601 -0.4966
444 | vn -0.1006 -0.8616 0.4975
445 | vn -0.1332 -0.8583 -0.4955
446 | vn 0.0000 0.0000 -1.0000
447 | vn -0.9994 0.0146 -0.0327
448 | vn -0.0594 0.9982 0.0000
449 | vn -0.0459 0.7908 -0.6104
450 | vn 0.2077 0.9208 -0.3300
451 | vn -0.0078 0.0326 -0.9994
452 | vn -0.0235 0.9981 0.0564
453 | vn -0.1532 0.5053 0.8492
454 | vn 0.4134 0.8078 0.4203
455 | vn -0.1401 0.9221 0.3606
456 | vn 0.2952 0.8880 0.3525
457 | vn -0.0126 0.7586 0.6514
458 | vn 0.2149 0.2157 0.9525
459 | vn -0.0432 -0.2415 0.9694
460 | vn 0.1356 -0.9498 0.2821
461 | vn -0.0082 -0.9988 0.0489
462 | vn 0.0323 0.9988 0.0380
463 | vn -0.0495 0.9987 0.0075
464 | vn -0.0000 0.9995 0.0325
465 | vn 0.1886 -0.1492 0.9706
466 | vn -0.0338 -0.9733 0.2269
467 | vn -0.0260 0.7565 0.6535
468 | vn 0.0845 0.3161 0.9449
469 | vn -0.0188 -0.9821 0.1874
470 | vn 0.0254 -0.9991 0.0347
471 | vn -0.1362 -0.0635 0.9886
472 | vn 0.0075 0.0417 0.9991
473 | vn -0.0639 -0.9980 -0.0020
474 | vn 0.0000 -0.9995 0.0304
475 | vn 0.0473 0.2546 0.9659
476 | vn 0.0259 0.1110 0.9935
477 | vn 0.0489 0.9988 -0.0005
478 | vn 0.0377 0.1598 0.9864
479 | vn -0.0380 0.1598 0.9864
480 | vn 0.0309 0.0888 0.9956
481 | vn -0.0034 -0.9948 -0.1014
482 | vn -0.1435 -0.9644 -0.2220
483 | vn 0.0000 0.9801 0.1984
484 | vn 0.1999 0.9345 0.2944
485 | vn -0.0664 -0.0000 0.9978
486 | vn -0.1461 -0.8548 -0.4980
487 | vn -0.0875 -0.8624 0.4986
488 | vn -0.1127 0.8794 -0.4625
489 | vn -0.1063 0.8513 0.5138
490 | vn -0.1121 -0.0000 0.9937
491 | vn -0.0981 -0.8599 -0.5010
492 | vn -0.1345 0.0000 -0.9909
493 | vn -0.1329 -0.8580 0.4961
494 | vn 0.0000 -0.8312 0.5560
495 | vn -0.0113 -0.0207 0.9997
496 | vn -1.0000 0.0037 -0.0025
497 | vn -0.0483 -0.9727 0.2268
498 | vn 0.0399 0.0542 0.9977
499 | vn 0.1504 0.2054 0.9671
500 | vn -0.0172 0.9828 0.1838
501 | vn 0.0000 -0.9950 -0.0996
502 | vn -0.0542 0.9967 0.0597
503 | vn -0.0448 0.7850 0.6179
504 | vn 0.0410 -0.0534 0.9977
505 | vn 0.0009 -0.2601 0.9656
506 | vn 0.1085 -0.1922 0.9754
507 | vn 0.0702 -0.2761 0.9586
508 | vn 0.0817 0.1032 0.9913
509 | vn 0.0960 0.4632 0.8811
510 | vn 0.0576 0.4009 0.9143
511 | vn 0.1272 -0.0875 0.9880
512 | vn -0.0704 0.7650 0.6402
513 | vn 0.0279 0.5141 0.8573
514 | vn 0.1904 0.9128 0.3614
515 | vn -0.0550 0.8548 0.5160
516 | vn 0.0587 0.2165 0.9745
517 | vn 0.4256 0.8122 0.3990
518 | vn -0.0461 -0.2598 0.9645
519 | vn -0.0249 -0.9874 0.1563
520 | vn -0.0263 -0.9816 0.1890
521 | vn -0.0202 -0.9818 0.1891
522 | vn 0.0357 0.0885 0.9954
523 | vn 0.8969 0.4422 -0.0057
524 | vn 0.0342 0.1266 0.9914
525 | vn -0.0235 0.0907 0.9956
526 | vn -0.0889 0.8626 -0.4980
527 | vn -0.1171 0.8601 0.4966
528 | vn -0.1006 -0.8616 -0.4975
529 | vn -0.1332 -0.8583 0.4955
530 | vn 0.0000 0.0000 1.0000
531 | vn -0.9994 0.0146 0.0327
532 | vn -0.0459 0.7908 0.6104
533 | vn 0.2077 0.9208 0.3300
534 | vn -0.0078 0.0326 0.9994
535 | usemtl Material.002
536 | s off
537 | f 6/1/1 7/2/1 45/3/1
538 | f 6/4/2 111/5/2 47/6/2
539 | f 2/7/3 113/8/3 112/9/3
540 | f 111/10/4 114/11/4 47/6/4
541 | f 2/12/5 112/13/5 95/14/5
542 | f 7/15/6 6/16/6 3/17/6
543 | f 2/18/7 95/19/7 10/20/7
544 | f 9/21/8 13/22/8 35/23/8
545 | f 95/24/9 98/25/9 17/26/9
546 | f 17/26/10 99/27/10 16/28/10
547 | f 16/29/11 1/30/11 11/31/11
548 | f 9/32/12 11/31/12 30/33/12
549 | f 11/31/13 1/30/13 30/33/13
550 | f 10/20/14 95/19/14 17/34/14
551 | f 12/35/15 101/36/15 13/37/15
552 | f 105/38/16 19/39/16 18/40/16
553 | f 5/41/17 7/15/17 3/17/17
554 | f 22/42/18 21/43/18 2/18/18
555 | f 100/44/19 19/45/19 106/46/19
556 | f 1/47/20 16/48/20 14/49/20
557 | f 97/50/21 103/51/21 15/52/21
558 | f 15/52/22 104/53/22 102/54/22
559 | f 9/55/23 30/56/23 14/49/23
560 | f 30/56/24 1/47/24 14/49/24
561 | f 6/4/25 46/57/25 2/58/25
562 | f 12/59/26 18/60/26 19/61/26
563 | f 99/62/27 18/63/27 16/64/27
564 | f 3/17/28 16/65/28 11/66/28
565 | f 9/21/29 3/17/29 11/66/29
566 | f 12/59/30 14/67/30 16/65/30
567 | f 10/68/31 23/69/31 24/70/31
568 | f 23/69/32 21/71/32 25/72/32
569 | f 22/73/33 24/74/33 25/75/33
570 | f 22/73/34 10/76/34 24/74/34
571 | f 28/77/35 30/33/35 26/78/35
572 | f 31/79/36 34/80/36 27/81/36
573 | f 28/82/37 32/83/37 30/56/37
574 | f 29/84/38 27/85/38 34/86/38
575 | f 26/78/39 33/87/39 28/77/39
576 | f 29/84/40 33/87/40 26/78/40
577 | f 27/81/41 1/47/41 31/79/41
578 | f 1/47/42 32/83/42 31/79/42
579 | f 32/83/43 28/82/43 33/88/43
580 | f 33/88/44 31/79/44 32/83/44
581 | f 27/85/45 29/84/45 1/30/45
582 | f 1/30/46 26/78/46 30/33/46
583 | f 35/23/47 5/41/47 9/21/47
584 | f 20/89/48 101/36/48 107/90/48
585 | f 20/89/49 35/91/49 13/37/49
586 | f 8/92/50 41/93/50 108/94/50
587 | f 36/95/51 8/92/51 5/96/51
588 | f 40/97/52 5/96/52 35/98/52
589 | f 39/99/53 20/100/53 107/101/53
590 | f 41/93/54 107/101/54 108/94/54
591 | f 20/102/55 40/97/55 35/98/55
592 | f 108/103/56 42/104/56 8/105/56
593 | f 39/106/56 41/107/56 37/108/56
594 | f 109/109/57 43/110/57 42/111/57
595 | f 15/52/58 44/112/58 97/50/58
596 | f 42/111/59 44/112/59 15/52/59
597 | f 97/113/60 43/110/60 110/114/60
598 | f 101/36/61 20/89/61 13/37/61
599 | f 96/115/62 15/52/62 102/54/62
600 | f 2/18/63 10/20/63 22/42/63
601 | f 21/116/64 22/73/64 25/75/64
602 | f 24/70/65 23/69/65 25/72/65
603 | f 8/117/66 45/3/66 7/2/66
604 | f 111/118/67 45/3/67 96/119/67
605 | f 96/119/68 42/120/68 15/121/68
606 | f 12/59/69 13/22/69 14/67/69
607 | f 23/122/70 10/20/70 17/34/70
608 | f 23/122/71 17/34/71 21/43/71
609 | f 16/65/72 4/123/72 21/43/72
610 | f 21/43/73 4/123/73 6/16/73
611 | f 6/16/74 2/18/74 21/43/74
612 | f 17/34/75 16/65/75 21/43/75
613 | f 96/119/76 45/3/76 42/120/76
614 | f 6/16/77 4/123/77 3/17/77
615 | f 46/57/78 114/11/78 113/8/78
616 | f 5/41/79 8/124/79 7/15/79
617 | f 6/4/80 47/6/80 46/57/80
618 | f 2/7/81 46/57/81 113/8/81
619 | f 9/21/82 14/67/82 13/22/82
620 | f 17/26/83 98/25/83 99/27/83
621 | f 12/35/84 100/44/84 101/36/84
622 | f 105/38/16 106/125/16 19/39/16
623 | f 100/44/85 12/35/85 19/45/85
624 | f 12/59/86 16/65/86 18/60/86
625 | f 99/62/87 105/126/87 18/63/87
626 | f 3/17/88 4/123/88 16/65/88
627 | f 9/21/89 5/41/89 3/17/89
628 | f 29/84/40 34/86/40 33/87/40
629 | f 1/47/42 30/56/42 32/83/42
630 | f 33/88/44 34/80/44 31/79/44
631 | f 1/30/46 29/84/46 26/78/46
632 | f 8/92/90 37/127/90 41/93/90
633 | f 36/95/91 37/127/91 8/92/91
634 | f 40/97/52 36/95/52 5/96/52
635 | f 39/99/92 38/128/92 20/100/92
636 | f 41/93/54 39/99/54 107/101/54
637 | f 20/102/93 38/129/93 40/97/93
638 | f 108/103/56 109/130/56 42/104/56
639 | f 37/108/56 36/131/56 40/132/56
640 | f 40/132/56 38/133/56 37/108/56
641 | f 38/133/56 39/106/56 37/108/56
642 | f 109/109/57 110/134/57 43/110/57
643 | f 42/111/94 43/110/94 44/112/94
644 | f 97/113/95 44/112/95 43/110/95
645 | f 8/117/96 42/120/96 45/3/96
646 | f 111/118/97 6/1/97 45/3/97
647 | f 46/57/98 47/6/98 114/11/98
648 | f 103/51/99 104/53/99 15/52/99
649 | f 53/135/100 92/136/100 54/137/100
650 | f 53/138/101 94/139/101 111/5/101
651 | f 49/140/102 112/9/102 113/8/102
652 | f 111/10/103 94/139/103 114/11/103
653 | f 49/141/104 95/14/104 112/13/104
654 | f 54/142/105 50/143/105 53/144/105
655 | f 49/145/106 57/146/106 95/19/106
656 | f 56/147/107 82/148/107 60/149/107
657 | f 95/24/108 64/150/108 98/25/108
658 | f 64/150/109 63/151/109 99/27/109
659 | f 63/152/110 58/153/110 48/154/110
660 | f 56/155/111 77/156/111 58/153/111
661 | f 58/153/112 77/156/112 48/154/112
662 | f 57/146/113 64/157/113 95/19/113
663 | f 59/158/114 60/159/114 101/36/114
664 | f 105/38/16 65/160/16 66/161/16
665 | f 52/162/115 50/143/115 54/142/115
666 | f 69/163/116 49/145/116 68/164/116
667 | f 100/44/117 106/46/117 66/165/117
668 | f 48/166/118 61/167/118 63/168/118
669 | f 97/50/119 62/169/119 103/51/119
670 | f 62/169/120 102/54/120 104/53/120
671 | f 56/170/121 61/167/121 77/171/121
672 | f 77/171/122 61/167/122 48/166/122
673 | f 53/138/123 49/172/123 93/173/123
674 | f 59/174/124 66/175/124 65/176/124
675 | f 99/62/125 63/177/125 65/178/125
676 | f 50/143/126 58/179/126 63/180/126
677 | f 56/147/127 58/179/127 50/143/127
678 | f 59/174/128 63/180/128 61/181/128
679 | f 57/182/129 71/183/129 70/184/129
680 | f 70/184/130 72/185/130 68/186/130
681 | f 69/187/131 72/188/131 71/189/131
682 | f 69/187/132 71/189/132 57/190/132
683 | f 75/191/39 73/192/39 77/156/39
684 | f 78/193/41 74/194/41 81/195/41
685 | f 75/196/43 77/171/43 79/197/43
686 | f 76/198/45 81/199/45 74/200/45
687 | f 73/192/35 75/191/35 80/201/35
688 | f 76/198/46 73/192/46 80/201/46
689 | f 74/194/36 78/193/36 48/166/36
690 | f 48/166/44 78/193/44 79/197/44
691 | f 79/197/37 80/202/37 75/196/37
692 | f 80/202/42 79/197/42 78/193/42
693 | f 74/200/38 48/154/38 76/198/38
694 | f 48/154/40 77/156/40 73/192/40
695 | f 82/148/133 56/147/133 52/162/133
696 | f 67/203/134 107/90/134 101/36/134
697 | f 67/203/135 60/159/135 82/204/135
698 | f 55/205/136 108/94/136 88/206/136
699 | f 83/207/137 52/208/137 55/205/137
700 | f 87/209/138 82/210/138 52/208/138
701 | f 86/211/139 107/101/139 67/212/139
702 | f 88/206/140 108/94/140 107/101/140
703 | f 67/213/141 82/210/141 87/209/141
704 | f 108/103/56 55/214/56 89/215/56
705 | f 86/216/56 84/217/56 88/218/56
706 | f 109/109/142 89/219/142 90/220/142
707 | f 62/169/58 97/50/58 91/221/58
708 | f 89/219/143 62/169/143 91/221/143
709 | f 97/113/144 110/114/144 90/220/144
710 | f 101/36/145 60/159/145 67/203/145
711 | f 96/115/146 102/54/146 62/169/146
712 | f 49/145/147 69/163/147 57/146/147
713 | f 68/222/148 72/188/148 69/187/148
714 | f 71/183/149 72/185/149 70/184/149
715 | f 55/223/150 54/137/150 92/136/150
716 | f 111/118/151 96/119/151 92/136/151
717 | f 96/119/152 62/224/152 89/225/152
718 | f 59/174/153 61/181/153 60/149/153
719 | f 70/226/154 64/157/154 57/146/154
720 | f 70/226/155 68/164/155 64/157/155
721 | f 63/180/156 68/164/156 51/227/156
722 | f 68/164/157 53/144/157 51/227/157
723 | f 53/144/158 68/164/158 49/145/158
724 | f 64/157/159 68/164/159 63/180/159
725 | f 96/119/160 89/225/160 92/136/160
726 | f 53/144/161 50/143/161 51/227/161
727 | f 93/173/162 113/8/162 114/11/162
728 | f 52/162/163 54/142/163 55/228/163
729 | f 53/138/164 93/173/164 94/139/164
730 | f 49/140/165 113/8/165 93/173/165
731 | f 56/147/166 60/149/166 61/181/166
732 | f 64/150/167 99/27/167 98/25/167
733 | f 59/158/168 101/36/168 100/44/168
734 | f 105/38/16 66/161/16 106/125/16
735 | f 100/44/169 66/165/169 59/158/169
736 | f 59/174/170 65/176/170 63/180/170
737 | f 99/62/171 65/178/171 105/126/171
738 | f 50/143/172 63/180/172 51/227/172
739 | f 56/147/173 50/143/173 52/162/173
740 | f 76/198/46 80/201/46 81/199/46
741 | f 48/166/44 79/197/44 77/171/44
742 | f 80/202/42 78/193/42 81/195/42
743 | f 48/154/40 73/192/40 76/198/40
744 | f 55/205/174 88/206/174 84/229/174
745 | f 83/207/175 55/205/175 84/229/175
746 | f 87/209/138 52/208/138 83/207/138
747 | f 86/211/176 67/212/176 85/230/176
748 | f 88/206/140 107/101/140 86/211/140
749 | f 67/213/177 87/209/177 85/231/177
750 | f 108/103/56 89/215/56 109/130/56
751 | f 84/217/56 87/232/56 83/233/56
752 | f 87/232/56 84/217/56 85/234/56
753 | f 85/234/56 84/217/56 86/216/56
754 | f 109/109/142 90/220/142 110/134/142
755 | f 89/219/178 91/221/178 90/220/178
756 | f 97/113/179 90/220/179 91/221/179
757 | f 55/223/96 92/136/96 89/225/96
758 | f 111/118/180 92/136/180 53/135/180
759 | f 93/173/181 114/11/181 94/139/181
760 | f 103/51/182 62/169/182 104/53/182
761 |
--------------------------------------------------------------------------------