├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc ├── README.md ├── eslint.config.js ├── package-lock.json ├── package.json ├── prettier.config.js ├── readme_images ├── adjacent_faces.png └── parallax.png ├── src ├── index.html ├── js │ ├── assetsLoadingIndicator.js │ ├── createNormalCubeMap.js │ ├── gui.js │ ├── index.js │ ├── initMeshMaterial.js │ ├── loadMesh.js │ ├── sceneSetup.js │ ├── subsurfaceShader.js │ └── uniforms.js └── styles.css ├── static ├── draco │ ├── README.md │ ├── draco_decoder.js │ ├── draco_decoder.wasm │ ├── draco_encoder.js │ ├── draco_wasm_wrapper.js │ └── gltf │ │ ├── draco_decoder.js │ │ ├── draco_decoder.wasm │ │ ├── draco_encoder.js │ │ └── draco_wasm_wrapper.js ├── models │ ├── crystal_d_3_min.glb │ ├── crystal_d_6_min.glb │ └── crystal_heart_min.glb └── textures │ ├── lava_02_emissive.webp │ ├── marble_32-diffuse.webp │ ├── stonewall_18_basecolor_gs.webp │ ├── stonewall_18_normal.webp │ ├── tree_11_diffuse_gs.webp │ └── tree_11_normal.webp └── vite.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | .vscode 4 | .DS_Store 5 | dist 6 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npm run lint-staged 2 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "src/**/*.js": "eslint --fix", 3 | "*.md": "prettier --write" 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [Live demo](https://prizemlenie.github.io/subsurface-refraction-shader/) 2 | 3 | I happened to come across a random video by accident, and it [showcased](https://youtu.be/whyJzrVEgVc?si=dj4ux1Zh0TA0wn1f&t=1690) a visually stunning effect that immediately caught my attention. The simplicity of the effect made it seem like something I could recreate in just a few days. 4 | 5 | With some extra time during the holidays, I also saw this as a great opportunity to experiment with the [Three.js Shading Language](https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language). This combination of factors led to the creation of this demo. 6 | 7 | I used free assets created by various authors and made some modifications to better suit this demo’s objectives and reduce file sizes for easier web delivery. Below is a list of the original assets and their creators: 8 | 9 | - [Crystal Kyber](https://sketchfab.com/3d-models/crystal-kyber-1d769339a8cc45fda2ba6b31ce91160f) by [Ian Diaz](https://sketchfab.com/Dyckzu) - [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 10 | - [Crystal Very Noisy Material](https://sketchfab.com/3d-models/crystal-very-noisy-material-d66faac2b1de4fff8221e5ce51f4841d) by [Brett Dow](https://sketchfab.com/BrettDow) - [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 11 | - [Crystal Heart](https://sketchfab.com/3d-models/crystal-heart-8e8515a0834b4c1e9e477ce0f392a90c) by [Talaei](https://sketchfab.com/habedi) - [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 12 | - [Stone Wall 18](https://www.sharetextures.com/textures/wall/stone-wall-18) 13 | - [Tree Bark Texture 11](https://www.sharetextures.com/textures/wood/tree-bark-11) 14 | - [Lava 06](https://www.sharetextures.com/textures/abstract/lava-06) 15 | - [Marble 32](https://www.sharetextures.com/textures/floor/marble-32) 16 | 17 | You can build this demo locally by cloning the repository and running `npm install` followed by `npm run dev`. 18 | 19 | # Subsurface Refraction Shader Demo 20 | 21 | This demo showcases a technique I first came across in this [video](https://youtu.be/whyJzrVEgVc?si=R_Cb48J8CuXa7Emb&t=1672). The technique is outlined by Alexander Sannikov, one of the developers of Path of Exile (PoE) and PoE 2. It was used in PoE for rendering things like water and crystals. The video itself only covers the high-level concept — no deep implementation details — yet it does explain why this method can be superior to other approaches for rendering translucent objects, as well as where it’s most useful. 22 | 23 | A similar (or perhaps the same) technique is shown in another [video](https://youtu.be/b5hIzkANWF4?si=BkxgroO_rqa0M9lX). In the [comments](https://www.youtube.com/watch?v=b5hIzkANWF4&lc=Ugw0wmK5vnr5gvyI2P14AaABAg.98_wt7lYrIE98aC0Z__p4d), Alexander explains that he uses a precomputed LUT to approximate scattering events, which is then sampled during rendering. In this project, I decided not to implement those precomputations. 24 | 25 | I’m certain the version used in a real game is both more efficient and more complex than what I’m demonstrating here. However, I still find the results of my simpler implementation surprisingly convincing. It’s also worth noting that what I’ve done might not match Alexander’s technique exactly — it’s my own take on the overall idea. 26 | 27 | ## Rendering Subsurface Material 28 | 29 | So, the goal is to render a subsurface in a scattering medium. To make this look believable, we need to capture two major effects: 30 | 31 | 1. Parallax shift of the subsurface when viewed at different angles 32 | 2. Diffuse scattering within the subsurface 33 | 34 | Alexander’s idea is that you can think of the subsurface as lying at a fixed depth below the surface — almost like layering a bunch of planes parallel to the outer surface. It’s reminiscent of parallax occlusion mapping, but simpler because we know exactly how far below the surface the subsurface lies. As a result, it’s cheaper computationally. Then you handle the diffuse scattering by changing the mip level of the texture based on how deep the light (or view ray) travels through the material. This creates a shader with constant complexity — no loops required, and a pretty believable effect. 35 | 36 | ## Implementation Overview 37 | 38 | I won’t go into every gritty detail here; you can check out the source code for that. Instead, I’ll focus on the general idea. 39 | 40 | ### Parallax Shift 41 | 42 | What exactly is “parallax shift”? Imagine a three-dimensional translucent surface with a secondary layer (subsurface) underneath it. When you look at this object from different viewing angles, the subsurface appears to shift relative to the outer surface. 43 | 44 | ![Parallax](readme_images/parallax.png) 45 | 46 | In the diagram above, both rays intersect the outer surface at the same point, yet they hit different points on the subsurface. That shift is the parallax effect. 47 | 48 | It’s straightforward to compute the vector from the viewing ray’s intersection with the surface to where it intersects the subsurface. Simply multiply the normalized view vector by the ratio of its dot product with the surface normal to the subsurface depth. Initially, I tried using this newly computed offset vector directly to shift the texture coordinates in tangent space (TBN), but ran into issues around sharp normal changes — UV coordinates began showing discontinuities in those areas. 49 | 50 | So, instead, I compute the subsurface intersection position in model space and then use triplanar projection to derive the texture coordinates. (This approach is definitely not perfect, and I’ll talk about potential improvements in a bit.) 51 | 52 | ### Diffuse Scattering 53 | 54 | The medium you’re rendering doesn’t have to exhibit diffuse scattering — but when it does, this method offers a notable performance advantage. You simply choose a higher mip level based on how far the ray travels in the material or, in simpler terms, how steep the angle is. 55 | 56 | In effect, this means you can render a diffusely scattering medium for less cost than a non-scattering one, because sampling higher mip levels is cheaper than sampling the base texture at full resolution. 57 | 58 | ### Visibility of Adjacent Faces 59 | 60 | ![Parallax](readme_images/adjacent_faces.png) 61 | 62 | Here’s another challenge: sometimes the ray you trace would theoretically exit the geometry, so you shouldn’t see the subsurface from that angle. Without handling this case correctly, you lose the illusion. 63 | 64 | To solve this, I use a normal cubemap that’s generated at model load time. This cubemap is used to figure out the expected subsurface normal at a given model-space coordinate. Then I multiply the subsurface color by the dot product between that normal and the view vector. If you check the demo, you’ll see that this simple correction goes a long way toward making the effect look believable. 65 | 66 | ## Possible Improvements 67 | 68 | - **Triplanar Projection** 69 | I chose triplanar projection because it doesn’t rely on a tightly matched UV layout. For this demo, I used free models and textures that weren’t designed to go together, so their UV maps don’t align perfectly. If you have models and textures created with consistent UVs in mind, you could potentially get even better results—for instance, by using a cubemap for the subsurface color—and improve performance by reducing texture samples. 70 | 71 | - **Normal Mapping** 72 | You can easily apply normal maps to the subsurface. In my demo, any texture whose filename has “nm” in it is a normal map. 73 | 74 | - **Multiple Subsurfaces** 75 | You can render multiple layers of subsurface, each with its own texture (and transparency). Alexander Sannikov actually shows examples of this in another [video](https://youtu.be/TrHHTQqmAaM?si=ESG04p6orajXfgYq&t=260) about Path of Exile 2. It’s not a huge stretch to extend the technique to multiple layers, and it can look absolutely stunning if you have the artistic skills to design such content. 76 | 77 | - **Animated Subsurface** 78 | In the same video about PoE 2, Alexander demonstrates an approach for animating the texture without creating noticeable artifacts, and even shares [an example implementation](https://www.shadertoy.com/view/mlscz8). If your project calls for it, you can certainly adapt that to bring your subsurface to life — rippling water, shifting crystals, you name it. 79 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js'; 2 | import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; 3 | import globals from 'globals'; 4 | 5 | export default [ 6 | js.configs.recommended, 7 | eslintPluginPrettierRecommended, 8 | { 9 | languageOptions: { 10 | sourceType: 'module', 11 | ecmaVersion: 2022, 12 | globals: { 13 | ...globals.browser, 14 | }, 15 | }, 16 | rules: { 17 | 'one-var': ['error', 'never'], 18 | 'one-var-declaration-per-line': ['error', 'always'], 19 | semi: ['error', 'always'], 20 | }, 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "subsurface-scattering-shader-demo", 3 | "private": true, 4 | "version": "1.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint ./src/**/*.js", 10 | "lint-staged": "lint-staged", 11 | "prepare": "husky" 12 | }, 13 | "devDependencies": { 14 | "@eslint/js": "^9.17.0", 15 | "eslint": "9.17.0", 16 | "eslint-config-prettier": "^9.1.0", 17 | "eslint-plugin-import": "^2.31.0", 18 | "eslint-plugin-prettier": "^5.2.1", 19 | "globals": "^15.14.0", 20 | "husky": "^9.1.7", 21 | "lint-staged": "^15.3.0", 22 | "prettier": "^3.4.2", 23 | "vite": "^5.3.3", 24 | "vite-plugin-restart": "^0.4.1" 25 | }, 26 | "dependencies": { 27 | "@tweakpane/plugin-essentials": "^0.2.1", 28 | "three": "^0.171", 29 | "tweakpane": "^4.0.5" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | trailingComma: 'es5', 3 | tabWidth: 4, 4 | semi: true, 5 | singleQuote: true, 6 | printWidth: 80, 7 | }; 8 | -------------------------------------------------------------------------------- /readme_images/adjacent_faces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prizemlenie/subsurface-refraction-shader/3a22842f254db329e65e874e78433e88335c63ff/readme_images/adjacent_faces.png -------------------------------------------------------------------------------- /readme_images/parallax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prizemlenie/subsurface-refraction-shader/3a22842f254db329e65e874e78433e88335c63ff/readme_images/parallax.png -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Subsurface refraction shader demo 8 | 9 | 10 | 11 | 12 | 13 | 22 |
23 |
24 |
25 |
26 |
27 | Loading... 28 |
29 |
30 |
31 | Loading... 32 |
33 |
34 |
35 |
36 | 37 |
38 | This demo includes assets from various authors.

39 | I have modified them to better fit the demo’s objectives and to ensure faster loading on the web.
40 | Below is a list of the original assets:


41 |
42 | Crystal Kyber 44 |
45 | Author: Ian Diaz
46 |
License: CC BY 4.0
47 |
48 |
49 | Crystal Very Noisy Material 51 |
Author: Brett Dow
52 | 53 |
License: CC BY 4.0
54 |
55 |
56 | Crystal Heart 58 |
59 | Author: Talaei 60 |
61 |
62 | License: CC BY 4.0 63 |
64 |
65 | Stone Wall 18 66 | Tree Bark Texture 11 67 | Lava 06 68 | Marble 32 69 | 70 |
71 |
72 |
73 | Close 74 |
75 |
76 |
77 | Close 78 |
79 |
80 |
81 |
82 |
83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/js/assetsLoadingIndicator.js: -------------------------------------------------------------------------------- 1 | const assetsRegistry = {}; 2 | 3 | const progressBar = document.querySelector('.load-indicator-bar'); 4 | const indicator = document.querySelector('.load-indicator'); 5 | const text = document.querySelectorAll('.text-container'); 6 | const container = document.querySelector('.load-indicator-container'); 7 | 8 | let frameRequested = false; 9 | const updateLoadIndicator = () => { 10 | if (frameRequested) { 11 | return; 12 | } 13 | 14 | frameRequested = true; 15 | requestAnimationFrame(() => { 16 | frameRequested = false; 17 | let l = 0; 18 | let t = 0; 19 | const textContent = Object.entries(assetsRegistry).reduce( 20 | (msg, [assetType, { total, loaded }]) => { 21 | l += loaded; 22 | t += total; 23 | return `${msg} ${assetType} (${loaded}/${total})`; 24 | }, 25 | 'Loading:' 26 | ); 27 | text.forEach((el) => (el.textContent = textContent)); 28 | progressBar.style.width = `${(l / t) * 100}%`; 29 | 30 | if (l === t) { 31 | setTimeout(() => { 32 | container.addEventListener( 33 | 'transitionend', 34 | (evt) => 35 | evt.target === container && 36 | container.parentElement && 37 | container.parentElement.removeChild(container) 38 | ); 39 | container.classList.add('hidden'); 40 | }, 1000); 41 | } 42 | }); 43 | }; 44 | 45 | const initLoadIndicator = () => { 46 | indicator.style.display = 'block'; 47 | }; 48 | 49 | export const addAsset = (assetType, count = 1) => { 50 | assetsRegistry[assetType] 51 | ? (assetsRegistry[assetType].total += count) 52 | : (assetsRegistry[assetType] = { total: count, loaded: 0 }); 53 | initLoadIndicator(); 54 | updateLoadIndicator(); 55 | }; 56 | 57 | export const assetLoaded = (assetType) => { 58 | assetsRegistry[assetType].loaded += 1; 59 | updateLoadIndicator(); 60 | }; 61 | -------------------------------------------------------------------------------- /src/js/createNormalCubeMap.js: -------------------------------------------------------------------------------- 1 | import { 2 | CubeCamera, 3 | DoubleSide, 4 | HalfFloatType, 5 | LinearFilter, 6 | LinearSRGBColorSpace, 7 | Mesh, 8 | NodeMaterial, 9 | RGBAFormat, 10 | Scene, 11 | WebGLCubeRenderTarget, 12 | } from 'three/webgpu'; 13 | import { attribute, normalGeometry, vec4 } from 'three/tsl'; 14 | 15 | const scene = new Scene(); 16 | const normalMaterial = new NodeMaterial(); 17 | normalMaterial.side = DoubleSide; 18 | normalMaterial.colorNode = vec4(normalGeometry.add(1).div(2), 1); 19 | const tangentMaterial = new NodeMaterial(); 20 | tangentMaterial.side = DoubleSide; 21 | tangentMaterial.colorNode = vec4(attribute('tangent', 'vec3').add(1).div(2), 1); 22 | const cubeCamera = new CubeCamera(0.1, 3); 23 | cubeCamera.position.set(0, 0, 0); 24 | 25 | export const createNormalCubeMap = async (renderer, geometry) => { 26 | if (!renderer._initialized) { 27 | await renderer.init(); 28 | } 29 | 30 | const normalsCubeRT = new WebGLCubeRenderTarget(256, { 31 | generateMipmaps: true, 32 | minFilter: LinearFilter, 33 | magFilter: LinearFilter, 34 | format: RGBAFormat, 35 | colorSpace: LinearSRGBColorSpace, 36 | type: HalfFloatType, 37 | }); 38 | const tangentsCubeRT = new WebGLCubeRenderTarget(256, { 39 | generateMipmaps: true, 40 | minFilter: LinearFilter, 41 | magFilter: LinearFilter, 42 | format: RGBAFormat, 43 | colorSpace: LinearSRGBColorSpace, 44 | type: HalfFloatType, 45 | }); 46 | 47 | const mesh = new Mesh(geometry, normalMaterial); 48 | cubeCamera.renderTarget = normalsCubeRT; 49 | geometry.computeTangents(); 50 | 51 | scene.clear(); 52 | scene.add(mesh); 53 | scene.add(cubeCamera); 54 | cubeCamera.update(renderer, scene); 55 | 56 | mesh.material = tangentMaterial; 57 | cubeCamera.renderTarget = tangentsCubeRT; 58 | cubeCamera.update(renderer, scene); 59 | 60 | return [normalsCubeRT.texture, tangentsCubeRT.texture]; 61 | }; 62 | -------------------------------------------------------------------------------- /src/js/gui.js: -------------------------------------------------------------------------------- 1 | import { Pane } from 'tweakpane'; 2 | import * as EssentialsPlugin from '@tweakpane/plugin-essentials'; 3 | 4 | export const pane = new Pane(); 5 | pane.registerPlugin(EssentialsPlugin); 6 | export const info = pane.addFolder({ title: 'Info', expanded: false }); 7 | export const settings = pane.addFolder({ title: 'Settings', expanded: false }); 8 | export const nextBtn = settings.addButton({ title: 'Next mesh >>', index: 0 }); 9 | settings.addBlade({ view: 'separator', index: 1 }); 10 | -------------------------------------------------------------------------------- /src/js/index.js: -------------------------------------------------------------------------------- 1 | import { Group } from 'three/webgpu'; 2 | 3 | import { loadMesh } from './loadMesh'; 4 | import { onFrame, scene } from './sceneSetup'; 5 | import { nextBtn } from './gui'; 6 | import { initMeshMaterial } from './initMeshMaterial'; 7 | import { assetLoaded } from './assetsLoadingIndicator'; 8 | 9 | const main = async () => { 10 | // ******************************************************************************************************************* 11 | // Assets 12 | 13 | let meshes = await Promise.all([ 14 | loadMesh('models/crystal_d_3_min.glb'), 15 | loadMesh('models/crystal_d_6_min.glb'), 16 | loadMesh('models/crystal_heart_min.glb'), 17 | ]); 18 | 19 | meshes = await Promise.all( 20 | meshes.map(async (mesh) => { 21 | await initMeshMaterial(mesh); 22 | assetLoaded('Meshes'); 23 | return mesh; 24 | }) 25 | ); 26 | 27 | // ******************************************************************************************************************* 28 | // GUI 29 | 30 | let currentMeshIndex = 0; 31 | const meshGroup = new Group(); 32 | scene.add(meshGroup); 33 | 34 | const applyMesh = async () => { 35 | meshGroup.clear(); 36 | const mesh = meshes[currentMeshIndex]; 37 | 38 | onFrame((delta, params) => { 39 | mesh.rotation.y += Math.PI * 2 * delta * params.rotationSpeed; 40 | }); 41 | 42 | meshGroup.add(mesh); 43 | }; 44 | const nextMesh = async () => { 45 | currentMeshIndex = (currentMeshIndex + 1) % meshes.length; 46 | await applyMesh(); 47 | }; 48 | 49 | await applyMesh(); 50 | 51 | nextBtn.on('click', nextMesh); 52 | 53 | const assetsInfo = document.querySelector('.assets-info'); 54 | document.querySelectorAll('.assets-info-toggle').forEach((element) => 55 | element.addEventListener('click', () => { 56 | assetsInfo.classList.toggle('visible'); 57 | }) 58 | ); 59 | }; 60 | 61 | main(); 62 | -------------------------------------------------------------------------------- /src/js/initMeshMaterial.js: -------------------------------------------------------------------------------- 1 | import { 2 | LoadingManager, 3 | TextureLoader, 4 | MirroredRepeatWrapping, 5 | RepeatWrapping, 6 | SRGBColorSpace, 7 | NodeMaterial, 8 | DoubleSide, 9 | MeshStandardNodeMaterial, 10 | FrontSide, 11 | } from 'three/webgpu'; 12 | import { 13 | cameraPosition, 14 | dot, 15 | min, 16 | normalWorld, 17 | normalize, 18 | positionWorld, 19 | vec3, 20 | vec4, 21 | } from 'three/tsl'; 22 | import { 23 | buildEmissiveShader, 24 | buildSubsurfaceShader, 25 | getSubsurfaceNormalWithNormalMapping, 26 | getSubsurfaceNormalValue, 27 | } from './subsurfaceShader'; 28 | import { createNormalCubeMap } from './createNormalCubeMap'; 29 | import { renderer, togglePostProcessing } from './sceneSetup'; 30 | import { uniforms, updateUniform } from './uniforms'; 31 | import { settings } from './gui'; 32 | import { addAsset, assetLoaded } from './assetsLoadingIndicator'; 33 | 34 | const subsurfaceShader = buildSubsurfaceShader(uniforms); 35 | const emissiveShader = buildEmissiveShader(uniforms); 36 | const loadingManager = new LoadingManager(); 37 | const textureLoader = new TextureLoader(loadingManager); 38 | 39 | // ********************************************************************************************************************* 40 | // GUI 41 | const materials = []; 42 | const meshes = []; 43 | const loadedTextures = new Set(); 44 | const loadTexture = (url) => { 45 | if (loadedTextures.has(url)) { 46 | return textureLoader.load(url); 47 | } 48 | 49 | loadedTextures.add(url); 50 | addAsset('Textures'); 51 | 52 | return textureLoader.load(url, () => assetLoaded('Textures')); 53 | }; 54 | 55 | const texuresConfig = [ 56 | { 57 | colorMap: loadTexture('textures/stonewall_18_basecolor_gs.webp'), 58 | normalMap: loadTexture('textures/stonewall_18_normal.webp'), 59 | scale: 4, 60 | label: 'stone nm', 61 | mirror: true, 62 | colorBoost: 6.5, 63 | onSelect: () => { 64 | // updateUniform('minMipLevel', 2); 65 | // updateUniform('mipMultiplier', 10); 66 | togglePostProcessing(false); 67 | }, 68 | }, 69 | { 70 | colorMap: loadTexture('textures/stonewall_18_basecolor_gs.webp'), 71 | scale: 4, 72 | label: 'stone', 73 | mirror: true, 74 | colorBoost: 6.5, 75 | onSelect: () => { 76 | // updateUniform('minMipLevel', 2); 77 | // updateUniform('mipMultiplier', 10); 78 | togglePostProcessing(false); 79 | }, 80 | }, 81 | { 82 | colorMap: loadTexture('textures/tree_11_diffuse_gs.webp'), 83 | normalMap: loadTexture('textures/tree_11_normal.webp'), 84 | scale: 2, 85 | label: 'tree nm', 86 | mirror: true, 87 | colorBoost: 6.5, 88 | onSelect: () => { 89 | // updateUniform('minMipLevel', 1); 90 | // updateUniform('mipMultiplier', 8); 91 | togglePostProcessing(false); 92 | }, 93 | }, 94 | { 95 | colorMap: loadTexture('textures/tree_11_diffuse_gs.webp'), 96 | scale: 2, 97 | label: 'tree', 98 | mirror: true, 99 | colorBoost: 6.5, 100 | onSelect: () => { 101 | // updateUniform('minMipLevel', 1); 102 | // updateUniform('mipMultiplier', 8); 103 | togglePostProcessing(false); 104 | }, 105 | }, 106 | { 107 | colorMap: loadTexture('textures/lava_02_emissive.webp'), 108 | emissiveMap: loadTexture('textures/lava_02_emissive.webp'), 109 | colorBoost: 6, 110 | emissiveColorBoost: 1.5, 111 | scale: 4, 112 | label: 'emissive', 113 | onSelect: () => { 114 | // updateUniform('minMipLevel', 2); 115 | // updateUniform('mipMultiplier', 8); 116 | togglePostProcessing(true); 117 | }, 118 | }, 119 | { 120 | colorMap: loadTexture('textures/marble_32-diffuse.webp'), 121 | mirror: false, 122 | scale: 3, 123 | label: 'potato', 124 | colorBoost: 2, 125 | onSelect: () => { 126 | // updateUniform('minMipLevel', 0); 127 | // updateUniform('mipMultiplier', 6); 128 | togglePostProcessing(false); 129 | }, 130 | }, 131 | ]; 132 | 133 | texuresConfig.forEach(({ colorMap, emissiveMap, mirror, normalMap }) => { 134 | const em = emissiveMap || {}; 135 | const nm = normalMap || {}; 136 | const wrap = mirror ? MirroredRepeatWrapping : RepeatWrapping; 137 | nm.wrapS = em.wrapS = colorMap.wrapS = wrap; 138 | nm.wrapT = em.wrapT = colorMap.wrapT = wrap; 139 | em.colorSpace = colorMap.colorSpace = SRGBColorSpace; 140 | }); 141 | 142 | const applyTexture = (textureIndex, material) => { 143 | const config = texuresConfig[textureIndex]; 144 | config.normalMap 145 | ? (material.colorNode = subsurfaceShader({ 146 | colorTexture: config.colorMap, 147 | normalCubeMap: material.userData.normalCubeMap, 148 | colorBoost: config.colorBoost || 1, 149 | textureScale: config.scale || 1, 150 | getSubsurfaceNormal: ({ 151 | subsurfacePosition, 152 | viewVec, 153 | mipLevel, 154 | }) => 155 | getSubsurfaceNormalWithNormalMapping({ 156 | normalCubeMap: material.userData.normalCubeMap, 157 | tangentCubeMap: material.userData.tangentCubeMap, 158 | subsurfacePosition, 159 | textureScale: config.scale || 1, 160 | normalMap: config.normalMap, 161 | viewVec, 162 | mipLevel, 163 | }), 164 | })) 165 | : (material.colorNode = subsurfaceShader({ 166 | colorTexture: config.colorMap, 167 | normalCubeMap: material.userData.normalCubeMap, 168 | colorBoost: config.colorBoost || 1, 169 | textureScale: config.scale || 1, 170 | getSubsurfaceNormal: getSubsurfaceNormalValue, 171 | })); 172 | 173 | material.emissiveNode = config.emissiveMap 174 | ? emissiveShader({ 175 | emissiveMap: config.emissiveMap, 176 | colorBoost: config.emissiveColorBoost || 1, 177 | textureScale: config.scale || 1, 178 | }) 179 | : null; 180 | config.onSelect?.(); 181 | material.needsUpdate = true; 182 | }; 183 | const params = { textureIndex: 0, wireframe: false, scattering: 1 }; 184 | 185 | settings 186 | .addBinding(params, 'wireframe', { 187 | label: 'Wireframe', 188 | index: 2, 189 | }) 190 | .on('change', ({ value }) => { 191 | meshes.forEach( 192 | (mesh) => 193 | (mesh.material = value 194 | ? mesh.userData.overviewMaterial 195 | : mesh.userData.effectMaterial) 196 | ); 197 | }); 198 | 199 | const applyScatteringPreset = (value) => { 200 | updateUniform('minMipLevel', [0, 1, 2][value]); 201 | updateUniform('mipMultiplier', [0, 6, 10][value]); 202 | }; 203 | applyScatteringPreset(params.scattering); 204 | settings 205 | .addBinding(params, 'scattering', { 206 | label: 'Scattering intensity', 207 | groupName: 'Scattering intensity', 208 | view: 'radiogrid', 209 | size: [3, 1], 210 | cells: (x) => ({ value: x, title: ['Off', 'Low', 'High'][x] }), 211 | index: 4, 212 | }) 213 | .on('change', ({ value }) => applyScatteringPreset(value)); 214 | 215 | settings 216 | .addBinding(params, 'textureIndex', { 217 | label: 'Subsurface texture', 218 | groupName: 'Subsurface texture', 219 | view: 'radiogrid', 220 | size: [2, 3], 221 | cells: (x, y) => { 222 | const index = x + y * 2; 223 | const item = texuresConfig[index]; 224 | return { value: index, title: item.label }; 225 | }, 226 | index: 3, 227 | }) 228 | .on('change', ({ value }) => { 229 | materials.forEach((material) => applyTexture(value, material)); 230 | }); 231 | 232 | // ********************************************************************************************************************* 233 | // Mesh material initialization 234 | 235 | const overviewMaterial = new NodeMaterial(); 236 | overviewMaterial.colorNode = (() => { 237 | const viewVec = normalize(cameraPosition.sub(positionWorld)); 238 | return vec4( 239 | vec3(min(dot(normalWorld, viewVec).mul(0.5).add(0.5).add(0.2), 0.7)), 240 | 1 241 | ); 242 | })(); 243 | overviewMaterial.wireframe = true; 244 | overviewMaterial.side = DoubleSide; 245 | 246 | export const initMeshMaterial = async (mesh) => { 247 | const [normalCubeMap, tangentCubeMap] = await createNormalCubeMap( 248 | renderer, 249 | mesh.geometry 250 | ); 251 | const originalMaterial = mesh.material; 252 | const newMaterial = new MeshStandardNodeMaterial(); 253 | 254 | // This may look odd, but I ran into a peculiar problem. 255 | // Even though the normal cube maps for two different meshes were generated correctly and were distinct, 256 | // The cube maps passed to the subsurface shader ended up being the same for both meshes. 257 | // Interestingly, this issue only appeared for those specific two meshes, 258 | // And changing their loading order didn’t help. 259 | // I tried investigating the problem, but decided not to spend too much time on it. 260 | // Recreating the material for each mesh ultimately resolved the issue. 261 | Object.entries(originalMaterial).forEach( 262 | ([key, value]) => (newMaterial[key] = value) 263 | ); 264 | newMaterial.emissiveIntensity = 0; 265 | newMaterial.aoMapIntensity = 0; 266 | newMaterial.userData = { normalCubeMap, tangentCubeMap }; 267 | newMaterial.emissiveMap = null; 268 | newMaterial.side = FrontSide; 269 | applyTexture(0, newMaterial); 270 | mesh.material = newMaterial; 271 | 272 | materials.push(newMaterial); 273 | meshes.push(mesh); 274 | 275 | mesh.userData = { effectMaterial: newMaterial, overviewMaterial }; 276 | return mesh; 277 | }; 278 | -------------------------------------------------------------------------------- /src/js/loadMesh.js: -------------------------------------------------------------------------------- 1 | import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; 2 | import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'; 3 | import { addAsset } from './assetsLoadingIndicator'; 4 | 5 | const dracoLoader = new DRACOLoader(); 6 | dracoLoader.setDecoderPath('draco/'); 7 | 8 | const gltfLoader = new GLTFLoader(); 9 | gltfLoader.setDRACOLoader(dracoLoader); 10 | 11 | export const loadMesh = (path) => { 12 | addAsset('Meshes'); 13 | return new Promise((resolve) => { 14 | gltfLoader.load(path, (gltf) => { 15 | let mesh = null; 16 | gltf.scene.traverse((child) => { 17 | child.type === 'Mesh' && (mesh = child); 18 | }); 19 | 20 | mesh.material.emissiveIntensity = 0; 21 | mesh.material.aoMapIntensity = 0; 22 | mesh.castShadow = false; 23 | 24 | const { geometry } = mesh; 25 | geometry.center(); 26 | const scale = 27 | 1.8 / 28 | geometry.boundingBox.max.sub(geometry.boundingBox.min).length(); 29 | geometry.scale(scale, scale, scale); 30 | mesh.scale.set(1, 1, 1); 31 | mesh.rotation.set(0, 0, 0); 32 | 33 | resolve(mesh); 34 | }); 35 | }); 36 | }; 37 | -------------------------------------------------------------------------------- /src/js/sceneSetup.js: -------------------------------------------------------------------------------- 1 | import { 2 | Scene, 3 | PerspectiveCamera, 4 | AmbientLight, 5 | DirectionalLight, 6 | WebGPURenderer, 7 | NoColorSpace, 8 | Clock, 9 | PostProcessing, 10 | } from 'three/webgpu'; 11 | import { pass, mrt, output, emissive } from 'three/tsl'; 12 | import { bloom } from 'three/addons/tsl/display/BloomNode.js'; 13 | import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; 14 | import { info, settings } from './gui'; 15 | 16 | const fpsGraph = info.addBlade({ 17 | view: 'fpsgraph', 18 | label: 'fps', 19 | rows: 2, 20 | min: 0, 21 | max: 300, 22 | }); 23 | // ******************************************************************************************************************* 24 | // Canvas 25 | const canvas = document.querySelector('canvas'); 26 | 27 | // ******************************************************************************************************************* 28 | // Scene 29 | export const scene = new Scene(); 30 | 31 | // ******************************************************************************************************************* 32 | // Base camera 33 | export const camera = new PerspectiveCamera( 34 | 65, 35 | window.innerWidth / window.innerHeight, 36 | 0.1, 37 | 100 38 | ); 39 | camera.position.set(1.8, 0.3, -4).divideScalar(2.7); 40 | scene.add(camera); 41 | 42 | // ******************************************************************************************************************* 43 | // Lighting 44 | const alight = new AmbientLight(0xffffff, 5.5); 45 | const dlight1 = new DirectionalLight(0xffffff, 6.5); 46 | const dlight2 = new DirectionalLight(0xffffff, 3.5); 47 | 48 | dlight1.position.set(-3, 2, 3); 49 | dlight1.lookAt(0, 0, 0); 50 | 51 | dlight2.position.set(3, 2, 3); 52 | dlight2.lookAt(0, 0, 0); 53 | 54 | scene.add(alight, dlight1, dlight2); 55 | 56 | // ******************************************************************************************************************* 57 | // Controls 58 | const controls = new OrbitControls(camera, canvas); 59 | controls.target.set(0, 0, 0); 60 | controls.enableDamping = true; 61 | controls.enablePan = false; 62 | 63 | // ******************************************************************************************************************* 64 | // Renderer 65 | export const renderer = new WebGPURenderer({ 66 | // forceWebGL: true, 67 | canvas, 68 | antialias: true, 69 | }); 70 | renderer.setClearColor(0x000000, 1); 71 | renderer.setSize(window.innerWidth, window.innerHeight); 72 | renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); 73 | renderer.outputColorSpace = NoColorSpace; 74 | 75 | window.addEventListener('resize', () => { 76 | const [width, height] = [window.innerWidth, window.innerHeight]; 77 | 78 | camera.aspect = width / height; 79 | camera.updateProjectionMatrix(); 80 | 81 | renderer.setSize(width, height); 82 | }); 83 | 84 | // ******************************************************************************************************************* 85 | // Postprocessing 86 | const scenePass = pass(scene, camera); 87 | scenePass.setMRT( 88 | mrt({ 89 | output, 90 | emissive, 91 | }) 92 | ); 93 | 94 | const outputPass = scenePass.getTextureNode(); 95 | const emissivePass = scenePass.getTextureNode('emissive'); 96 | 97 | const bloomPass = bloom(emissivePass, 6.5, 1.5); 98 | 99 | const postProcessing = new PostProcessing(renderer); 100 | postProcessing.outputNode = outputPass.add(bloomPass); 101 | 102 | // ******************************************************************************************************************* 103 | // GUI 104 | const params = { 105 | rotationSpeed: 0.07, 106 | spotLight: false, 107 | frameTime: 0, 108 | avgFrameTime: 0, 109 | framesTimeSum: 0, 110 | framesCount: 0, 111 | floor: false, 112 | }; 113 | info.addBinding(params, 'frameTime', { 114 | label: 'Frame time', 115 | view: 'graph', 116 | readonly: true, 117 | min: 0, 118 | max: 15, 119 | rows: 2, 120 | }); 121 | info.addBinding(params, 'avgFrameTime', { 122 | label: 'Avg frame time', 123 | readonly: true, 124 | interval: 1000, 125 | }); 126 | 127 | settings.addBinding(params, 'rotationSpeed', { 128 | label: 'rot/sec', 129 | min: 0, 130 | max: 0.3, 131 | step: 0.01, 132 | }); 133 | settings 134 | .addBinding(dlight1, 'intensity', { 135 | label: 'Light intensity', 136 | min: 0, 137 | max: 20, 138 | step: 0.1, 139 | }) 140 | .on('change', ({ value }) => { 141 | dlight2.intensity = value / 2; 142 | }); 143 | 144 | // ******************************************************************************************************************* 145 | // Animation loop 146 | const clock = new Clock(); 147 | const avgFrameTimeBatchSize = 100; 148 | let animationCallback = null; 149 | let postProcessingEnabled = false; 150 | 151 | const tick = () => { 152 | const delta = clock.getDelta(); 153 | 154 | controls.update(); 155 | 156 | animationCallback?.(delta, params); 157 | 158 | fpsGraph.begin(); 159 | const start = performance.now(); 160 | postProcessingEnabled 161 | ? postProcessing.renderAsync() 162 | : renderer.renderAsync(scene, camera); 163 | params.frameTime = performance.now() - start; 164 | fpsGraph.end(); 165 | 166 | if (params.framesCounterStartTime === null) { 167 | params.framesCounterStartTime = start; 168 | } 169 | params.framesCount += 1; 170 | params.framesTimeSum += params.frameTime; 171 | if (params.framesCount >= avgFrameTimeBatchSize) { 172 | params.avgFrameTime = params.framesTimeSum / params.framesCount; 173 | params.framesCount = 0; 174 | params.framesTimeSum = 0; 175 | } 176 | 177 | requestAnimationFrame(tick); 178 | }; 179 | tick(); 180 | 181 | export const onFrame = (callback) => { 182 | animationCallback = callback; 183 | }; 184 | 185 | export const togglePostProcessing = (enabled) => { 186 | postProcessingEnabled = enabled; 187 | }; 188 | -------------------------------------------------------------------------------- /src/js/subsurfaceShader.js: -------------------------------------------------------------------------------- 1 | import { 2 | Fn, 3 | abs, 4 | clamp, 5 | colorToDirection, 6 | cross, 7 | cubeTexture, 8 | dot, 9 | mat3, 10 | max, 11 | min, 12 | mix, 13 | normalGeometry, 14 | normalize, 15 | positionGeometry, 16 | pow, 17 | step, 18 | texture, 19 | vec4, 20 | } from 'three/tsl'; 21 | 22 | let subsurfaceNormal; 23 | let subsurfacePosition; 24 | let surfaceNormal; 25 | let viewVecDotSubsurfNormal; 26 | let viewVec; 27 | let subsurfaceBasicNormal; 28 | let mipLevel; 29 | 30 | const calcFinalColor = Fn( 31 | ([surfaceColor, mediumColor, thinMediumColor, subsurfaceTint]) => 32 | mix( 33 | mix( 34 | mediumColor, 35 | surfaceColor.mul(subsurfaceTint), 36 | min(pow(max(viewVecDotSubsurfNormal, 0), 4), 1) 37 | ), 38 | thinMediumColor, 39 | pow(max(viewVecDotSubsurfNormal.negate(), 0), 4) 40 | ) 41 | ); 42 | const triplanarProject = ([ 43 | textureSampler, 44 | position, 45 | normal, 46 | mipLevel, 47 | scale, 48 | ]) => { 49 | const absN = abs(normal); 50 | const positionScaled = position.toVar().mul(scale); 51 | const uvX = positionScaled.yz; 52 | const uvY = positionScaled.xz; 53 | const uvZ = positionScaled.xy; 54 | const componentsSum = absN.x.add(absN.y).add(absN.z).toVar(); 55 | const blendX = absN.x.div(componentsSum); 56 | const blendY = absN.y.div(componentsSum); 57 | const blendZ = absN.z.div(componentsSum); 58 | const xColor = texture(textureSampler, uvX, mipLevel); 59 | const yColor = texture(textureSampler, uvY, mipLevel); 60 | const zColor = texture(textureSampler, uvZ, mipLevel); 61 | 62 | return xColor.mul(blendX).add(yColor.mul(blendY)).add(zColor.mul(blendZ)); 63 | }; 64 | 65 | export const getSubsurfaceNormalFromCubeMap = Fn( 66 | ({ subsurfacePosition, normalCubeMap }) => 67 | normalize( 68 | cubeTexture(normalCubeMap, subsurfacePosition).xyz.mul(2).sub(1) 69 | ) 70 | ); 71 | 72 | export const getSubsurfaceNormalValue = Fn(() => subsurfaceBasicNormal); 73 | 74 | export const getSubsurfaceNormalWithNormalMapping = Fn( 75 | ({ 76 | subsurfacePosition, 77 | tangentCubeMap, 78 | normalMap, 79 | textureScale, 80 | viewVec, 81 | mipLevel, 82 | }) => { 83 | const tangent = normalize( 84 | cubeTexture(tangentCubeMap, subsurfacePosition).xyz.mul(2).sub(1) 85 | ); 86 | const bitangent = cross(subsurfaceBasicNormal, tangent); 87 | const TBN = mat3(tangent, bitangent, subsurfaceBasicNormal); 88 | const normalMapValue = colorToDirection( 89 | triplanarProject([ 90 | normalMap, 91 | subsurfacePosition, 92 | surfaceNormal, 93 | mipLevel, 94 | textureScale, 95 | ]) 96 | ); 97 | const mappedNormal = TBN.mul(normalMapValue); 98 | const normalDotViewVec = dot(subsurfaceBasicNormal, viewVec); 99 | 100 | return mix( 101 | subsurfaceBasicNormal, 102 | mappedNormal, 103 | step(0.1, normalDotViewVec) 104 | ); 105 | } 106 | ); 107 | 108 | export const buildSubsurfaceShader = ({ 109 | modelSpaceCameraPos, 110 | mediumColor, 111 | thinMediumColor, 112 | subsurfaceTint, 113 | depth, 114 | mipMultiplier, 115 | minMipLevel, 116 | }) => 117 | Fn( 118 | ({ 119 | normalCubeMap, 120 | colorTexture, 121 | colorBoost, 122 | textureScale, 123 | getSubsurfaceNormal, 124 | }) => { 125 | viewVec = normalize( 126 | modelSpaceCameraPos.sub(positionGeometry) 127 | ).toVar(); 128 | const scaleFactor = depth.div(dot(viewVec, normalGeometry)); 129 | const subsurfaceIntersectionVector = viewVec 130 | .negate() 131 | .mul(scaleFactor); 132 | subsurfacePosition = positionGeometry 133 | .add(subsurfaceIntersectionVector) 134 | .toVar(); 135 | surfaceNormal = normalize( 136 | cubeTexture(normalCubeMap, positionGeometry).xyz.mul(2).sub(1) 137 | ).toVar(); 138 | 139 | subsurfaceBasicNormal = getSubsurfaceNormalFromCubeMap({ 140 | normalCubeMap, 141 | subsurfacePosition, 142 | }).toVar(); 143 | 144 | mipLevel = dot(subsurfaceBasicNormal, viewVec) 145 | .oneMinus() 146 | .mul(mipMultiplier) 147 | .add(minMipLevel); 148 | 149 | subsurfaceNormal = getSubsurfaceNormal({ 150 | subsurfacePosition, 151 | viewVec, 152 | mipLevel, 153 | }).toVar(); 154 | 155 | const surfaceColor = triplanarProject([ 156 | colorTexture, 157 | subsurfacePosition, 158 | surfaceNormal, 159 | mipLevel, 160 | textureScale, 161 | ]); 162 | 163 | viewVecDotSubsurfNormal = dot( 164 | subsurfaceNormal.xyz, 165 | viewVec 166 | ).toVar(); 167 | 168 | return calcFinalColor([ 169 | vec4(surfaceColor.mul(colorBoost).rgb, 1), 170 | mediumColor, 171 | thinMediumColor, 172 | subsurfaceTint, 173 | ]); 174 | } 175 | ); 176 | 177 | export const buildEmissiveShader = ({ subsurfaceTint }) => 178 | Fn(({ emissiveMap, colorBoost, textureScale }) => { 179 | const emission = triplanarProject([ 180 | emissiveMap, 181 | subsurfacePosition, 182 | surfaceNormal, 183 | mipLevel, 184 | textureScale, 185 | ]); 186 | const mcolor = mix( 187 | vec4(0, 0, 0, 1), 188 | emission.mul(subsurfaceTint), 189 | pow(clamp(viewVecDotSubsurfNormal, 0, 1), 4) 190 | ); 191 | 192 | return vec4(mcolor.rgb.mul(colorBoost), 1); 193 | }); 194 | -------------------------------------------------------------------------------- /src/js/uniforms.js: -------------------------------------------------------------------------------- 1 | import { Color, Matrix4, Vector4 } from 'three/webgpu'; 2 | import { uniform } from 'three/tsl'; 3 | import { settings } from './gui'; 4 | import { camera } from './sceneSetup'; 5 | 6 | const bindings = {}; 7 | // ******************************************************************************************************************* 8 | // Uniforms 9 | const colorParams = { view: 'color', color: { alpha: false, type: 'float' } }; 10 | const params = [ 11 | { 12 | name: 'depth', 13 | value: 0.1, 14 | params: { 15 | label: 'Depth', 16 | min: 0, 17 | max: 0.15, 18 | step: 0.001, 19 | index: 2, 20 | }, 21 | }, 22 | 'delimiter', 23 | { 24 | name: 'mediumColor', 25 | value: new Color(0.05, 0.02, 0.02), 26 | params: { ...colorParams, label: 'Medium color' }, 27 | }, 28 | { 29 | name: 'thinMediumColor', 30 | value: new Color(0.79, 0.07, 0.04), 31 | params: { ...colorParams, label: 'Thin medium color' }, 32 | }, 33 | { 34 | name: 'subsurfaceTint', 35 | value: new Color(0.6, 0.32, 0.09), 36 | params: { ...colorParams, label: 'Subsurface tint' }, 37 | }, 38 | 'delimiter', 39 | { 40 | name: 'mipMultiplier', 41 | value: 4, 42 | params: { label: 'Mip multiplier', min: 0, max: 20, step: 1 }, 43 | }, 44 | { 45 | name: 'minMipLevel', 46 | value: 2, 47 | params: { label: 'Min mip level', min: 0, max: 10, step: 1 }, 48 | }, 49 | ]; 50 | 51 | export const uniforms = params.reduce((result, param) => { 52 | if (param !== 'delimiter') { 53 | result[param.name] = uniform(param.value); 54 | } 55 | return result; 56 | }, {}); 57 | 58 | const _inverseModelMatrix = new Matrix4(); 59 | const _cameraPosition = new Vector4(); 60 | uniforms.modelSpaceCameraPos = uniform(camera.position); 61 | uniforms.modelSpaceCameraPos.onObjectUpdate(({ object }) => 62 | _cameraPosition 63 | .copy(camera.position) 64 | .applyMatrix4(_inverseModelMatrix.copy(object.matrixWorld).transpose()) 65 | ); 66 | 67 | params.forEach((param) => { 68 | if (param === 'delimiter') { 69 | settings.addBlade({ view: 'separator' }); 70 | return; 71 | } 72 | if (typeof param === 'function') { 73 | param(); 74 | return; 75 | } 76 | 77 | bindings[param.name] = settings 78 | .addBinding(param, 'value', param.params) 79 | .on('change', () => (uniforms[param.name].value = param.value)); 80 | }); 81 | 82 | // ******************************************************************************************************************* 83 | // Color preset 84 | const p = { colorPreset: 'Red' }; 85 | const colorPresets = { 86 | Red: { 87 | mediumColor: new Color(0.05, 0.02, 0.02), 88 | thinMediumColor: new Color(0.79, 0.07, 0.04), 89 | subsurfaceTint: new Color(0.6, 0.32, 0.09), 90 | }, 91 | Yellow: { 92 | mediumColor: new Color(0.05, 0.03, 0.02), 93 | thinMediumColor: new Color(0.82, 0.38, 0.02), 94 | subsurfaceTint: new Color(0.56, 0.32, 0.09), 95 | }, 96 | Green: { 97 | mediumColor: new Color(0.02, 0.05, 0.02), 98 | thinMediumColor: new Color(0.02, 0.76, 0.13), 99 | subsurfaceTint: new Color(0.08, 0.47, 0.34), 100 | }, 101 | 'Light blue': { 102 | mediumColor: new Color(0.02, 0.05, 0.05), 103 | thinMediumColor: new Color(0.03, 0.75, 0.89), 104 | subsurfaceTint: new Color(0.1, 0.45, 0.59), 105 | }, 106 | Purple: { 107 | mediumColor: new Color(0.04, 0.02, 0.05), 108 | thinMediumColor: new Color(0.46, 0.03, 0.89), 109 | subsurfaceTint: new Color(0.46, 0.1, 0.59), 110 | }, 111 | White: { 112 | mediumColor: new Color(0.08, 0.08, 0.08), 113 | thinMediumColor: new Color(0.78, 0.71, 0.66), 114 | subsurfaceTint: new Color(0.36, 0.41, 0.41), 115 | }, 116 | }; 117 | 118 | settings 119 | .addBinding(p, 'colorPreset', { 120 | label: 'Color preset', 121 | groupName: 'Color preset', 122 | view: 'radiogrid', 123 | size: [2, 3], 124 | cells: (x, y) => { 125 | const key = Object.keys(colorPresets)[x + y * 2]; 126 | return { value: key, title: key }; 127 | }, 128 | index: 2, 129 | }) 130 | .on('change', ({ value }) => { 131 | const preset = colorPresets[value]; 132 | Object.keys(preset).forEach((key) => { 133 | params.find((param) => param.name === key).value.copy(preset[key]); 134 | bindings[key].refresh(); 135 | }); 136 | }); 137 | 138 | settings.addBlade({ view: 'separator', index: 3 }); 139 | 140 | export const updateUniform = (name, value) => { 141 | params.find((param) => param.name === name).value = value; 142 | bindings[name].refresh(); 143 | }; 144 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, 7 | body { 8 | overflow: hidden; 9 | --rainbow-gradient: linear-gradient( 10 | 90deg, 11 | rgba(255, 0, 0, 1), 12 | rgba(255, 154, 0, 1), 13 | rgba(208, 222, 33, 1), 14 | rgba(79, 220, 74, 1), 15 | rgba(63, 218, 216, 1), 16 | rgba(47, 201, 226, 1), 17 | rgba(28, 127, 238, 1), 18 | rgba(95, 21, 242, 1), 19 | rgba(186, 12, 248, 1), 20 | rgba(251, 7, 217, 1), 21 | rgba(255, 0, 0, 1), 22 | rgba(255, 154, 0, 1), 23 | rgba(208, 222, 33, 1), 24 | rgba(79, 220, 74, 1), 25 | rgba(63, 218, 216, 1), 26 | rgba(47, 201, 226, 1), 27 | rgba(28, 127, 238, 1), 28 | rgba(95, 21, 242, 1), 29 | rgba(186, 12, 248, 1), 30 | rgba(251, 7, 217, 1), 31 | rgba(255, 0, 0, 1), 32 | rgba(255, 154, 0, 1), 33 | rgba(208, 222, 33, 1), 34 | rgba(79, 220, 74, 1), 35 | rgba(63, 218, 216, 1), 36 | rgba(47, 201, 226, 1), 37 | rgba(28, 127, 238, 1), 38 | rgba(95, 21, 242, 1), 39 | rgba(186, 12, 248, 1), 40 | rgba(251, 7, 217, 1), 41 | rgba(255, 0, 0, 1) 42 | ); 43 | } 44 | 45 | .webgl { 46 | position: fixed; 47 | top: 0; 48 | left: 0; 49 | outline: none; 50 | } 51 | 52 | .load-indicator-container { 53 | position: fixed; 54 | inset: 0; 55 | background: #000; 56 | will-change: transform, opacity; 57 | transition: 58 | opacity 0.5s ease-in, 59 | transform 0.5s ease-in; 60 | opacity: 1; 61 | z-index: 10; 62 | } 63 | 64 | @keyframes rainbow { 65 | 0% { 66 | background-position: 0 0; 67 | } 68 | 100% { 69 | background-position: -300% 0; 70 | } 71 | } 72 | 73 | .load-indicator-container.hidden { 74 | transform: scale(1.5); 75 | opacity: 0; 76 | } 77 | 78 | .load-indicator { 79 | position: absolute; 80 | top: 50%; 81 | left: 50%; 82 | transform: translate(-50%, -50%); 83 | padding: 6px; 84 | background: var(--rainbow-gradient); 85 | background-repeat: repeat; 86 | background-size: 300% 100%; 87 | animation: rainbow 5s linear infinite; 88 | } 89 | 90 | .text-container { 91 | text-wrap: nowrap; 92 | color: #fff; 93 | font-size: 12px; 94 | font-family: monospace; 95 | text-align: center; 96 | padding: 30px 60px; 97 | } 98 | 99 | .load-indicator-bar { 100 | position: absolute; 101 | inset: 0; 102 | width: 0; 103 | 104 | background: #fff; 105 | overflow: hidden; 106 | will-change: width; 107 | transition: 0.2s width; 108 | } 109 | 110 | .load-indicator-bar-container { 111 | position: relative; 112 | z-index: 10; 113 | background-color: #000; 114 | } 115 | 116 | .load-indicator-bar > .text-container { 117 | color: #000; 118 | } 119 | 120 | .assets-info { 121 | position: fixed; 122 | inset: 0; 123 | background: #000; 124 | display: flex; 125 | flex-direction: column; 126 | align-items: center; 127 | justify-content: center; 128 | color: #fff; 129 | font-size: 14px; 130 | font-family: monospace; 131 | text-align: center; 132 | padding: 30px; 133 | z-index: 5; 134 | opacity: 0; 135 | transform: scale(1.5); 136 | will-change: opacity, transform; 137 | transition: opacity 0.5s, transform 0.5s; 138 | pointer-events: none; 139 | } 140 | 141 | .assets-info.visible { 142 | transform: scale(1); 143 | opacity: 1; 144 | pointer-events: all; 145 | } 146 | 147 | .assets-info a { 148 | color: #22acc4; 149 | text-decoration: underline; 150 | margin-bottom: 10px; 151 | } 152 | 153 | .asset-attribution { 154 | margin-bottom: 10px; 155 | } 156 | 157 | .links { 158 | position: absolute; 159 | bottom: 0; 160 | left: 0; 161 | z-index: 2; 162 | display: flex; 163 | flex-direction: column; 164 | padding: 10px 0; 165 | } 166 | 167 | .link { 168 | padding: 5px 15px; 169 | color: #fff; 170 | font-size: 16px; 171 | font-family: monospace; 172 | text-align: left; 173 | cursor: pointer; 174 | transition: opacity 0.5s; 175 | opacity: 0.3; 176 | text-decoration: none; 177 | } 178 | 179 | .link:hover { 180 | opacity: 1; 181 | } 182 | 183 | .button.assets-info-toggle { 184 | cursor: pointer; 185 | display: block; 186 | border: none; 187 | border-radius: 0; 188 | background: var(--rainbow-gradient); 189 | background-repeat: repeat; 190 | background-size: 300% 100%; 191 | animation: rainbow 5s linear infinite; 192 | padding: 4px; 193 | margin-top: 100px; 194 | } 195 | 196 | .button.assets-info-toggle .text { 197 | font-family: monospace; 198 | font-size: 12px; 199 | padding: 15px 80px; 200 | } 201 | 202 | .black-bg { 203 | position: relative; 204 | background: #000; 205 | } 206 | 207 | .black-bg .text { 208 | color: #fff; 209 | } 210 | 211 | .white-bg { 212 | background: #fff; 213 | position: absolute; 214 | inset: 0; 215 | width: 0; 216 | transition: width 0.5s; 217 | overflow: hidden; 218 | } 219 | 220 | .button.assets-info-toggle:hover .white-bg { 221 | width: 100%; 222 | } 223 | 224 | .white-bg > .text { 225 | color: #000; 226 | } 227 | -------------------------------------------------------------------------------- /static/draco/README.md: -------------------------------------------------------------------------------- 1 | # Draco 3D Data Compression 2 | 3 | Draco is an open-source library for compressing and decompressing 3D geometric meshes and point clouds. It is intended to improve the storage and transmission of 3D graphics. 4 | 5 | [Website](https://google.github.io/draco/) | [GitHub](https://github.com/google/draco) 6 | 7 | ## Contents 8 | 9 | This folder contains three utilities: 10 | 11 | - `draco_decoder.js` — Emscripten-compiled decoder, compatible with any modern browser. 12 | - `draco_decoder.wasm` — WebAssembly decoder, compatible with newer browsers and devices. 13 | - `draco_wasm_wrapper.js` — JavaScript wrapper for the WASM decoder. 14 | 15 | Each file is provided in two variations: 16 | 17 | - **Default:** Latest stable builds, tracking the project's [master branch](https://github.com/google/draco). 18 | - **glTF:** Builds targeted by the [glTF mesh compression extension](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression), tracking the [corresponding Draco branch](https://github.com/google/draco/tree/gltf_2.0_draco_extension). 19 | 20 | Either variation may be used with `DRACOLoader`: 21 | 22 | ```js 23 | var dracoLoader = new DRACOLoader(); 24 | dracoLoader.setDecoderPath('path/to/decoders/'); 25 | dracoLoader.setDecoderConfig({ type: 'js' }); // (Optional) Override detection of WASM support. 26 | ``` 27 | 28 | Further [documentation on GitHub](https://github.com/google/draco/tree/master/javascript/example#static-loading-javascript-decoder). 29 | 30 | ## License 31 | 32 | [Apache License 2.0](https://github.com/google/draco/blob/master/LICENSE) 33 | -------------------------------------------------------------------------------- /static/draco/draco_decoder.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prizemlenie/subsurface-refraction-shader/3a22842f254db329e65e874e78433e88335c63ff/static/draco/draco_decoder.wasm -------------------------------------------------------------------------------- /static/draco/draco_wasm_wrapper.js: -------------------------------------------------------------------------------- 1 | var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(k){var n=0;return function(){return n>>0,$jscomp.propertyToPolyfillSymbol[h]=$jscomp.IS_SYMBOL_NATIVE? 7 | $jscomp.global.Symbol(h):$jscomp.POLYFILL_PREFIX+l+"$"+h),$jscomp.defineProperty(p,$jscomp.propertyToPolyfillSymbol[h],{configurable:!0,writable:!0,value:n})))}; 8 | $jscomp.polyfill("Promise",function(k){function n(){this.batch_=null}function l(f){return f instanceof h?f:new h(function(q,v){q(f)})}if(k&&(!($jscomp.FORCE_POLYFILL_PROMISE||$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION&&"undefined"===typeof $jscomp.global.PromiseRejectionEvent)||!$jscomp.global.Promise||-1===$jscomp.global.Promise.toString().indexOf("[native code]")))return k;n.prototype.asyncExecute=function(f){if(null==this.batch_){this.batch_=[];var q=this;this.asyncExecuteFunction(function(){q.executeBatch_()})}this.batch_.push(f)}; 9 | var p=$jscomp.global.setTimeout;n.prototype.asyncExecuteFunction=function(f){p(f,0)};n.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var f=this.batch_;this.batch_=[];for(var q=0;q=A}},"es6","es3"); 19 | $jscomp.polyfill("Array.prototype.copyWithin",function(k){function n(l){l=Number(l);return Infinity===l||-Infinity===l?l:l|0}return k?k:function(l,p,h){var A=this.length;l=n(l);p=n(p);h=void 0===h?A:n(h);l=0>l?Math.max(A+l,0):Math.min(l,A);p=0>p?Math.max(A+p,0):Math.min(p,A);h=0>h?Math.max(A+h,0):Math.min(h,A);if(lp;)--h in this?this[--l]=this[h]:delete this[--l];return this}},"es6","es3"); 20 | $jscomp.typedArrayCopyWithin=function(k){return k?k:Array.prototype.copyWithin};$jscomp.polyfill("Int8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8ClampedArray.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5"); 21 | $jscomp.polyfill("Uint16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float64Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5"); 22 | var DracoDecoderModule=function(){var k="undefined"!==typeof document&&document.currentScript?document.currentScript.src:void 0;"undefined"!==typeof __filename&&(k=k||__filename);return function(n){function l(e){return a.locateFile?a.locateFile(e,U):U+e}function p(e,b,c){var d=b+c;for(c=b;e[c]&&!(c>=d);)++c;if(16g?d+=String.fromCharCode(g):(g-=65536,d+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else d+=String.fromCharCode(g)}return d}function h(e,b){return e?p(ea,e,b):""}function A(){var e=ja.buffer;a.HEAP8=Y=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAP32=ca=new Int32Array(e);a.HEAPU8=ea=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAPU32=V=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}function f(e){if(a.onAbort)a.onAbort(e); 24 | e="Aborted("+e+")";da(e);wa=!0;e=new WebAssembly.RuntimeError(e+". Build with -sASSERTIONS for more info.");ka(e);throw e;}function q(e){try{if(e==P&&fa)return new Uint8Array(fa);if(ma)return ma(e);throw"both async and sync fetching of the wasm failed";}catch(b){f(b)}}function v(){if(!fa&&(xa||ha)){if("function"==typeof fetch&&!P.startsWith("file://"))return fetch(P,{credentials:"same-origin"}).then(function(e){if(!e.ok)throw"failed to load wasm binary file at '"+P+"'";return e.arrayBuffer()}).catch(function(){return q(P)}); 25 | if(na)return new Promise(function(e,b){na(P,function(c){e(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return q(P)})}function z(e){for(;0>2]=b};this.get_type=function(){return V[this.ptr+4>>2]};this.set_destructor=function(b){V[this.ptr+8>>2]=b};this.get_destructor=function(){return V[this.ptr+8>>2]};this.set_refcount=function(b){ca[this.ptr>>2]=b};this.set_caught=function(b){Y[this.ptr+ 26 | 12>>0]=b?1:0};this.get_caught=function(){return 0!=Y[this.ptr+12>>0]};this.set_rethrown=function(b){Y[this.ptr+13>>0]=b?1:0};this.get_rethrown=function(){return 0!=Y[this.ptr+13>>0]};this.init=function(b,c){this.set_adjusted_ptr(0);this.set_type(b);this.set_destructor(c);this.set_refcount(0);this.set_caught(!1);this.set_rethrown(!1)};this.add_ref=function(){ca[this.ptr>>2]+=1};this.release_ref=function(){var b=ca[this.ptr>>2];ca[this.ptr>>2]=b-1;return 1===b};this.set_adjusted_ptr=function(b){V[this.ptr+ 27 | 16>>2]=b};this.get_adjusted_ptr=function(){return V[this.ptr+16>>2]};this.get_exception_ptr=function(){if(ya(this.get_type()))return V[this.excPtr>>2];var b=this.get_adjusted_ptr();return 0!==b?b:this.excPtr}}function ba(){function e(){if(!la&&(la=!0,a.calledRun=!0,!wa)){za=!0;z(oa);Aa(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)Ba.unshift(a.postRun.shift());z(Ba)}}if(!(0=d?b++:2047>=d?b+=2:55296<=d&&57343>= 29 | d?(b+=4,++c):b+=3}b=Array(b+1);c=0;d=b.length;if(0=u){var X=e.charCodeAt(++g);u=65536+((u&1023)<<10)|X&1023}if(127>=u){if(c>=d)break;b[c++]=u}else{if(2047>=u){if(c+1>=d)break;b[c++]=192|u>>6}else{if(65535>=u){if(c+2>=d)break;b[c++]=224|u>>12}else{if(c+3>=d)break;b[c++]=240|u>>18;b[c++]=128|u>>12&63}b[c++]=128|u>>6&63}b[c++]=128|u&63}}b[c]=0}e=r.alloc(b,Y);r.copy(b,Y,e);return e}return e}function pa(e){if("object"===typeof e){var b= 30 | r.alloc(e,Y);r.copy(e,Y,b);return b}return e}function Z(){throw"cannot construct a VoidPtr, no constructor in IDL";}function S(){this.ptr=Da();x(S)[this.ptr]=this}function Q(){this.ptr=Ea();x(Q)[this.ptr]=this}function W(){this.ptr=Fa();x(W)[this.ptr]=this}function w(){this.ptr=Ga();x(w)[this.ptr]=this}function C(){this.ptr=Ha();x(C)[this.ptr]=this}function F(){this.ptr=Ia();x(F)[this.ptr]=this}function G(){this.ptr=Ja();x(G)[this.ptr]=this}function E(){this.ptr=Ka();x(E)[this.ptr]=this}function T(){this.ptr= 31 | La();x(T)[this.ptr]=this}function B(){throw"cannot construct a Status, no constructor in IDL";}function H(){this.ptr=Ma();x(H)[this.ptr]=this}function I(){this.ptr=Na();x(I)[this.ptr]=this}function J(){this.ptr=Oa();x(J)[this.ptr]=this}function K(){this.ptr=Pa();x(K)[this.ptr]=this}function L(){this.ptr=Qa();x(L)[this.ptr]=this}function M(){this.ptr=Ra();x(M)[this.ptr]=this}function N(){this.ptr=Sa();x(N)[this.ptr]=this}function y(){this.ptr=Ta();x(y)[this.ptr]=this}function m(){this.ptr=Ua();x(m)[this.ptr]= 32 | this}n=void 0===n?{}:n;var a="undefined"!=typeof n?n:{},Aa,ka;a.ready=new Promise(function(e,b){Aa=e;ka=b});var Va=!1,Wa=!1;a.onRuntimeInitialized=function(){Va=!0;if(Wa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Wa=!0;if(Va&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=function(e){if("string"!==typeof e)return!1;e=e.split(".");return 2>e.length||3=e[1]?!0:0!=e[0]||10>>=0;if(2147483648=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,e+100663296);var g=Math;d=Math.max(e,d);g=g.min.call(g,2147483648,d+(65536-d%65536)%65536);a:{d=ja.buffer;try{ja.grow(g-d.byteLength+65535>>>16);A();var u=1;break a}catch(X){}u=void 0}if(u)return!0}return!1},f:function(e){return 52},d:function(e,b,c,d,g){return 70},c:function(e,b,c,d){for(var g=0,u=0;u>2],ab=V[b+4>>2];b+=8;for(var sa=0;sa>2]=g;return 0}};(function(){function e(g,u){a.asm=g.exports;ja=a.asm.h;A();oa.unshift(a.asm.i);aa--;a.monitorRunDependencies&&a.monitorRunDependencies(aa);0==aa&&(null!==ra&&(clearInterval(ra),ra=null),ia&&(g=ia,ia=null,g()))}function b(g){e(g.instance)}function c(g){return v().then(function(u){return WebAssembly.instantiate(u,d)}).then(function(u){return u}).then(g,function(u){da("failed to asynchronously prepare wasm: "+u);f(u)})}var d={a:xd};aa++;a.monitorRunDependencies&&a.monitorRunDependencies(aa); 39 | if(a.instantiateWasm)try{return a.instantiateWasm(d,e)}catch(g){da("Module.instantiateWasm callback failed with error: "+g),ka(g)}(function(){return fa||"function"!=typeof WebAssembly.instantiateStreaming||P.startsWith("data:application/octet-stream;base64,")||P.startsWith("file://")||Ya||"function"!=typeof fetch?c(b):fetch(P,{credentials:"same-origin"}).then(function(g){return WebAssembly.instantiateStreaming(g,d).then(b,function(u){da("wasm streaming compile failed: "+u);da("falling back to ArrayBuffer instantiation"); 40 | return c(b)})})})().catch(ka);return{}})();var bb=a._emscripten_bind_VoidPtr___destroy___0=function(){return(bb=a._emscripten_bind_VoidPtr___destroy___0=a.asm.k).apply(null,arguments)},Da=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=function(){return(Da=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=a.asm.l).apply(null,arguments)},cb=a._emscripten_bind_DecoderBuffer_Init_2=function(){return(cb=a._emscripten_bind_DecoderBuffer_Init_2=a.asm.m).apply(null,arguments)},db=a._emscripten_bind_DecoderBuffer___destroy___0= 41 | function(){return(db=a._emscripten_bind_DecoderBuffer___destroy___0=a.asm.n).apply(null,arguments)},Ea=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return(Ea=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=a.asm.o).apply(null,arguments)},eb=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return(eb=a._emscripten_bind_AttributeTransformData_transform_type_0=a.asm.p).apply(null,arguments)},fb=a._emscripten_bind_AttributeTransformData___destroy___0= 42 | function(){return(fb=a._emscripten_bind_AttributeTransformData___destroy___0=a.asm.q).apply(null,arguments)},Fa=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(Fa=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=a.asm.r).apply(null,arguments)},gb=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return(gb=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.s).apply(null,arguments)},Ga=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(Ga= 43 | a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.t).apply(null,arguments)},hb=a._emscripten_bind_PointAttribute_size_0=function(){return(hb=a._emscripten_bind_PointAttribute_size_0=a.asm.u).apply(null,arguments)},ib=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=function(){return(ib=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=a.asm.v).apply(null,arguments)},jb=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(jb=a._emscripten_bind_PointAttribute_attribute_type_0= 44 | a.asm.w).apply(null,arguments)},kb=a._emscripten_bind_PointAttribute_data_type_0=function(){return(kb=a._emscripten_bind_PointAttribute_data_type_0=a.asm.x).apply(null,arguments)},lb=a._emscripten_bind_PointAttribute_num_components_0=function(){return(lb=a._emscripten_bind_PointAttribute_num_components_0=a.asm.y).apply(null,arguments)},mb=a._emscripten_bind_PointAttribute_normalized_0=function(){return(mb=a._emscripten_bind_PointAttribute_normalized_0=a.asm.z).apply(null,arguments)},nb=a._emscripten_bind_PointAttribute_byte_stride_0= 45 | function(){return(nb=a._emscripten_bind_PointAttribute_byte_stride_0=a.asm.A).apply(null,arguments)},ob=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return(ob=a._emscripten_bind_PointAttribute_byte_offset_0=a.asm.B).apply(null,arguments)},pb=a._emscripten_bind_PointAttribute_unique_id_0=function(){return(pb=a._emscripten_bind_PointAttribute_unique_id_0=a.asm.C).apply(null,arguments)},qb=a._emscripten_bind_PointAttribute___destroy___0=function(){return(qb=a._emscripten_bind_PointAttribute___destroy___0= 46 | a.asm.D).apply(null,arguments)},Ha=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return(Ha=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=a.asm.E).apply(null,arguments)},rb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=function(){return(rb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=a.asm.F).apply(null,arguments)},sb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0= 47 | function(){return(sb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=a.asm.G).apply(null,arguments)},tb=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return(tb=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=a.asm.H).apply(null,arguments)},ub=a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return(ub=a._emscripten_bind_AttributeQuantizationTransform_range_0=a.asm.I).apply(null,arguments)},vb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0= 48 | function(){return(vb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=a.asm.J).apply(null,arguments)},Ia=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return(Ia=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=a.asm.K).apply(null,arguments)},wb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return(wb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=a.asm.L).apply(null, 49 | arguments)},xb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return(xb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=a.asm.M).apply(null,arguments)},yb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return(yb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=a.asm.N).apply(null,arguments)},Ja=a._emscripten_bind_PointCloud_PointCloud_0=function(){return(Ja=a._emscripten_bind_PointCloud_PointCloud_0=a.asm.O).apply(null, 50 | arguments)},zb=a._emscripten_bind_PointCloud_num_attributes_0=function(){return(zb=a._emscripten_bind_PointCloud_num_attributes_0=a.asm.P).apply(null,arguments)},Ab=a._emscripten_bind_PointCloud_num_points_0=function(){return(Ab=a._emscripten_bind_PointCloud_num_points_0=a.asm.Q).apply(null,arguments)},Bb=a._emscripten_bind_PointCloud___destroy___0=function(){return(Bb=a._emscripten_bind_PointCloud___destroy___0=a.asm.R).apply(null,arguments)},Ka=a._emscripten_bind_Mesh_Mesh_0=function(){return(Ka= 51 | a._emscripten_bind_Mesh_Mesh_0=a.asm.S).apply(null,arguments)},Cb=a._emscripten_bind_Mesh_num_faces_0=function(){return(Cb=a._emscripten_bind_Mesh_num_faces_0=a.asm.T).apply(null,arguments)},Db=a._emscripten_bind_Mesh_num_attributes_0=function(){return(Db=a._emscripten_bind_Mesh_num_attributes_0=a.asm.U).apply(null,arguments)},Eb=a._emscripten_bind_Mesh_num_points_0=function(){return(Eb=a._emscripten_bind_Mesh_num_points_0=a.asm.V).apply(null,arguments)},Fb=a._emscripten_bind_Mesh___destroy___0=function(){return(Fb= 52 | a._emscripten_bind_Mesh___destroy___0=a.asm.W).apply(null,arguments)},La=a._emscripten_bind_Metadata_Metadata_0=function(){return(La=a._emscripten_bind_Metadata_Metadata_0=a.asm.X).apply(null,arguments)},Gb=a._emscripten_bind_Metadata___destroy___0=function(){return(Gb=a._emscripten_bind_Metadata___destroy___0=a.asm.Y).apply(null,arguments)},Hb=a._emscripten_bind_Status_code_0=function(){return(Hb=a._emscripten_bind_Status_code_0=a.asm.Z).apply(null,arguments)},Ib=a._emscripten_bind_Status_ok_0=function(){return(Ib= 53 | a._emscripten_bind_Status_ok_0=a.asm._).apply(null,arguments)},Jb=a._emscripten_bind_Status_error_msg_0=function(){return(Jb=a._emscripten_bind_Status_error_msg_0=a.asm.$).apply(null,arguments)},Kb=a._emscripten_bind_Status___destroy___0=function(){return(Kb=a._emscripten_bind_Status___destroy___0=a.asm.aa).apply(null,arguments)},Ma=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return(Ma=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=a.asm.ba).apply(null,arguments)}, 54 | Lb=a._emscripten_bind_DracoFloat32Array_GetValue_1=function(){return(Lb=a._emscripten_bind_DracoFloat32Array_GetValue_1=a.asm.ca).apply(null,arguments)},Mb=a._emscripten_bind_DracoFloat32Array_size_0=function(){return(Mb=a._emscripten_bind_DracoFloat32Array_size_0=a.asm.da).apply(null,arguments)},Nb=a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return(Nb=a._emscripten_bind_DracoFloat32Array___destroy___0=a.asm.ea).apply(null,arguments)},Na=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0= 55 | function(){return(Na=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=a.asm.fa).apply(null,arguments)},Ob=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return(Ob=a._emscripten_bind_DracoInt8Array_GetValue_1=a.asm.ga).apply(null,arguments)},Pb=a._emscripten_bind_DracoInt8Array_size_0=function(){return(Pb=a._emscripten_bind_DracoInt8Array_size_0=a.asm.ha).apply(null,arguments)},Qb=a._emscripten_bind_DracoInt8Array___destroy___0=function(){return(Qb=a._emscripten_bind_DracoInt8Array___destroy___0= 56 | a.asm.ia).apply(null,arguments)},Oa=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return(Oa=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=a.asm.ja).apply(null,arguments)},Rb=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return(Rb=a._emscripten_bind_DracoUInt8Array_GetValue_1=a.asm.ka).apply(null,arguments)},Sb=a._emscripten_bind_DracoUInt8Array_size_0=function(){return(Sb=a._emscripten_bind_DracoUInt8Array_size_0=a.asm.la).apply(null,arguments)},Tb=a._emscripten_bind_DracoUInt8Array___destroy___0= 57 | function(){return(Tb=a._emscripten_bind_DracoUInt8Array___destroy___0=a.asm.ma).apply(null,arguments)},Pa=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return(Pa=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=a.asm.na).apply(null,arguments)},Ub=a._emscripten_bind_DracoInt16Array_GetValue_1=function(){return(Ub=a._emscripten_bind_DracoInt16Array_GetValue_1=a.asm.oa).apply(null,arguments)},Vb=a._emscripten_bind_DracoInt16Array_size_0=function(){return(Vb=a._emscripten_bind_DracoInt16Array_size_0= 58 | a.asm.pa).apply(null,arguments)},Wb=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return(Wb=a._emscripten_bind_DracoInt16Array___destroy___0=a.asm.qa).apply(null,arguments)},Qa=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return(Qa=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=a.asm.ra).apply(null,arguments)},Xb=a._emscripten_bind_DracoUInt16Array_GetValue_1=function(){return(Xb=a._emscripten_bind_DracoUInt16Array_GetValue_1=a.asm.sa).apply(null,arguments)}, 59 | Yb=a._emscripten_bind_DracoUInt16Array_size_0=function(){return(Yb=a._emscripten_bind_DracoUInt16Array_size_0=a.asm.ta).apply(null,arguments)},Zb=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return(Zb=a._emscripten_bind_DracoUInt16Array___destroy___0=a.asm.ua).apply(null,arguments)},Ra=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=function(){return(Ra=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=a.asm.va).apply(null,arguments)},$b=a._emscripten_bind_DracoInt32Array_GetValue_1= 60 | function(){return($b=a._emscripten_bind_DracoInt32Array_GetValue_1=a.asm.wa).apply(null,arguments)},ac=a._emscripten_bind_DracoInt32Array_size_0=function(){return(ac=a._emscripten_bind_DracoInt32Array_size_0=a.asm.xa).apply(null,arguments)},bc=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return(bc=a._emscripten_bind_DracoInt32Array___destroy___0=a.asm.ya).apply(null,arguments)},Sa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=function(){return(Sa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0= 61 | a.asm.za).apply(null,arguments)},cc=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return(cc=a._emscripten_bind_DracoUInt32Array_GetValue_1=a.asm.Aa).apply(null,arguments)},dc=a._emscripten_bind_DracoUInt32Array_size_0=function(){return(dc=a._emscripten_bind_DracoUInt32Array_size_0=a.asm.Ba).apply(null,arguments)},ec=a._emscripten_bind_DracoUInt32Array___destroy___0=function(){return(ec=a._emscripten_bind_DracoUInt32Array___destroy___0=a.asm.Ca).apply(null,arguments)},Ta=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0= 62 | function(){return(Ta=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=a.asm.Da).apply(null,arguments)},fc=a._emscripten_bind_MetadataQuerier_HasEntry_2=function(){return(fc=a._emscripten_bind_MetadataQuerier_HasEntry_2=a.asm.Ea).apply(null,arguments)},gc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return(gc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=a.asm.Fa).apply(null,arguments)},hc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=function(){return(hc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3= 63 | a.asm.Ga).apply(null,arguments)},ic=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return(ic=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=a.asm.Ha).apply(null,arguments)},jc=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return(jc=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=a.asm.Ia).apply(null,arguments)},kc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return(kc=a._emscripten_bind_MetadataQuerier_NumEntries_1=a.asm.Ja).apply(null,arguments)}, 64 | lc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return(lc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=a.asm.Ka).apply(null,arguments)},mc=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return(mc=a._emscripten_bind_MetadataQuerier___destroy___0=a.asm.La).apply(null,arguments)},Ua=a._emscripten_bind_Decoder_Decoder_0=function(){return(Ua=a._emscripten_bind_Decoder_Decoder_0=a.asm.Ma).apply(null,arguments)},nc=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=function(){return(nc= 65 | a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=a.asm.Na).apply(null,arguments)},oc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=function(){return(oc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=a.asm.Oa).apply(null,arguments)},pc=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return(pc=a._emscripten_bind_Decoder_GetAttributeId_2=a.asm.Pa).apply(null,arguments)},qc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return(qc=a._emscripten_bind_Decoder_GetAttributeIdByName_2= 66 | a.asm.Qa).apply(null,arguments)},rc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return(rc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=a.asm.Ra).apply(null,arguments)},sc=a._emscripten_bind_Decoder_GetAttribute_2=function(){return(sc=a._emscripten_bind_Decoder_GetAttribute_2=a.asm.Sa).apply(null,arguments)},tc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return(tc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=a.asm.Ta).apply(null,arguments)}, 67 | uc=a._emscripten_bind_Decoder_GetMetadata_1=function(){return(uc=a._emscripten_bind_Decoder_GetMetadata_1=a.asm.Ua).apply(null,arguments)},vc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return(vc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=a.asm.Va).apply(null,arguments)},wc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=function(){return(wc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=a.asm.Wa).apply(null,arguments)},xc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2= 68 | function(){return(xc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=a.asm.Xa).apply(null,arguments)},yc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=function(){return(yc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=a.asm.Ya).apply(null,arguments)},zc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=function(){return(zc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=a.asm.Za).apply(null,arguments)},Ac=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return(Ac= 69 | a._emscripten_bind_Decoder_GetAttributeFloat_3=a.asm._a).apply(null,arguments)},Bc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=function(){return(Bc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=a.asm.$a).apply(null,arguments)},Cc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return(Cc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=a.asm.ab).apply(null,arguments)},Dc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=function(){return(Dc= 70 | a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=a.asm.bb).apply(null,arguments)},Ec=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return(Ec=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=a.asm.cb).apply(null,arguments)},Fc=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return(Fc=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=a.asm.db).apply(null,arguments)},Gc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3= 71 | function(){return(Gc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=a.asm.eb).apply(null,arguments)},Hc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return(Hc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=a.asm.fb).apply(null,arguments)},Ic=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return(Ic=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=a.asm.gb).apply(null,arguments)},Jc=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5= 72 | function(){return(Jc=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=a.asm.hb).apply(null,arguments)},Kc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return(Kc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=a.asm.ib).apply(null,arguments)},Lc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=function(){return(Lc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=a.asm.jb).apply(null,arguments)},Mc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2= 73 | function(){return(Mc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=a.asm.kb).apply(null,arguments)},Nc=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return(Nc=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=a.asm.lb).apply(null,arguments)},Oc=a._emscripten_bind_Decoder___destroy___0=function(){return(Oc=a._emscripten_bind_Decoder___destroy___0=a.asm.mb).apply(null,arguments)},Pc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=function(){return(Pc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM= 74 | a.asm.nb).apply(null,arguments)},Qc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return(Qc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=a.asm.ob).apply(null,arguments)},Rc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return(Rc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=a.asm.pb).apply(null,arguments)},Sc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM= 75 | function(){return(Sc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=a.asm.qb).apply(null,arguments)},Tc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(Tc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.rb).apply(null,arguments)},Uc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(Uc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=a.asm.sb).apply(null,arguments)},Vc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL= 76 | function(){return(Vc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.tb).apply(null,arguments)},Wc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(Wc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.ub).apply(null,arguments)},Xc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(Xc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=a.asm.vb).apply(null,arguments)},Yc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC= 77 | function(){return(Yc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.wb).apply(null,arguments)},Zc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(Zc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.xb).apply(null,arguments)},$c=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return($c=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=a.asm.yb).apply(null,arguments)},ad=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH= 78 | function(){return(ad=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.zb).apply(null,arguments)},bd=a._emscripten_enum_draco_DataType_DT_INVALID=function(){return(bd=a._emscripten_enum_draco_DataType_DT_INVALID=a.asm.Ab).apply(null,arguments)},cd=a._emscripten_enum_draco_DataType_DT_INT8=function(){return(cd=a._emscripten_enum_draco_DataType_DT_INT8=a.asm.Bb).apply(null,arguments)},dd=a._emscripten_enum_draco_DataType_DT_UINT8=function(){return(dd=a._emscripten_enum_draco_DataType_DT_UINT8= 79 | a.asm.Cb).apply(null,arguments)},ed=a._emscripten_enum_draco_DataType_DT_INT16=function(){return(ed=a._emscripten_enum_draco_DataType_DT_INT16=a.asm.Db).apply(null,arguments)},fd=a._emscripten_enum_draco_DataType_DT_UINT16=function(){return(fd=a._emscripten_enum_draco_DataType_DT_UINT16=a.asm.Eb).apply(null,arguments)},gd=a._emscripten_enum_draco_DataType_DT_INT32=function(){return(gd=a._emscripten_enum_draco_DataType_DT_INT32=a.asm.Fb).apply(null,arguments)},hd=a._emscripten_enum_draco_DataType_DT_UINT32= 80 | function(){return(hd=a._emscripten_enum_draco_DataType_DT_UINT32=a.asm.Gb).apply(null,arguments)},id=a._emscripten_enum_draco_DataType_DT_INT64=function(){return(id=a._emscripten_enum_draco_DataType_DT_INT64=a.asm.Hb).apply(null,arguments)},jd=a._emscripten_enum_draco_DataType_DT_UINT64=function(){return(jd=a._emscripten_enum_draco_DataType_DT_UINT64=a.asm.Ib).apply(null,arguments)},kd=a._emscripten_enum_draco_DataType_DT_FLOAT32=function(){return(kd=a._emscripten_enum_draco_DataType_DT_FLOAT32=a.asm.Jb).apply(null, 81 | arguments)},ld=a._emscripten_enum_draco_DataType_DT_FLOAT64=function(){return(ld=a._emscripten_enum_draco_DataType_DT_FLOAT64=a.asm.Kb).apply(null,arguments)},md=a._emscripten_enum_draco_DataType_DT_BOOL=function(){return(md=a._emscripten_enum_draco_DataType_DT_BOOL=a.asm.Lb).apply(null,arguments)},nd=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=function(){return(nd=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=a.asm.Mb).apply(null,arguments)},od=a._emscripten_enum_draco_StatusCode_OK=function(){return(od= 82 | a._emscripten_enum_draco_StatusCode_OK=a.asm.Nb).apply(null,arguments)},pd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=function(){return(pd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=a.asm.Ob).apply(null,arguments)},qd=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return(qd=a._emscripten_enum_draco_StatusCode_IO_ERROR=a.asm.Pb).apply(null,arguments)},rd=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=function(){return(rd=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER= 83 | a.asm.Qb).apply(null,arguments)},sd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return(sd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=a.asm.Rb).apply(null,arguments)},td=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return(td=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=a.asm.Sb).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Tb).apply(null,arguments)};a._free=function(){return(a._free=a.asm.Ub).apply(null,arguments)}; 84 | var ya=function(){return(ya=a.asm.Vb).apply(null,arguments)};a.___start_em_js=15856;a.___stop_em_js=15954;var la;ia=function b(){la||ba();la||(ia=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0=r.size?(0>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var g=0;gb.byteLength)return a.INVALID_GEOMETRY_TYPE;switch(b[7]){case 0:return a.POINT_CLOUD;case 1:return a.TRIANGULAR_MESH;default:return a.INVALID_GEOMETRY_TYPE}};return n.ready}}(); 117 | "object"===typeof exports&&"object"===typeof module?module.exports=DracoDecoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoDecoderModule}):"object"===typeof exports&&(exports.DracoDecoderModule=DracoDecoderModule); 118 | -------------------------------------------------------------------------------- /static/draco/gltf/draco_decoder.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prizemlenie/subsurface-refraction-shader/3a22842f254db329e65e874e78433e88335c63ff/static/draco/gltf/draco_decoder.wasm -------------------------------------------------------------------------------- /static/draco/gltf/draco_wasm_wrapper.js: -------------------------------------------------------------------------------- 1 | var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(h){var n=0;return function(){return n>>0,$jscomp.propertyToPolyfillSymbol[l]=$jscomp.IS_SYMBOL_NATIVE? 7 | $jscomp.global.Symbol(l):$jscomp.POLYFILL_PREFIX+k+"$"+l),$jscomp.defineProperty(p,$jscomp.propertyToPolyfillSymbol[l],{configurable:!0,writable:!0,value:n})))}; 8 | $jscomp.polyfill("Promise",function(h){function n(){this.batch_=null}function k(f){return f instanceof l?f:new l(function(q,u){q(f)})}if(h&&(!($jscomp.FORCE_POLYFILL_PROMISE||$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION&&"undefined"===typeof $jscomp.global.PromiseRejectionEvent)||!$jscomp.global.Promise||-1===$jscomp.global.Promise.toString().indexOf("[native code]")))return h;n.prototype.asyncExecute=function(f){if(null==this.batch_){this.batch_=[];var q=this;this.asyncExecuteFunction(function(){q.executeBatch_()})}this.batch_.push(f)}; 9 | var p=$jscomp.global.setTimeout;n.prototype.asyncExecuteFunction=function(f){p(f,0)};n.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var f=this.batch_;this.batch_=[];for(var q=0;q=y}},"es6","es3"); 19 | $jscomp.polyfill("Array.prototype.copyWithin",function(h){function n(k){k=Number(k);return Infinity===k||-Infinity===k?k:k|0}return h?h:function(k,p,l){var y=this.length;k=n(k);p=n(p);l=void 0===l?y:n(l);k=0>k?Math.max(y+k,0):Math.min(k,y);p=0>p?Math.max(y+p,0):Math.min(p,y);l=0>l?Math.max(y+l,0):Math.min(l,y);if(kp;)--l in this?this[--k]=this[l]:delete this[--k];return this}},"es6","es3"); 20 | $jscomp.typedArrayCopyWithin=function(h){return h?h:Array.prototype.copyWithin};$jscomp.polyfill("Int8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8ClampedArray.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5"); 21 | $jscomp.polyfill("Uint16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float64Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5"); 22 | var DracoDecoderModule=function(){var h="undefined"!==typeof document&&document.currentScript?document.currentScript.src:void 0;"undefined"!==typeof __filename&&(h=h||__filename);return function(n){function k(e){return a.locateFile?a.locateFile(e,U):U+e}function p(e,b){if(e){var c=ia;var d=e+b;for(b=e;c[b]&&!(b>=d);)++b;if(16g?d+=String.fromCharCode(g):(g-=65536,d+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else d+=String.fromCharCode(g)}c=d}}else c="";return c}function l(){var e=ja.buffer;a.HEAP8=W=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAP32=ca=new Int32Array(e);a.HEAPU8=ia=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAPU32=Y=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}function y(e){if(a.onAbort)a.onAbort(e); 24 | e="Aborted("+e+")";da(e);sa=!0;e=new WebAssembly.RuntimeError(e+". Build with -sASSERTIONS for more info.");ka(e);throw e;}function f(e){try{if(e==P&&ea)return new Uint8Array(ea);if(ma)return ma(e);throw"both async and sync fetching of the wasm failed";}catch(b){y(b)}}function q(){if(!ea&&(ta||fa)){if("function"==typeof fetch&&!P.startsWith("file://"))return fetch(P,{credentials:"same-origin"}).then(function(e){if(!e.ok)throw"failed to load wasm binary file at '"+P+"'";return e.arrayBuffer()}).catch(function(){return f(P)}); 25 | if(na)return new Promise(function(e,b){na(P,function(c){e(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return f(P)})}function u(e){for(;0>2]=b};this.get_type=function(){return Y[this.ptr+4>>2]};this.set_destructor=function(b){Y[this.ptr+8>>2]=b};this.get_destructor=function(){return Y[this.ptr+8>>2]};this.set_refcount=function(b){ca[this.ptr>>2]=b};this.set_caught=function(b){W[this.ptr+ 26 | 12>>0]=b?1:0};this.get_caught=function(){return 0!=W[this.ptr+12>>0]};this.set_rethrown=function(b){W[this.ptr+13>>0]=b?1:0};this.get_rethrown=function(){return 0!=W[this.ptr+13>>0]};this.init=function(b,c){this.set_adjusted_ptr(0);this.set_type(b);this.set_destructor(c);this.set_refcount(0);this.set_caught(!1);this.set_rethrown(!1)};this.add_ref=function(){ca[this.ptr>>2]+=1};this.release_ref=function(){var b=ca[this.ptr>>2];ca[this.ptr>>2]=b-1;return 1===b};this.set_adjusted_ptr=function(b){Y[this.ptr+ 27 | 16>>2]=b};this.get_adjusted_ptr=function(){return Y[this.ptr+16>>2]};this.get_exception_ptr=function(){if(ua(this.get_type()))return Y[this.excPtr>>2];var b=this.get_adjusted_ptr();return 0!==b?b:this.excPtr}}function F(){function e(){if(!la&&(la=!0,a.calledRun=!0,!sa)){va=!0;u(oa);wa(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)xa.unshift(a.postRun.shift());u(xa)}}if(!(0=d?b++:2047>=d?b+=2:55296<=d&&57343>= 29 | d?(b+=4,++c):b+=3}b=Array(b+1);c=0;d=b.length;if(0=t){var aa=e.charCodeAt(++g);t=65536+((t&1023)<<10)|aa&1023}if(127>=t){if(c>=d)break;b[c++]=t}else{if(2047>=t){if(c+1>=d)break;b[c++]=192|t>>6}else{if(65535>=t){if(c+2>=d)break;b[c++]=224|t>>12}else{if(c+3>=d)break;b[c++]=240|t>>18;b[c++]=128|t>>12&63}b[c++]=128|t>>6&63}b[c++]=128|t&63}}b[c]=0}e=r.alloc(b,W);r.copy(b,W,e);return e}return e}function Z(e){if("object"=== 30 | typeof e){var b=r.alloc(e,W);r.copy(e,W,b);return b}return e}function X(){throw"cannot construct a VoidPtr, no constructor in IDL";}function S(){this.ptr=za();w(S)[this.ptr]=this}function Q(){this.ptr=Aa();w(Q)[this.ptr]=this}function V(){this.ptr=Ba();w(V)[this.ptr]=this}function x(){this.ptr=Ca();w(x)[this.ptr]=this}function D(){this.ptr=Da();w(D)[this.ptr]=this}function G(){this.ptr=Ea();w(G)[this.ptr]=this}function H(){this.ptr=Fa();w(H)[this.ptr]=this}function E(){this.ptr=Ga();w(E)[this.ptr]= 31 | this}function T(){this.ptr=Ha();w(T)[this.ptr]=this}function C(){throw"cannot construct a Status, no constructor in IDL";}function I(){this.ptr=Ia();w(I)[this.ptr]=this}function J(){this.ptr=Ja();w(J)[this.ptr]=this}function K(){this.ptr=Ka();w(K)[this.ptr]=this}function L(){this.ptr=La();w(L)[this.ptr]=this}function M(){this.ptr=Ma();w(M)[this.ptr]=this}function N(){this.ptr=Na();w(N)[this.ptr]=this}function O(){this.ptr=Oa();w(O)[this.ptr]=this}function z(){this.ptr=Pa();w(z)[this.ptr]=this}function m(){this.ptr= 32 | Qa();w(m)[this.ptr]=this}n=void 0===n?{}:n;var a="undefined"!=typeof n?n:{},wa,ka;a.ready=new Promise(function(e,b){wa=e;ka=b});var Ra=!1,Sa=!1;a.onRuntimeInitialized=function(){Ra=!0;if(Sa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Sa=!0;if(Ra&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=function(e){if("string"!==typeof e)return!1;e=e.split(".");return 2>e.length||3=e[1]?!0:0!=e[0]||10< 33 | e[1]?!1:!0};var Ta=Object.assign({},a),ta="object"==typeof window,fa="function"==typeof importScripts,Ua="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,U="";if(Ua){var Va=require("fs"),pa=require("path");U=fa?pa.dirname(U)+"/":__dirname+"/";var Wa=function(e,b){e=e.startsWith("file://")?new URL(e):pa.normalize(e);return Va.readFileSync(e,b?void 0:"utf8")};var ma=function(e){e=Wa(e,!0);e.buffer||(e=new Uint8Array(e));return e};var na=function(e, 34 | b,c){e=e.startsWith("file://")?new URL(e):pa.normalize(e);Va.readFile(e,function(d,g){d?c(d):b(g.buffer)})};1>>=0;if(2147483648=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,e+100663296);var g=Math;d=Math.max(e,d);g=g.min.call(g,2147483648,d+(65536-d%65536)%65536);a:{d=ja.buffer;try{ja.grow(g-d.byteLength+65535>>>16);l();var t=1;break a}catch(aa){}t=void 0}if(t)return!0}return!1}};(function(){function e(g,t){a.asm=g.exports;ja=a.asm.e;l();oa.unshift(a.asm.f);ba--;a.monitorRunDependencies&&a.monitorRunDependencies(ba);0==ba&&(null!==qa&&(clearInterval(qa),qa=null),ha&&(g=ha,ha=null,g()))}function b(g){e(g.instance)} 38 | function c(g){return q().then(function(t){return WebAssembly.instantiate(t,d)}).then(function(t){return t}).then(g,function(t){da("failed to asynchronously prepare wasm: "+t);y(t)})}var d={a:qd};ba++;a.monitorRunDependencies&&a.monitorRunDependencies(ba);if(a.instantiateWasm)try{return a.instantiateWasm(d,e)}catch(g){da("Module.instantiateWasm callback failed with error: "+g),ka(g)}(function(){return ea||"function"!=typeof WebAssembly.instantiateStreaming||P.startsWith("data:application/octet-stream;base64,")|| 39 | P.startsWith("file://")||Ua||"function"!=typeof fetch?c(b):fetch(P,{credentials:"same-origin"}).then(function(g){return WebAssembly.instantiateStreaming(g,d).then(b,function(t){da("wasm streaming compile failed: "+t);da("falling back to ArrayBuffer instantiation");return c(b)})})})().catch(ka);return{}})();var Xa=a._emscripten_bind_VoidPtr___destroy___0=function(){return(Xa=a._emscripten_bind_VoidPtr___destroy___0=a.asm.h).apply(null,arguments)},za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0= 40 | function(){return(za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=a.asm.i).apply(null,arguments)},Ya=a._emscripten_bind_DecoderBuffer_Init_2=function(){return(Ya=a._emscripten_bind_DecoderBuffer_Init_2=a.asm.j).apply(null,arguments)},Za=a._emscripten_bind_DecoderBuffer___destroy___0=function(){return(Za=a._emscripten_bind_DecoderBuffer___destroy___0=a.asm.k).apply(null,arguments)},Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return(Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0= 41 | a.asm.l).apply(null,arguments)},$a=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return($a=a._emscripten_bind_AttributeTransformData_transform_type_0=a.asm.m).apply(null,arguments)},ab=a._emscripten_bind_AttributeTransformData___destroy___0=function(){return(ab=a._emscripten_bind_AttributeTransformData___destroy___0=a.asm.n).apply(null,arguments)},Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0= 42 | a.asm.o).apply(null,arguments)},bb=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return(bb=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.p).apply(null,arguments)},Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.q).apply(null,arguments)},cb=a._emscripten_bind_PointAttribute_size_0=function(){return(cb=a._emscripten_bind_PointAttribute_size_0=a.asm.r).apply(null,arguments)},db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0= 43 | function(){return(db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=a.asm.s).apply(null,arguments)},eb=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(eb=a._emscripten_bind_PointAttribute_attribute_type_0=a.asm.t).apply(null,arguments)},fb=a._emscripten_bind_PointAttribute_data_type_0=function(){return(fb=a._emscripten_bind_PointAttribute_data_type_0=a.asm.u).apply(null,arguments)},gb=a._emscripten_bind_PointAttribute_num_components_0=function(){return(gb=a._emscripten_bind_PointAttribute_num_components_0= 44 | a.asm.v).apply(null,arguments)},hb=a._emscripten_bind_PointAttribute_normalized_0=function(){return(hb=a._emscripten_bind_PointAttribute_normalized_0=a.asm.w).apply(null,arguments)},ib=a._emscripten_bind_PointAttribute_byte_stride_0=function(){return(ib=a._emscripten_bind_PointAttribute_byte_stride_0=a.asm.x).apply(null,arguments)},jb=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return(jb=a._emscripten_bind_PointAttribute_byte_offset_0=a.asm.y).apply(null,arguments)},kb=a._emscripten_bind_PointAttribute_unique_id_0= 45 | function(){return(kb=a._emscripten_bind_PointAttribute_unique_id_0=a.asm.z).apply(null,arguments)},lb=a._emscripten_bind_PointAttribute___destroy___0=function(){return(lb=a._emscripten_bind_PointAttribute___destroy___0=a.asm.A).apply(null,arguments)},Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return(Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=a.asm.B).apply(null,arguments)},mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1= 46 | function(){return(mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=a.asm.C).apply(null,arguments)},nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=function(){return(nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=a.asm.D).apply(null,arguments)},ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return(ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=a.asm.E).apply(null,arguments)},pb= 47 | a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return(pb=a._emscripten_bind_AttributeQuantizationTransform_range_0=a.asm.F).apply(null,arguments)},qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=function(){return(qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=a.asm.G).apply(null,arguments)},Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return(Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0= 48 | a.asm.H).apply(null,arguments)},rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return(rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=a.asm.I).apply(null,arguments)},sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return(sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=a.asm.J).apply(null,arguments)},tb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return(tb= 49 | a._emscripten_bind_AttributeOctahedronTransform___destroy___0=a.asm.K).apply(null,arguments)},Fa=a._emscripten_bind_PointCloud_PointCloud_0=function(){return(Fa=a._emscripten_bind_PointCloud_PointCloud_0=a.asm.L).apply(null,arguments)},ub=a._emscripten_bind_PointCloud_num_attributes_0=function(){return(ub=a._emscripten_bind_PointCloud_num_attributes_0=a.asm.M).apply(null,arguments)},vb=a._emscripten_bind_PointCloud_num_points_0=function(){return(vb=a._emscripten_bind_PointCloud_num_points_0=a.asm.N).apply(null, 50 | arguments)},wb=a._emscripten_bind_PointCloud___destroy___0=function(){return(wb=a._emscripten_bind_PointCloud___destroy___0=a.asm.O).apply(null,arguments)},Ga=a._emscripten_bind_Mesh_Mesh_0=function(){return(Ga=a._emscripten_bind_Mesh_Mesh_0=a.asm.P).apply(null,arguments)},xb=a._emscripten_bind_Mesh_num_faces_0=function(){return(xb=a._emscripten_bind_Mesh_num_faces_0=a.asm.Q).apply(null,arguments)},yb=a._emscripten_bind_Mesh_num_attributes_0=function(){return(yb=a._emscripten_bind_Mesh_num_attributes_0= 51 | a.asm.R).apply(null,arguments)},zb=a._emscripten_bind_Mesh_num_points_0=function(){return(zb=a._emscripten_bind_Mesh_num_points_0=a.asm.S).apply(null,arguments)},Ab=a._emscripten_bind_Mesh___destroy___0=function(){return(Ab=a._emscripten_bind_Mesh___destroy___0=a.asm.T).apply(null,arguments)},Ha=a._emscripten_bind_Metadata_Metadata_0=function(){return(Ha=a._emscripten_bind_Metadata_Metadata_0=a.asm.U).apply(null,arguments)},Bb=a._emscripten_bind_Metadata___destroy___0=function(){return(Bb=a._emscripten_bind_Metadata___destroy___0= 52 | a.asm.V).apply(null,arguments)},Cb=a._emscripten_bind_Status_code_0=function(){return(Cb=a._emscripten_bind_Status_code_0=a.asm.W).apply(null,arguments)},Db=a._emscripten_bind_Status_ok_0=function(){return(Db=a._emscripten_bind_Status_ok_0=a.asm.X).apply(null,arguments)},Eb=a._emscripten_bind_Status_error_msg_0=function(){return(Eb=a._emscripten_bind_Status_error_msg_0=a.asm.Y).apply(null,arguments)},Fb=a._emscripten_bind_Status___destroy___0=function(){return(Fb=a._emscripten_bind_Status___destroy___0= 53 | a.asm.Z).apply(null,arguments)},Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return(Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=a.asm._).apply(null,arguments)},Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=function(){return(Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=a.asm.$).apply(null,arguments)},Hb=a._emscripten_bind_DracoFloat32Array_size_0=function(){return(Hb=a._emscripten_bind_DracoFloat32Array_size_0=a.asm.aa).apply(null,arguments)},Ib= 54 | a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return(Ib=a._emscripten_bind_DracoFloat32Array___destroy___0=a.asm.ba).apply(null,arguments)},Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=function(){return(Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=a.asm.ca).apply(null,arguments)},Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return(Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=a.asm.da).apply(null,arguments)},Kb=a._emscripten_bind_DracoInt8Array_size_0= 55 | function(){return(Kb=a._emscripten_bind_DracoInt8Array_size_0=a.asm.ea).apply(null,arguments)},Lb=a._emscripten_bind_DracoInt8Array___destroy___0=function(){return(Lb=a._emscripten_bind_DracoInt8Array___destroy___0=a.asm.fa).apply(null,arguments)},Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return(Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=a.asm.ga).apply(null,arguments)},Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return(Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1= 56 | a.asm.ha).apply(null,arguments)},Nb=a._emscripten_bind_DracoUInt8Array_size_0=function(){return(Nb=a._emscripten_bind_DracoUInt8Array_size_0=a.asm.ia).apply(null,arguments)},Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=function(){return(Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=a.asm.ja).apply(null,arguments)},La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return(La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=a.asm.ka).apply(null,arguments)},Pb=a._emscripten_bind_DracoInt16Array_GetValue_1= 57 | function(){return(Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=a.asm.la).apply(null,arguments)},Qb=a._emscripten_bind_DracoInt16Array_size_0=function(){return(Qb=a._emscripten_bind_DracoInt16Array_size_0=a.asm.ma).apply(null,arguments)},Rb=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return(Rb=a._emscripten_bind_DracoInt16Array___destroy___0=a.asm.na).apply(null,arguments)},Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return(Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0= 58 | a.asm.oa).apply(null,arguments)},Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=function(){return(Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=a.asm.pa).apply(null,arguments)},Tb=a._emscripten_bind_DracoUInt16Array_size_0=function(){return(Tb=a._emscripten_bind_DracoUInt16Array_size_0=a.asm.qa).apply(null,arguments)},Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return(Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=a.asm.ra).apply(null,arguments)},Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0= 59 | function(){return(Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=a.asm.sa).apply(null,arguments)},Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=function(){return(Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=a.asm.ta).apply(null,arguments)},Wb=a._emscripten_bind_DracoInt32Array_size_0=function(){return(Wb=a._emscripten_bind_DracoInt32Array_size_0=a.asm.ua).apply(null,arguments)},Xb=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return(Xb=a._emscripten_bind_DracoInt32Array___destroy___0= 60 | a.asm.va).apply(null,arguments)},Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=function(){return(Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=a.asm.wa).apply(null,arguments)},Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return(Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=a.asm.xa).apply(null,arguments)},Zb=a._emscripten_bind_DracoUInt32Array_size_0=function(){return(Zb=a._emscripten_bind_DracoUInt32Array_size_0=a.asm.ya).apply(null,arguments)},$b=a._emscripten_bind_DracoUInt32Array___destroy___0= 61 | function(){return($b=a._emscripten_bind_DracoUInt32Array___destroy___0=a.asm.za).apply(null,arguments)},Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=function(){return(Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=a.asm.Aa).apply(null,arguments)},ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=function(){return(ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=a.asm.Ba).apply(null,arguments)},bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return(bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2= 62 | a.asm.Ca).apply(null,arguments)},cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=function(){return(cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=a.asm.Da).apply(null,arguments)},dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return(dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=a.asm.Ea).apply(null,arguments)},ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return(ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=a.asm.Fa).apply(null, 63 | arguments)},fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return(fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=a.asm.Ga).apply(null,arguments)},gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return(gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=a.asm.Ha).apply(null,arguments)},hc=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return(hc=a._emscripten_bind_MetadataQuerier___destroy___0=a.asm.Ia).apply(null,arguments)},Qa=a._emscripten_bind_Decoder_Decoder_0= 64 | function(){return(Qa=a._emscripten_bind_Decoder_Decoder_0=a.asm.Ja).apply(null,arguments)},ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=function(){return(ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=a.asm.Ka).apply(null,arguments)},jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=function(){return(jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=a.asm.La).apply(null,arguments)},kc=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return(kc=a._emscripten_bind_Decoder_GetAttributeId_2= 65 | a.asm.Ma).apply(null,arguments)},lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return(lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=a.asm.Na).apply(null,arguments)},mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return(mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=a.asm.Oa).apply(null,arguments)},nc=a._emscripten_bind_Decoder_GetAttribute_2=function(){return(nc=a._emscripten_bind_Decoder_GetAttribute_2=a.asm.Pa).apply(null,arguments)}, 66 | oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return(oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=a.asm.Qa).apply(null,arguments)},pc=a._emscripten_bind_Decoder_GetMetadata_1=function(){return(pc=a._emscripten_bind_Decoder_GetMetadata_1=a.asm.Ra).apply(null,arguments)},qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return(qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=a.asm.Sa).apply(null,arguments)},rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3= 67 | function(){return(rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=a.asm.Ta).apply(null,arguments)},sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=function(){return(sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=a.asm.Ua).apply(null,arguments)},tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=function(){return(tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=a.asm.Va).apply(null,arguments)},uc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=function(){return(uc= 68 | a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=a.asm.Wa).apply(null,arguments)},vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return(vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=a.asm.Xa).apply(null,arguments)},wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=function(){return(wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=a.asm.Ya).apply(null,arguments)},xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return(xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3= 69 | a.asm.Za).apply(null,arguments)},yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=function(){return(yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=a.asm._a).apply(null,arguments)},zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return(zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=a.asm.$a).apply(null,arguments)},Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return(Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3= 70 | a.asm.ab).apply(null,arguments)},Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=function(){return(Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=a.asm.bb).apply(null,arguments)},Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return(Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=a.asm.cb).apply(null,arguments)},Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return(Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3= 71 | a.asm.db).apply(null,arguments)},Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=function(){return(Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=a.asm.eb).apply(null,arguments)},Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return(Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=a.asm.fb).apply(null,arguments)},Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=function(){return(Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1= 72 | a.asm.gb).apply(null,arguments)},Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=function(){return(Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=a.asm.hb).apply(null,arguments)},Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return(Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=a.asm.ib).apply(null,arguments)},Jc=a._emscripten_bind_Decoder___destroy___0=function(){return(Jc=a._emscripten_bind_Decoder___destroy___0=a.asm.jb).apply(null,arguments)},Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM= 73 | function(){return(Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=a.asm.kb).apply(null,arguments)},Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return(Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=a.asm.lb).apply(null,arguments)},Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return(Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM= 74 | a.asm.mb).apply(null,arguments)},Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=function(){return(Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=a.asm.nb).apply(null,arguments)},Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.ob).apply(null,arguments)},Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION= 75 | a.asm.pb).apply(null,arguments)},Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.qb).apply(null,arguments)},Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.rb).apply(null,arguments)},Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD= 76 | a.asm.sb).apply(null,arguments)},Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.tb).apply(null,arguments)},Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.ub).apply(null,arguments)},Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD= 77 | a.asm.vb).apply(null,arguments)},Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.wb).apply(null,arguments)},Xc=a._emscripten_enum_draco_DataType_DT_INVALID=function(){return(Xc=a._emscripten_enum_draco_DataType_DT_INVALID=a.asm.xb).apply(null,arguments)},Yc=a._emscripten_enum_draco_DataType_DT_INT8=function(){return(Yc=a._emscripten_enum_draco_DataType_DT_INT8=a.asm.yb).apply(null,arguments)},Zc= 78 | a._emscripten_enum_draco_DataType_DT_UINT8=function(){return(Zc=a._emscripten_enum_draco_DataType_DT_UINT8=a.asm.zb).apply(null,arguments)},$c=a._emscripten_enum_draco_DataType_DT_INT16=function(){return($c=a._emscripten_enum_draco_DataType_DT_INT16=a.asm.Ab).apply(null,arguments)},ad=a._emscripten_enum_draco_DataType_DT_UINT16=function(){return(ad=a._emscripten_enum_draco_DataType_DT_UINT16=a.asm.Bb).apply(null,arguments)},bd=a._emscripten_enum_draco_DataType_DT_INT32=function(){return(bd=a._emscripten_enum_draco_DataType_DT_INT32= 79 | a.asm.Cb).apply(null,arguments)},cd=a._emscripten_enum_draco_DataType_DT_UINT32=function(){return(cd=a._emscripten_enum_draco_DataType_DT_UINT32=a.asm.Db).apply(null,arguments)},dd=a._emscripten_enum_draco_DataType_DT_INT64=function(){return(dd=a._emscripten_enum_draco_DataType_DT_INT64=a.asm.Eb).apply(null,arguments)},ed=a._emscripten_enum_draco_DataType_DT_UINT64=function(){return(ed=a._emscripten_enum_draco_DataType_DT_UINT64=a.asm.Fb).apply(null,arguments)},fd=a._emscripten_enum_draco_DataType_DT_FLOAT32= 80 | function(){return(fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=a.asm.Gb).apply(null,arguments)},gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=function(){return(gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=a.asm.Hb).apply(null,arguments)},hd=a._emscripten_enum_draco_DataType_DT_BOOL=function(){return(hd=a._emscripten_enum_draco_DataType_DT_BOOL=a.asm.Ib).apply(null,arguments)},id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=function(){return(id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT= 81 | a.asm.Jb).apply(null,arguments)},jd=a._emscripten_enum_draco_StatusCode_OK=function(){return(jd=a._emscripten_enum_draco_StatusCode_OK=a.asm.Kb).apply(null,arguments)},kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=function(){return(kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=a.asm.Lb).apply(null,arguments)},ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return(ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=a.asm.Mb).apply(null,arguments)},md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER= 82 | function(){return(md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=a.asm.Nb).apply(null,arguments)},nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return(nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=a.asm.Ob).apply(null,arguments)},od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return(od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=a.asm.Pb).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Qb).apply(null,arguments)}; 83 | a._free=function(){return(a._free=a.asm.Rb).apply(null,arguments)};var ua=function(){return(ua=a.asm.Sb).apply(null,arguments)};a.___start_em_js=11660;a.___stop_em_js=11758;var la;ha=function b(){la||F();la||(ha=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0=r.size?(0>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var g=0;gb.byteLength)return a.INVALID_GEOMETRY_TYPE;switch(b[7]){case 0:return a.POINT_CLOUD;case 1:return a.TRIANGULAR_MESH;default:return a.INVALID_GEOMETRY_TYPE}};return n.ready}}();"object"===typeof exports&&"object"===typeof module?module.exports=DracoDecoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoDecoderModule}):"object"===typeof exports&&(exports.DracoDecoderModule=DracoDecoderModule); 117 | -------------------------------------------------------------------------------- /static/models/crystal_d_3_min.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prizemlenie/subsurface-refraction-shader/3a22842f254db329e65e874e78433e88335c63ff/static/models/crystal_d_3_min.glb -------------------------------------------------------------------------------- /static/models/crystal_d_6_min.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prizemlenie/subsurface-refraction-shader/3a22842f254db329e65e874e78433e88335c63ff/static/models/crystal_d_6_min.glb -------------------------------------------------------------------------------- /static/models/crystal_heart_min.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prizemlenie/subsurface-refraction-shader/3a22842f254db329e65e874e78433e88335c63ff/static/models/crystal_heart_min.glb -------------------------------------------------------------------------------- /static/textures/lava_02_emissive.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prizemlenie/subsurface-refraction-shader/3a22842f254db329e65e874e78433e88335c63ff/static/textures/lava_02_emissive.webp -------------------------------------------------------------------------------- /static/textures/marble_32-diffuse.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prizemlenie/subsurface-refraction-shader/3a22842f254db329e65e874e78433e88335c63ff/static/textures/marble_32-diffuse.webp -------------------------------------------------------------------------------- /static/textures/stonewall_18_basecolor_gs.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prizemlenie/subsurface-refraction-shader/3a22842f254db329e65e874e78433e88335c63ff/static/textures/stonewall_18_basecolor_gs.webp -------------------------------------------------------------------------------- /static/textures/stonewall_18_normal.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prizemlenie/subsurface-refraction-shader/3a22842f254db329e65e874e78433e88335c63ff/static/textures/stonewall_18_normal.webp -------------------------------------------------------------------------------- /static/textures/tree_11_diffuse_gs.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prizemlenie/subsurface-refraction-shader/3a22842f254db329e65e874e78433e88335c63ff/static/textures/tree_11_diffuse_gs.webp -------------------------------------------------------------------------------- /static/textures/tree_11_normal.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prizemlenie/subsurface-refraction-shader/3a22842f254db329e65e874e78433e88335c63ff/static/textures/tree_11_normal.webp -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import restart from 'vite-plugin-restart'; 2 | 3 | export default { 4 | root: 'src/', 5 | publicDir: '../static/', 6 | base: './', 7 | server: { 8 | host: true, 9 | open: true, 10 | }, 11 | build: { 12 | outDir: '../dist', 13 | emptyOutDir: true, 14 | sourcemap: true, 15 | }, 16 | plugins: [restart({ restart: ['../static/**'] })], 17 | }; 18 | --------------------------------------------------------------------------------