├── shaders ├── ShaderLib.hx ├── SignedDistances.hx ├── noise │ ├── README │ ├── cellular2x2.glsl │ ├── noise2D.glsl │ ├── cellular2D.glsl │ ├── cellular2x2x2.glsl │ ├── noise3D.glsl │ ├── classicnoise2D.glsl │ ├── noise3Dgrad.glsl │ ├── noise4D.glsl │ ├── noise4Dgrad.glsl │ ├── classicnoise3D.glsl │ └── cellular3D.glsl ├── Fxaa.hx ├── fxaa-vert.glsl ├── Copy.hx ├── BloomBlend.hx ├── OutputMap.hx ├── hash.glsl ├── fxaa-frag.glsl └── Blur1D.hx ├── haxelib.json ├── rendering ├── RenderLayer.hx ├── WebGLRenderTarget.hx ├── Texture.hx ├── FragmentShader.hx ├── UVRenderer.hx ├── FragmentRenderer.hx ├── DualRenderTarget.hx ├── CompLayer.hx ├── HeightMapRenderer.hx ├── ShaderMaterialParameters.hx ├── PosDeltaSampler.hx ├── RenderTargetStore.hx ├── FloatTexturePacker.hx └── WorldPositionRenderer.hx ├── objects ├── UnitPlaneGeometry.hx ├── UVTriangle.hx ├── ClipSpaceTriangle.hx ├── XZPlaneBufferGeometry.hx ├── BackgroundEnvironment.hx └── GlassReflectiveFloor.hx ├── three ├── Layers.hx ├── Uniform.hx └── WebGLRenderTargetOptions.hx ├── math ├── Matrix.hx ├── ExponentialMovingAverage.hx ├── Random.hx ├── Quat.hx └── Scalar.hx ├── ds └── Watchable.hx ├── ui └── dat_gui │ ├── GUIParams.hx │ ├── GUIController.hx │ └── GUI.hx ├── README.md ├── fluid ├── vertex-shader.glsl └── shared.glsl ├── tool ├── LightingDebugProbes.hx ├── EventEmitter.hx ├── TextureTools.hx ├── CameraTools.hx ├── StringTools.hx ├── EventTools.hx ├── CpuTextureSampler.hx ├── IBLGenerator.hx ├── ShaderDev.hx ├── WebGLPerformanceMonitor.hx └── Object3DTools.hx ├── event ├── PointerEvent.hx ├── KeyboardEvent.hx ├── WheelEvent.hx └── PointerState.hx ├── noise ├── CurlNoise.hx └── Snoise.hx ├── Partial.hx ├── animation ├── Tween.hx ├── Easing.hx ├── Spring.hx └── Animator.hx ├── CompileTime.hx ├── environment └── EnvironmentManager.hx ├── control └── ArcBallControl.hx └── material └── CustomPhysicalMaterial.hx /shaders/ShaderLib.hx: -------------------------------------------------------------------------------- 1 | package shaders; 2 | 3 | import tool.CompileTime; 4 | 5 | final hash = CompileTime.embedShader('hash.glsl'); -------------------------------------------------------------------------------- /haxelib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-toolkit", 3 | "url": "https://github.com/haxiomic/three-toolkit", 4 | "dependencies": { 5 | "vector-math": "" 6 | } 7 | } -------------------------------------------------------------------------------- /rendering/RenderLayer.hx: -------------------------------------------------------------------------------- 1 | package rendering; 2 | 3 | enum abstract RenderLayer(Int) { 4 | var Main = 0; 5 | var Blended; 6 | var DepthPrepass; 7 | var WorldPosition; 8 | var UVPosition; 9 | @:to inline function toFloat(): Float return this; 10 | } -------------------------------------------------------------------------------- /objects/UnitPlaneGeometry.hx: -------------------------------------------------------------------------------- 1 | package objects; 2 | 3 | import three.PlaneGeometry; 4 | 5 | class UnitPlaneGeometry extends three.PlaneGeometry { 6 | 7 | public function new() { 8 | super(1, 1, 1, 1); 9 | } 10 | 11 | static public final instance = new UnitPlaneGeometry(); 12 | 13 | } -------------------------------------------------------------------------------- /rendering/WebGLRenderTarget.hx: -------------------------------------------------------------------------------- 1 | package rendering; 2 | 3 | /** 4 | Wraps three.WebGLRenderTarget but ensures its inner texture always has width and height defined 5 | **/ 6 | @:forward 7 | @:forward.new 8 | abstract WebGLRenderTarget(three.WebGLRenderTarget) from three.WebGLRenderTarget to three.WebGLRenderTarget { 9 | 10 | public var texture(get, never): rendering.Texture; 11 | inline function get_texture() return cast this.texture; 12 | 13 | } -------------------------------------------------------------------------------- /rendering/Texture.hx: -------------------------------------------------------------------------------- 1 | package rendering; 2 | 3 | /** 4 | Represents a three.Texture with a width and height field defined 5 | **/ 6 | @:forward 7 | @:forward.new 8 | @:transitive 9 | abstract Texture(three.Texture) to three.Texture { 10 | 11 | public var width(get, never): Float; 12 | public var height(get, never): Float; 13 | inline function get_width() return untyped this.image.width; 14 | inline function get_height() return untyped this.image.height; 15 | 16 | } -------------------------------------------------------------------------------- /three/Layers.hx: -------------------------------------------------------------------------------- 1 | package three; 2 | 3 | import rendering.RenderLayer; 4 | 5 | @:jsRequire("three", "Layers") extern class Layers { 6 | function new(); 7 | var mask : Int; 8 | function set(channel:RenderLayer):Void; 9 | function enable(channel:RenderLayer):Void; 10 | function enableAll():Void; 11 | function toggle(channel:RenderLayer):Void; 12 | function disable(channel:RenderLayer):Void; 13 | function disableAll():Void; 14 | function test(layers:Layers):Bool; 15 | static var prototype : Layers; 16 | } -------------------------------------------------------------------------------- /shaders/SignedDistances.hx: -------------------------------------------------------------------------------- 1 | package shaders; 2 | 3 | class SignedDistances { 4 | 5 | public static final sdSegment = ' 6 | // generalizes to n dimensions 7 | float sdSegment(vec2 x, vec2 a, vec2 b, out float alpha) { 8 | vec2 ab = b - a; 9 | vec2 ax = x - a; 10 | alpha = dot(ab, ax) / dot(ab, ab); 11 | vec2 d = ax - clamp(alpha, 0., 1.) * ab; 12 | return length(d); 13 | } 14 | 15 | float sdSegment(vec2 x, vec2 a, vec2 b) { 16 | float alpha; 17 | return sdSegment(x, a, b, alpha); 18 | } 19 | '; 20 | 21 | } -------------------------------------------------------------------------------- /three/Uniform.hx: -------------------------------------------------------------------------------- 1 | package three; 2 | 3 | /** 4 | Overrides Uniform to give it a type parameter, which was removed from @types/three at one point 5 | **/ 6 | @:jsRequire("three", "Uniform") extern class Uniform { 7 | @:overload(function(type:String, value:T): Uniform { }) 8 | function new(value:T); 9 | var type : String; 10 | var value : T; 11 | @:native("dynamic") 12 | var dynamic_ : Bool; 13 | dynamic function onUpdateCallback():Void; 14 | function onUpdate(callback:haxe.Constraints.Function):Uniform; 15 | static var prototype : Uniform; 16 | } -------------------------------------------------------------------------------- /math/Matrix.hx: -------------------------------------------------------------------------------- 1 | package math; 2 | 3 | import three.Matrix4; 4 | import three.Plane; 5 | 6 | function reflectionMatrixFromPlane(plane: Plane, ?target: Matrix4): Matrix4 { 7 | var reflectionMatrix = target != null ? target : new Matrix4(); 8 | var a = plane.normal.x; 9 | var b = plane.normal.y; 10 | var c = plane.normal.z; 11 | var d = plane.constant; 12 | reflectionMatrix.set( 13 | 1-2*a*a, -2*a*b, -2*a*c, -2*a*d, 14 | -2*a*b, 1-2*b*b, -2*b*c, -2*b*d, 15 | -2*a*c, -2*b*c, 1-2*c*c, -2*c*d, 16 | 0, 0, 0, 1 17 | ); 18 | return reflectionMatrix; 19 | } -------------------------------------------------------------------------------- /ds/Watchable.hx: -------------------------------------------------------------------------------- 1 | package ds; 2 | 3 | class Watchable { 4 | 5 | @:isVar public var value(get, set): T; 6 | final callbacks = new Array<(v:T) -> Void>(); 7 | 8 | public inline function new(v: T) { 9 | this.value = v; 10 | } 11 | 12 | public inline function watch(cb: (v: T) -> Void) { 13 | callbacks.push(cb); 14 | cb(value); 15 | return { unwatch: () -> unwatch(cb) }; 16 | } 17 | 18 | public inline function unwatch(cb: (v: T) -> Void) { 19 | return callbacks.remove(cb); 20 | } 21 | 22 | inline function set_value(v: T) { 23 | this.value = v; 24 | for (cb in callbacks) cb(v); 25 | return v; 26 | } 27 | 28 | inline function get_value() { 29 | return this.value; 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /shaders/noise/README: -------------------------------------------------------------------------------- 1 | These files contain noise functions that are compatible with all 2 | current versions of GLSL (1.20 and up), and all you need to use them 3 | is provided in the source file. There is no external data, and no 4 | setup procedure. Just cut and paste and call the function. 5 | 6 | GLSL has a very rudimentary linker, so some helper functions are 7 | included in several of the files with the same name. If you want to 8 | use more than one of these functions in the same shader, you may run 9 | into problems with redefinition of the functions mod289() and permute(). 10 | If that happens, just delete any superfluous definitions. 11 | 12 | 13 | ----- 14 | 15 | @haxiomic 16 | Added noise4Dgrad.glsl from @Markyparky56 -------------------------------------------------------------------------------- /objects/UVTriangle.hx: -------------------------------------------------------------------------------- 1 | package objects; 2 | 3 | import three.Material; 4 | import three.BufferAttribute; 5 | import js.lib.Float32Array; 6 | import three.BufferGeometry; 7 | import three.Mesh; 8 | 9 | class UVTriangle extends Mesh { 10 | 11 | public function new(material: Null) { 12 | super(globalGeom, material); 13 | this.frustumCulled = false; 14 | this.castShadow = false; 15 | this.receiveShadow = false; 16 | } 17 | 18 | static final globalGeom: BufferGeometry = { 19 | var buffer = new BufferGeometry(); 20 | var triangle = new Float32Array([ 21 | 0, 0, 22 | 2, 0, 23 | 0, 2, 24 | ]); 25 | buffer.setAttribute('position', new BufferAttribute(triangle, 2)); 26 | 27 | buffer; 28 | }; 29 | 30 | } -------------------------------------------------------------------------------- /three/WebGLRenderTargetOptions.hx: -------------------------------------------------------------------------------- 1 | package three; 2 | 3 | import three.PixelFormat; 4 | 5 | /** 6 | Redefines WebGLRenderTargetOptions to correct type of format (PixelFormat) 7 | **/ 8 | typedef WebGLRenderTargetOptions = { 9 | @:optional 10 | var wrapS : Wrapping; 11 | @:optional 12 | var wrapT : Wrapping; 13 | @:optional 14 | var magFilter : TextureFilter; 15 | @:optional 16 | var minFilter : TextureFilter; 17 | @:optional 18 | var format : PixelFormat; 19 | @:optional 20 | var type : TextureDataType; 21 | @:optional 22 | var anisotropy : Float; 23 | @:optional 24 | var depthBuffer : Bool; 25 | @:optional 26 | var stencilBuffer : Bool; 27 | @:optional 28 | var generateMipmaps : Bool; 29 | @:optional 30 | var depthTexture : DepthTexture; 31 | @:optional 32 | var encoding : TextureEncoding; 33 | }; -------------------------------------------------------------------------------- /math/ExponentialMovingAverage.hx: -------------------------------------------------------------------------------- 1 | package math; 2 | 3 | class ExponentialMovingAverage { 4 | 5 | // decay parameter 6 | public var alpha: Float; 7 | 8 | public var average (default, null): Float; 9 | public var variance (default, null): Float; 10 | public var stdDev (default, null): Float; 11 | 12 | /** 13 | Using an alpha value closer to 1 means rapid exponential decay (fast transition to new values) 14 | **/ 15 | public function new(initialValue: Float, alpha: Float) { 16 | this.alpha = alpha; 17 | this.average = initialValue; 18 | this.variance = 0.; 19 | this.stdDev = 0.; 20 | addSample(initialValue); 21 | } 22 | 23 | public function addSample(v: Float) { 24 | var q = v - average; 25 | average = average + alpha * q; 26 | variance = (1.0 - alpha) * (variance + alpha * q * q); 27 | stdDev = Math.sqrt(variance); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /objects/ClipSpaceTriangle.hx: -------------------------------------------------------------------------------- 1 | package objects; 2 | 3 | import three.Material; 4 | import three.BufferAttribute; 5 | import js.lib.Float32Array; 6 | import three.BufferGeometry; 7 | import three.Mesh; 8 | 9 | class ClipSpaceTriangle extends Mesh { 10 | 11 | public function new(material: Null) { 12 | super(globalGeom, material); 13 | this.frustumCulled = false; 14 | this.castShadow = false; 15 | this.receiveShadow = false; 16 | } 17 | 18 | static final globalGeom: BufferGeometry = { 19 | var buffer = new BufferGeometry(); 20 | var triangle = new Float32Array([ 21 | -1, -1, 22 | 3, -1, 23 | -1, 3, 24 | ]); 25 | var uv = new Float32Array(triangle.map(v -> v * 0.5 + 0.5)); 26 | buffer.setAttribute('position', new BufferAttribute(triangle, 2)); 27 | buffer.setAttribute('uv', new BufferAttribute(uv, 2)); 28 | 29 | buffer; 30 | }; 31 | 32 | } -------------------------------------------------------------------------------- /math/Random.hx: -------------------------------------------------------------------------------- 1 | package math; 2 | 3 | import Math.random; 4 | import Math.PI; 5 | import VectorMath; 6 | 7 | class Random { 8 | 9 | static public inline function randomPointOnSphere(): Vec3 { 10 | var u = Math.random(); 11 | var v = Math.random(); 12 | var s = Math.random(); 13 | var theta = 2 * Math.PI * u; 14 | var phi = acos(2 * v - 1); 15 | return vec3( 16 | cos(theta) * sin(phi), 17 | sin(theta) * sin(phi), 18 | cos(phi) 19 | ); 20 | } 21 | 22 | static public inline function randomGaussian() { 23 | return sqrt(-2 * log(random())) * cos(2 * PI * random()); 24 | } 25 | 26 | static public inline function randomGaussian2D() { 27 | // https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform 28 | var u1 = random(); 29 | var u2 = random(); 30 | return { 31 | x: sqrt(-2 * log(u1)) * cos(2 * PI * u2), 32 | y: sqrt(-2 * log(u2)) * cos(2 * PI * u1) 33 | } 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /ui/dat_gui/GUIParams.hx: -------------------------------------------------------------------------------- 1 | package ui.dat_gui; 2 | 3 | typedef GUIParams = { 4 | /** 5 | Handles GUI's element placement for you. 6 | **/ 7 | @:optional 8 | var autoPlace : Bool; 9 | /** 10 | If true, starts closed. 11 | **/ 12 | @:optional 13 | var closed : Bool; 14 | /** 15 | If true, close/open button shows on top of the GUI. 16 | **/ 17 | @:optional 18 | var closeOnTop : Bool; 19 | /** 20 | If true, GUI is closed by the "h" keypress. 21 | **/ 22 | @:optional 23 | var hideable : Bool; 24 | /** 25 | JSON object representing the saved state of this GUI. 26 | **/ 27 | @:optional 28 | var load : Dynamic; 29 | /** 30 | The name of this GUI. 31 | **/ 32 | @:optional 33 | var name : String; 34 | /** 35 | The identifier for a set of saved values. 36 | **/ 37 | @:optional 38 | var preset : String; 39 | /** 40 | The width of GUI element. 41 | **/ 42 | @:optional 43 | var width : Float; 44 | }; -------------------------------------------------------------------------------- /shaders/Fxaa.hx: -------------------------------------------------------------------------------- 1 | package shaders; 2 | 3 | import three.Vector2; 4 | import js.html.HTMLAllCollection; 5 | import three.Texture; 6 | import three.Uniform; 7 | import three.RawShaderMaterial; 8 | 9 | class Fxaa extends RawShaderMaterial { 10 | 11 | static public inline function get(texture: Texture, width: Float, height: Float) { 12 | instance.uTexture.value = texture; 13 | instance.uTexelSize.value.set(1/width, 1/height); 14 | return instance; 15 | } 16 | static final instance = new Fxaa(); 17 | 18 | public final uTexture = new Uniform(null); 19 | public final uTexelSize = new Uniform(new Vector2(1,1)); 20 | 21 | public function new() { 22 | super({ 23 | uniforms: { 24 | textureSampler: uTexture, 25 | texelSize: uTexelSize, 26 | }, 27 | vertexShader: tool.CompileTime.embedShader('./fxaa-vert.glsl'), 28 | fragmentShader: tool.CompileTime.embedShader('./fxaa-frag.glsl'), 29 | side: DoubleSide 30 | }); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /ui/dat_gui/GUIController.hx: -------------------------------------------------------------------------------- 1 | package ui.dat_gui; 2 | 3 | @:jsRequire("dat.gui", "GUIController") extern class GUIController { 4 | function new(object:Dynamic, property:String); 5 | var domElement : js.html.Element; 6 | var object : Dynamic; 7 | var property : String; 8 | function options(option:Dynamic):GUIController; 9 | function name(name:String):GUIController; 10 | function listen():GUIController; 11 | function remove():GUIController; 12 | function onChange(fnc:ts.AnyOf2<() -> Void, (value:Dynamic) -> Void>):GUIController; 13 | function onFinishChange(fnc:ts.AnyOf2<() -> Void, (value:Dynamic) -> Void>):GUIController; 14 | function setValue(value:Dynamic):GUIController; 15 | function getValue():Dynamic; 16 | function updateDisplay():GUIController; 17 | function isModified():Bool; 18 | function min(n:Float):GUIController; 19 | function max(n:Float):GUIController; 20 | function step(n:Float):GUIController; 21 | function fire():GUIController; 22 | static var prototype : GUIController; 23 | } -------------------------------------------------------------------------------- /shaders/fxaa-vert.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | // Attributes 4 | attribute vec2 position; 5 | uniform vec2 texelSize; 6 | 7 | // Output 8 | varying vec2 vUV; 9 | varying vec2 sampleCoordS; 10 | varying vec2 sampleCoordE; 11 | varying vec2 sampleCoordN; 12 | varying vec2 sampleCoordW; 13 | varying vec2 sampleCoordNW; 14 | varying vec2 sampleCoordSE; 15 | varying vec2 sampleCoordNE; 16 | varying vec2 sampleCoordSW; 17 | 18 | const vec2 madd = vec2(0.5, 0.5); 19 | 20 | void main(void) { 21 | vUV = (position * madd + madd); 22 | 23 | sampleCoordS = vUV + vec2( 0.0, 1.0) * texelSize; 24 | sampleCoordE = vUV + vec2( 1.0, 0.0) * texelSize; 25 | sampleCoordN = vUV + vec2( 0.0,-1.0) * texelSize; 26 | sampleCoordW = vUV + vec2(-1.0, 0.0) * texelSize; 27 | 28 | sampleCoordNW = vUV + vec2(-1.0,-1.0) * texelSize; 29 | sampleCoordSE = vUV + vec2( 1.0, 1.0) * texelSize; 30 | sampleCoordNE = vUV + vec2( 1.0,-1.0) * texelSize; 31 | sampleCoordSW = vUV + vec2(-1.0, 1.0) * texelSize; 32 | 33 | gl_Position = vec4(position, 0.0, 1.0); 34 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Three.js Toolkit 2 | 3 | This is an assortment of classes I use when developing three.js haxe projects. Feel free to use them too, however, it's not designed to be a user-friendly library and code here is likely to change frequently. To use it, it's best to link to a specific commit to avoid troubles building in the future 4 | 5 | Happy to accept PRs and issues! 6 | 7 | ## Requirements 8 | - I use three.js r135 via dts2hx, other version may work 9 | - Newer versions of three.js require `-D js-es=6` (however three.js and earlier r121 does not) 10 | 11 | To generate the required three.js types: 12 | ``` 13 | npm install three@0.135.0 @types/three@0.135.0 dts2hx 14 | dts2hx three three/examples/jsm/loaders/RGBELoader three/examples/jsm/loaders/GLTFLoader three/examples/jsm/objects/Reflector -m 15 | ``` 16 | 17 | Some types rely on [VectorMath](https://github.com/haxiomic/vector-math), install with `haxelib install vector-math` 18 | 19 | See [haxiomic/haxe-threejs-template](https://github.com/haxiomic/haxe-threejs-template) for an example on usage 20 | -------------------------------------------------------------------------------- /fluid/vertex-shader.glsl: -------------------------------------------------------------------------------- 1 | // clip-space 2 | attribute vec2 position; 3 | 4 | uniform vec3 invResolution; // (1/w, 1/h, h/w) 5 | 6 | #ifdef UV 7 | varying vec2 vUv; 8 | #endif 9 | 10 | #ifdef FINITE_DIFFERENCE 11 | // precomute texel offsets as varyings to enable texture prefetching 12 | varying vec2 vL; 13 | varying vec2 vR; 14 | varying vec2 vB; 15 | varying vec2 vT; 16 | #endif 17 | 18 | #ifdef SIMULATION_POSITION 19 | // clip-space where aspect ratio is maintained and height is fixed at 1 20 | varying vec2 p; 21 | #endif 22 | 23 | void main() { 24 | vec2 texelCoord = position * 0.5 + 0.5; 25 | 26 | #ifdef FINITE_DIFFERENCE 27 | vL = texelCoord - vec2(invResolution.x,0); 28 | vR = texelCoord + vec2(invResolution.x,0); 29 | vB = texelCoord - vec2(0,invResolution.y); 30 | vT = texelCoord + vec2(0,invResolution.y); 31 | #endif 32 | 33 | #ifdef UV 34 | vUv = texelCoord; 35 | #endif 36 | 37 | #ifdef SIMULATION_POSITION 38 | p = vec2(position.x / invResolution.z, position.y); 39 | #endif 40 | 41 | gl_Position = vec4(position, 0.0, 1.0 ); 42 | } 43 | -------------------------------------------------------------------------------- /shaders/Copy.hx: -------------------------------------------------------------------------------- 1 | package shaders; 2 | 3 | import three.Texture; 4 | import three.Uniform; 5 | import three.RawShaderMaterial; 6 | 7 | class Copy extends RawShaderMaterial { 8 | 9 | static public inline function get(texture: Texture) { 10 | instance.uTexture.value = texture; 11 | return instance; 12 | } 13 | static final instance = new Copy(); 14 | 15 | public final uTexture: Uniform; 16 | 17 | public function new() { 18 | var uTexture = new Uniform(cast null); 19 | super({ 20 | uniforms: { 21 | uTexture: uTexture, 22 | }, 23 | vertexShader: ' 24 | attribute vec2 position; 25 | 26 | varying vec2 vUv; 27 | 28 | void main() { 29 | vUv = position * 0.5 + 0.5; 30 | gl_Position = vec4(position, 0., 1.); 31 | } 32 | ', 33 | fragmentShader: ' 34 | precision highp float; 35 | 36 | uniform sampler2D uTexture; 37 | 38 | varying vec2 vUv; 39 | 40 | void main() { 41 | gl_FragColor = texture2D(uTexture, vUv); 42 | } 43 | ', 44 | side: DoubleSide 45 | }); 46 | this.uTexture = uTexture; 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /rendering/FragmentShader.hx: -------------------------------------------------------------------------------- 1 | package rendering; 2 | 3 | import three.RawShaderMaterial; 4 | 5 | /** 6 | Base for fragment renderer shaders 7 | **/ 8 | class FragmentShader extends RawShaderMaterial { 9 | 10 | public final typedUniforms: T; 11 | 12 | public function new(?parameters: ShaderMaterialParameters, ?fragmentShader: String) { 13 | var baseParameters = { 14 | uniforms: { }, 15 | vertexShader: ' 16 | attribute vec2 position; 17 | varying vec2 vUv; 18 | void main() { 19 | vUv = position * 0.5 + 0.5; 20 | gl_Position = vec4(position, 0., 1.); 21 | } 22 | ', 23 | fragmentShader: ' 24 | precision highp float; 25 | varying vec2 vUv; 26 | 27 | void main() { 28 | gl_FragColor = vec4(vUv, 0.0, 1.); 29 | } 30 | ', 31 | depthWrite: false, 32 | depthTest: false, 33 | } 34 | if (parameters != null) { 35 | baseParameters = Structure.extendAny(baseParameters, parameters); 36 | } 37 | if (fragmentShader != null) { 38 | baseParameters.fragmentShader = fragmentShader; 39 | } 40 | 41 | super(baseParameters); 42 | 43 | this.typedUniforms = cast this.uniforms; 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /fluid/shared.glsl: -------------------------------------------------------------------------------- 1 | vec2 clipToSimSpace(vec2 clipSpace){ 2 | return vec2(clipSpace.x / invResolution.z, clipSpace.y); 3 | } 4 | 5 | vec2 simToTexelSpace(vec2 simSpace){ 6 | return vec2(simSpace.x * invResolution.z + 1.0 , simSpace.y + 1.0)*.5; 7 | } 8 | 9 | // pure Neumann boundary conditions: 0 pressure gradient across the boundary 10 | // dP/dx = 0 11 | // this is implict applied with CLAMP_TO_EDGE when reading from the pressure texture so we don't actually need to to anything in the shader 12 | // #define PRESSURE_BOUNDARY 13 | 14 | // free-slip boundary: the average flow across the boundary is restricted to 0 15 | // avg(uA.xy, uB.xy) dot (boundary normal).xy = 0 16 | // this is applied by reflecting the velocity across the boundary (i.e, multipling the boundary velocity by -1 when reading outside) 17 | 18 | // must not make any changes to coord after it arrives from vertex shader (including no swizzle) to enable inter-stage texture prefetching 19 | #define samplePressure(texture, coord) ( texture2D(pressure, coord).x ) 20 | #define outOfBoundsVelocityMultiplier(coord) (velocityBoundaryEnabled ? (step(vec2(0.), coord) * step(coord, vec2(1.)) * 2. - 1. ) : vec2(1.0)) 21 | 22 | #define sampleVelocity(texture, coord) ( outOfBoundsVelocityMultiplier(coord) * texture2D(velocity, coord).xy ) -------------------------------------------------------------------------------- /rendering/UVRenderer.hx: -------------------------------------------------------------------------------- 1 | package rendering; 2 | 3 | import three.TextureDataType; 4 | import three.Side; 5 | import three.WebGLRenderer; 6 | 7 | class UVRenderer extends WorldPositionRenderer { 8 | 9 | public function new( 10 | renderer: WebGLRenderer, 11 | width: Int, 12 | height: Int, 13 | renderLayer: RenderLayer = UVPosition, 14 | depthPrepassLayer: RenderLayer = DepthPrepass, 15 | side: Side = DoubleSide, 16 | type: TextureDataType = HalfFloatType 17 | ) { 18 | super( 19 | renderer, 20 | width, 21 | height, 22 | renderLayer, 23 | depthPrepassLayer, 24 | side, 25 | type 26 | ); 27 | this.shaderMaterial.vertexShader = ' 28 | varying vec3 vUv; 29 | 30 | void main() { 31 | vUv = vec3(uv, 0.); 32 | 33 | // use the red channel of the vertex color for uv.z 34 | // this can be used to distinguish vertex islands 35 | #ifdef USE_COLOR 36 | vUv.z = color.r; 37 | #endif 38 | 39 | vec4 p = vec4(position, 1.0); 40 | vec4 worldP = modelMatrix * p; 41 | 42 | gl_Position = projectionMatrix * viewMatrix * worldP; 43 | } 44 | '; 45 | this.shaderMaterial.fragmentShader = ' 46 | varying vec3 vUv; 47 | 48 | void main() { 49 | gl_FragColor = vec4(vUv, 1.0); 50 | } 51 | '; 52 | this.shaderMaterial.vertexColors = true; 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /tool/LightingDebugProbes.hx: -------------------------------------------------------------------------------- 1 | package tool; 2 | 3 | import three.Color; 4 | import three.Mesh; 5 | import three.MeshStandardMaterial; 6 | import three.Object3D; 7 | import three.SphereGeometry; 8 | 9 | #if (three >= "0.133.0") 10 | private typedef Object3D = three.Object3D; 11 | #end 12 | 13 | class LightingDebugProbes extends Object3D { 14 | 15 | public function new() { 16 | super(); 17 | var probeGeom = new SphereGeometry(1, 20, 20); 18 | var nProbes = 4; 19 | var width = 2; 20 | for ( i in 0...nProbes) { 21 | for (j in 0...nProbes) { 22 | for (k in 0...nProbes) { 23 | var u = nProbes == 1 ? 0 : i / (nProbes - 1); 24 | var v = nProbes == 1 ? 0 : j / (nProbes - 1); 25 | var w = nProbes == 1 ? 0 : k / (nProbes - 1); 26 | var probeMat = new MeshStandardMaterial({ 27 | color: new Color(Math.pow(u, 2.2), Math.pow(u, 2.2), Math.pow(u, 2.2)), 28 | metalness: w, 29 | roughness: v, 30 | fog: true, 31 | blending: NoBlending, 32 | }); 33 | var probe = new Mesh(probeGeom, probeMat); 34 | probe.layers.enable(DepthPrepass); 35 | probe.scale.setScalar(0.1); 36 | probe.position.x = u * width - width * 0.5; 37 | probe.position.y = v * width - width * 0.5; 38 | probe.position.z = w * width - width * 0.5; 39 | this.add(probe); 40 | } 41 | } 42 | } 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /tool/EventEmitter.hx: -------------------------------------------------------------------------------- 1 | package tool; 2 | 3 | private typedef Listener = { 4 | var callback: T -> Void; 5 | var once: Bool; 6 | } 7 | 8 | class EventEmitter { 9 | 10 | final listeners = new Map>>(); 11 | 12 | public function dispatchEvent(name: String, data: Dynamic = null) { 13 | var array = listeners.get(name); 14 | if (array != null) { 15 | var i = array.length - 1; 16 | while (i >= 0) { 17 | var listener = array[i]; 18 | var callback = listener.callback; 19 | callback(data); 20 | if (listener.once) { 21 | array.splice(i, 1); 22 | } 23 | i--; 24 | } 25 | } 26 | } 27 | 28 | public function addEventListener(name: String, callback: (data: Null) -> Void, once: Bool = false) { 29 | var array = listeners.get(name); 30 | if (array == null) { 31 | array = []; 32 | listeners.set(name, array); 33 | } 34 | array.push({ 35 | callback: callback, 36 | once: once, 37 | }); 38 | } 39 | 40 | public function removeEventListener(name: String, callback: (data: Null) -> Void) { 41 | var array = listeners.get(name); 42 | if (array != null) { 43 | var i = array.length - 1; 44 | while (i >= 0) { 45 | if (array[i].callback == callback) { 46 | array.splice(i, 1); 47 | } 48 | i--; 49 | } 50 | } 51 | if (array.length == 0) { 52 | listeners.remove(name); 53 | } 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /tool/TextureTools.hx: -------------------------------------------------------------------------------- 1 | package tool; 2 | 3 | import js.lib.Promise; 4 | import three.Texture; 5 | import three.TextureEncoding; 6 | import three.TextureFilter; 7 | import three.TextureLoader; 8 | 9 | typedef TextureOptions = { 10 | ?generateMipmaps: Bool, 11 | ?encoding: TextureEncoding, 12 | ?magFilter: TextureFilter, 13 | ?minFilter: TextureFilter, 14 | } 15 | 16 | class TextureTools { 17 | 18 | static public function textureFromUrl(path: String, ?options: TextureOptions) { 19 | if (options == null) { 20 | options = {}; 21 | } 22 | return new Promise((resolve, reject) -> { 23 | new TextureLoader().load(path, (t) -> { 24 | if (options.generateMipmaps != null) { 25 | t.generateMipmaps = options.generateMipmaps; 26 | } 27 | if (options.encoding != null) { 28 | t.encoding = options.encoding; 29 | } 30 | if (options.magFilter != null) { 31 | t.magFilter = options.magFilter; 32 | } 33 | if (options.minFilter != null) { 34 | t.minFilter = options.minFilter; 35 | } 36 | resolve(t); 37 | }); 38 | }); 39 | } 40 | 41 | static public function textureFromBase64(base64: String, mimeType: String, ?options: { 42 | ?generateMipmaps: Bool, 43 | ?encoding: TextureEncoding, 44 | ?magFilter: TextureFilter, 45 | ?minFilter: TextureFilter, 46 | }): Promise { 47 | var dataUrl = 'data:$mimeType;base64,$base64'; 48 | return textureFromUrl(dataUrl, options); 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /tool/CameraTools.hx: -------------------------------------------------------------------------------- 1 | package tool; 2 | 3 | import three.Vector3; 4 | import three.Box3; 5 | import three.Object3D; 6 | import three.OrthographicCamera; 7 | 8 | #if (three >= "0.133.0") 9 | private typedef Object3D = three.Object3D; 10 | #end 11 | 12 | class CameraTools { 13 | 14 | static public function fitOrthographicView(camera: OrthographicCamera, target: Object3D) { 15 | target.updateMatrixWorld(); 16 | var bb = new Box3().setFromObject(target); 17 | var points: Array = [ 18 | new Vector3(bb.min.x, bb.min.y, bb.min.z), // 000 19 | new Vector3(bb.min.x, bb.min.y, bb.max.z), // 001 20 | new Vector3(bb.min.x, bb.max.y, bb.min.z), // 010 21 | new Vector3(bb.min.x, bb.max.y, bb.max.z), // 011 22 | new Vector3(bb.max.x, bb.min.y, bb.min.z), // 100 23 | new Vector3(bb.max.x, bb.min.y, bb.max.z), // 101 24 | new Vector3(bb.max.x, bb.max.y, bb.min.z), // 110 25 | new Vector3(bb.max.x, bb.max.y, bb.max.z), // 111 26 | ]; 27 | 28 | camera.updateMatrixWorld(); 29 | 30 | var min = new Vector3(Math.POSITIVE_INFINITY, Math.POSITIVE_INFINITY, Math.POSITIVE_INFINITY); 31 | var max = new Vector3(Math.NEGATIVE_INFINITY, Math.NEGATIVE_INFINITY, Math.NEGATIVE_INFINITY); 32 | for (p in points) { 33 | camera.worldToLocal(p); 34 | p.z *= -1; 35 | min.min(p); 36 | max.max(p); 37 | } 38 | 39 | camera.left = min.x; 40 | camera.right = max.x; 41 | camera.top = max.y; 42 | camera.bottom = min.y; 43 | camera.far = max.z; 44 | camera.near = min.z; 45 | camera.updateProjectionMatrix(); 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /tool/StringTools.hx: -------------------------------------------------------------------------------- 1 | package tool; 2 | 3 | class StringTools { 4 | 5 | /** 6 | Removes matching quotes either side of a string 7 | i.e. "hello" -> hello 8 | and "hello' -> "hello' (no change because quotes are unmatched) 9 | **/ 10 | static public function unwrapQuotes(str: String) { 11 | return switch str.charAt(0) { 12 | case q = '"', q = '\'', q = '`' if (str.charAt(str.length - 1) == q): 13 | str.substr(1, str.length - 2); 14 | default: str; 15 | } 16 | } 17 | 18 | /** 19 | Remove common indentation from lines in a string 20 | **/ 21 | static public function removeIndentation(str: String) { 22 | // find common indentation across all lines 23 | var lines = str.split('\n'); 24 | var commonTabsCount: Null = null; 25 | var commonSpaceCount: Null = null; 26 | var spacePrefixPattern = ~/^([ \t]*)[^\s]/; 27 | for (line in lines) { 28 | if (spacePrefixPattern.match(line)) { 29 | var space = spacePrefixPattern.matched(1); 30 | var tabsCount = 0; 31 | var spaceCount = 0; 32 | for (i in 0...space.length) { 33 | if (space.charAt(i) == '\t') tabsCount++; 34 | if (space.charAt(i) == ' ') spaceCount++; 35 | } 36 | commonTabsCount = commonTabsCount != null ? Std.int(Math.min(commonTabsCount, tabsCount)) : tabsCount; 37 | commonSpaceCount = commonSpaceCount != null ? Std.int(Math.min(commonSpaceCount, spaceCount)) : spaceCount; 38 | } 39 | } 40 | 41 | var spaceCharCount: Int = commonTabsCount + commonSpaceCount; 42 | 43 | // remove commonSpacePrefix from lines 44 | return spaceCharCount > 0 ? lines.map( 45 | line -> spacePrefixPattern.match(line) ? line.substr(spaceCharCount) : line 46 | ).join('\n') : str; 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /event/PointerEvent.hx: -------------------------------------------------------------------------------- 1 | package event; 2 | 3 | /** 4 | * Bit offsets for buttons state bit mask 5 | * See https://www.w3.org/TR/pointerevents/#the-buttons-property 6 | */ 7 | enum abstract ButtonChange(Int) to Int from Int { 8 | 9 | final Nothing = -1; 10 | 11 | /** 12 | * left mouse / touch contact 13 | */ 14 | final Primary = 0; 15 | 16 | final MiddleMouse = 1; 17 | 18 | /** 19 | * right mouse / pen-barrel 20 | */ 21 | final Secondary = 2; 22 | 23 | /** 24 | * X1 / back mouse 25 | */ 26 | final Back = 3; 27 | 28 | /** 29 | * X2 / forward mouse 30 | */ 31 | final Forward = 4; 32 | 33 | final PenEraser = 5; 34 | 35 | } 36 | 37 | /** 38 | See https://www.w3.org/TR/pointerevents 39 | **/ 40 | @:publicFields 41 | @:structInit 42 | @:unreflective 43 | #if cpp @:keep #end 44 | class PointerEvent extends PointerState { 45 | 46 | /** 47 | Indicates button who's state-change caused the event 48 | - `-1` - no buttons changed since the last event 49 | - `0` - left mouse button or touch/pen contact 50 | - `1` - middle mouse button 51 | - `2` - right mouse button or pen barrel button 52 | - `3` - mouse back button 53 | - `4` - mouse forward button 54 | - `5` - pen eraser button 55 | 56 | See https://www.w3.org/TR/pointerevents/#the-button-property 57 | **/ 58 | final button: ButtonChange; 59 | 60 | final preventDefault: () -> Void; 61 | final defaultPrevented: () -> Bool; 62 | 63 | /** 64 | * milliseconds 65 | */ 66 | final timeStamp: Float; 67 | 68 | /** 69 | Reference to original native event object – type varies between platform 70 | Could be PointerEvent, MouseEvent or TouchEvent on js 71 | **/ 72 | final nativeEvent: #if js js.html.Event #else Dynamic #end; 73 | 74 | } -------------------------------------------------------------------------------- /tool/EventTools.hx: -------------------------------------------------------------------------------- 1 | package tool; 2 | 3 | import js.html.WheelEvent; 4 | 5 | /** 6 | Tries to isolate vertical or horizontal scrolling with a continuous mapping. 7 | Originally written for valis 8 | **/ 9 | function scrollDirectionDisambiguation(event: WheelEvent): {x: Float, y: Float} { 10 | // gesture disambiguation; when dominantly zooming we want to reduce panning speed 11 | // normalize scroll vector 12 | var scrollVectorLengthSq = event.deltaX * event.deltaX + event.deltaY * event.deltaY; 13 | // avoid divide by 0 normalization issues 14 | if (scrollVectorLengthSq <= 0) { 15 | scrollVectorLengthSq = 1; 16 | } 17 | var scrollVectorLength = Math.sqrt(scrollVectorLengthSq); 18 | var normScrollX = event.deltaX / scrollVectorLength; // cosAngleY 19 | var normScrollY = event.deltaY / scrollVectorLength; // cosAngleX 20 | // as normScrollVectorY approaches 1, we should scale event.deltaX to 21 | var absAngleY = Math.acos(Math.abs(normScrollY)); 22 | var fractionalAngleY = 2 * absAngleY / (Math.PI); // 0 = points along y, 1 = points along x 23 | var absAngleX = Math.acos(Math.abs(normScrollX)); 24 | var fractionalAngleX = 2 * absAngleX / (Math.PI); // 0 = points along x, 1 = points along y 25 | 26 | // use fraction angle to reduce x as angle approaches y-pointing 27 | // see https://www.desmos.com/calculator/butkwn0xdt for function exploration 28 | var edge = 0.75; 29 | var xReductionFactor = Math.sin( 30 | Math.pow(Math.min(fractionalAngleY / edge, 1), 3) * (Math.PI / 2) 31 | ); 32 | var yReductionFactor = Math.sin( 33 | Math.pow(Math.min(fractionalAngleX / edge, 1), 3) * (Math.PI / 2) 34 | ); 35 | 36 | return { 37 | x: event.deltaX * xReductionFactor, 38 | y: event.deltaY * yReductionFactor, 39 | } 40 | } -------------------------------------------------------------------------------- /rendering/FragmentRenderer.hx: -------------------------------------------------------------------------------- 1 | package rendering; 2 | 3 | import three.Material; 4 | import three.RawShaderMaterial; 5 | import objects.ClipSpaceTriangle; 6 | import three.OrthographicCamera; 7 | import three.Scene; 8 | import three.ShaderMaterial; 9 | import three.Vector4; 10 | import three.WebGLRenderTarget; 11 | import three.WebGLRenderer; 12 | 13 | @:nullSafety 14 | class FragmentRenderer { 15 | 16 | final renderer: WebGLRenderer; 17 | static final rttScene = new Scene(); 18 | static final rttCamera = new OrthographicCamera(-1, 1, 1, -1, 0, 1); 19 | static final rttMesh = { 20 | var mesh = new ClipSpaceTriangle(null); 21 | rttScene.add(mesh); 22 | mesh; 23 | }; 24 | 25 | public function new(renderer: WebGLRenderer) { 26 | this.renderer = renderer; 27 | } 28 | 29 | var _oldViewport = new Vector4(); 30 | public function render( 31 | target: Null, 32 | material: Material, 33 | ?clearColor: three.ColorRepresentation, 34 | ?viewport: Vector4 35 | ) { 36 | renderer.setRenderTarget(target); 37 | 38 | var restoreViewport = false; 39 | if (viewport != null) { 40 | restoreViewport = true; 41 | renderer.getViewport(_oldViewport); 42 | renderer.setViewport(viewport.x, viewport.y, viewport.z, viewport.w); 43 | } 44 | 45 | rttMesh.material = material; 46 | rttMesh.visible = material != null; 47 | 48 | if (clearColor != null) { 49 | renderer.setClearColor(clearColor); 50 | // clearing the depth is important here, just in case the target has a depth buffer 51 | renderer.clear(true, true, true); 52 | } 53 | 54 | renderer.render(rttScene, rttCamera); 55 | 56 | if (restoreViewport) { 57 | renderer.setViewport(_oldViewport.x, _oldViewport.y, _oldViewport.z, _oldViewport.w); 58 | } 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /math/Quat.hx: -------------------------------------------------------------------------------- 1 | package math; 2 | 3 | /** 4 | Quaternion implementation extending VectorMath Vec4 5 | **/ 6 | @:forward 7 | abstract Quat(Vec4) to Vec4 from Vec4 { 8 | 9 | public inline function new(x: Float, y: Float, z: Float, w: Float){ 10 | this = new Vec4(x, y, z, w); 11 | } 12 | 13 | public inline function inverse():Quat{ 14 | return new Quat(-this.x, -this.y, -this.z, this.w); 15 | } 16 | 17 | /** 18 | Efficiently rotate a vector by this quaternion 19 | **/ 20 | public inline function transform(v: Vec3): Vec3 { 21 | // https://gamedev.stackexchange.com/a/50545 22 | var u = this.xyz; 23 | var s = this.w; 24 | return 25 | 2 * u.dot(v) * u 26 | + (s*s - u.dot(u)) * v 27 | + 2 * s * u.cross(v); 28 | } 29 | 30 | // assignment overload should come before other binary ops to ensure they have priority 31 | @:op(a *= b) 32 | static inline function mulEq(a: Quat, b: Quat): Quat 33 | return a.copyFrom(mul(a, b)); 34 | 35 | @:op(a * b) 36 | static inline function mul(lhs: Quat, rhs: Quat): Quat { 37 | return new Quat( 38 | lhs.x * rhs.w + lhs.y * rhs.z - lhs.z * rhs.y + lhs.w * rhs.x, 39 | -lhs.x * rhs.z + lhs.y * rhs.w + lhs.z * rhs.x + lhs.w * rhs.y, 40 | lhs.x * rhs.y - lhs.y * rhs.x + lhs.z * rhs.w + lhs.w * rhs.z, 41 | -lhs.x * rhs.x - lhs.y * rhs.y - lhs.z * rhs.z + lhs.w * rhs.w 42 | ); 43 | } 44 | 45 | static public inline function fromAxisAngle(axis:Vec3, angle:Float) { 46 | var sa = Math.sin(angle * 0.5); 47 | return new Quat( 48 | axis.x * sa, 49 | axis.y * sa, 50 | axis.z * sa, 51 | Math.cos(angle * 0.5) 52 | ); 53 | } 54 | 55 | /** 56 | * Quaternion representing no rotation 57 | */ 58 | static public inline function unitQuaternion(): Quat { 59 | return new Quat(0, 0, 0, 1); 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /event/KeyboardEvent.hx: -------------------------------------------------------------------------------- 1 | package event; 2 | 3 | /** 4 | See https://w3c.github.io/uievents/#idl-keyboardevent 5 | **/ 6 | @:publicFields 7 | @:structInit 8 | @:unreflective 9 | #if cpp @:keep #end 10 | class KeyboardEvent { 11 | 12 | /** 13 | Locale-aware key 14 | 15 | Either a 16 | - A key string that corresponds to the character typed (accounting for the user's current locale and mappings), e.g. `"a"` 17 | - A named key mapping to the values in the [specification](https://www.w3.org/TR/uievents-key/#named-key-attribute-value) e.g. `"ArrowDown"` 18 | 19 | Example use-cases include detecting keyboard shortcuts 20 | 21 | See https://www.w3.org/TR/uievents-key/#key-attribute-value 22 | **/ 23 | final key: String; 24 | 25 | /** 26 | A string that identifies the physical key being pressed, it differs from the `key` field in that it **doesn't** account for the user's current locale and mappings. 27 | The list of possible codes and their mappings to physical keys is given here https://www.w3.org/TR/uievents-code/. 28 | 29 | Example use-cases include detecting WASD keys for moving controls in a game 30 | 31 | See https://w3c.github.io/uievents/#keys-codevalues 32 | **/ 33 | final code: String; 34 | 35 | final location: KeyLocation; 36 | 37 | final altKey: Bool; 38 | final ctrlKey: Bool; 39 | final metaKey: Bool; 40 | final shiftKey: Bool; 41 | 42 | final preventDefault: () -> Void; 43 | final defaultPrevented: () -> Bool; 44 | 45 | /** 46 | Reference to original native event object – type varies between platform 47 | **/ 48 | final nativeEvent: #if js js.html.KeyboardEvent #else Dynamic #end; 49 | 50 | } 51 | 52 | /** 53 | See https://w3c.github.io/uievents/#dom-keyboardevent-dom_key_location_standard 54 | **/ 55 | enum abstract KeyLocation(Int) to Int from Int { 56 | var STANDARD = 0; 57 | var LEFT = 1; 58 | var RIGHT = 2; 59 | var NUMPAD = 3; 60 | } -------------------------------------------------------------------------------- /event/WheelEvent.hx: -------------------------------------------------------------------------------- 1 | package event; 2 | 3 | /** 4 | See https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent 5 | **/ 6 | @:publicFields 7 | @:structInit 8 | @:unreflective 9 | #if cpp @:keep #end 10 | class WheelEvent { 11 | 12 | /** 13 | The horizontal scroll amount in **points**, if scrolling a page this corresponds to the horizontal scroll distance that would be applied 14 | **/ 15 | var deltaX: Float; 16 | 17 | /** 18 | The vertical scroll amount in **points**, if scrolling a page this corresponds to the vertical scroll distance that would be applied 19 | **/ 20 | var deltaY: Float; 21 | 22 | /** 23 | Z-axis scroll amount (and 0 when unsupported) 24 | **/ 25 | var deltaZ: Float; 26 | 27 | /** 28 | Horizontal position in units of **points** where 0 corresponds to the left of the view 29 | **/ 30 | var x: Float; 31 | 32 | /** 33 | Vertical position in units of **points** where 0 corresponds to the top of the view 34 | **/ 35 | var y: Float; 36 | 37 | /** 38 | Width of view where the event occurred in **points** units 39 | **/ 40 | var viewWidth: Float; 41 | 42 | /** 43 | Height of view where the event occurred in **points** units 44 | **/ 45 | var viewHeight: Float; 46 | 47 | // The motivation for supplying special-key state with the wheel event (when it can also be obtained from keyboard events) is to catch state when the document is not focused 48 | // for example, you mouse wheel on a page without the document focused keyboard events will not fire but special-key state will still be available in this event 49 | 50 | var altKey: Bool; 51 | var ctrlKey: Bool; 52 | var metaKey: Bool; 53 | var shiftKey: Bool; 54 | 55 | final preventDefault: () -> Void; 56 | final defaultPrevented: () -> Bool; 57 | 58 | final timeStamp: Float; 59 | 60 | /** 61 | Reference to original native event object – type varies between platform 62 | **/ 63 | final nativeEvent: #if js js.html.WheelEvent #else Dynamic #end; 64 | 65 | } -------------------------------------------------------------------------------- /tool/CpuTextureSampler.hx: -------------------------------------------------------------------------------- 1 | package tool; 2 | 3 | import js.lib.Float32Array; 4 | 5 | enum abstract WrapMode(Int) { 6 | var REPEAT; 7 | var CLAMP; 8 | } 9 | 10 | class CpuTextureSampler { 11 | 12 | public var width: Int; 13 | public var height: Int; 14 | public var pixels: Float32Array; 15 | public var wrapS: WrapMode; 16 | public var wrapT: WrapMode; 17 | 18 | public function new(tightlyPackedPixels: Float32Array, width: Int, height: Int, wrapS = CLAMP, wrapT = CLAMP) { 19 | this.pixels = tightlyPackedPixels; 20 | this.width = width; 21 | this.height = height; 22 | this.wrapS = wrapS; 23 | this.wrapT = wrapT; 24 | } 25 | 26 | public function sampleLinear(uvX: Float, uvY: Float) { 27 | // repeat wrapping 28 | var x = uvX * width + 0.5; 29 | var y = uvY * height + 0.5; 30 | var i = Math.floor(x); 31 | var j = Math.floor(y); 32 | 33 | var fx = mod(x, 1.); 34 | var fy = mod(y, 1.); 35 | 36 | var tl = getPixel(i - 1, j); 37 | var tr = getPixel(i, j); 38 | var bl = getPixel(i - 1, j - 1); 39 | var br = getPixel(i, j - 1); 40 | 41 | var topRow = tl * (1.0 - fx) + tr * fx; 42 | var bottomRow = bl * (1.0 - fx) + br * fx; 43 | var bilerp = bottomRow * (1.0 - fy) + topRow * fy; 44 | 45 | return bilerp; 46 | } 47 | 48 | public function sampleNearest(uvX: Float, uvY: Float) { 49 | // repeat wrapping 50 | var i = Math.floor(uvX * width); 51 | var j = Math.floor(uvY * height); 52 | return getPixel(i, j); 53 | } 54 | 55 | public function getPixel(i: Int, j: Int) { 56 | // repeat wrapping 57 | i = switch wrapS { 58 | case REPEAT: Std.int(mod(i, width)); 59 | case CLAMP: i < 0 ? 0 : (i >= width ? width - 1 : i); 60 | } 61 | j = switch wrapS { 62 | case REPEAT: Std.int(mod(j, height)); 63 | case CLAMP: j < 0 ? 0 : (j >= height ? height - 1 : j); 64 | } 65 | return pixels[j * width + i]; 66 | } 67 | 68 | inline function mod(n: Float, m: Float) { 69 | return ((n % m) + m) % m; 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /noise/CurlNoise.hx: -------------------------------------------------------------------------------- 1 | /** 2 | * Curl Noise 4D 3 | * 4 | * Allows for time-varying 3D curl noise field 5 | * 6 | * Based on Robert Bridson's 2007 paper 7 | * https://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph2007-curlnoise.pdf 8 | * 9 | * Implementation: @haxiomic 10 | * GLSL version: https://gist.github.com/haxiomic/7453f867b396eee1af599633e03816b3 11 | **/ 12 | package noise; 13 | 14 | import VectorMath; 15 | import noise.Snoise; 16 | 17 | private final dx = vec4(0); 18 | private final dy = vec4(0); 19 | private final dz = vec4(0); 20 | private final u2 = vec4(0.); 21 | inline function curlSnoisePotential(u: Vec4, outGx: Vec3, outGy: Vec3, outGz: Vec3) { 22 | // generate 3 random values from p, t as a field vector 23 | // along with analytic gradients 24 | var field = vec3( 25 | snoise(u, dx), 26 | snoise(u2.copyFrom(u + vec4(38.231, -29.321, 12.314, -11.033)), dy), 27 | snoise(u2.copyFrom(u + vec4(-238.231, -123.321, 130.314, -111.999)), dz) 28 | ); 29 | outGx.copyFrom(dx.xyz); 30 | outGy.copyFrom(dy.xyz); 31 | outGz.copyFrom(dz.xyz); 32 | return field; 33 | } 34 | 35 | inline function potentialGradients(u: Vec4, outGx: Vec3, outGy: Vec3, outGz: Vec3) { 36 | outGx.set(0, 0, 0); 37 | outGy.set(0, 0, 0); 38 | outGz.set(0, 0, 0); 39 | 40 | var dx = vec3(0), dy = vec3(0), dz = vec3(0); 41 | 42 | curlSnoisePotential(u, dx, dy, dz); 43 | outGx += dx; outGy += dy; outGz += dz; 44 | 45 | // here you could add other potentials for the fluid to interact with 46 | // gx, gy, gz are the potential-field gradients, you can combine them additively 47 | // see Bridson's curl noise paper for more details 48 | } 49 | 50 | private final gx = vec3(0.); 51 | private final gy = vec3(0.); 52 | private final gz = vec3(0.); 53 | private final pt = vec4(0.); 54 | function curlNoise(p: Vec3, t: Float): Vec3 { 55 | pt.copyFrom(vec4(p, t)); 56 | potentialGradients(pt, gx, gy, gz); 57 | return vec3( 58 | gz.y - gy.z, 59 | gx.z - gz.x, 60 | gy.x - gx.y 61 | ); 62 | } -------------------------------------------------------------------------------- /math/Scalar.hx: -------------------------------------------------------------------------------- 1 | package math; 2 | 3 | import Math.*; 4 | 5 | class Scalar { 6 | 7 | // GLSL style functions 8 | 9 | static public inline function mod(x: Float, m: Float) { 10 | return ( (x % m) + m ) % m; 11 | } 12 | 13 | static public inline function sign(x: Float) { 14 | return x >= 0 ? 1 : -1; 15 | } 16 | 17 | static public inline function fract(x: Float) { 18 | return abs(x % 1); 19 | } 20 | 21 | static public inline function mix(a: Float, b: Float, t: Float) { 22 | return a * (1.0 - t) + b * t; 23 | } 24 | 25 | static public inline function clamp(x: Float, min: Float, max: Float) { 26 | return x < min ? min : (x > max ? max : x); 27 | } 28 | 29 | static public inline function step(edge: Float, x: Float) { 30 | return x < edge ? 0.0 : 1.0; 31 | } 32 | 33 | static public inline function smoothstep(edge0: Float, edge1: Float, x: Float) { 34 | var t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); 35 | return t * t * (3.0 - 2.0 * t); 36 | } 37 | 38 | static public inline function linstep(edge0: Float, edge1: Float, x: Float) { 39 | return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); 40 | } 41 | 42 | static public inline function int(?f: Float, ?b: Bool): Int { 43 | return if (f != null) Std.int(f); 44 | else if (b != null) b ? 1 : 0; 45 | else 0; 46 | } 47 | 48 | static public inline function float(?i: Int, ?b: Bool): Float { 49 | return if (i != null) i; 50 | else if (b != null) b ? 1.0 : 0.0; 51 | else 0.0; 52 | } 53 | 54 | static public inline function cubicPulse(c: Float, w: Float, x: Float){ 55 | x = abs(x - c); 56 | if( x>w ) return 0.0; 57 | x /= w; 58 | return 1.0 - x*x*(3.0-2.0*x); 59 | } 60 | 61 | static public inline final LN2 = 0.6931471805599453; 62 | static public inline function floorPowerOfTwo(x: Float) { 63 | return Math.pow(2, Math.floor(Math.log(x) / LN2)); 64 | } 65 | static public inline function ceilPowerOfTwo(x: Float) { 66 | return Math.pow(2, Math.ceil(Math.log(x) / LN2)); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /Partial.hx: -------------------------------------------------------------------------------- 1 | import haxe.macro.Context; 2 | import haxe.macro.Expr; 3 | import haxe.macro.TypeTools; 4 | 5 | #if !macro 6 | @:genericBuild(PartialMacro.build()) 7 | #end 8 | class Partial {} 9 | 10 | class PartialMacro { 11 | #if macro 12 | @:access(haxe.macro.TypeTools) 13 | static function build() 14 | { 15 | switch Context.getLocalType() 16 | { 17 | // Match when class's type parameter leads to an anonymous type (we convert to a complex type in the process to make it easier to work with) 18 | case TInst(_, [Context.followWithAbstracts(_) => TypeTools.toComplexType(_) => TAnonymous(fields)]): 19 | // Add @:optional meta to all fields 20 | var newFields = fields.map(addMeta); 21 | return TAnonymous(newFields); 22 | 23 | // Match when type parameter leads to a class instance 24 | case TInst(_, [Context.followWithAbstracts(_) => TInst(_.get() => _.fields.get() => classFields, params)]): 25 | // Convert all fields to anon fields and then add @:optional to each 26 | var anonFields: Array = classFields.map(TypeTools.toField); 27 | var newFields = anonFields.map(addMeta); 28 | return TAnonymous(newFields); 29 | 30 | default: 31 | Context.fatalError('Type parameter should be an anonymous structure', Context.currentPos()); 32 | } 33 | 34 | return null; 35 | } 36 | 37 | static function addMeta(field: Field): Field 38 | { 39 | // Handle Null and optional fields already parsed by the compiler 40 | var kind = switch (field.kind) { 41 | case FVar(TPath({ 42 | name: 'StdTypes', 43 | sub: 'Null', 44 | params: [TPType(TPath(tpath))] 45 | }), write): 46 | FVar(TPath(tpath), write); 47 | 48 | default: 49 | field.kind; 50 | } 51 | 52 | return { 53 | name: field.name, 54 | kind: kind, 55 | access: field.access, 56 | meta: field.meta.concat([{ 57 | name: ':optional', 58 | params: [], 59 | pos: Context.currentPos() 60 | }]), 61 | pos: field.pos 62 | }; 63 | } 64 | #end 65 | } -------------------------------------------------------------------------------- /animation/Tween.hx: -------------------------------------------------------------------------------- 1 | package animation; 2 | 3 | import animation.Easing.EasingFunction; 4 | import animation.Easing.EasingFunctions; 5 | 6 | class Tween { 7 | 8 | final easeFn: EasingFunction; 9 | final setValue: (v: Float) -> Void; 10 | final x0: Float; 11 | final x1: Float; 12 | final duration_s: Float; 13 | final onUpdate: Null<(value: Float) -> Void>; 14 | final onComplete: Null<() -> Void>; 15 | 16 | // current state 17 | var t: Float = 0.; 18 | 19 | public function new( 20 | easing: Easing, 21 | setValue: (x: Float) -> Void, 22 | x0: Float, 23 | x1: Float, 24 | duration_s: Float, 25 | ?onUpdate: (value: Float) -> Void, 26 | ?onComplete: () -> Void 27 | ) { 28 | this.easeFn = getEasingFunction(easing); 29 | this.setValue = setValue; 30 | this.x0 = x0; 31 | this.x1 = x1; 32 | this.duration_s = duration_s; 33 | this.onUpdate = onUpdate; 34 | this.onComplete = onComplete; 35 | } 36 | 37 | public function step(dt_s: Float) { 38 | t += dt_s; 39 | 40 | var complete = isComplete(); 41 | 42 | var x = complete ? x1 : easeFn(x0, x1, duration_s, t); 43 | 44 | setValue(x); 45 | 46 | if (onUpdate != null) { 47 | onUpdate(x); 48 | } 49 | 50 | if (complete && onComplete != null) { 51 | onComplete(); 52 | } 53 | } 54 | 55 | public function setTime(t_s: Float) { 56 | t = t_s; 57 | step(0); 58 | } 59 | 60 | public function isComplete() { 61 | return t >= duration_s; 62 | } 63 | 64 | public function forceCompletion() { 65 | setTime(duration_s); 66 | } 67 | 68 | function getEasingFunction(easing: Easing) { 69 | return switch easing { 70 | case Custom(fn): fn; 71 | case Linear: EasingFunctions.linear; 72 | case SineIn: EasingFunctions.sineIn; 73 | case SineOut: EasingFunctions.sineOut; 74 | case SineInOut: EasingFunctions.sineInOut; 75 | case QuadIn: EasingFunctions.quadIn; 76 | case QuadOut: EasingFunctions.quadOut; 77 | case QuadInOut: EasingFunctions.quadInOut; 78 | case CubicIn: EasingFunctions.cubicIn; 79 | case CubicOut: EasingFunctions.cubicOut; 80 | case CubicInOut: EasingFunctions.cubicInOut; 81 | } 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /animation/Easing.hx: -------------------------------------------------------------------------------- 1 | package animation; 2 | 3 | enum Easing { 4 | Linear; 5 | SineIn; 6 | SineOut; 7 | SineInOut; 8 | QuadIn; 9 | QuadOut; 10 | QuadInOut; 11 | CubicIn; 12 | CubicOut; 13 | CubicInOut; 14 | Custom(fn: EasingFunction); 15 | } 16 | 17 | typedef EasingFunction = (x0:Float, x1:Float, duration:Float, t:Float) -> Float; 18 | 19 | class EasingFunctions { 20 | 21 | static public inline function linear(x0:Float, x1:Float, duration:Float, t:Float):Float 22 | return x0 + (x1 - x0)*(t / duration); 23 | 24 | static public inline function sineIn(x0:Float, x1:Float, duration:Float, t:Float):Float 25 | return x1 - (x1 - x0)*Math.cos((Math.PI/2)*(t/duration)); 26 | 27 | static public inline function sineOut(x0:Float, x1:Float, duration:Float, t:Float):Float 28 | return x0 + (x1 - x0)*Math.sin((Math.PI/2)*(t/duration)); 29 | 30 | static public inline function sineInOut(x0:Float, x1:Float, duration:Float, t:Float):Float 31 | return x0 - .5*(x1 - x0)*(Math.cos(Math.PI * (t / duration)) - 1); 32 | 33 | static public inline function quadIn(x0:Float, x1:Float, duration:Float, t:Float):Float{ 34 | t /= duration; 35 | return (x1 - x0)*t*t + x0; 36 | } 37 | 38 | static public inline function quadOut(x0:Float, x1:Float, duration:Float, t:Float):Float{ 39 | t /= duration; 40 | return -(x1 - x0)*t*(t-2) + x0; 41 | } 42 | 43 | static public inline function quadInOut(x0:Float, x1:Float, duration:Float, t:Float):Float{ 44 | t *= 2/duration; 45 | if(t < 1) return (x1 - x0)*.5*t*t + x0; 46 | t--; 47 | return -(x1 - x0)*.5*(t*(t-2) - 1) + x0; 48 | } 49 | 50 | static public inline function cubicIn(x0:Float, x1:Float, duration:Float, t:Float):Float{ 51 | t /= duration; 52 | return (x1 - x0)*t*t*t + x0; 53 | } 54 | 55 | static public inline function cubicOut(x0:Float, x1:Float, duration:Float, t:Float):Float{ 56 | t = (t/duration) - 1; 57 | return (x1 - x0)*(t*t*t + 1) + x0; 58 | } 59 | 60 | static public inline function cubicInOut(x0:Float, x1:Float, duration:Float, t:Float):Float{ 61 | t *= 2/duration; 62 | if(t < 1) return (x1 - x0)*.5*t*t*t + x0; 63 | t -= 2; 64 | return (x1 - x0)*.5*(t*t*t + 2) + x0; 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /rendering/DualRenderTarget.hx: -------------------------------------------------------------------------------- 1 | package rendering; 2 | 3 | import three.WebGLRenderer; 4 | import three.Texture; 5 | import three.Uniform; 6 | import three.WebGLRenderTargetOptions; 7 | import rendering.WebGLRenderTarget; 8 | 9 | class DualRenderTarget { 10 | 11 | public final uniform: Uniform; 12 | 13 | public var width(get, null): Int; 14 | public var height(get, null): Int; 15 | 16 | var options: WebGLRenderTargetOptions; 17 | 18 | var a: WebGLRenderTarget; 19 | var b: WebGLRenderTarget; 20 | 21 | final postProcess: PostProcess; 22 | 23 | public function new(renderer: WebGLRenderer, width: Float, height: Float, ?options: WebGLRenderTargetOptions) { 24 | this.options = options; 25 | this.postProcess = new PostProcess(renderer); 26 | a = new WebGLRenderTarget(width, height, options); 27 | b = new WebGLRenderTarget(width, height, options); 28 | uniform = new Uniform(b.texture); 29 | } 30 | 31 | public function setOptions(newOptions: WebGLRenderTargetOptions) { 32 | this.options = newOptions; 33 | // recreate the render targets 34 | this.resize(this.width, this.height); 35 | } 36 | 37 | public function resize(newWidth: Int, newHeight: Int) { 38 | var aNew = new WebGLRenderTarget(newWidth, newHeight, options); 39 | var bNew = new WebGLRenderTarget(newWidth, newHeight, options); 40 | 41 | // // copy content to new texture (following whatever filtering params the textures use) 42 | postProcess.blit(a.texture, aNew); 43 | postProcess.blit(b.texture, bNew); 44 | 45 | a.dispose(); 46 | b.dispose(); 47 | 48 | a = aNew; 49 | b = bNew; 50 | 51 | uniform.value = b.texture; 52 | } 53 | 54 | public inline function swap() { 55 | var t = a; 56 | a = b; 57 | b = t; 58 | uniform.value = b.texture; 59 | } 60 | 61 | public inline function getWriteRenderTarget() { 62 | return a; 63 | } 64 | 65 | public inline function getReadRenderTarget() { 66 | return b; 67 | } 68 | 69 | public inline function getRenderTarget() { 70 | return a; 71 | } 72 | 73 | public inline function getTexture() { 74 | return b.texture; 75 | } 76 | 77 | inline function get_width() return Std.int(a.width); 78 | inline function get_height() return Std.int(a.height); 79 | 80 | } -------------------------------------------------------------------------------- /ui/dat_gui/GUI.hx: -------------------------------------------------------------------------------- 1 | package ui.dat_gui; 2 | 3 | @:jsRequire("dat.gui", "GUI") 4 | extern class GUI { 5 | function new(?option:GUIParams); 6 | var __controllers : Array; 7 | var __folders : { }; 8 | var domElement : js.html.Element; 9 | @:overload(function(target:Dynamic, propName:String, status:Bool):GUIController { }) 10 | @:overload(function(target:Dynamic, propName:String, items:Array):GUIController { }) 11 | @:overload(function(target:Dynamic, propName:String, items:Array):GUIController { }) 12 | @:overload(function(target:Dynamic, propName:String, items:Dynamic):GUIController { }) 13 | function add(target:Dynamic, propName:String, ?min:Float, ?max:Float, ?step:Float):GUIController; 14 | function addColor(target:Dynamic, propName:String):GUIController; 15 | function remove(controller:GUIController):Void; 16 | function destroy():Void; 17 | function addFolder(propName:String):GUI; 18 | function removeFolder(subFolder:GUI):Void; 19 | function open():Void; 20 | function close():Void; 21 | function hide():Void; 22 | function show():Void; 23 | function remember(target:Dynamic, additionalTargets:haxe.extern.Rest):Void; 24 | function getRoot():GUI; 25 | function getSaveObject():Dynamic; 26 | function save():Void; 27 | function saveAs(presetName:String):Void; 28 | function revert(gui:GUI):Void; 29 | function listen(controller:GUIController):Void; 30 | function updateDisplay():Void; 31 | final parent : GUI; 32 | final scrollable : Bool; 33 | final autoPlace : Bool; 34 | var preset : String; 35 | var width : Float; 36 | var name : String; 37 | var closed : Bool; 38 | final load : Dynamic; 39 | var useLocalStorage : Bool; 40 | static var prototype : GUI; 41 | static var CLASS_AUTO_PLACE : String; 42 | static var CLASS_AUTO_PLACE_CONTAINER : String; 43 | static var CLASS_MAIN : String; 44 | static var CLASS_CONTROLLER_ROW : String; 45 | static var CLASS_TOO_TALL : String; 46 | static var CLASS_CLOSED : String; 47 | static var CLASS_CLOSE_BUTTON : String; 48 | static var CLASS_CLOSE_TOP : String; 49 | static var CLASS_CLOSE_BOTTOM : String; 50 | static var CLASS_DRAG : String; 51 | static var DEFAULT_WIDTH : Float; 52 | static var TEXT_CLOSED : String; 53 | static var TEXT_OPEN : String; 54 | } -------------------------------------------------------------------------------- /tool/IBLGenerator.hx: -------------------------------------------------------------------------------- 1 | package tool; 2 | 3 | import three.WebGLRenderTargetOptions; 4 | import three.TextureEncoding; 5 | import three.PixelFormat; 6 | import three.TextureDataType; 7 | import three.TextureFilter; 8 | import three.Texture; 9 | import three.WebGLRenderTarget; 10 | import three.WebGLRenderer; 11 | 12 | @:jsRequire("three", "PMREMGenerator") 13 | extern class PMREMGeneratorInternal extends three.PMREMGenerator { 14 | 15 | var _pingPongRenderTarget: WebGLRenderTarget; 16 | 17 | public function new(renderer: WebGLRenderer); 18 | function _allocateTargets(equirectangular: Texture): WebGLRenderTarget; 19 | 20 | } 21 | 22 | // RGBD vs RGBM vs RGBE https://lousodrome.net/blog/light/tag/rgbm/ 23 | 24 | class IBLGenerator extends PMREMGeneratorInternal { 25 | 26 | static final LOD_MIN = 4; 27 | static final LOD_MAX = 8; 28 | static final SIZE_MAX = Math.pow(2, LOD_MAX); 29 | 30 | override function _allocateTargets(equirectangular: Texture): WebGLRenderTarget { 31 | var params = { 32 | magFilter: TextureFilter.NearestFilter, 33 | minFilter: TextureFilter.NearestFilter, 34 | generateMipmaps: false, 35 | type: TextureDataType.UnsignedByteType, 36 | format: PixelFormat.RGBEFormat, 37 | encoding: _isLDR(equirectangular) ? equirectangular.encoding : TextureEncoding.RGBDEncoding, 38 | depthBuffer: false, 39 | stencilBuffer: false 40 | }; 41 | 42 | var cubeUVRenderTarget = _createRenderTarget(cast params); 43 | cubeUVRenderTarget.depthBuffer = equirectangular != null ? false : true; 44 | this._pingPongRenderTarget = _createRenderTarget(cast params); 45 | return cubeUVRenderTarget; 46 | } 47 | 48 | function _isLDR(texture: Null) { 49 | if (texture == null || texture.type != UnsignedByteType) return false; 50 | return texture.encoding == LinearEncoding || texture.encoding == sRGBEncoding || texture.encoding == GammaEncoding; 51 | } 52 | 53 | function _createRenderTarget(params: WebGLRenderTargetOptions) { 54 | var cubeUVRenderTarget = new WebGLRenderTarget(3 * SIZE_MAX, 3 * SIZE_MAX, params); 55 | cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; 56 | cubeUVRenderTarget.texture.name = 'PMREM.cubeUv'; 57 | cubeUVRenderTarget.scissorTest = true; 58 | return cubeUVRenderTarget; 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /shaders/noise/cellular2x2.glsl: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | // Cellular noise ("Worley noise") in 2D in GLSL. 4 | // Copyright (c) Stefan Gustavson 2011-04-19. All rights reserved. 5 | // This code is released under the conditions of the MIT license. 6 | // See LICENSE file for details. 7 | // https://github.com/stegu/webgl-noise 8 | 9 | // Modulo 289 without a division (only multiplications) 10 | vec2 mod289(vec2 x) { 11 | return x - floor(x * (1.0 / 289.0)) * 289.0; 12 | } 13 | 14 | vec4 mod289(vec4 x) { 15 | return x - floor(x * (1.0 / 289.0)) * 289.0; 16 | } 17 | 18 | // Modulo 7 without a division 19 | vec4 mod7(vec4 x) { 20 | return x - floor(x * (1.0 / 7.0)) * 7.0; 21 | } 22 | 23 | // Permutation polynomial: (34x^2 + x) mod 289 24 | vec4 permute(vec4 x) { 25 | return mod289((34.0 * x + 1.0) * x); 26 | } 27 | 28 | // Cellular noise, returning F1 and F2 in a vec2. 29 | // Speeded up by using 2x2 search window instead of 3x3, 30 | // at the expense of some strong pattern artifacts. 31 | // F2 is often wrong and has sharp discontinuities. 32 | // If you need a smooth F2, use the slower 3x3 version. 33 | // F1 is sometimes wrong, too, but OK for most purposes. 34 | vec2 cellular2x2(vec2 P) { 35 | #define K 0.142857142857 // 1/7 36 | #define K2 0.0714285714285 // K/2 37 | #define jitter 0.8 // jitter 1.0 makes F1 wrong more often 38 | vec2 Pi = mod289(floor(P)); 39 | vec2 Pf = fract(P); 40 | vec4 Pfx = Pf.x + vec4(-0.5, -1.5, -0.5, -1.5); 41 | vec4 Pfy = Pf.y + vec4(-0.5, -0.5, -1.5, -1.5); 42 | vec4 p = permute(Pi.x + vec4(0.0, 1.0, 0.0, 1.0)); 43 | p = permute(p + Pi.y + vec4(0.0, 0.0, 1.0, 1.0)); 44 | vec4 ox = mod7(p)*K+K2; 45 | vec4 oy = mod7(floor(p*K))*K+K2; 46 | vec4 dx = Pfx + jitter*ox; 47 | vec4 dy = Pfy + jitter*oy; 48 | vec4 d = dx * dx + dy * dy; // d11, d12, d21 and d22, squared 49 | // Sort out the two smallest distances 50 | #if 0 51 | // Cheat and pick only F1 52 | d.xy = min(d.xy, d.zw); 53 | d.x = min(d.x, d.y); 54 | return vec2(sqrt(d.x)); // F1 duplicated, F2 not computed 55 | #else 56 | // Do it right and find both F1 and F2 57 | d.xy = (d.x < d.y) ? d.xy : d.yx; // Swap if smaller 58 | d.xz = (d.x < d.z) ? d.xz : d.zx; 59 | d.xw = (d.x < d.w) ? d.xw : d.wx; 60 | d.y = min(d.y, d.z); 61 | d.y = min(d.y, d.w); 62 | return sqrt(d.xy); 63 | #endif 64 | } 65 | -------------------------------------------------------------------------------- /tool/ShaderDev.hx: -------------------------------------------------------------------------------- 1 | package tool; 2 | 3 | import three.BufferAttribute; 4 | import three.BufferGeometry; 5 | import three.Mesh; 6 | import three.OrthographicCamera; 7 | import three.Scene; 8 | import three.ShaderMaterial; 9 | import three.ShaderMaterialParameters; 10 | import three.Side; 11 | import three.WebGLRenderTarget; 12 | import three.WebGLRenderer; 13 | 14 | class ShaderDev extends Mesh { 15 | 16 | public final shaderMaterial: ShaderMaterial; 17 | 18 | public function new(parameters: ShaderMaterialParameters) { 19 | var shaderMaterial = new ShaderMaterial( 20 | Structure.extend( 21 | { 22 | vertexShader: ' 23 | varying vec2 vUv; 24 | 25 | void main() { 26 | vUv = position.xy; 27 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 28 | } 29 | ', 30 | depthWrite: false, 31 | depthTest: false, 32 | fog: false, 33 | side: DoubleSide, 34 | }, 35 | parameters 36 | ) 37 | ); 38 | var unitQuad = new BufferGeometry(); 39 | var position = new BufferAttribute(new js.lib.Float32Array([ 40 | 0, 0, 41 | 1, 0, 42 | 0, 1, 43 | 44 | 1, 1, 45 | 0, 1, 46 | 1, 0, 47 | ]), 2); 48 | unitQuad.setAttribute('position', position); 49 | super(unitQuad, shaderMaterial); 50 | this.frustumCulled = false; 51 | this.shaderMaterial = shaderMaterial; 52 | } 53 | 54 | public function renderToTexture(renderer: WebGLRenderer, renderTarget: WebGLRenderTarget) { 55 | var initialVs = shaderMaterial.vertexShader; 56 | var initialParent = this.parent; 57 | // disable vertex transform in vs 58 | shaderMaterial.vertexShader = ' 59 | varying vec2 vUv; 60 | 61 | void main() { 62 | vUv = position.xy; 63 | gl_Position = projectionMatrix * vec4(position, 1.0); 64 | } 65 | '; 66 | shaderMaterial.needsUpdate = true; 67 | 68 | var scene = new Scene(); 69 | scene.add(this); 70 | var camera2d = new OrthographicCamera(0, 1, 1, 0, -1, 1); 71 | renderer.setRenderTarget(renderTarget); 72 | renderer.render(scene, camera2d); 73 | scene.remove(this); 74 | 75 | if (initialParent != null) { 76 | initialParent.add(this); 77 | } 78 | 79 | shaderMaterial.vertexShader = initialVs; 80 | shaderMaterial.needsUpdate = true; 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /shaders/BloomBlend.hx: -------------------------------------------------------------------------------- 1 | package shaders; 2 | 3 | import three.BlendingDstFactor; 4 | import three.RawShaderMaterial; 5 | import three.Texture; 6 | import three.Uniform; 7 | 8 | class BloomBlend extends RawShaderMaterial { 9 | 10 | public final uTexture: Uniform; 11 | /** Setting to 0 gives additive blending, setting to 1.0 is full replace **/ 12 | public final uBloomMix: Uniform; 13 | public final uBloomMultiplier: Uniform; 14 | public final uBloomSubtract: Uniform; 15 | public final uBloomExponent: Uniform; 16 | 17 | public function new() { 18 | var uTexture = new Uniform(cast null); 19 | var uBloomMix = new Uniform(0.0); 20 | var uBloomMultiplier = new Uniform(1.); 21 | var uBloomSubtract = new Uniform(0.0); 22 | var uBloomExponent = new Uniform(1.); 23 | super({ 24 | uniforms: { 25 | uTexture: uTexture, 26 | uBloomMix: uBloomMix, 27 | uBloomMultiplier: uBloomMultiplier, 28 | uBloomSubtract: uBloomSubtract, 29 | uBloomExponent: uBloomExponent, 30 | }, 31 | vertexShader: ' 32 | attribute vec2 position; 33 | 34 | varying vec2 vUv; 35 | 36 | void main() { 37 | vUv = position * 0.5 + 0.5; 38 | gl_Position = vec4(position, 0., 1.); 39 | } 40 | ', 41 | fragmentShader: ' 42 | precision highp float; 43 | 44 | uniform sampler2D uTexture; 45 | uniform float uBloomMix; 46 | uniform float uBloomMultiplier; 47 | uniform float uBloomSubtract; 48 | uniform float uBloomExponent; 49 | 50 | varying vec2 vUv; 51 | 52 | void main() { 53 | gl_FragColor = texture2D(uTexture, vUv); 54 | gl_FragColor.rgb = max(gl_FragColor.rgb - uBloomSubtract, vec3(0.)); 55 | gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(uBloomExponent)) * uBloomMultiplier; 56 | gl_FragColor.a *= uBloomMix; 57 | } 58 | ', 59 | side: DoubleSide, 60 | 61 | // color = src + dst * (1.0 - src.a) 62 | blending: CustomBlending, 63 | blendEquation: AddEquation, 64 | blendSrc: BlendingDstFactor.OneFactor, 65 | blendDst: BlendingDstFactor.OneMinusSrcAlphaFactor, 66 | 67 | }); 68 | 69 | this.uTexture = uTexture; 70 | this.uBloomMix = uBloomMix; 71 | this.uBloomMultiplier = uBloomMultiplier; 72 | this.uBloomSubtract = uBloomSubtract; 73 | this.uBloomExponent = uBloomExponent; 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /shaders/noise/noise2D.glsl: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Array and textureless GLSL 2D simplex noise function. 3 | // Author : Ian McEwan, Ashima Arts. 4 | // Maintainer : stegu 5 | // Lastmod : 20110822 (ijm) 6 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 7 | // Distributed under the MIT License. See LICENSE file. 8 | // https://github.com/ashima/webgl-noise 9 | // https://github.com/stegu/webgl-noise 10 | // 11 | 12 | vec3 mod289(vec3 x) { 13 | return x - floor(x * (1.0 / 289.0)) * 289.0; 14 | } 15 | 16 | vec2 mod289(vec2 x) { 17 | return x - floor(x * (1.0 / 289.0)) * 289.0; 18 | } 19 | 20 | vec3 permute(vec3 x) { 21 | return mod289(((x*34.0)+1.0)*x); 22 | } 23 | 24 | float snoise(vec2 v) 25 | { 26 | const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 27 | 0.366025403784439, // 0.5*(sqrt(3.0)-1.0) 28 | -0.577350269189626, // -1.0 + 2.0 * C.x 29 | 0.024390243902439); // 1.0 / 41.0 30 | // First corner 31 | vec2 i = floor(v + dot(v, C.yy) ); 32 | vec2 x0 = v - i + dot(i, C.xx); 33 | 34 | // Other corners 35 | vec2 i1; 36 | //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0 37 | //i1.y = 1.0 - i1.x; 38 | i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); 39 | // x0 = x0 - 0.0 + 0.0 * C.xx ; 40 | // x1 = x0 - i1 + 1.0 * C.xx ; 41 | // x2 = x0 - 1.0 + 2.0 * C.xx ; 42 | vec4 x12 = x0.xyxy + C.xxzz; 43 | x12.xy -= i1; 44 | 45 | // Permutations 46 | i = mod289(i); // Avoid truncation effects in permutation 47 | vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) 48 | + i.x + vec3(0.0, i1.x, 1.0 )); 49 | 50 | vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); 51 | m = m*m ; 52 | m = m*m ; 53 | 54 | // Gradients: 41 points uniformly over a line, mapped onto a diamond. 55 | // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) 56 | 57 | vec3 x = 2.0 * fract(p * C.www) - 1.0; 58 | vec3 h = abs(x) - 0.5; 59 | vec3 ox = floor(x + 0.5); 60 | vec3 a0 = x - ox; 61 | 62 | // Normalise gradients implicitly by scaling m 63 | // Approximation of: m *= inversesqrt( a0*a0 + h*h ); 64 | m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); 65 | 66 | // Compute final noise value at P 67 | vec3 g; 68 | g.x = a0.x * x0.x + h.x * x0.y; 69 | g.yz = a0.yz * x12.xz + h.yz * x12.yw; 70 | return 130.0 * dot(m, g); 71 | } 72 | -------------------------------------------------------------------------------- /objects/XZPlaneBufferGeometry.hx: -------------------------------------------------------------------------------- 1 | package objects; 2 | 3 | import three.Float32BufferAttribute; 4 | import three.BufferAttribute; 5 | import js.lib.Uint32Array; 6 | import js.lib.Float32Array; 7 | import three.BufferGeometry; 8 | 9 | /** 10 | - Faster initialization than three.js plane 11 | - Doesn't use per-vertex normals – these should be set with vertexAttrib3f 12 | **/ 13 | class XZPlaneBufferGeometry extends BufferGeometry { 14 | 15 | public function new(width: Float, height: Float, widthSegments: Int, heightSegments: Int) { 16 | super(); 17 | var indexCount = widthSegments * heightSegments * 2 * 3; // 2 triangles per cell, 3 indices per triangle 18 | var vertexCount = (widthSegments + 1) * (heightSegments + 1); 19 | var wSpacing = width / widthSegments; 20 | var hSpacing = height / heightSegments; 21 | 22 | var vertices = new Float32Array(3 * vertexCount); 23 | var uvs = new Float32Array(2 * vertexCount); 24 | 25 | inline function vertexIndex(column, row) return row * (widthSegments + 1) + column; 26 | 27 | for (vertexRow in 0...(heightSegments + 1)) { 28 | for (vertexColumn in 0...(widthSegments + 1)) { 29 | var i = vertexIndex(vertexColumn, vertexRow); 30 | 31 | var vi = i * 3; 32 | vertices[vi + 0] = wSpacing * vertexColumn - width * 0.5; 33 | vertices[vi + 1] = 0; 34 | vertices[vi + 2] = hSpacing * vertexRow - height * 0.5; 35 | 36 | var ui = i * 2; 37 | uvs[ui + 0] = vertexColumn / widthSegments; 38 | uvs[ui + 1] = vertexRow / heightSegments; 39 | } 40 | } 41 | 42 | var indices = new Uint32Array(indexCount); 43 | var indexOffset = 0; 44 | for (vertexRow in 0...(heightSegments)) { 45 | for (vertexColumn in 0...(widthSegments)) { 46 | var tl = vertexIndex(vertexColumn, vertexRow); 47 | var tr = vertexIndex(vertexColumn + 1, vertexRow); 48 | var bl = vertexIndex(vertexColumn, vertexRow + 1); 49 | var br = vertexIndex(vertexColumn + 1, vertexRow + 1); 50 | 51 | var i = indexOffset; 52 | indices[i + 0] = tl; 53 | indices[i + 1] = bl; 54 | indices[i + 2] = br; 55 | 56 | indices[i + 3] = tl; 57 | indices[i + 4] = br; 58 | indices[i + 5] = tr; 59 | 60 | indexOffset += 6; 61 | } 62 | } 63 | 64 | this.setIndex(new BufferAttribute(indices, 1)); 65 | this.setAttribute( 'position', new Float32BufferAttribute(vertices, 3) ); 66 | // normal set by material 67 | // this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); 68 | this.setAttribute( 'uv', new Float32BufferAttribute(uvs, 2) ); 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /rendering/CompLayer.hx: -------------------------------------------------------------------------------- 1 | package rendering; 2 | 3 | import Structure.extend; 4 | import objects.ClipSpaceTriangle; 5 | import three.BlendingDstFactor; 6 | import three.ShaderMaterial; 7 | import three.Side; 8 | import three.Texture; 9 | import three.Uniform; 10 | 11 | /** 12 | Render a texture that spans screen-space 13 | **/ 14 | class CompLayer extends ClipSpaceTriangle { 15 | 16 | public final shaderMaterial: ShaderMaterial; 17 | public final uTexture: Uniform; 18 | 19 | public function new(options: { 20 | depth: Float, 21 | alphaBlend: Bool, 22 | ?premultipliedAlpha: Bool, 23 | ?encodingAndToneMapping: Bool, 24 | ?depthWrite: Bool, 25 | ?depthTest: Bool, 26 | }, ?texture: Texture) { 27 | var options = extend({ 28 | depth: 0.0, 29 | alphaBlend: false, 30 | premultipliedAlpha: false, 31 | encodingAndToneMapping: false, 32 | depthWrite: true, 33 | depthTest: true, 34 | }, options); 35 | var uTexture = new Uniform(texture); 36 | var shaderMaterial = new ShaderMaterial({ 37 | uniforms: { 38 | uTexture: uTexture, 39 | uDepth: new Uniform(options.depth), 40 | uToneMappingAndEncoding: new Uniform(options.encodingAndToneMapping), 41 | }, 42 | vertexShader: ' 43 | uniform float uDepth; 44 | 45 | varying vec2 vUv; 46 | 47 | void main() { 48 | vUv = position.xy * 0.5 + 0.5; 49 | gl_Position = vec4(position.xy, uDepth, 1.0); 50 | } 51 | ', 52 | fragmentShader: ' 53 | precision highp float; 54 | uniform sampler2D uTexture; 55 | uniform bool uToneMappingAndEncoding; 56 | 57 | varying vec2 vUv; 58 | 59 | void main() { 60 | gl_FragColor = texture2D(uTexture, vUv); 61 | 62 | if (uToneMappingAndEncoding) { 63 | #include 64 | #include 65 | #include 66 | } 67 | } 68 | ', 69 | side: Side.DoubleSide, 70 | depthWrite: options.depthWrite, 71 | depthTest: options.depthTest, 72 | fog: false, 73 | lights: false, 74 | }); 75 | if (options.alphaBlend) { 76 | shaderMaterial.blending = CustomBlending; 77 | shaderMaterial.blendEquation = AddEquation; 78 | shaderMaterial.blendSrc = BlendingDstFactor.OneFactor; 79 | shaderMaterial.blendDst = BlendingDstFactor.OneMinusSrcAlphaFactor; 80 | shaderMaterial.premultipliedAlpha = options.premultipliedAlpha; 81 | } else { 82 | shaderMaterial.blending = NoBlending; 83 | } 84 | super(shaderMaterial); 85 | this.uTexture = uTexture; 86 | this.shaderMaterial = shaderMaterial; 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /shaders/noise/cellular2D.glsl: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | // Cellular noise ("Worley noise") in 2D in GLSL. 4 | // Copyright (c) Stefan Gustavson 2011-04-19. All rights reserved. 5 | // This code is released under the conditions of the MIT license. 6 | // See LICENSE file for details. 7 | // https://github.com/stegu/webgl-noise 8 | 9 | // Modulo 289 without a division (only multiplications) 10 | vec3 mod289(vec3 x) { 11 | return x - floor(x * (1.0 / 289.0)) * 289.0; 12 | } 13 | 14 | vec2 mod289(vec2 x) { 15 | return x - floor(x * (1.0 / 289.0)) * 289.0; 16 | } 17 | 18 | // Modulo 7 without a division 19 | vec3 mod7(vec3 x) { 20 | return x - floor(x * (1.0 / 7.0)) * 7.0; 21 | } 22 | 23 | // Permutation polynomial: (34x^2 + x) mod 289 24 | vec3 permute(vec3 x) { 25 | return mod289((34.0 * x + 1.0) * x); 26 | } 27 | 28 | // Cellular noise, returning F1 and F2 in a vec2. 29 | // Standard 3x3 search window for good F1 and F2 values 30 | vec2 cellular(vec2 P) { 31 | #define K 0.142857142857 // 1/7 32 | #define Ko 0.428571428571 // 3/7 33 | #define jitter 1.0 // Less gives more regular pattern 34 | vec2 Pi = mod289(floor(P)); 35 | vec2 Pf = fract(P); 36 | vec3 oi = vec3(-1.0, 0.0, 1.0); 37 | vec3 of = vec3(-0.5, 0.5, 1.5); 38 | vec3 px = permute(Pi.x + oi); 39 | vec3 p = permute(px.x + Pi.y + oi); // p11, p12, p13 40 | vec3 ox = fract(p*K) - Ko; 41 | vec3 oy = mod7(floor(p*K))*K - Ko; 42 | vec3 dx = Pf.x + 0.5 + jitter*ox; 43 | vec3 dy = Pf.y - of + jitter*oy; 44 | vec3 d1 = dx * dx + dy * dy; // d11, d12 and d13, squared 45 | p = permute(px.y + Pi.y + oi); // p21, p22, p23 46 | ox = fract(p*K) - Ko; 47 | oy = mod7(floor(p*K))*K - Ko; 48 | dx = Pf.x - 0.5 + jitter*ox; 49 | dy = Pf.y - of + jitter*oy; 50 | vec3 d2 = dx * dx + dy * dy; // d21, d22 and d23, squared 51 | p = permute(px.z + Pi.y + oi); // p31, p32, p33 52 | ox = fract(p*K) - Ko; 53 | oy = mod7(floor(p*K))*K - Ko; 54 | dx = Pf.x - 1.5 + jitter*ox; 55 | dy = Pf.y - of + jitter*oy; 56 | vec3 d3 = dx * dx + dy * dy; // d31, d32 and d33, squared 57 | // Sort out the two smallest distances (F1, F2) 58 | vec3 d1a = min(d1, d2); 59 | d2 = max(d1, d2); // Swap to keep candidates for F2 60 | d2 = min(d2, d3); // neither F1 nor F2 are now in d3 61 | d1 = min(d1a, d2); // F1 is now in d1 62 | d2 = max(d1a, d2); // Swap to keep candidates for F2 63 | d1.xy = (d1.x < d1.y) ? d1.xy : d1.yx; // Swap if smaller 64 | d1.xz = (d1.x < d1.z) ? d1.xz : d1.zx; // F1 is in d1.x 65 | d1.yz = min(d1.yz, d2.yz); // F2 is now not in d2.yz 66 | d1.y = min(d1.y, d1.z); // nor in d1.z 67 | d1.y = min(d1.y, d2.x); // F2 is in d1.y, we're done. 68 | return sqrt(d1.xy); 69 | } 70 | -------------------------------------------------------------------------------- /shaders/OutputMap.hx: -------------------------------------------------------------------------------- 1 | package shaders; 2 | 3 | import three.Texture; 4 | import three.Uniform; 5 | import three.RawShaderMaterial; 6 | 7 | enum abstract ToneMapping(String) to String { 8 | var ACESFilmic = 'ACESFilmicToneMapping'; 9 | var Reinhard = 'ReinhardToneMapping'; 10 | var OptimizedCineon = 'OptimizedCineonToneMapping'; 11 | var Linear = 'LinearToneMapping'; 12 | var Abs = 'abs'; 13 | } 14 | 15 | enum abstract Encoding(String) to String { 16 | var Linear = 'LinearToLinear'; 17 | var Gamma = 'LinearToGamma'; 18 | var sRGB = 'LinearTosRGB'; 19 | } 20 | 21 | class OutputMap extends RawShaderMaterial { 22 | 23 | static public inline function get(texture: Texture, encoding: Encoding, toneMapping: ToneMapping, toneMappingExposure: Float) { 24 | instance.uTexture.value = texture; 25 | instance.uToneMappingExposure.value = toneMappingExposure; 26 | if ( 27 | Reflect.field(instance.defines, 'TONE_MAPPING_FN') != toneMapping || 28 | Reflect.field(instance.defines, 'ENCODING_FN') != encoding 29 | ) { 30 | instance.needsUpdate = true; 31 | } 32 | instance.defines = { 33 | 'TONE_MAPPING_FN': toneMapping, 34 | 'ENCODING_FN': encoding, 35 | }; 36 | return instance; 37 | } 38 | static final instance = new OutputMap(); 39 | 40 | public final uTexture = new Uniform(cast null); 41 | public final uToneMappingExposure = new Uniform(1.0); 42 | 43 | public function new() { 44 | super({ 45 | uniforms: { 46 | uTexture: uTexture, 47 | toneMappingExposure: uToneMappingExposure, 48 | }, 49 | vertexShader: ' 50 | attribute vec2 position; 51 | 52 | varying vec2 vUv; 53 | 54 | void main() { 55 | vUv = position * 0.5 + 0.5; 56 | gl_Position = vec4(position, 0., 1.); 57 | } 58 | ', 59 | fragmentShader: ' 60 | precision highp float; 61 | 62 | uniform sampler2D uTexture; 63 | 64 | varying vec2 vUv; 65 | 66 | #include 67 | 68 | vec4 LinearToGamma( in vec4 value, in float gammaFactor ) { 69 | return vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a ); 70 | } 71 | 72 | vec4 LinearToGamma( in vec4 value ) { 73 | return vec4( pow( value.rgb, vec3( 1.0 / 2.2 ) ), value.a ); 74 | } 75 | 76 | vec4 LinearTosRGB( in vec4 value ) { 77 | return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a ); 78 | } 79 | 80 | vec4 LinearToLinear( in vec4 value ) { 81 | return value; 82 | } 83 | 84 | void main() { 85 | gl_FragColor = texture2D(uTexture, vUv); 86 | // tone mapping and encoding 87 | gl_FragColor.rgb = TONE_MAPPING_FN(gl_FragColor.rgb); 88 | gl_FragColor = ENCODING_FN(gl_FragColor); 89 | } 90 | ', 91 | side: DoubleSide 92 | }); 93 | } 94 | 95 | } -------------------------------------------------------------------------------- /rendering/HeightMapRenderer.hx: -------------------------------------------------------------------------------- 1 | package rendering; 2 | 3 | import three.Mesh; 4 | import three.OrthographicCamera; 5 | import three.PixelFormat; 6 | import three.Scene; 7 | import three.ShaderMaterial; 8 | import three.Vector3; 9 | import three.WebGLRenderTarget; 10 | import three.WebGLRenderer; 11 | import tool.CameraTools; 12 | 13 | @:nullSafety 14 | class HeightMapRenderer { 15 | 16 | public final renderTarget: WebGLRenderTarget; 17 | final renderer: WebGLRenderer; 18 | final camera: OrthographicCamera; 19 | final scene: Scene; 20 | final shaderMaterial: ShaderMaterial; 21 | var mesh: Null>; 22 | 23 | public function new(renderer: WebGLRenderer, width: Int, height: Int, ?mesh: Mesh) { 24 | this.renderer = renderer; 25 | this.camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1); 26 | this.camera.position.set(0, 1, 0); 27 | this.camera.lookAt(new three.Vector3()); 28 | this.camera.rotation.z = 0; 29 | 30 | this.renderTarget = new WebGLRenderTarget(width, height, { 31 | magFilter: LinearFilter, 32 | minFilter: LinearFilter, 33 | depthBuffer: false, 34 | generateMipmaps: false, 35 | stencilBuffer: false, 36 | anisotropy: 0, 37 | type: HalfFloatType, 38 | format: PixelFormat.RGBAFormat, 39 | }); 40 | 41 | this.scene = new Scene(); 42 | this.shaderMaterial = new ShaderMaterial({ 43 | uniforms: { 44 | }, 45 | vertexShader: ' 46 | varying vec3 vWorldPosition; 47 | varying vec3 vWorldNormal; 48 | 49 | #include 50 | 51 | void main() { 52 | vec4 p = vec4( position, 1.0 ); 53 | vec4 worldP = modelMatrix * p; 54 | vWorldPosition = worldP.xyz; 55 | 56 | vec3 transformedNormal = normalMatrix * normal; 57 | 58 | vWorldNormal = inverseTransformDirection(transformedNormal, viewMatrix); 59 | 60 | gl_Position = projectionMatrix * viewMatrix * worldP; 61 | } 62 | ', 63 | fragmentShader: ' 64 | varying vec3 vWorldPosition; 65 | varying vec3 vWorldNormal; 66 | 67 | void main() { 68 | gl_FragColor = vec4(normalize(vWorldNormal), vWorldPosition.y); 69 | } 70 | ', 71 | side: DoubleSide, 72 | }); 73 | 74 | this.scene.overrideMaterial = shaderMaterial; 75 | 76 | if (mesh != null) inline setMesh(mesh); 77 | } 78 | 79 | public function setMesh(mesh: Mesh) { 80 | this.mesh = mesh; 81 | } 82 | 83 | public function setSize(width: Int, height: Int) { 84 | renderTarget.setSize(width, height); 85 | } 86 | 87 | public function render() { 88 | renderer.setRenderTarget(renderTarget); 89 | renderer.clear(true, false, false); 90 | 91 | if (mesh != null) { 92 | var parent = mesh.parent; 93 | scene.add(mesh); 94 | CameraTools.fitOrthographicView(camera, mesh); 95 | 96 | // flip view along z 97 | var t = camera.top; 98 | camera.top = camera.bottom; 99 | camera.bottom = t; 100 | camera.updateProjectionMatrix(); 101 | 102 | renderer.render(scene, camera); 103 | 104 | scene.remove(mesh); 105 | if (parent != null) { 106 | parent.add(mesh); 107 | } 108 | } 109 | } 110 | 111 | } -------------------------------------------------------------------------------- /shaders/noise/cellular2x2x2.glsl: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | // Cellular noise ("Worley noise") in 3D in GLSL. 4 | // Copyright (c) Stefan Gustavson 2011-04-19. All rights reserved. 5 | // This code is released under the conditions of the MIT license. 6 | // See LICENSE file for details. 7 | // https://github.com/stegu/webgl-noise 8 | 9 | // Modulo 289 without a division (only multiplications) 10 | vec3 mod289(vec3 x) { 11 | return x - floor(x * (1.0 / 289.0)) * 289.0; 12 | } 13 | 14 | vec4 mod289(vec4 x) { 15 | return x - floor(x * (1.0 / 289.0)) * 289.0; 16 | } 17 | 18 | // Modulo 7 without a division 19 | vec4 mod7(vec4 x) { 20 | return x - floor(x * (1.0 / 7.0)) * 7.0; 21 | } 22 | 23 | 24 | // Permutation polynomial: (34x^2 + x) mod 289 25 | vec3 permute(vec3 x) { 26 | return mod289((34.0 * x + 1.0) * x); 27 | } 28 | 29 | vec4 permute(vec4 x) { 30 | return mod289((34.0 * x + 1.0) * x); 31 | } 32 | 33 | // Cellular noise, returning F1 and F2 in a vec2. 34 | // Speeded up by using 2x2x2 search window instead of 3x3x3, 35 | // at the expense of some pattern artifacts. 36 | // F2 is often wrong and has sharp discontinuities. 37 | // If you need a good F2, use the slower 3x3x3 version. 38 | vec2 cellular2x2x2(vec3 P) { 39 | #define K 0.142857142857 // 1/7 40 | #define Ko 0.428571428571 // 1/2-K/2 41 | #define K2 0.020408163265306 // 1/(7*7) 42 | #define Kz 0.166666666667 // 1/6 43 | #define Kzo 0.416666666667 // 1/2-1/6*2 44 | #define jitter 0.8 // smaller jitter gives less errors in F2 45 | vec3 Pi = mod289(floor(P)); 46 | vec3 Pf = fract(P); 47 | vec4 Pfx = Pf.x + vec4(0.0, -1.0, 0.0, -1.0); 48 | vec4 Pfy = Pf.y + vec4(0.0, 0.0, -1.0, -1.0); 49 | vec4 p = permute(Pi.x + vec4(0.0, 1.0, 0.0, 1.0)); 50 | p = permute(p + Pi.y + vec4(0.0, 0.0, 1.0, 1.0)); 51 | vec4 p1 = permute(p + Pi.z); // z+0 52 | vec4 p2 = permute(p + Pi.z + vec4(1.0)); // z+1 53 | vec4 ox1 = fract(p1*K) - Ko; 54 | vec4 oy1 = mod7(floor(p1*K))*K - Ko; 55 | vec4 oz1 = floor(p1*K2)*Kz - Kzo; // p1 < 289 guaranteed 56 | vec4 ox2 = fract(p2*K) - Ko; 57 | vec4 oy2 = mod7(floor(p2*K))*K - Ko; 58 | vec4 oz2 = floor(p2*K2)*Kz - Kzo; 59 | vec4 dx1 = Pfx + jitter*ox1; 60 | vec4 dy1 = Pfy + jitter*oy1; 61 | vec4 dz1 = Pf.z + jitter*oz1; 62 | vec4 dx2 = Pfx + jitter*ox2; 63 | vec4 dy2 = Pfy + jitter*oy2; 64 | vec4 dz2 = Pf.z - 1.0 + jitter*oz2; 65 | vec4 d1 = dx1 * dx1 + dy1 * dy1 + dz1 * dz1; // z+0 66 | vec4 d2 = dx2 * dx2 + dy2 * dy2 + dz2 * dz2; // z+1 67 | 68 | // Sort out the two smallest distances (F1, F2) 69 | #if 0 70 | // Cheat and sort out only F1 71 | d1 = min(d1, d2); 72 | d1.xy = min(d1.xy, d1.wz); 73 | d1.x = min(d1.x, d1.y); 74 | return vec2(sqrt(d1.x)); 75 | #else 76 | // Do it right and sort out both F1 and F2 77 | vec4 d = min(d1,d2); // F1 is now in d 78 | d2 = max(d1,d2); // Make sure we keep all candidates for F2 79 | d.xy = (d.x < d.y) ? d.xy : d.yx; // Swap smallest to d.x 80 | d.xz = (d.x < d.z) ? d.xz : d.zx; 81 | d.xw = (d.x < d.w) ? d.xw : d.wx; // F1 is now in d.x 82 | d.yzw = min(d.yzw, d2.yzw); // F2 now not in d2.yzw 83 | d.y = min(d.y, d.z); // nor in d.z 84 | d.y = min(d.y, d.w); // nor in d.w 85 | d.y = min(d.y, d2.x); // F2 is now in d.y 86 | return sqrt(d.xy); // F1 and F2 87 | #endif 88 | } 89 | -------------------------------------------------------------------------------- /rendering/ShaderMaterialParameters.hx: -------------------------------------------------------------------------------- 1 | package rendering; 2 | 3 | import three.Blending; 4 | import three.BlendingDstFactor; 5 | import three.BlendingEquation; 6 | import three.BlendingSrcFactor; 7 | import three.DepthModes; 8 | import three.GLSLVersion; 9 | import three.Plane; 10 | import three.Side; 11 | import three.StencilFunc; 12 | import three.StencilOp; 13 | 14 | typedef ShaderMaterialParameters = { 15 | @:optional 16 | var uniforms : T; 17 | @:optional 18 | var vertexShader : String; 19 | @:optional 20 | var fragmentShader : String; 21 | @:optional 22 | var linewidth : Float; 23 | @:optional 24 | var wireframe : Bool; 25 | @:optional 26 | var wireframeLinewidth : Float; 27 | @:optional 28 | var lights : Bool; 29 | @:optional 30 | var clipping : Bool; 31 | @:optional 32 | var skinning : Bool; 33 | @:optional 34 | var morphTargets : Bool; 35 | @:optional 36 | var morphNormals : Bool; 37 | @:optional 38 | var extensions : { 39 | @:optional 40 | var derivatives : Bool; 41 | @:optional 42 | var fragDepth : Bool; 43 | @:optional 44 | var drawBuffers : Bool; 45 | @:optional 46 | var shaderTextureLOD : Bool; 47 | }; 48 | @:optional 49 | var glslVersion : GLSLVersion; 50 | @:optional 51 | var alphaTest : Float; 52 | @:optional 53 | var blendDst : BlendingDstFactor; 54 | @:optional 55 | var blendDstAlpha : Float; 56 | @:optional 57 | var blendEquation : BlendingEquation; 58 | @:optional 59 | var blendEquationAlpha : Float; 60 | @:optional 61 | var blending : Blending; 62 | @:optional 63 | var blendSrc : ts.AnyOf2; 64 | @:optional 65 | var blendSrcAlpha : Float; 66 | @:optional 67 | var clipIntersection : Bool; 68 | @:optional 69 | var clippingPlanes : Array; 70 | @:optional 71 | var clipShadows : Bool; 72 | @:optional 73 | var colorWrite : Bool; 74 | @:optional 75 | var defines : Dynamic; 76 | @:optional 77 | var depthFunc : DepthModes; 78 | @:optional 79 | var depthTest : Bool; 80 | @:optional 81 | var depthWrite : Bool; 82 | @:optional 83 | var fog : Bool; 84 | @:optional 85 | var name : String; 86 | @:optional 87 | var opacity : Float; 88 | @:optional 89 | var polygonOffset : Bool; 90 | @:optional 91 | var polygonOffsetFactor : Float; 92 | @:optional 93 | var polygonOffsetUnits : Float; 94 | @:optional 95 | var precision : String; 96 | @:optional 97 | var premultipliedAlpha : Bool; 98 | @:optional 99 | var dithering : Bool; 100 | @:optional 101 | var flatShading : Bool; 102 | @:optional 103 | var side : Side; 104 | @:optional 105 | var shadowSide : Side; 106 | @:optional 107 | var toneMapped : Bool; 108 | @:optional 109 | var transparent : Bool; 110 | @:optional 111 | var vertexColors : Bool; 112 | @:optional 113 | var visible : Bool; 114 | @:optional 115 | var stencilWrite : Bool; 116 | @:optional 117 | var stencilFunc : StencilFunc; 118 | @:optional 119 | var stencilRef : Float; 120 | @:optional 121 | var stencilMask : Float; 122 | @:optional 123 | var stencilFail : StencilOp; 124 | @:optional 125 | var stencilZFail : StencilOp; 126 | @:optional 127 | var stencilZPass : StencilOp; 128 | }; -------------------------------------------------------------------------------- /rendering/PosDeltaSampler.hx: -------------------------------------------------------------------------------- 1 | package rendering; 2 | 3 | import three.RawShaderMaterial; 4 | import three.Texture; 5 | import three.Uniform; 6 | import three.Vector2; 7 | import three.WebGLRenderer; 8 | 9 | enum abstract DataFormat(Int) { 10 | /** 11 | [ p q ] 12 | position = texture2D(uPosDelta, 0.5).xy 13 | lastPosition = texture2D(uPosDelta, 0.5).zw 14 | **/ 15 | var SinglePixel; 16 | 17 | /** 18 | Use if position contains more than two values 19 | [ p, q ] 20 | position = texture2D(uPosDelta, vec2(0.25, 0.5)); 21 | lastPosition = texture2D(uPosDelta, vec2(0.75, 0.5)); 22 | **/ 23 | var DoublePixel; 24 | } 25 | 26 | class PosDeltaSampler { 27 | 28 | public final uPosTexture = new Uniform(cast null); 29 | public final dataFormat: DataFormat; 30 | 31 | final renderer: WebGLRenderer; 32 | final fragmentRenderer: FragmentRenderer; 33 | final positionTexture: Texture; 34 | final renderTarget: DualRenderTarget; 35 | final shader: RawShaderMaterial; 36 | 37 | final uPointerUv = new Uniform(new Vector2()); 38 | 39 | public function new(renderer: WebGLRenderer, positionTexture: Texture, dataFormat: DataFormat) { 40 | this.renderer = renderer; 41 | this.fragmentRenderer = new FragmentRenderer(renderer); 42 | this.positionTexture = positionTexture; 43 | this.dataFormat = dataFormat; 44 | 45 | this.renderTarget = new DualRenderTarget(renderer, 2, 1, { 46 | minFilter: NearestFilter, 47 | magFilter: NearestFilter, 48 | encoding: LinearEncoding, 49 | type: positionTexture.type, 50 | stencilBuffer: false, 51 | depthBuffer: false, 52 | anisotropy: 0, 53 | format: positionTexture.format, 54 | generateMipmaps: false, 55 | }); 56 | 57 | this.shader = new RawShaderMaterial({ 58 | uniforms: { 59 | uPointerUv: uPointerUv, 60 | uPositionTexture: new Uniform(positionTexture), 61 | uLastFrameTexture: renderTarget.uniform, 62 | }, 63 | vertexShader: ' 64 | attribute vec2 position; 65 | 66 | varying vec2 vUv; 67 | 68 | void main() { 69 | vUv = position * 0.5 + 0.5; 70 | gl_Position = vec4(position, 0., 1.); 71 | } 72 | ', 73 | fragmentShader: ' 74 | precision highp float; 75 | 76 | uniform vec2 uPointerUv; 77 | 78 | uniform sampler2D uLastFrameTexture; 79 | uniform sampler2D uPositionTexture; 80 | 81 | ${switch dataFormat { case DoublePixel: 'varying vec2 vUv;'; case _: ''; }} 82 | 83 | void main() { 84 | ${switch dataFormat { 85 | case SinglePixel: ' 86 | gl_FragColor = vec4( 87 | texture2D(uPositionTexture, uPointerUv).xy, 88 | texture2D(uLastFrameTexture, vec2(0.5)).xy 89 | ); 90 | '; 91 | case DoublePixel: ' 92 | if (vUv.x > 0.5) { 93 | gl_FragColor = texture2D(uLastFrameTexture, vec2(0.25, 0.5)); 94 | } else { 95 | gl_FragColor = texture2D(uPositionTexture, uPointerUv); 96 | } 97 | '; 98 | }} 99 | } 100 | ', 101 | }); 102 | } 103 | 104 | public function update(pointerUv: Vector2) { 105 | (this.uPointerUv.value: Vector2).copy(pointerUv); 106 | 107 | this.fragmentRenderer.render(renderTarget.getRenderTarget(), shader); 108 | renderTarget.swap(); 109 | 110 | uPosTexture.value = renderTarget.getTexture(); 111 | } 112 | 113 | } -------------------------------------------------------------------------------- /shaders/noise/noise3D.glsl: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Array and textureless GLSL 2D/3D/4D simplex 3 | // noise functions. 4 | // Author : Ian McEwan, Ashima Arts. 5 | // Maintainer : stegu 6 | // Lastmod : 20110822 (ijm) 7 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 8 | // Distributed under the MIT License. See LICENSE file. 9 | // https://github.com/ashima/webgl-noise 10 | // https://github.com/stegu/webgl-noise 11 | // 12 | 13 | vec3 mod289(vec3 x) { 14 | return x - floor(x * (1.0 / 289.0)) * 289.0; 15 | } 16 | 17 | vec4 mod289(vec4 x) { 18 | return x - floor(x * (1.0 / 289.0)) * 289.0; 19 | } 20 | 21 | vec4 permute(vec4 x) { 22 | return mod289(((x*34.0)+1.0)*x); 23 | } 24 | 25 | vec4 taylorInvSqrt(vec4 r) 26 | { 27 | return 1.79284291400159 - 0.85373472095314 * r; 28 | } 29 | 30 | float snoise(vec3 v) 31 | { 32 | const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; 33 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); 34 | 35 | // First corner 36 | vec3 i = floor(v + dot(v, C.yyy) ); 37 | vec3 x0 = v - i + dot(i, C.xxx) ; 38 | 39 | // Other corners 40 | vec3 g = step(x0.yzx, x0.xyz); 41 | vec3 l = 1.0 - g; 42 | vec3 i1 = min( g.xyz, l.zxy ); 43 | vec3 i2 = max( g.xyz, l.zxy ); 44 | 45 | // x0 = x0 - 0.0 + 0.0 * C.xxx; 46 | // x1 = x0 - i1 + 1.0 * C.xxx; 47 | // x2 = x0 - i2 + 2.0 * C.xxx; 48 | // x3 = x0 - 1.0 + 3.0 * C.xxx; 49 | vec3 x1 = x0 - i1 + C.xxx; 50 | vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y 51 | vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y 52 | 53 | // Permutations 54 | i = mod289(i); 55 | vec4 p = permute( permute( permute( 56 | i.z + vec4(0.0, i1.z, i2.z, 1.0 )) 57 | + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) 58 | + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); 59 | 60 | // Gradients: 7x7 points over a square, mapped onto an octahedron. 61 | // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) 62 | float n_ = 0.142857142857; // 1.0/7.0 63 | vec3 ns = n_ * D.wyz - D.xzx; 64 | 65 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) 66 | 67 | vec4 x_ = floor(j * ns.z); 68 | vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N) 69 | 70 | vec4 x = x_ *ns.x + ns.yyyy; 71 | vec4 y = y_ *ns.x + ns.yyyy; 72 | vec4 h = 1.0 - abs(x) - abs(y); 73 | 74 | vec4 b0 = vec4( x.xy, y.xy ); 75 | vec4 b1 = vec4( x.zw, y.zw ); 76 | 77 | //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; 78 | //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; 79 | vec4 s0 = floor(b0)*2.0 + 1.0; 80 | vec4 s1 = floor(b1)*2.0 + 1.0; 81 | vec4 sh = -step(h, vec4(0.0)); 82 | 83 | vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; 84 | vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; 85 | 86 | vec3 p0 = vec3(a0.xy,h.x); 87 | vec3 p1 = vec3(a0.zw,h.y); 88 | vec3 p2 = vec3(a1.xy,h.z); 89 | vec3 p3 = vec3(a1.zw,h.w); 90 | 91 | //Normalise gradients 92 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 93 | p0 *= norm.x; 94 | p1 *= norm.y; 95 | p2 *= norm.z; 96 | p3 *= norm.w; 97 | 98 | // Mix final noise value 99 | vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); 100 | m = m * m; 101 | return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), 102 | dot(p2,x2), dot(p3,x3) ) ); 103 | } 104 | -------------------------------------------------------------------------------- /shaders/noise/classicnoise2D.glsl: -------------------------------------------------------------------------------- 1 | // 2 | // GLSL textureless classic 2D noise "cnoise", 3 | // with an RSL-style periodic variant "pnoise". 4 | // Author: Stefan Gustavson (stefan.gustavson@liu.se) 5 | // Version: 2011-08-22 6 | // 7 | // Many thanks to Ian McEwan of Ashima Arts for the 8 | // ideas for permutation and gradient selection. 9 | // 10 | // Copyright (c) 2011 Stefan Gustavson. All rights reserved. 11 | // Distributed under the MIT license. See LICENSE file. 12 | // https://github.com/stegu/webgl-noise 13 | // 14 | 15 | vec4 mod289(vec4 x) 16 | { 17 | return x - floor(x * (1.0 / 289.0)) * 289.0; 18 | } 19 | 20 | vec4 permute(vec4 x) 21 | { 22 | return mod289(((x*34.0)+1.0)*x); 23 | } 24 | 25 | vec4 taylorInvSqrt(vec4 r) 26 | { 27 | return 1.79284291400159 - 0.85373472095314 * r; 28 | } 29 | 30 | vec2 fade(vec2 t) { 31 | return t*t*t*(t*(t*6.0-15.0)+10.0); 32 | } 33 | 34 | // Classic Perlin noise 35 | float cnoise(vec2 P) 36 | { 37 | vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0); 38 | vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0); 39 | Pi = mod289(Pi); // To avoid truncation effects in permutation 40 | vec4 ix = Pi.xzxz; 41 | vec4 iy = Pi.yyww; 42 | vec4 fx = Pf.xzxz; 43 | vec4 fy = Pf.yyww; 44 | 45 | vec4 i = permute(permute(ix) + iy); 46 | 47 | vec4 gx = fract(i * (1.0 / 41.0)) * 2.0 - 1.0 ; 48 | vec4 gy = abs(gx) - 0.5 ; 49 | vec4 tx = floor(gx + 0.5); 50 | gx = gx - tx; 51 | 52 | vec2 g00 = vec2(gx.x,gy.x); 53 | vec2 g10 = vec2(gx.y,gy.y); 54 | vec2 g01 = vec2(gx.z,gy.z); 55 | vec2 g11 = vec2(gx.w,gy.w); 56 | 57 | vec4 norm = taylorInvSqrt(vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); 58 | g00 *= norm.x; 59 | g01 *= norm.y; 60 | g10 *= norm.z; 61 | g11 *= norm.w; 62 | 63 | float n00 = dot(g00, vec2(fx.x, fy.x)); 64 | float n10 = dot(g10, vec2(fx.y, fy.y)); 65 | float n01 = dot(g01, vec2(fx.z, fy.z)); 66 | float n11 = dot(g11, vec2(fx.w, fy.w)); 67 | 68 | vec2 fade_xy = fade(Pf.xy); 69 | vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x); 70 | float n_xy = mix(n_x.x, n_x.y, fade_xy.y); 71 | return 2.3 * n_xy; 72 | } 73 | 74 | // Classic Perlin noise, periodic variant 75 | float pnoise(vec2 P, vec2 rep) 76 | { 77 | vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0); 78 | vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0); 79 | Pi = mod(Pi, rep.xyxy); // To create noise with explicit period 80 | Pi = mod289(Pi); // To avoid truncation effects in permutation 81 | vec4 ix = Pi.xzxz; 82 | vec4 iy = Pi.yyww; 83 | vec4 fx = Pf.xzxz; 84 | vec4 fy = Pf.yyww; 85 | 86 | vec4 i = permute(permute(ix) + iy); 87 | 88 | vec4 gx = fract(i * (1.0 / 41.0)) * 2.0 - 1.0 ; 89 | vec4 gy = abs(gx) - 0.5 ; 90 | vec4 tx = floor(gx + 0.5); 91 | gx = gx - tx; 92 | 93 | vec2 g00 = vec2(gx.x,gy.x); 94 | vec2 g10 = vec2(gx.y,gy.y); 95 | vec2 g01 = vec2(gx.z,gy.z); 96 | vec2 g11 = vec2(gx.w,gy.w); 97 | 98 | vec4 norm = taylorInvSqrt(vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); 99 | g00 *= norm.x; 100 | g01 *= norm.y; 101 | g10 *= norm.z; 102 | g11 *= norm.w; 103 | 104 | float n00 = dot(g00, vec2(fx.x, fy.x)); 105 | float n10 = dot(g10, vec2(fx.y, fy.y)); 106 | float n01 = dot(g01, vec2(fx.z, fy.z)); 107 | float n11 = dot(g11, vec2(fx.w, fy.w)); 108 | 109 | vec2 fade_xy = fade(Pf.xy); 110 | vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x); 111 | float n_xy = mix(n_x.x, n_x.y, fade_xy.y); 112 | return 2.3 * n_xy; 113 | } 114 | -------------------------------------------------------------------------------- /rendering/RenderTargetStore.hx: -------------------------------------------------------------------------------- 1 | package rendering; 2 | 3 | import js.html.webgl.WebGL2RenderingContext; 4 | import three.WebGLMultisampleRenderTarget; 5 | import three.WebGLRenderTarget; 6 | import three.WebGLRenderTargetOptions; 7 | import three.WebGLRenderer; 8 | 9 | class RenderTargetStore { 10 | 11 | final map: Map; 12 | final renderer: WebGLRenderer; 13 | final maxMsaaSamples: Int; 14 | 15 | public function new(renderer: WebGLRenderer) { 16 | this.renderer = renderer; 17 | this.map = new Map(); 18 | var gl = renderer.getContext(); 19 | this.maxMsaaSamples = renderer.capabilities.isWebGL2 ? gl.getParameter(WebGL2RenderingContext.MAX_SAMPLES) : 0; 20 | } 21 | 22 | /** 23 | Creates render target with specified options if one does not exist, or synchronizes options (including resizing) if one does 24 | 25 | Content is undefined 26 | **/ 27 | public function acquire(uid: String, width: Float, height: Float, options: WebGLRenderTargetOptions & { ?msaaSamples: Int }, alwaysSyncOptions: Bool = false): rendering.WebGLRenderTarget { 28 | var target = this.map.get(uid); 29 | 30 | var needsNew = target == null; 31 | 32 | var msaaSamples = Math.min(maxMsaaSamples, options.msaaSamples != null ? options.msaaSamples : 0); 33 | 34 | // here options may change at runtime so we check if the options are correct and create a new target if mismatching 35 | if (alwaysSyncOptions && !needsNew) { 36 | needsNew = ( 37 | (options.depthBuffer != null && options.depthBuffer != target.depthBuffer) || 38 | (options.stencilBuffer != null && options.stencilBuffer != target.stencilBuffer) || 39 | (options.depthTexture != null && options.depthTexture != target.depthTexture) 40 | ) || ( 41 | (options.wrapS != null && target.texture.wrapS != options.wrapS) || 42 | (options.wrapT != null && target.texture.wrapT != options.wrapT) || 43 | (options.magFilter != null && target.texture.magFilter != options.magFilter) || 44 | (options.minFilter != null && target.texture.minFilter != options.minFilter) || 45 | (options.format != null && target.texture.format != options.format) || 46 | (options.type != null && target.texture.type != options.type) || 47 | (options.anisotropy != null && target.texture.anisotropy != options.anisotropy) || 48 | ((cast target: WebGLMultisampleRenderTarget).samples != msaaSamples) 49 | ); 50 | } 51 | 52 | if (needsNew) { 53 | if (target != null) { 54 | target.dispose(); 55 | } 56 | target = if (msaaSamples > 0) { 57 | var _ = new WebGLMultisampleRenderTarget(width, height, options); 58 | _.samples = msaaSamples; 59 | _; 60 | } else { 61 | new WebGLRenderTarget(width, height, options); 62 | } 63 | Reflect.setField(target, 'name', uid); 64 | this.map.set(uid, target); 65 | } else { 66 | // synchronize props 67 | target.texture.generateMipmaps = options.generateMipmaps; 68 | target.texture.encoding = options.encoding; 69 | 70 | var needsResize = width != target.width || height != target.height; 71 | if (needsResize) { 72 | target.setSize(width, height); 73 | } 74 | } 75 | 76 | return target; 77 | } 78 | 79 | public function clearAndDispose() { 80 | for (target in this.map) { 81 | target.dispose(); 82 | } 83 | this.map.clear(); 84 | } 85 | 86 | public inline function get(uid: String) return this.map.get(uid); 87 | 88 | public inline function exists(uid: String) return this.map.exists(uid); 89 | 90 | public inline function iterator() return this.map.iterator(); 91 | public inline function keyValueIterator() return this.map.keyValueIterator(); 92 | 93 | } -------------------------------------------------------------------------------- /event/PointerState.hx: -------------------------------------------------------------------------------- 1 | package event; 2 | 3 | @:publicFields 4 | @:structInit 5 | @:unreflective 6 | class PointerState { 7 | 8 | /** 9 | Unique identifier for the pointer. 10 | See https://www.w3.org/TR/pointerevents/#dom-pointerevent-pointerid 11 | **/ 12 | final pointerId: Int; 13 | 14 | /** 15 | See https://www.w3.org/TR/pointerevents/#dom-pointerevent-pointertype 16 | **/ 17 | final pointerType: PointerType; 18 | 19 | /** 20 | See https://www.w3.org/TR/pointerevents/#dfn-primary-pointer 21 | **/ 22 | final isPrimary: Bool; 23 | 24 | /** 25 | Current state of the pointer's buttons as a bitmask. 26 | See https://www.w3.org/TR/pointerevents/#the-buttons-property 27 | **/ 28 | var buttons: ButtonState; 29 | 30 | /** 31 | Horizontal position in units of **points** where 0 corresponds to the left of the view 32 | **/ 33 | var x: Float; 34 | 35 | /** 36 | Vertical position in units of **points** where 0 corresponds to the top of the view 37 | **/ 38 | var y: Float; 39 | 40 | /** 41 | Horizontal dimension in units of **points** for inputs with a contact size (defaults to 1 for point-like inputs) 42 | **/ 43 | var width: Float; 44 | 45 | /** 46 | Vertical dimension in units of **points** for inputs with a contact size (defaults to 1 for point-like inputs) 47 | **/ 48 | var height: Float; 49 | 50 | /** 51 | Width of view where the event occurred in **points** units 52 | **/ 53 | var viewWidth: Float; 54 | 55 | /** 56 | Height of view where the event occurred in **points** units 57 | **/ 58 | var viewHeight: Float; 59 | 60 | /** 61 | Normalized pressure ranging from 0 to 1. For hardware that does not support pressure this value will be 0.5. 62 | See https://www.w3.org/TR/pointerevents/#dom-pointerevent-pressure 63 | **/ 64 | var pressure: Float; 65 | 66 | /** 67 | See https://www.w3.org/TR/pointerevents/#dom-pointerevent-tangentialpressure 68 | **/ 69 | var tangentialPressure: Float; 70 | 71 | /** 72 | Pen tilt in the horizontal direction in units of **degrees**, ranging from -90 to 90. 73 | See https://www.w3.org/TR/pointerevents/#dom-pointerevent-tiltx 74 | **/ 75 | var tiltX: Float; 76 | 77 | /** 78 | Pen tilt in the vertical direction in units of **degrees**, ranging from -90 to 90. 79 | See https://www.w3.org/TR/pointerevents/#dom-pointerevent-tilty 80 | **/ 81 | var tiltY: Float; 82 | 83 | /** 84 | Clockwise rotation in units of **degrees** (see `rotationAngle` for touches https://w3c.github.io/touch-events/#dom-touch-rotationangle) 85 | **/ 86 | var twist: Float; 87 | 88 | } 89 | 90 | enum abstract PointerType(String) to String from String { 91 | var MOUSE = "mouse"; 92 | var PEN = "pen"; 93 | var TOUCH = "touch"; 94 | } 95 | 96 | /** 97 | * Bit offsets for buttons state bit mask 98 | * See https://www.w3.org/TR/pointerevents/#the-buttons-property 99 | */ 100 | enum abstract Button(Int) to Int from Int { 101 | 102 | /** 103 | * left mouse / touch contact 104 | */ 105 | final Primary = 1 << 0; 106 | 107 | /** 108 | * right mouse / pen-barrel 109 | */ 110 | final Secondary = 1 << 1; 111 | 112 | final MiddleMouse = 1; 113 | 114 | /** 115 | * X1 / back mouse 116 | */ 117 | final Back = 1 << 3; 118 | 119 | /** 120 | * X2 / forward mouse 121 | */ 122 | final Forward = 1 << 4; 123 | 124 | final PenEraser = 1 << 5; 125 | 126 | } 127 | 128 | abstract ButtonState(Int) to Int from Int { 129 | 130 | public inline function isDown(button: Button) { 131 | return this & button != 0; 132 | } 133 | 134 | /** No buttons pressed */ 135 | public inline function isEmpty() { 136 | return this == 0; 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /shaders/noise/noise3Dgrad.glsl: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Array and textureless GLSL 2D/3D/4D simplex 3 | // noise functions. 4 | // Author : Ian McEwan, Ashima Arts. 5 | // Maintainer : stegu 6 | // Lastmod : 20150104 (JcBernack) 7 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 8 | // Distributed under the MIT License. See LICENSE file. 9 | // https://github.com/ashima/webgl-noise 10 | // https://github.com/stegu/webgl-noise 11 | // 12 | 13 | vec3 mod289(vec3 x) { 14 | return x - floor(x * (1.0 / 289.0)) * 289.0; 15 | } 16 | 17 | vec4 mod289(vec4 x) { 18 | return x - floor(x * (1.0 / 289.0)) * 289.0; 19 | } 20 | 21 | vec4 permute(vec4 x) { 22 | return mod289(((x*34.0)+1.0)*x); 23 | } 24 | 25 | vec4 taylorInvSqrt(vec4 r) 26 | { 27 | return 1.79284291400159 - 0.85373472095314 * r; 28 | } 29 | 30 | float snoise(vec3 v, out vec3 gradient) 31 | { 32 | const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; 33 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); 34 | 35 | // First corner 36 | vec3 i = floor(v + dot(v, C.yyy) ); 37 | vec3 x0 = v - i + dot(i, C.xxx) ; 38 | 39 | // Other corners 40 | vec3 g = step(x0.yzx, x0.xyz); 41 | vec3 l = 1.0 - g; 42 | vec3 i1 = min( g.xyz, l.zxy ); 43 | vec3 i2 = max( g.xyz, l.zxy ); 44 | 45 | // x0 = x0 - 0.0 + 0.0 * C.xxx; 46 | // x1 = x0 - i1 + 1.0 * C.xxx; 47 | // x2 = x0 - i2 + 2.0 * C.xxx; 48 | // x3 = x0 - 1.0 + 3.0 * C.xxx; 49 | vec3 x1 = x0 - i1 + C.xxx; 50 | vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y 51 | vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y 52 | 53 | // Permutations 54 | i = mod289(i); 55 | vec4 p = permute( permute( permute( 56 | i.z + vec4(0.0, i1.z, i2.z, 1.0 )) 57 | + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) 58 | + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); 59 | 60 | // Gradients: 7x7 points over a square, mapped onto an octahedron. 61 | // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) 62 | float n_ = 0.142857142857; // 1.0/7.0 63 | vec3 ns = n_ * D.wyz - D.xzx; 64 | 65 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) 66 | 67 | vec4 x_ = floor(j * ns.z); 68 | vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N) 69 | 70 | vec4 x = x_ *ns.x + ns.yyyy; 71 | vec4 y = y_ *ns.x + ns.yyyy; 72 | vec4 h = 1.0 - abs(x) - abs(y); 73 | 74 | vec4 b0 = vec4( x.xy, y.xy ); 75 | vec4 b1 = vec4( x.zw, y.zw ); 76 | 77 | //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; 78 | //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; 79 | vec4 s0 = floor(b0)*2.0 + 1.0; 80 | vec4 s1 = floor(b1)*2.0 + 1.0; 81 | vec4 sh = -step(h, vec4(0.0)); 82 | 83 | vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; 84 | vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; 85 | 86 | vec3 p0 = vec3(a0.xy,h.x); 87 | vec3 p1 = vec3(a0.zw,h.y); 88 | vec3 p2 = vec3(a1.xy,h.z); 89 | vec3 p3 = vec3(a1.zw,h.w); 90 | 91 | //Normalise gradients 92 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 93 | p0 *= norm.x; 94 | p1 *= norm.y; 95 | p2 *= norm.z; 96 | p3 *= norm.w; 97 | 98 | // Mix final noise value 99 | vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); 100 | vec4 m2 = m * m; 101 | vec4 m4 = m2 * m2; 102 | vec4 pdotx = vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)); 103 | 104 | // Determine noise gradient 105 | vec4 temp = m2 * m * pdotx; 106 | gradient = -8.0 * (temp.x * x0 + temp.y * x1 + temp.z * x2 + temp.w * x3); 107 | gradient += m4.x * p0 + m4.y * p1 + m4.z * p2 + m4.w * p3; 108 | gradient *= 42.0; 109 | 110 | return 42.0 * dot(m4, pdotx); 111 | } 112 | -------------------------------------------------------------------------------- /rendering/FloatTexturePacker.hx: -------------------------------------------------------------------------------- 1 | package rendering; 2 | 3 | import three.Vector2; 4 | import js.lib.Float32Array; 5 | import js.lib.Uint8Array; 6 | import three.Uniform; 7 | import three.PixelFormat; 8 | import three.WebGLRenderer; 9 | import three.WebGLRenderTarget; 10 | import three.RawShaderMaterial; 11 | import three.Texture; 12 | 13 | class FloatTexturePacker { 14 | 15 | public final renderTarget: WebGLRenderTarget; 16 | final shader: RawShaderMaterial; 17 | final renderer: WebGLRenderer; 18 | final fragmentRenderer: FragmentRenderer; 19 | 20 | final uSource = new Uniform(cast null); 21 | final uRange = new Uniform(new Vector2(0, 1)); 22 | 23 | public function new(renderer: WebGLRenderer, channel: String) { 24 | this.renderer = renderer; 25 | this.fragmentRenderer = new FragmentRenderer(renderer); 26 | this.renderTarget = new WebGLRenderTarget(2, 1, { 27 | minFilter: NearestFilter, 28 | magFilter: NearestFilter, 29 | encoding: LinearEncoding, 30 | type: UnsignedByteType, 31 | stencilBuffer: false, 32 | depthBuffer: false, 33 | anisotropy: 0, 34 | format: PixelFormat.RGBAFormat, 35 | generateMipmaps: false, 36 | }); 37 | 38 | this.shader = new RawShaderMaterial({ 39 | uniforms: { 40 | uSource: uSource, 41 | uRange: uRange, 42 | }, 43 | vertexShader: ' 44 | attribute vec2 position; 45 | 46 | varying vec2 vUv; 47 | 48 | void main() { 49 | vUv = position * 0.5 + 0.5; 50 | gl_Position = vec4(position, 0., 1.); 51 | } 52 | ', 53 | fragmentShader: ' 54 | precision highp float; 55 | 56 | uniform sampler2D uSource; 57 | uniform vec2 uRange; 58 | 59 | varying vec2 vUv; 60 | 61 | vec4 packFloat8bitRGBA(in float val){ 62 | vec4 pack = vec4(1.0, 255.0, 65025.0, 16581375.0) * val; 63 | 64 | pack = fract(pack); 65 | pack -= vec4(pack.yzw / 255.0, 0.0); 66 | pack.x += step(1.0, val); 67 | 68 | return pack; 69 | } 70 | 71 | void main() { 72 | float sample = texture2D(uSource, vUv).$channel; 73 | float rangeLength = uRange.y - uRange.x; 74 | float sampleNormalized = clamp((sample - uRange.x) / rangeLength, 0., 1.); // converted to 0<->1 range 75 | gl_FragColor = packFloat8bitRGBA(sampleNormalized); 76 | } 77 | ', 78 | }); 79 | } 80 | 81 | public function render(source: Texture, width: Int, height: Int, min: Float, max: Float) { 82 | if (renderTarget.width != width || renderTarget.height != height) { 83 | renderTarget.setSize(width, height); 84 | } 85 | uSource.value = source; 86 | (uRange.value: Vector2).x = min; 87 | (uRange.value: Vector2).y = max; 88 | 89 | fragmentRenderer.render(renderTarget, shader); 90 | } 91 | 92 | public function readFloats(source: Texture, x: Int, y: Int, width: Int, height: Int, min: Float, max: Float): Float32Array { 93 | render(source, width, height, min, max); 94 | 95 | var pixelCount = width * height; 96 | var bytes = new Uint8Array(pixelCount * 4); 97 | renderer.readRenderTargetPixels(renderTarget, x, y, width, height, bytes); 98 | 99 | // unpack bytes to floats 100 | var floats = new Float32Array(pixelCount); 101 | var rangeLength = max - min; 102 | for (i in 0...pixelCount) { 103 | var offset = i * 4; 104 | floats[i] = unpackFloat8bitRGBA( 105 | bytes[offset + 0], 106 | bytes[offset + 1], 107 | bytes[offset + 2], 108 | bytes[offset + 3] 109 | ) * rangeLength + min; 110 | } 111 | 112 | return floats; 113 | } 114 | 115 | inline function unpackFloat8bitRGBA(b0: Int, b1: Int, b2: Int, b3: Int){ 116 | return ( 117 | b0 + 118 | b1 / 255.0 + 119 | b2 / 65025.0 + 120 | b3 / 16581375.0 121 | ) / 255.0; 122 | } 123 | 124 | } -------------------------------------------------------------------------------- /tool/WebGLPerformanceMonitor.hx: -------------------------------------------------------------------------------- 1 | package tool; 2 | 3 | import js.Lib; 4 | import js.html.webgl.RenderingContext; 5 | 6 | private typedef Times = { 7 | textureUploadTime_ms: Float, 8 | textureUploadCallCount: Int, 9 | programCompileTime_ms: Float, 10 | programCompileCallCount: Int, 11 | longestFrameTime_ms: Float, 12 | longestFrameChangeCount: Int, 13 | } 14 | 15 | class WebGLPerformanceMonitor { 16 | 17 | public var times: Times = { 18 | textureUploadTime_ms: 0.0, 19 | textureUploadCallCount: 0, 20 | programCompileTime_ms: 0.0, 21 | programCompileCallCount: 0, 22 | longestFrameTime_ms: 0.0, 23 | longestFrameChangeCount: 0, 24 | }; 25 | 26 | public var onTextureUpload: (functionName: String, dt_ms: Float) -> Void = (_, _) -> {}; 27 | public var onSubProgramCompile: (functionName: String, dt_ms: Float) -> Void = (_, _) -> {}; 28 | public var onProgramCompile: (functionName: String, dt_ms: Float) -> Void = (_, _) -> {}; 29 | public var onUpdate: (functionName: String, times: Times) -> Void = (_, _) -> {}; 30 | 31 | public function new(gl: RenderingContext) { 32 | // monitor texture upload 33 | 34 | var onTextureUpload = (functionName, dt_ms: Float) -> { 35 | times.textureUploadTime_ms += dt_ms; 36 | times.textureUploadCallCount++; 37 | this.onTextureUpload(functionName, dt_ms); 38 | this.onUpdate(functionName, times); 39 | } 40 | 41 | monitorFunction(gl, 'texImage2D', onTextureUpload); 42 | monitorFunction(gl, 'texSubImage2D', onTextureUpload); 43 | monitorFunction(gl, 'compressedTexImage2D', onTextureUpload); 44 | monitorFunction(gl, 'compressedTexSubImage2D', onTextureUpload); 45 | 46 | // monitor shader compilation 47 | 48 | var onSubProgramCompile = (functionName, dt_ms) -> { 49 | times.programCompileTime_ms += dt_ms; 50 | // don't increment call count because we only want to consider complete programs 51 | this.onSubProgramCompile(functionName, dt_ms); 52 | this.onUpdate(functionName, times); 53 | } 54 | 55 | var onProgramCompile = (functionName, dt_ms) -> { 56 | times.programCompileTime_ms += dt_ms; 57 | times.programCompileCallCount++; 58 | this.onProgramCompile(functionName, dt_ms); 59 | this.onUpdate(functionName, times); 60 | } 61 | 62 | monitorFunction(gl, 'linkProgram', onProgramCompile); 63 | monitorFunction(gl, 'compileShader', onSubProgramCompile); 64 | monitorFunction(gl, 'validateProgram', onSubProgramCompile); 65 | monitorFunction(gl, 'createProgram', onSubProgramCompile); 66 | monitorFunction(gl, 'createShader', onSubProgramCompile); 67 | monitorFunction(gl, 'shaderSource', onSubProgramCompile); 68 | monitorFunction(gl, 'getShaderInfoLog', onSubProgramCompile); 69 | monitorFunction(gl, 'getShaderParameter', onSubProgramCompile); 70 | monitorFunction(gl, 'getShaderSource', onSubProgramCompile); 71 | monitorFunction(gl, 'getShaderPrecisionFormat', onSubProgramCompile); 72 | monitorFunction(gl, 'getAttachedShaders', onSubProgramCompile); 73 | monitorFunction(gl, 'getProgramInfoLog', onSubProgramCompile); 74 | } 75 | 76 | public function reset() { 77 | times = { 78 | textureUploadTime_ms: 0.0, 79 | textureUploadCallCount: 0, 80 | programCompileTime_ms: 0.0, 81 | programCompileCallCount: 0, 82 | longestFrameTime_ms: 0.0, 83 | longestFrameChangeCount: 0, 84 | }; 85 | } 86 | 87 | } 88 | 89 | private function monitorFunction(obj: Any, functionName: String, onCalled: (functionName: String, dt_ms: Float) -> Void) { 90 | var obj: haxe.DynamicAccess = obj; 91 | 92 | /** @type {Function} */ 93 | var original = obj[functionName]; 94 | 95 | obj[functionName] = cast function() { 96 | var t0 = js.Browser.window.performance.now(); 97 | var ret = original.apply(Lib.nativeThis, js.Syntax.code('arguments')); 98 | var dt_ms = js.Browser.window.performance.now() - t0; 99 | onCalled(functionName, dt_ms); 100 | return ret; 101 | } 102 | } -------------------------------------------------------------------------------- /rendering/WorldPositionRenderer.hx: -------------------------------------------------------------------------------- 1 | package rendering; 2 | 3 | import three.TextureDataType; 4 | import three.Side; 5 | import rendering.RenderLayer; 6 | import three.Camera; 7 | import three.MeshBasicMaterial; 8 | import three.Object3D; 9 | import three.PixelFormat; 10 | import three.Scene; 11 | import three.ShaderMaterial; 12 | import three.WebGLRenderTarget; 13 | import three.WebGLRenderer; 14 | 15 | #if (three >= "0.133.0") 16 | private typedef Object3D = three.Object3D; 17 | #end 18 | 19 | /** 20 | **Uses the `WorldPosition` layer** 21 | Make sure to enable `WorldPosition` on objects that should be rendered 22 | **/ 23 | @:nullSafety 24 | class WorldPositionRenderer { 25 | 26 | public var width(default, null): Float; 27 | public var height(default, null): Float; 28 | 29 | public final renderTarget: WebGLRenderTarget; 30 | final renderer: WebGLRenderer; 31 | final scene: Scene; 32 | final shaderMaterial: ShaderMaterial; 33 | final renderLayer: RenderLayer; 34 | final depthPrepassLayer: RenderLayer; 35 | final depthPrepassMaterial = new MeshBasicMaterial({color: 0x00FFFF, fog: false, side: DoubleSide}); 36 | 37 | var object: Null; 38 | var depthPrepass: Bool = false; 39 | 40 | public function new( 41 | renderer: WebGLRenderer, 42 | width: Int, 43 | height: Int, 44 | renderLayer: RenderLayer = WorldPosition, 45 | depthPrepassLayer: RenderLayer = DepthPrepass, 46 | side: Side = DoubleSide, 47 | type: TextureDataType = HalfFloatType 48 | ) { 49 | this.renderer = renderer; 50 | this.width = width; 51 | this.height = height; 52 | this.renderLayer = renderLayer; 53 | this.depthPrepassLayer = depthPrepassLayer; 54 | 55 | this.renderTarget = new WebGLRenderTarget(width, height, { 56 | magFilter: LinearFilter, 57 | minFilter: LinearFilter, 58 | depthBuffer: true, 59 | generateMipmaps: false, 60 | stencilBuffer: false, 61 | anisotropy: 0, 62 | type: type, 63 | format: PixelFormat.RGBAFormat, 64 | }); 65 | 66 | this.scene = new Scene(); 67 | this.shaderMaterial = new ShaderMaterial({ 68 | uniforms: {}, 69 | vertexShader: ' 70 | varying vec3 vWorldPosition; 71 | 72 | void main() { 73 | vec4 p = vec4( position, 1.0 ); 74 | vec4 worldP = modelMatrix * p; 75 | vWorldPosition = worldP.xyz; 76 | 77 | gl_Position = projectionMatrix * viewMatrix * worldP; 78 | } 79 | ', 80 | fragmentShader: ' 81 | varying vec3 vWorldPosition; 82 | 83 | void main() { 84 | gl_FragColor = vec4(vWorldPosition, 1.0); 85 | } 86 | ', 87 | blending: NoBlending, 88 | side: side, 89 | }); 90 | 91 | this.scene.overrideMaterial = shaderMaterial; 92 | } 93 | 94 | public function setSize(width: Float, height: Float) { 95 | renderTarget.setSize(width, height); 96 | this.width = width; 97 | this.height = height; 98 | } 99 | 100 | public function setObject(object: Object3D, depthPrepass: Bool) { 101 | this.object = object; 102 | this.depthPrepass = depthPrepass; 103 | } 104 | 105 | public function render(camera: Camera) { 106 | renderer.setRenderTarget(renderTarget); 107 | var clearAlphaBefore = renderer.getClearAlpha(); 108 | renderer.setClearAlpha(0); 109 | 110 | renderer.clear(true, true, false); 111 | 112 | if (object != null) { 113 | var parent = object.parent; 114 | scene.add(object); 115 | 116 | var maskBefore = camera.layers.mask; 117 | 118 | // render depth prepass 119 | if (depthPrepass) { 120 | camera.layers.set(depthPrepassLayer); 121 | var gl = renderer.getContext(); 122 | gl.colorMask(false, false, false, false); 123 | renderer.render(scene, camera); 124 | gl.colorMask(true, true, true, true); 125 | } 126 | 127 | camera.layers.set(renderLayer); 128 | renderer.render(scene, camera); 129 | camera.layers.mask = maskBefore; 130 | 131 | scene.remove(object); 132 | if (parent != null) { 133 | parent.add(object); 134 | } 135 | } 136 | 137 | renderer.setClearAlpha(clearAlphaBefore); 138 | } 139 | 140 | } -------------------------------------------------------------------------------- /tool/Object3DTools.hx: -------------------------------------------------------------------------------- 1 | package tool; 2 | 3 | import three.Matrix4; 4 | import three.BufferGeometry; 5 | import three.Material; 6 | import three.Mesh; 7 | import VectorMath; 8 | 9 | #if (three >= "0.133.0") 10 | private typedef Object3D = three.Object3D; 11 | #end 12 | 13 | class Object3DTools { 14 | 15 | /** 16 | Recursive iteration of child objects 17 | **/ 18 | static public function iterate(obj: Object3D, cb: Object3D -> Void) { 19 | cb(obj); 20 | for (child in obj.children) { 21 | iterate(child, cb); 22 | } 23 | } 24 | 25 | /** 26 | Recursive iteration of child meshes 27 | **/ 28 | static public function iterateMeshes(obj: Object3D, cb: Mesh -> Void) { 29 | if (Std.is(obj, Mesh)) cb(cast obj); 30 | for (child in obj.children) { 31 | iterateMeshes(child, cb); 32 | } 33 | } 34 | 35 | static public function forEachDescendant(obj: Object3D, cb: Object3D -> Void) { 36 | for (child in obj.children) { 37 | cb(child); 38 | forEachDescendant(child, cb); 39 | } 40 | } 41 | 42 | static public function forEachDescendantMesh(obj: Object3D, cb: Mesh -> Void) { 43 | for (child in obj.children) { 44 | if (Std.is(child, Mesh)) cb(cast child); 45 | forEachDescendantMesh(child, cb); 46 | } 47 | } 48 | 49 | static public function findDescendant(obj: Object3D, test: Object3D -> Bool): Null { 50 | for (child in obj.children) { 51 | if (test(child)) { 52 | return child; 53 | } 54 | } 55 | for (child in obj.children) { 56 | var m = findDescendant(child, test); 57 | if (m != null) { 58 | return m; 59 | } 60 | } 61 | return null; 62 | } 63 | 64 | static public function filterDescendants(obj: Object3D, test: Object3D -> Bool): Array { 65 | var result = new Array(); 66 | for (child in obj.children) { 67 | if (test(child)) { 68 | result.push(child); 69 | } 70 | } 71 | for (child in obj.children) { 72 | var a = filterDescendants(child, test); 73 | result = result.concat(a); 74 | } 75 | return result; 76 | } 77 | 78 | static public function getAllMaterials(obj: Object3D) { 79 | var materials = new Array(); 80 | iterateMeshes(obj, mesh -> { 81 | if (mesh.material != null && materials.indexOf(mesh.material) == -1) { 82 | materials.push(mesh.material); 83 | } 84 | }); 85 | return materials; 86 | } 87 | 88 | static public function getMaterialByName(obj: Object3D, name: String): Null { 89 | if ((obj: Dynamic).material != null && (obj: Dynamic).material.name == name) { 90 | return (obj: Dynamic).material; 91 | } 92 | for (child in obj.children) { 93 | var m = getMaterialByName(child, name); 94 | if (m != null) { 95 | return m; 96 | } 97 | } 98 | return null; 99 | } 100 | 101 | /** 102 | * Replaces materials with a given name recursively within an object 103 | * @param within 104 | * @param searchMaterialName 105 | * @param replacement 106 | * @return number of replacements that occurred Int 107 | */ 108 | static public function replaceMaterial(obj: Object3D, searchMaterialName: String, replacement: Material): Int { 109 | var replacements = 0; 110 | if ((obj: Dynamic).material != null && (obj: Dynamic).material.name == searchMaterialName) { 111 | (obj: Dynamic).material = replacement; 112 | replacements += 1; 113 | } 114 | for (child in obj.children) { 115 | replacements += replaceMaterial(child, searchMaterialName, replacement); 116 | } 117 | return replacements; 118 | } 119 | 120 | static var setRotationFromBasis_tmpMatrix4 = new Matrix4(); 121 | static public inline function setRotationFromBasis(obj: Object3D, basis: { x: Vec3, y: Vec3, z: Vec3 }) { 122 | var rotationMatrix = mat4( 123 | vec4(basis.x, 0.0), 124 | vec4(basis.y, 0.0), 125 | vec4(basis.z, 0.0), 126 | vec4(0.0, 0.0, 0.0, 1.0) 127 | ); 128 | rotationMatrix.copyIntoArray(setRotationFromBasis_tmpMatrix4.elements, 0); 129 | obj.setRotationFromMatrix(setRotationFromBasis_tmpMatrix4); 130 | } 131 | 132 | } -------------------------------------------------------------------------------- /shaders/noise/noise4D.glsl: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Array and textureless GLSL 2D/3D/4D simplex 3 | // noise functions. 4 | // Author : Ian McEwan, Ashima Arts. 5 | // Maintainer : stegu 6 | // Lastmod : 20110822 (ijm) 7 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 8 | // Distributed under the MIT License. See LICENSE file. 9 | // https://github.com/ashima/webgl-noise 10 | // https://github.com/stegu/webgl-noise 11 | // 12 | 13 | vec4 mod289(vec4 x) { 14 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 15 | 16 | float mod289(float x) { 17 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 18 | 19 | vec4 permute(vec4 x) { 20 | return mod289(((x*34.0)+1.0)*x); 21 | } 22 | 23 | float permute(float x) { 24 | return mod289(((x*34.0)+1.0)*x); 25 | } 26 | 27 | vec4 taylorInvSqrt(vec4 r) 28 | { 29 | return 1.79284291400159 - 0.85373472095314 * r; 30 | } 31 | 32 | float taylorInvSqrt(float r) 33 | { 34 | return 1.79284291400159 - 0.85373472095314 * r; 35 | } 36 | 37 | vec4 grad4(float j, vec4 ip) 38 | { 39 | const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0); 40 | vec4 p,s; 41 | 42 | p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0; 43 | p.w = 1.5 - dot(abs(p.xyz), ones.xyz); 44 | s = vec4(lessThan(p, vec4(0.0))); 45 | p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www; 46 | 47 | return p; 48 | } 49 | 50 | // (sqrt(5) - 1)/4 = F4, used once below 51 | #define F4 0.309016994374947451 52 | 53 | float snoise(vec4 v) 54 | { 55 | const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4 56 | 0.276393202250021, // 2 * G4 57 | 0.414589803375032, // 3 * G4 58 | -0.447213595499958); // -1 + 4 * G4 59 | 60 | // First corner 61 | vec4 i = floor(v + dot(v, vec4(F4)) ); 62 | vec4 x0 = v - i + dot(i, C.xxxx); 63 | 64 | // Other corners 65 | 66 | // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) 67 | vec4 i0; 68 | vec3 isX = step( x0.yzw, x0.xxx ); 69 | vec3 isYZ = step( x0.zww, x0.yyz ); 70 | // i0.x = dot( isX, vec3( 1.0 ) ); 71 | i0.x = isX.x + isX.y + isX.z; 72 | i0.yzw = 1.0 - isX; 73 | // i0.y += dot( isYZ.xy, vec2( 1.0 ) ); 74 | i0.y += isYZ.x + isYZ.y; 75 | i0.zw += 1.0 - isYZ.xy; 76 | i0.z += isYZ.z; 77 | i0.w += 1.0 - isYZ.z; 78 | 79 | // i0 now contains the unique values 0,1,2,3 in each channel 80 | vec4 i3 = clamp( i0, 0.0, 1.0 ); 81 | vec4 i2 = clamp( i0-1.0, 0.0, 1.0 ); 82 | vec4 i1 = clamp( i0-2.0, 0.0, 1.0 ); 83 | 84 | // x0 = x0 - 0.0 + 0.0 * C.xxxx 85 | // x1 = x0 - i1 + 1.0 * C.xxxx 86 | // x2 = x0 - i2 + 2.0 * C.xxxx 87 | // x3 = x0 - i3 + 3.0 * C.xxxx 88 | // x4 = x0 - 1.0 + 4.0 * C.xxxx 89 | vec4 x1 = x0 - i1 + C.xxxx; 90 | vec4 x2 = x0 - i2 + C.yyyy; 91 | vec4 x3 = x0 - i3 + C.zzzz; 92 | vec4 x4 = x0 + C.wwww; 93 | 94 | // Permutations 95 | i = mod289(i); 96 | float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x); 97 | vec4 j1 = permute( permute( permute( permute ( 98 | i.w + vec4(i1.w, i2.w, i3.w, 1.0 )) 99 | + i.z + vec4(i1.z, i2.z, i3.z, 1.0 )) 100 | + i.y + vec4(i1.y, i2.y, i3.y, 1.0 )) 101 | + i.x + vec4(i1.x, i2.x, i3.x, 1.0 )); 102 | 103 | // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope 104 | // 7*7*6 = 294, which is close to the ring size 17*17 = 289. 105 | vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ; 106 | 107 | vec4 p0 = grad4(j0, ip); 108 | vec4 p1 = grad4(j1.x, ip); 109 | vec4 p2 = grad4(j1.y, ip); 110 | vec4 p3 = grad4(j1.z, ip); 111 | vec4 p4 = grad4(j1.w, ip); 112 | 113 | // Normalise gradients 114 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 115 | p0 *= norm.x; 116 | p1 *= norm.y; 117 | p2 *= norm.z; 118 | p3 *= norm.w; 119 | p4 *= taylorInvSqrt(dot(p4,p4)); 120 | 121 | // Mix contributions from the five corners 122 | vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0); 123 | vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0); 124 | m0 = m0 * m0; 125 | m1 = m1 * m1; 126 | return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 ))) 127 | + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ; 128 | 129 | } 130 | -------------------------------------------------------------------------------- /CompileTime.hx: -------------------------------------------------------------------------------- 1 | #if macro 2 | import haxe.crypto.Base64; 3 | import haxe.io.Path; 4 | import haxe.macro.Context; 5 | import haxe.macro.Expr; 6 | import haxe.macro.ExprTools; 7 | import haxe.macro.MacroStringTools; 8 | import sys.FileSystem; 9 | import sys.io.File; 10 | #end 11 | 12 | class CompileTime { 13 | 14 | static public macro function embedString(path: String) { 15 | var resolvedPath = resolvePath(path); 16 | Context.registerModuleDependency(Context.getLocalModule(), resolvedPath); 17 | return macro $v{File.getContent(resolvedPath)}; 18 | } 19 | 20 | static public macro function embedShader(path: String) { 21 | var resolvedPath = resolvePath(path); 22 | Context.registerModuleDependency(Context.getLocalModule(), resolvedPath); 23 | var content = File.getContent(resolvedPath); 24 | return macro ${MacroStringTools.formatString(content, Context.currentPos())}; 25 | } 26 | 27 | static public macro function embedBase64(path: String) { 28 | var resolvedPath = resolvePath(path); 29 | Context.registerModuleDependency(Context.getLocalModule(), resolvedPath); 30 | return macro $v{Base64.encode(File.getBytes(resolvedPath))}; 31 | } 32 | 33 | static public macro function embedImageDataUri(path: String, ?mimeType: String) { 34 | var resolvedPath = resolvePath(path); 35 | Context.registerModuleDependency(Context.getLocalModule(), resolvedPath); 36 | if (mimeType == null) { 37 | mimeType = switch Path.extension(path).toLowerCase() { 38 | case 'bmp': 'image/bmp'; 39 | case 'gif': 'image/gif'; 40 | case 'ico': 'image/vnd.microsoft.icon'; 41 | case 'png': 'image/png'; 42 | case 'svg': 'image/svg+xml'; 43 | case 'webp': 'image/webp'; 44 | case 'jpeg', 'jpg': 'image/jpeg'; 45 | case 'tif', 'tiff': 'image/tiff'; 46 | default: 'image/xyz'; // let the browser figure it out 47 | } 48 | } 49 | return macro 'data:image/' + $v{mimeType} + ';base64,' + $v{Base64.encode(File.getBytes(resolvedPath))}; 50 | } 51 | 52 | static public macro function embed3dModelDataUri(path: String, ?mimeType: String) { 53 | var resolvedPath = resolvePath(path); 54 | Context.registerModuleDependency(Context.getLocalModule(), resolvedPath); 55 | if (mimeType == null) { 56 | mimeType = switch Path.extension(path).toLowerCase() { 57 | case 'glb': 'model/gltf-binary'; 58 | case 'gltf': 'model/gltf+json'; 59 | case 'stl': 'model/stl'; 60 | case 'obj': 'model/obj'; 61 | case 'mtl': 'model/mtl'; 62 | case 'wrl' | 'wrz': 'model/vrml'; 63 | default: 'application/octet-stream'; 64 | } 65 | } 66 | return macro 'data:image/' + $v{mimeType} + ';base64,' + $v{Base64.encode(File.getBytes(resolvedPath))}; 67 | } 68 | 69 | static public macro function embedJson(path:String) { 70 | var resolvedPath = resolvePath(path); 71 | return try { 72 | var json = haxe.Json.parse(sys.io.File.getContent(resolvedPath)); 73 | macro $v{json}; 74 | } catch (e) { 75 | haxe.macro.Context.error('Failed to load json: $e', haxe.macro.Context.currentPos()); 76 | } 77 | } 78 | 79 | static public macro function getPathsInDirectory(directoryPath: String, ?matching: ExprOf) { 80 | var resolvedPath = resolvePath(directoryPath); 81 | var filenames = sys.FileSystem.readDirectory(resolvedPath); 82 | var paths = filenames.map(filename -> Path.join([directoryPath, filename])); 83 | 84 | switch matching.expr { 85 | case EConst(CIdent("null")): // no filtering 86 | case EConst(CRegexp(reg, opt)): 87 | var pattern = new EReg(reg, opt); 88 | paths = paths.filter(path -> pattern.match(path)); 89 | default: 90 | Context.error("\"matching\" must be a regex expression or null", matching.pos); 91 | } 92 | return macro $v{paths}; 93 | } 94 | 95 | static public macro function checkFile(path: String) { 96 | if (!sys.FileSystem.exists(path)) { 97 | Context.error('File not found "$path"', Context.currentPos()); 98 | } 99 | return macro $v{path}; 100 | } 101 | 102 | static public macro function haxeVersion() { 103 | return macro $v{Context.definedValue('haxe')}; 104 | } 105 | 106 | #if macro 107 | static function resolvePath(path: String) { 108 | if (!Path.isAbsolute(path)) { 109 | var pos = haxe.macro.PositionTools.toLocation(Context.currentPos()); 110 | var directory = Path.directory(pos.file.toString()); 111 | var localPath = Path.join([directory, path]); 112 | 113 | if (FileSystem.exists(localPath)) { 114 | return localPath; 115 | } 116 | } 117 | return Context.resolvePath(path); 118 | } 119 | #end 120 | 121 | } -------------------------------------------------------------------------------- /noise/Snoise.hx: -------------------------------------------------------------------------------- 1 | package noise; 2 | 3 | import VectorMath; 4 | 5 | // translated from noise4Dgrad.glsl 6 | // 7 | // Description : Array and textureless GLSL 2D/3D/4D simplex 8 | // noise functions. 9 | // Author : Ian McEwan, Ashima Arts. 10 | // Maintainer : stegu 11 | // Lastmod : 20110822 (ijm) 12 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 13 | // Distributed under the MIT License. See LICENSE file. 14 | // https://github.com/ashima/webgl-noise 15 | // https://github.com/stegu/webgl-noise 16 | // 17 | // Gradient Calculation extended from noise3Dgrad.glsl by Mark A. Ropper (@Markyparky56) 18 | 19 | private overload extern inline function permute(x: Vec4): Vec4 return mod(((x*34.0)+1.0)*x, 289); 20 | private overload extern inline function permute(x: Float): Float return mod(((x*34.0)+1.0)*x, 289); 21 | 22 | private overload extern inline function taylorInvSqrt(r: Vec4): Vec4 return 1.79284291400159 - 0.85373472095314 * r; 23 | private overload extern inline function taylorInvSqrt(r: Float): Float return 1.79284291400159 - 0.85373472095314 * r; 24 | 25 | private inline function lessThan(a: Vec4, b: Vec4) { 26 | return vec4( 27 | a.x < b.x ? 1 : 0, 28 | a.y < b.y ? 1 : 0, 29 | a.z < b.z ? 1 : 0, 30 | a.w < b.w ? 1 : 0 31 | ); 32 | } 33 | 34 | inline function grad4(j: Float, ip: Vec4) { 35 | final ones = vec4(1.0, 1.0, 1.0, -1.0); 36 | var p = vec4(0.); 37 | 38 | p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0; 39 | p.w = 1.5 - dot(abs(p.xyz), ones.xyz); 40 | var s = vec4(lessThan(p, vec4(0.0))); 41 | p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www; 42 | 43 | return p; 44 | } 45 | 46 | // (sqrt(5) - 1)/4 = F4, used once below 47 | private inline final F4 = 0.309016994374947451; 48 | 49 | function snoise(v: Vec4, outGradient: Vec4) { 50 | final C = vec4( 51 | 0.138196601125011, // (5 - sqrt(5))/20 G4 52 | 0.276393202250021, // 2 * G4 53 | 0.414589803375032, // 3 * G4 54 | -0.447213595499958); // -1 + 4 * G4 55 | 56 | // First corner 57 | var i = floor(v + dot(v, vec4(F4)) ); 58 | var x0 = v - i + dot(i, C.xxxx); 59 | 60 | // Other corners 61 | 62 | // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) 63 | var i0 = vec4(0.); 64 | var isX = step( x0.yzw, x0.xxx ); 65 | var isYZ = step( x0.zww, x0.yyz ); 66 | // i0.x = dot( isX, vec3( 1.0 ) ); 67 | i0.x = isX.x + isX.y + isX.z; 68 | i0.yzw = 1.0 - isX; 69 | // i0.y += dot( isYZ.xy, vec2( 1.0 ) ); 70 | i0.y += isYZ.x + isYZ.y; 71 | i0.zw += 1.0 - isYZ.xy; 72 | i0.z += isYZ.z; 73 | i0.w += 1.0 - isYZ.z; 74 | 75 | // i0 now contains the unique values 0,1,2,3 in each channel 76 | var i3 = clamp( i0, 0.0, 1.0 ); 77 | var i2 = clamp( i0-1.0, 0.0, 1.0 ); 78 | var i1 = clamp( i0-2.0, 0.0, 1.0 ); 79 | 80 | // x0 = x0 - 0.0 + 0.0 * C.xxxx 81 | // x1 = x0 - i1 + 1.0 * C.xxxx 82 | // x2 = x0 - i2 + 2.0 * C.xxxx 83 | // x3 = x0 - i3 + 3.0 * C.xxxx 84 | // x4 = x0 - 1.0 + 4.0 * C.xxxx 85 | var x1 = x0 - i1 + C.xxxx; 86 | var x2 = x0 - i2 + C.yyyy; 87 | var x3 = x0 - i3 + C.zzzz; 88 | var x4 = x0 + C.wwww; 89 | 90 | // Permutations 91 | var i = mod(i, 289); 92 | var j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x); 93 | var j1 = permute( permute( permute( permute ( 94 | i.w + vec4(i1.w, i2.w, i3.w, 1.0 )) 95 | + i.z + vec4(i1.z, i2.z, i3.z, 1.0 )) 96 | + i.y + vec4(i1.y, i2.y, i3.y, 1.0 )) 97 | + i.x + vec4(i1.x, i2.x, i3.x, 1.0 )); 98 | 99 | // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope 100 | // 7*7*6 = 294, which is close to the ring size 17*17 = 289. 101 | var ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ; 102 | 103 | var p0 = grad4(j0, ip); 104 | var p1 = grad4(j1.x, ip); 105 | var p2 = grad4(j1.y, ip); 106 | var p3 = grad4(j1.z, ip); 107 | var p4 = grad4(j1.w, ip); 108 | 109 | // Normalise gradients 110 | var norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 111 | p0 *= norm.x; 112 | p1 *= norm.y; 113 | p2 *= norm.z; 114 | p3 *= norm.w; 115 | p4 *= taylorInvSqrt(dot(p4,p4)); 116 | 117 | // Mix contributions from the five corners 118 | var m0 = max(0.5 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0); 119 | var m1 = max(0.5 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0); 120 | var m02 = m0 * m0; 121 | var m12 = m1 * m1; 122 | var m04 = m02 * m02; 123 | var m14 = m12 * m12; 124 | var pdotx0 = vec3(dot(p0,x0), dot(p1,x1), dot(p2,x2)); 125 | var pdotx1 = vec2(dot(p3,x3), dot(p4,x4)); 126 | 127 | var temp0 = m02 * m0 * pdotx0; 128 | var temp1 = m12 * m1 * pdotx1; 129 | outGradient.copyFrom(-8.0 * (temp0.x * x0 + temp0.y * x1 + temp0.z * x2 + temp1.x * x3 + temp1.y * x4)); 130 | outGradient += m04.x * p0 + m04 .y * p1 + m04.z * p2 + m14.x * p3 + m14.y * p4; 131 | outGradient *= 109.319; 132 | 133 | return 109.319 * ( dot(m02*m02, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 ))) 134 | + dot(m12*m12, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ; 135 | } -------------------------------------------------------------------------------- /animation/Spring.hx: -------------------------------------------------------------------------------- 1 | package animation; 2 | 3 | import Math.*; 4 | 5 | /** 6 | Visualization of parameters 7 | https://www.desmos.com/calculator/fayu8nu1md 8 | **/ 9 | class Spring { 10 | 11 | public var value: Float; 12 | public var target: Float; 13 | public var velocity: Float; 14 | public var strength: Float; 15 | public var damping: Float; 16 | public var minEnergyThreshold = 1e-5; 17 | public var onUpdate: Null<(value: Float, velocity: Float) -> Void>; 18 | public var onComplete: Null<() -> Void>; 19 | 20 | public function new( 21 | initialValue: Float, 22 | ?target: Float, 23 | ?style: SpringStyle, 24 | velocity: Float = 0.0, 25 | ?onUpdate: (value: Float, velocity: Float) -> Void, 26 | ?onComplete: () -> Void 27 | ) { 28 | if (style == null) { 29 | style = Critical(0.5); 30 | } 31 | 32 | this.value = initialValue; 33 | this.target = target == null ? initialValue : target; 34 | this.velocity = velocity; 35 | this.onUpdate = onUpdate; 36 | this.onComplete = onComplete; 37 | 38 | this.setStyle(style); 39 | } 40 | 41 | public function setStyle(style: SpringStyle) { 42 | switch style { 43 | case Critical(approxHalfLife_s): 44 | this.damping = 3.356694 / approxHalfLife_s; 45 | this.strength = this.damping * this.damping / 4; 46 | case Underdamped(halfLife_s, springStrength): 47 | this.damping = 2 * Math.log(2) / halfLife_s; 48 | // 4k - b^2 > 0 49 | var bSq = this.damping * this.damping; 50 | this.strength = bSq + springStrength; 51 | 52 | case Custom(strength, damping): 53 | this.damping = damping; 54 | this.strength = strength; 55 | } 56 | } 57 | 58 | public function step(dt_s: Float) { 59 | // analytic integration (unconditionally stable) 60 | // visualization: https://www.desmos.com/calculator/c2iug0kerh 61 | // references: 62 | // https://mathworld.wolfram.com/OverdampedSimpleHarmonicMotion.html 63 | // https://mathworld.wolfram.com/CriticallyDampedSimpleHarmonicMotion.html 64 | // https://mathworld.wolfram.com/UnderdampedSimpleHarmonicMotion.html 65 | 66 | var t = dt_s; 67 | var V0: Float = this.velocity; 68 | var X0: Float = this.value - this.target; 69 | 70 | if ((X0 == 0 && V0 == 0) || t == 0) return; // nothing will change 71 | 72 | var k = this.strength; 73 | var b = this.damping; // β in wolfram reference 74 | 75 | if (getTotalEnergy() < minEnergyThreshold) { 76 | this.velocity = 0; 77 | this.value = this.target; 78 | if (onComplete != null) { 79 | onComplete(); 80 | } 81 | } else { 82 | var critical = k * 4 - b * b; 83 | 84 | if (critical > 0) { 85 | // under damped 86 | var q = 0.5 * sqrt(critical); // γ 87 | 88 | var A = X0; 89 | var B = ((b * X0) * 0.5 + V0) / q; 90 | 91 | var m = exp(-b * 0.5 * t); 92 | var c = cos(q * t); 93 | var s = sin(q * t); 94 | 95 | var x1 = m * (A*c + B*s); 96 | var v1 = m * ( 97 | ( B*q - 0.5*A*b) * c + 98 | (-A*q - 0.5*b*B) * s 99 | ); 100 | 101 | this.velocity = v1; 102 | this.value = x1 + this.target; 103 | } else if (critical < 0) { 104 | // over damped 105 | var u = 0.5 * sqrt(-critical); 106 | var p = -0.5 * b + u; 107 | var n = -0.5 * b - u; 108 | var B = -(n*X0 - V0)/(2*u); 109 | var A = X0 - B; 110 | 111 | var ep = exp(p * t); 112 | var en = exp(n * t); 113 | 114 | var x1 = A * en + B * ep; 115 | var v1 = A * n * en + B * p * ep; 116 | 117 | this.velocity = v1; 118 | this.value = x1 + this.target; 119 | } else { 120 | // critically damped 121 | var w = sqrt(k); // ω 122 | 123 | var A = X0; 124 | var B = V0 + w * X0; 125 | var e = exp(-w * t); 126 | 127 | var x1 = (A + B * t) * e; 128 | var v1 = (B - w * (A + B * t)) * e; 129 | 130 | this.velocity = v1; 131 | this.value = x1 + this.target; 132 | } 133 | } 134 | 135 | if (onUpdate != null) onUpdate(value, velocity); 136 | } 137 | 138 | public function getTotalEnergy() { 139 | var x: Float = value - target; 140 | return 141 | 0.5 * velocity * velocity + // kinetic energy 142 | 0.5 * strength * x * x; // potential energy 143 | } 144 | 145 | public function set(v: Float) { 146 | forceCompletion(v); 147 | } 148 | 149 | public function isComplete() { 150 | return this.velocity == 0 && value == target; 151 | } 152 | 153 | public function forceCompletion(?v: Float) { 154 | if (v != null) { 155 | target = v; 156 | } 157 | value = target; 158 | velocity = 0; 159 | step(0); 160 | } 161 | 162 | } 163 | 164 | 165 | enum SpringStyle { 166 | 167 | // Soft; 168 | // Hard; 169 | // Bouncy; 170 | 171 | /** 172 | * Critically damped spring, similar to exponential falloff 173 | * Starting with 0 velocity, this parameter describes how long it would take to reach half-way to the target 174 | * 175 | * `damping = 3.356694 / approxHalfLife_s` 176 | * 177 | * `strength = damping * damping / 4` 178 | */ 179 | Critical(approxHalfLife_s: Float); 180 | Underdamped(halfLife_s: Float, springStrength: Float); 181 | 182 | Custom(strength: Float, damping: Float); 183 | 184 | } -------------------------------------------------------------------------------- /shaders/hash.glsl: -------------------------------------------------------------------------------- 1 | // Hash without Sine 2 | // MIT License... 3 | /* Copyright (c)2014 David Hoskins. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE.*/ 22 | 23 | //---------------------------------------------------------------------------------------- 24 | // 1 out, 1 in... 25 | float hash11(float p) 26 | { 27 | p = fract(p * .1031); 28 | p *= p + 33.33; 29 | p *= p + p; 30 | return fract(p); 31 | } 32 | 33 | //---------------------------------------------------------------------------------------- 34 | // 1 out, 2 in... 35 | float hash12(vec2 p) 36 | { 37 | vec3 p3 = fract(vec3(p.xyx) * .1031); 38 | p3 += dot(p3, p3.yzx + 33.33); 39 | return fract((p3.x + p3.y) * p3.z); 40 | } 41 | 42 | //---------------------------------------------------------------------------------------- 43 | // 1 out, 3 in... 44 | float hash13(vec3 p3) 45 | { 46 | p3 = fract(p3 * .1031); 47 | p3 += dot(p3, p3.zyx + 31.32); 48 | return fract((p3.x + p3.y) * p3.z); 49 | } 50 | 51 | //---------------------------------------------------------------------------------------- 52 | // 2 out, 1 in... 53 | vec2 hash21(float p) 54 | { 55 | vec3 p3 = fract(vec3(p) * vec3(.1031, .1030, .0973)); 56 | p3 += dot(p3, p3.yzx + 33.33); 57 | return fract((p3.xx+p3.yz)*p3.zy); 58 | 59 | } 60 | 61 | //---------------------------------------------------------------------------------------- 62 | /// 2 out, 2 in... 63 | vec2 hash22(vec2 p) 64 | { 65 | vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973)); 66 | p3 += dot(p3, p3.yzx+33.33); 67 | return fract((p3.xx+p3.yz)*p3.zy); 68 | 69 | } 70 | 71 | //---------------------------------------------------------------------------------------- 72 | /// 2 out, 3 in... 73 | vec2 hash23(vec3 p3) 74 | { 75 | p3 = fract(p3 * vec3(.1031, .1030, .0973)); 76 | p3 += dot(p3, p3.yzx+33.33); 77 | return fract((p3.xx+p3.yz)*p3.zy); 78 | } 79 | 80 | //---------------------------------------------------------------------------------------- 81 | // 3 out, 1 in... 82 | vec3 hash31(float p) 83 | { 84 | vec3 p3 = fract(vec3(p) * vec3(.1031, .1030, .0973)); 85 | p3 += dot(p3, p3.yzx+33.33); 86 | return fract((p3.xxy+p3.yzz)*p3.zyx); 87 | } 88 | 89 | 90 | //---------------------------------------------------------------------------------------- 91 | /// 3 out, 2 in... 92 | vec3 hash32(vec2 p) 93 | { 94 | vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973)); 95 | p3 += dot(p3, p3.yxz+33.33); 96 | return fract((p3.xxy+p3.yzz)*p3.zyx); 97 | } 98 | 99 | //---------------------------------------------------------------------------------------- 100 | /// 3 out, 3 in... 101 | vec3 hash33(vec3 p3) 102 | { 103 | p3 = fract(p3 * vec3(.1031, .1030, .0973)); 104 | p3 += dot(p3, p3.yxz+33.33); 105 | return fract((p3.xxy + p3.yxx)*p3.zyx); 106 | 107 | } 108 | 109 | //---------------------------------------------------------------------------------------- 110 | // 4 out, 1 in... 111 | vec4 hash41(float p) 112 | { 113 | vec4 p4 = fract(vec4(p) * vec4(.1031, .1030, .0973, .1099)); 114 | p4 += dot(p4, p4.wzxy+33.33); 115 | return fract((p4.xxyz+p4.yzzw)*p4.zywx); 116 | 117 | } 118 | 119 | //---------------------------------------------------------------------------------------- 120 | // 4 out, 2 in... 121 | vec4 hash42(vec2 p) 122 | { 123 | vec4 p4 = fract(vec4(p.xyxy) * vec4(.1031, .1030, .0973, .1099)); 124 | p4 += dot(p4, p4.wzxy+33.33); 125 | return fract((p4.xxyz+p4.yzzw)*p4.zywx); 126 | 127 | } 128 | 129 | //---------------------------------------------------------------------------------------- 130 | // 4 out, 3 in... 131 | vec4 hash43(vec3 p) 132 | { 133 | vec4 p4 = fract(vec4(p.xyzx) * vec4(.1031, .1030, .0973, .1099)); 134 | p4 += dot(p4, p4.wzxy+33.33); 135 | return fract((p4.xxyz+p4.yzzw)*p4.zywx); 136 | } 137 | 138 | //---------------------------------------------------------------------------------------- 139 | // 4 out, 4 in... 140 | vec4 hash44(vec4 p4) 141 | { 142 | p4 = fract(p4 * vec4(.1031, .1030, .0973, .1099)); 143 | p4 += dot(p4, p4.wzxy+33.33); 144 | return fract((p4.xxyz+p4.yzzw)*p4.zywx); 145 | } -------------------------------------------------------------------------------- /objects/BackgroundEnvironment.hx: -------------------------------------------------------------------------------- 1 | package objects; 2 | 3 | import three.Color; 4 | import three.BufferGeometry; 5 | import three.Uniform; 6 | import three.ShaderMaterial; 7 | import three.Texture; 8 | import three.Mesh; 9 | import three.WebGLRenderer; 10 | import three.Scene; 11 | import three.Camera; 12 | import three.Material; 13 | import three.Group; 14 | 15 | /** 16 | Draws the scene's environment texture with variable roughness 17 | In contrast to scene.background which has fixed roughness 18 | **/ 19 | class BackgroundEnvironment extends Mesh { 20 | 21 | public var roughness(get, set): Float; 22 | public var multiplier(get, set): Color; 23 | 24 | public function new(roughness: Float = 0.5) { 25 | var environmentMaterial = new EnvironmentMaterial(roughness); 26 | super(new three.BoxGeometry(1, 1, 1), environmentMaterial); 27 | 28 | geometry.deleteAttribute('normal'); 29 | geometry.deleteAttribute('uv'); 30 | 31 | this.name = 'BackgroundEnvironment'; 32 | this.frustumCulled = false; 33 | this.castShadow = false; 34 | this.receiveShadow = false; 35 | this.matrixAutoUpdate = false; 36 | this.renderOrder = Math.NEGATIVE_INFINITY; // render last to take advantage of depth culling 37 | 38 | this.onBeforeRender = (renderer:WebGLRenderer, scene:Scene, camera:Camera, geometry:ts.AnyOf2, material:Material, group:Group) -> { 39 | this.material.envMap = scene.environment; 40 | this.matrixWorld.copyPosition(camera.matrixWorld); 41 | }; 42 | } 43 | 44 | inline function get_roughness() { 45 | return this.material.uRoughness.value; 46 | } 47 | inline function set_roughness(v: Float) { 48 | return this.material.uRoughness.value = v; 49 | } 50 | 51 | inline function get_multiplier() { 52 | return this.material.uMultiplier.value; 53 | } 54 | inline function set_multiplier(v: Color) { 55 | return this.material.uMultiplier.value = v; 56 | } 57 | 58 | } 59 | 60 | class EnvironmentMaterial extends ShaderMaterial { 61 | 62 | @:isVar public var envMap(get, set): Null; 63 | public final uRoughness: Uniform; 64 | public final uFlipEnvMap: Uniform; 65 | public final uMultiplier: Uniform; 66 | 67 | final uEnvMap: Uniform; 68 | 69 | public function new(roughness: Float) { 70 | final uRoughness = new Uniform(0.5); 71 | final uFlipEnvMap = new Uniform(-1); 72 | final uEnvMap = new Uniform(null); 73 | final uMultiplier = new Uniform(new Color(1,1,1)); 74 | 75 | super({ 76 | name: 'BackgroundEnvironment', 77 | uniforms: { 78 | 'envMap': uEnvMap, 79 | 'flipEnvMap': uFlipEnvMap, 80 | 'uRoughness': uRoughness, 81 | 'uMultiplier': uMultiplier, 82 | }, 83 | vertexShader: Three.ShaderLib.cube.vertexShader, 84 | fragmentShader: 85 | ' 86 | uniform float uRoughness; 87 | uniform vec3 uMultiplier; 88 | #include 89 | #ifdef USE_ENVMAP 90 | varying vec3 vWorldDirection; 91 | #endif 92 | #include 93 | 94 | void main() { 95 | #ifdef USE_ENVMAP 96 | vec3 reflectVec = vWorldDirection; 97 | #ifdef ENVMAP_TYPE_CUBE 98 | vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); 99 | #elif defined( ENVMAP_TYPE_CUBE_UV ) 100 | vec4 envColor = textureCubeUV(envMap, reflectVec, uRoughness); 101 | #elif defined( ENVMAP_TYPE_EQUIREC ) 102 | vec2 sampleUV; 103 | reflectVec = normalize( reflectVec ); 104 | sampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; 105 | sampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5; 106 | vec4 envColor = texture2D( envMap, sampleUV ); 107 | #elif defined( ENVMAP_TYPE_SPHERE ) 108 | reflectVec = normalize( reflectVec ); 109 | vec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) ); 110 | vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 ); 111 | #else 112 | vec4 envColor = vec4( 0.0 ); 113 | #endif 114 | #ifndef ENVMAP_TYPE_CUBE_UV 115 | envColor = envMapTexelToLinear( envColor ); 116 | #endif 117 | #endif 118 | #ifdef USE_ENVMAP 119 | gl_FragColor = envColor; 120 | #else 121 | gl_FragColor = vec4(1., 1., 1., 1.); 122 | #endif 123 | 124 | 125 | 126 | gl_FragColor.rgb *= uMultiplier; 127 | #include 128 | #include 129 | } 130 | ', 131 | side: DoubleSide, 132 | depthWrite: false, 133 | depthTest: true, 134 | blending: NoBlending, 135 | }); 136 | 137 | this.uRoughness = uRoughness; 138 | this.uFlipEnvMap = uFlipEnvMap; 139 | this.uEnvMap = uEnvMap; 140 | this.uMultiplier = uMultiplier; 141 | 142 | uRoughness.value = roughness; 143 | } 144 | 145 | inline function set_envMap(v: Null) { 146 | if (v != envMap) needsUpdate = true; 147 | if (v != null) { 148 | uFlipEnvMap.value = untyped v.isCubeTexture == true ? -1 : 1; 149 | } 150 | uEnvMap.value = v; 151 | return this.envMap = v; 152 | } 153 | 154 | inline function get_envMap() { 155 | return uEnvMap.value; 156 | } 157 | 158 | } -------------------------------------------------------------------------------- /shaders/noise/noise4Dgrad.glsl: -------------------------------------------------------------------------------- 1 | /** 2 | This file is not included in Ashima Art and Stefan Gustavson webgl-noise repository (ashima/webgl-noise) 3 | 4 | Instead it was developed by Mark A. Ropper (@Markyparky56), extending noise4D with analytic graidents 5 | 6 | See https://www.shadertoy.com/view/3d2GDW 7 | **/ 8 | 9 | // 10 | // Description : Array and textureless GLSL 2D/3D/4D simplex 11 | // noise functions. 12 | // Author : Ian McEwan, Ashima Arts. 13 | // Maintainer : stegu 14 | // Lastmod : 20110822 (ijm) 15 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 16 | // Distributed under the MIT License. See LICENSE file. 17 | // https://github.com/ashima/webgl-noise 18 | // https://github.com/stegu/webgl-noise 19 | // 20 | // Gradient Calculation extended from noise3Dgrad.glsl by Mark A. Ropper (@Markyparky56) 21 | 22 | vec4 mod289(vec4 x) { 23 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 24 | 25 | float mod289(float x) { 26 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 27 | 28 | vec4 permute(vec4 x) { 29 | return mod289(((x*34.0)+1.0)*x); 30 | } 31 | 32 | float permute(float x) { 33 | return mod289(((x*34.0)+1.0)*x); 34 | } 35 | 36 | vec4 taylorInvSqrt(vec4 r) 37 | { 38 | return 1.79284291400159 - 0.85373472095314 * r; 39 | } 40 | 41 | float taylorInvSqrt(float r) 42 | { 43 | return 1.79284291400159 - 0.85373472095314 * r; 44 | } 45 | 46 | vec4 grad4(float j, vec4 ip) 47 | { 48 | const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0); 49 | vec4 p,s; 50 | 51 | p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0; 52 | p.w = 1.5 - dot(abs(p.xyz), ones.xyz); 53 | s = vec4(lessThan(p, vec4(0.0))); 54 | p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www; 55 | 56 | return p; 57 | } 58 | 59 | // (sqrt(5) - 1)/4 = F4, used once below 60 | #define F4 0.309016994374947451 61 | 62 | float snoise(vec4 v, out vec4 gradient) 63 | { 64 | const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4 65 | 0.276393202250021, // 2 * G4 66 | 0.414589803375032, // 3 * G4 67 | -0.447213595499958); // -1 + 4 * G4 68 | 69 | // First corner 70 | vec4 i = floor(v + dot(v, vec4(F4)) ); 71 | vec4 x0 = v - i + dot(i, C.xxxx); 72 | 73 | // Other corners 74 | 75 | // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) 76 | vec4 i0; 77 | vec3 isX = step( x0.yzw, x0.xxx ); 78 | vec3 isYZ = step( x0.zww, x0.yyz ); 79 | // i0.x = dot( isX, vec3( 1.0 ) ); 80 | i0.x = isX.x + isX.y + isX.z; 81 | i0.yzw = 1.0 - isX; 82 | // i0.y += dot( isYZ.xy, vec2( 1.0 ) ); 83 | i0.y += isYZ.x + isYZ.y; 84 | i0.zw += 1.0 - isYZ.xy; 85 | i0.z += isYZ.z; 86 | i0.w += 1.0 - isYZ.z; 87 | 88 | // i0 now contains the unique values 0,1,2,3 in each channel 89 | vec4 i3 = clamp( i0, 0.0, 1.0 ); 90 | vec4 i2 = clamp( i0-1.0, 0.0, 1.0 ); 91 | vec4 i1 = clamp( i0-2.0, 0.0, 1.0 ); 92 | 93 | // x0 = x0 - 0.0 + 0.0 * C.xxxx 94 | // x1 = x0 - i1 + 1.0 * C.xxxx 95 | // x2 = x0 - i2 + 2.0 * C.xxxx 96 | // x3 = x0 - i3 + 3.0 * C.xxxx 97 | // x4 = x0 - 1.0 + 4.0 * C.xxxx 98 | vec4 x1 = x0 - i1 + C.xxxx; 99 | vec4 x2 = x0 - i2 + C.yyyy; 100 | vec4 x3 = x0 - i3 + C.zzzz; 101 | vec4 x4 = x0 + C.wwww; 102 | 103 | // Permutations 104 | i = mod289(i); 105 | float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x); 106 | vec4 j1 = permute( permute( permute( permute ( 107 | i.w + vec4(i1.w, i2.w, i3.w, 1.0 )) 108 | + i.z + vec4(i1.z, i2.z, i3.z, 1.0 )) 109 | + i.y + vec4(i1.y, i2.y, i3.y, 1.0 )) 110 | + i.x + vec4(i1.x, i2.x, i3.x, 1.0 )); 111 | 112 | // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope 113 | // 7*7*6 = 294, which is close to the ring size 17*17 = 289. 114 | vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ; 115 | 116 | vec4 p0 = grad4(j0, ip); 117 | vec4 p1 = grad4(j1.x, ip); 118 | vec4 p2 = grad4(j1.y, ip); 119 | vec4 p3 = grad4(j1.z, ip); 120 | vec4 p4 = grad4(j1.w, ip); 121 | 122 | // Normalise gradients 123 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 124 | p0 *= norm.x; 125 | p1 *= norm.y; 126 | p2 *= norm.z; 127 | p3 *= norm.w; 128 | p4 *= taylorInvSqrt(dot(p4,p4)); 129 | 130 | // Mix contributions from the five corners 131 | vec3 m0 = max(0.5 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0); 132 | vec2 m1 = max(0.5 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0); 133 | vec3 m02 = m0 * m0; 134 | vec2 m12 = m1 * m1; 135 | vec3 m04 = m02 * m02; 136 | vec2 m14 = m12 * m12; 137 | vec3 pdotx0 = vec3(dot(p0,x0), dot(p1,x1), dot(p2,x2)); 138 | vec2 pdotx1 = vec2(dot(p3,x3), dot(p4,x4)); 139 | 140 | // Determine noise gradient; 141 | vec3 temp0 = m02 * m0 * pdotx0; 142 | vec2 temp1 = m12 * m1 * pdotx1; 143 | gradient = -8.0 * (temp0.x * x0 + temp0.y * x1 + temp0.z * x2 + temp1.x * x3 + temp1.y * x4); 144 | gradient += m04.x * p0 + m04 .y * p1 + m04.z * p2 + m14.x * p3 + m14.y * p4; 145 | gradient *= 109.319; 146 | 147 | return 109.319 * ( dot(m02*m02, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 ))) 148 | + dot(m12*m12, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ; 149 | 150 | } 151 | -------------------------------------------------------------------------------- /environment/EnvironmentManager.hx: -------------------------------------------------------------------------------- 1 | package environment; 2 | 3 | import haxe.io.Path; 4 | import three.AmbientLight; 5 | import three.DirectionalLight; 6 | import three.Scene; 7 | import three.Texture; 8 | import three.TextureLoader; 9 | import three.WebGLRenderTarget; 10 | import three.WebGLRenderer; 11 | import three.examples.jsm.loaders.rgbeloader.RGBELoader; 12 | import tool.IBLGenerator; 13 | 14 | class EnvironmentManager { 15 | 16 | public var environmentPath(get, set): Null; 17 | var _environmentPath: Null = null; 18 | 19 | public final environmentSun: DirectionalLight; 20 | public final environmentAmbient: AmbientLight; 21 | final renderer: WebGLRenderer; 22 | final scene: Scene; 23 | final onEnvironmentLoaded: Texture -> Void; 24 | 25 | public function new(renderer: WebGLRenderer, scene: Scene, ?path: String, ?onEnvironmentLoaded: (envMap: Texture) -> Void) { 26 | this.renderer = renderer; 27 | this.scene = scene; 28 | this.onEnvironmentLoaded = onEnvironmentLoaded != null ? onEnvironmentLoaded : _ -> {}; 29 | 30 | environmentSun = new DirectionalLight(0xFFFFFF, 0); 31 | environmentSun.castShadow = true; 32 | environmentSun.shadow.bias = -0.001; 33 | environmentSun.shadow.radius = 7; 34 | environmentSun.layers.enable(Blended); 35 | environmentSun.visible = false; 36 | scene.add(environmentSun); 37 | 38 | environmentAmbient = new AmbientLight(0x000000, 1); 39 | environmentAmbient.visible = false; 40 | scene.add(environmentAmbient); 41 | 42 | setEnvironmentMapPath(path); 43 | } 44 | 45 | var _pmremRenderTarget: Null; 46 | public function setEnvironmentMapPath(path: Null, ?onLoaded: (envMap: Texture) -> Void, ?onError: String -> Void) { 47 | if (path == environmentPath) return; // no change 48 | if (onLoaded == null) onLoaded = (e) -> {}; 49 | if (onError == null) onError = (e) -> js.Browser.console.error(e); 50 | _environmentPath = path; 51 | 52 | if (path != null) { 53 | var ext = Path.extension(path); 54 | 55 | switch ext.toLowerCase() { 56 | case 'hdr': 57 | var iblGenerator = new tool.IBLGenerator(renderer); 58 | iblGenerator.compileEquirectangularShader(); 59 | new RGBELoader() 60 | .setDataType(FloatType) 61 | .load( 62 | path, 63 | (texture, texData) -> { 64 | if (_pmremRenderTarget != null) { 65 | _pmremRenderTarget.dispose(); 66 | } 67 | 68 | _pmremRenderTarget = iblGenerator.fromEquirectangular(texture); 69 | iblGenerator.dispose(); 70 | _pmremRenderTarget.texture.sourceFile = path; 71 | setEnvironmentMap(_pmremRenderTarget.texture); 72 | onLoaded(_pmremRenderTarget.texture); 73 | onEnvironmentLoaded(_pmremRenderTarget.texture); 74 | }); 75 | case 'png': 76 | new TextureLoader().load(path, texture -> { 77 | texture.minFilter = NearestFilter; 78 | texture.magFilter = NearestFilter; 79 | texture.type = UnsignedByteType; 80 | texture.format = RGBEFormat; 81 | texture.encoding = RGBDEncoding; 82 | texture.mapping = CubeUVReflectionMapping; 83 | texture.generateMipmaps = false; 84 | texture.flipY = false; 85 | texture.sourceFile = path; 86 | setEnvironmentMap(texture); 87 | onLoaded(texture); 88 | onEnvironmentLoaded(texture); 89 | }); 90 | default: 91 | var error = 'Unknown environment extension $ext'; 92 | js.Browser.console.error(error); 93 | onError(error); 94 | } 95 | } 96 | } 97 | 98 | public function setEnvironmentMap(texture: Texture) { 99 | if (scene.environment != null) { 100 | scene.environment.dispose(); 101 | } 102 | scene.environment = texture; 103 | } 104 | 105 | public function downloadPmremEnvironmentMap() { 106 | final document = js.Browser.document; 107 | final renderTarget = this._pmremRenderTarget; 108 | final environmentPath = this.environmentPath; 109 | final imageKind = 'png'; 110 | 111 | if (renderTarget != null && environmentPath != null) { 112 | var w = Std.int(renderTarget.width); 113 | var h = Std.int(renderTarget.height); 114 | var byteCount = Std.int(w*h*4); 115 | var buffer = new js.lib.Uint8ClampedArray(byteCount); 116 | renderer.readRenderTargetPixels(renderTarget, 0, 0, w, h, buffer); 117 | 118 | var pngCanvas = document.createCanvasElement(); 119 | pngCanvas.width = w; 120 | pngCanvas.height = h; 121 | var ctx = pngCanvas.getContext2d(); 122 | var rgbaData = new js.html.ImageData(buffer, w, h); 123 | ctx.putImageData(rgbaData, 0, 0); 124 | 125 | var encodingName = switch renderTarget.texture.encoding { 126 | case RGBDEncoding: 'rgbd'; 127 | case RGBEEncoding: 'rgbe'; 128 | case RGBM16Encoding: 'rgbm17'; 129 | case RGBM7Encoding: 'rgbm7'; 130 | default: null; 131 | } 132 | 133 | var filename = haxe.io.Path.withoutDirectory(haxe.io.Path.withoutExtension(environmentPath)) + (encodingName != null ? '.$encodingName' : '') + '.$imageKind'; 134 | 135 | pngCanvas.toBlob((blob) -> { 136 | // trigger download 137 | var a = document.createAnchorElement(); 138 | document.body.appendChild(a); 139 | a.style.display = 'none'; 140 | var url = js.html.URL.createObjectURL(blob); 141 | a.href = url; 142 | a.download = filename; 143 | a.click(); 144 | js.html.URL.revokeObjectURL(url); 145 | }, 'image/$imageKind', 1); 146 | 147 | // document.body.appendChild(pngCanvas); 148 | // pngCanvas.style.position = 'absolute'; 149 | // pngCanvas.style.zIndex = '1000'; 150 | } else { 151 | js.Browser.alert('First load a .hdr environment file in order to download a pre-processed version'); 152 | } 153 | } 154 | 155 | function set_environmentPath(v: String) { 156 | setEnvironmentMapPath(v); 157 | return v; 158 | } 159 | 160 | function get_environmentPath() { 161 | return _environmentPath; 162 | } 163 | 164 | } -------------------------------------------------------------------------------- /shaders/noise/classicnoise3D.glsl: -------------------------------------------------------------------------------- 1 | // 2 | // GLSL textureless classic 3D noise "cnoise", 3 | // with an RSL-style periodic variant "pnoise". 4 | // Author: Stefan Gustavson (stefan.gustavson@liu.se) 5 | // Version: 2011-10-11 6 | // 7 | // Many thanks to Ian McEwan of Ashima Arts for the 8 | // ideas for permutation and gradient selection. 9 | // 10 | // Copyright (c) 2011 Stefan Gustavson. All rights reserved. 11 | // Distributed under the MIT license. See LICENSE file. 12 | // https://github.com/stegu/webgl-noise 13 | // 14 | 15 | vec3 mod289(vec3 x) 16 | { 17 | return x - floor(x * (1.0 / 289.0)) * 289.0; 18 | } 19 | 20 | vec4 mod289(vec4 x) 21 | { 22 | return x - floor(x * (1.0 / 289.0)) * 289.0; 23 | } 24 | 25 | vec4 permute(vec4 x) 26 | { 27 | return mod289(((x*34.0)+1.0)*x); 28 | } 29 | 30 | vec4 taylorInvSqrt(vec4 r) 31 | { 32 | return 1.79284291400159 - 0.85373472095314 * r; 33 | } 34 | 35 | vec3 fade(vec3 t) { 36 | return t*t*t*(t*(t*6.0-15.0)+10.0); 37 | } 38 | 39 | // Classic Perlin noise 40 | float cnoise(vec3 P) 41 | { 42 | vec3 Pi0 = floor(P); // Integer part for indexing 43 | vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1 44 | Pi0 = mod289(Pi0); 45 | Pi1 = mod289(Pi1); 46 | vec3 Pf0 = fract(P); // Fractional part for interpolation 47 | vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0 48 | vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); 49 | vec4 iy = vec4(Pi0.yy, Pi1.yy); 50 | vec4 iz0 = Pi0.zzzz; 51 | vec4 iz1 = Pi1.zzzz; 52 | 53 | vec4 ixy = permute(permute(ix) + iy); 54 | vec4 ixy0 = permute(ixy + iz0); 55 | vec4 ixy1 = permute(ixy + iz1); 56 | 57 | vec4 gx0 = ixy0 * (1.0 / 7.0); 58 | vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5; 59 | gx0 = fract(gx0); 60 | vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); 61 | vec4 sz0 = step(gz0, vec4(0.0)); 62 | gx0 -= sz0 * (step(0.0, gx0) - 0.5); 63 | gy0 -= sz0 * (step(0.0, gy0) - 0.5); 64 | 65 | vec4 gx1 = ixy1 * (1.0 / 7.0); 66 | vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5; 67 | gx1 = fract(gx1); 68 | vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); 69 | vec4 sz1 = step(gz1, vec4(0.0)); 70 | gx1 -= sz1 * (step(0.0, gx1) - 0.5); 71 | gy1 -= sz1 * (step(0.0, gy1) - 0.5); 72 | 73 | vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); 74 | vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); 75 | vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); 76 | vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); 77 | vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); 78 | vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); 79 | vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); 80 | vec3 g111 = vec3(gx1.w,gy1.w,gz1.w); 81 | 82 | vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); 83 | g000 *= norm0.x; 84 | g010 *= norm0.y; 85 | g100 *= norm0.z; 86 | g110 *= norm0.w; 87 | vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); 88 | g001 *= norm1.x; 89 | g011 *= norm1.y; 90 | g101 *= norm1.z; 91 | g111 *= norm1.w; 92 | 93 | float n000 = dot(g000, Pf0); 94 | float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); 95 | float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); 96 | float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); 97 | float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); 98 | float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); 99 | float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); 100 | float n111 = dot(g111, Pf1); 101 | 102 | vec3 fade_xyz = fade(Pf0); 103 | vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); 104 | vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); 105 | float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); 106 | return 2.2 * n_xyz; 107 | } 108 | 109 | // Classic Perlin noise, periodic variant 110 | float pnoise(vec3 P, vec3 rep) 111 | { 112 | vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period 113 | vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period 114 | Pi0 = mod289(Pi0); 115 | Pi1 = mod289(Pi1); 116 | vec3 Pf0 = fract(P); // Fractional part for interpolation 117 | vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0 118 | vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); 119 | vec4 iy = vec4(Pi0.yy, Pi1.yy); 120 | vec4 iz0 = Pi0.zzzz; 121 | vec4 iz1 = Pi1.zzzz; 122 | 123 | vec4 ixy = permute(permute(ix) + iy); 124 | vec4 ixy0 = permute(ixy + iz0); 125 | vec4 ixy1 = permute(ixy + iz1); 126 | 127 | vec4 gx0 = ixy0 * (1.0 / 7.0); 128 | vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5; 129 | gx0 = fract(gx0); 130 | vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); 131 | vec4 sz0 = step(gz0, vec4(0.0)); 132 | gx0 -= sz0 * (step(0.0, gx0) - 0.5); 133 | gy0 -= sz0 * (step(0.0, gy0) - 0.5); 134 | 135 | vec4 gx1 = ixy1 * (1.0 / 7.0); 136 | vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5; 137 | gx1 = fract(gx1); 138 | vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); 139 | vec4 sz1 = step(gz1, vec4(0.0)); 140 | gx1 -= sz1 * (step(0.0, gx1) - 0.5); 141 | gy1 -= sz1 * (step(0.0, gy1) - 0.5); 142 | 143 | vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); 144 | vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); 145 | vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); 146 | vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); 147 | vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); 148 | vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); 149 | vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); 150 | vec3 g111 = vec3(gx1.w,gy1.w,gz1.w); 151 | 152 | vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); 153 | g000 *= norm0.x; 154 | g010 *= norm0.y; 155 | g100 *= norm0.z; 156 | g110 *= norm0.w; 157 | vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); 158 | g001 *= norm1.x; 159 | g011 *= norm1.y; 160 | g101 *= norm1.z; 161 | g111 *= norm1.w; 162 | 163 | float n000 = dot(g000, Pf0); 164 | float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); 165 | float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); 166 | float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); 167 | float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); 168 | float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); 169 | float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); 170 | float n111 = dot(g111, Pf1); 171 | 172 | vec3 fade_xyz = fade(Pf0); 173 | vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); 174 | vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); 175 | float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); 176 | return 2.2 * n_xyz; 177 | } 178 | -------------------------------------------------------------------------------- /shaders/noise/cellular3D.glsl: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | // Cellular noise ("Worley noise") in 3D in GLSL. 4 | // Copyright (c) Stefan Gustavson 2011-04-19. All rights reserved. 5 | // This code is released under the conditions of the MIT license. 6 | // See LICENSE file for details. 7 | // https://github.com/stegu/webgl-noise 8 | 9 | // Modulo 289 without a division (only multiplications) 10 | vec3 mod289(vec3 x) { 11 | return x - floor(x * (1.0 / 289.0)) * 289.0; 12 | } 13 | 14 | // Modulo 7 without a division 15 | vec3 mod7(vec3 x) { 16 | return x - floor(x * (1.0 / 7.0)) * 7.0; 17 | } 18 | 19 | // Permutation polynomial: (34x^2 + x) mod 289 20 | vec3 permute(vec3 x) { 21 | return mod289((34.0 * x + 1.0) * x); 22 | } 23 | 24 | // Cellular noise, returning F1 and F2 in a vec2. 25 | // 3x3x3 search region for good F2 everywhere, but a lot 26 | // slower than the 2x2x2 version. 27 | // The code below is a bit scary even to its author, 28 | // but it has at least half decent performance on a 29 | // modern GPU. In any case, it beats any software 30 | // implementation of Worley noise hands down. 31 | 32 | vec2 cellular(vec3 P) { 33 | #define K 0.142857142857 // 1/7 34 | #define Ko 0.428571428571 // 1/2-K/2 35 | #define K2 0.020408163265306 // 1/(7*7) 36 | #define Kz 0.166666666667 // 1/6 37 | #define Kzo 0.416666666667 // 1/2-1/6*2 38 | #define jitter 1.0 // smaller jitter gives more regular pattern 39 | 40 | vec3 Pi = mod289(floor(P)); 41 | vec3 Pf = fract(P) - 0.5; 42 | 43 | vec3 Pfx = Pf.x + vec3(1.0, 0.0, -1.0); 44 | vec3 Pfy = Pf.y + vec3(1.0, 0.0, -1.0); 45 | vec3 Pfz = Pf.z + vec3(1.0, 0.0, -1.0); 46 | 47 | vec3 p = permute(Pi.x + vec3(-1.0, 0.0, 1.0)); 48 | vec3 p1 = permute(p + Pi.y - 1.0); 49 | vec3 p2 = permute(p + Pi.y); 50 | vec3 p3 = permute(p + Pi.y + 1.0); 51 | 52 | vec3 p11 = permute(p1 + Pi.z - 1.0); 53 | vec3 p12 = permute(p1 + Pi.z); 54 | vec3 p13 = permute(p1 + Pi.z + 1.0); 55 | 56 | vec3 p21 = permute(p2 + Pi.z - 1.0); 57 | vec3 p22 = permute(p2 + Pi.z); 58 | vec3 p23 = permute(p2 + Pi.z + 1.0); 59 | 60 | vec3 p31 = permute(p3 + Pi.z - 1.0); 61 | vec3 p32 = permute(p3 + Pi.z); 62 | vec3 p33 = permute(p3 + Pi.z + 1.0); 63 | 64 | vec3 ox11 = fract(p11*K) - Ko; 65 | vec3 oy11 = mod7(floor(p11*K))*K - Ko; 66 | vec3 oz11 = floor(p11*K2)*Kz - Kzo; // p11 < 289 guaranteed 67 | 68 | vec3 ox12 = fract(p12*K) - Ko; 69 | vec3 oy12 = mod7(floor(p12*K))*K - Ko; 70 | vec3 oz12 = floor(p12*K2)*Kz - Kzo; 71 | 72 | vec3 ox13 = fract(p13*K) - Ko; 73 | vec3 oy13 = mod7(floor(p13*K))*K - Ko; 74 | vec3 oz13 = floor(p13*K2)*Kz - Kzo; 75 | 76 | vec3 ox21 = fract(p21*K) - Ko; 77 | vec3 oy21 = mod7(floor(p21*K))*K - Ko; 78 | vec3 oz21 = floor(p21*K2)*Kz - Kzo; 79 | 80 | vec3 ox22 = fract(p22*K) - Ko; 81 | vec3 oy22 = mod7(floor(p22*K))*K - Ko; 82 | vec3 oz22 = floor(p22*K2)*Kz - Kzo; 83 | 84 | vec3 ox23 = fract(p23*K) - Ko; 85 | vec3 oy23 = mod7(floor(p23*K))*K - Ko; 86 | vec3 oz23 = floor(p23*K2)*Kz - Kzo; 87 | 88 | vec3 ox31 = fract(p31*K) - Ko; 89 | vec3 oy31 = mod7(floor(p31*K))*K - Ko; 90 | vec3 oz31 = floor(p31*K2)*Kz - Kzo; 91 | 92 | vec3 ox32 = fract(p32*K) - Ko; 93 | vec3 oy32 = mod7(floor(p32*K))*K - Ko; 94 | vec3 oz32 = floor(p32*K2)*Kz - Kzo; 95 | 96 | vec3 ox33 = fract(p33*K) - Ko; 97 | vec3 oy33 = mod7(floor(p33*K))*K - Ko; 98 | vec3 oz33 = floor(p33*K2)*Kz - Kzo; 99 | 100 | vec3 dx11 = Pfx + jitter*ox11; 101 | vec3 dy11 = Pfy.x + jitter*oy11; 102 | vec3 dz11 = Pfz.x + jitter*oz11; 103 | 104 | vec3 dx12 = Pfx + jitter*ox12; 105 | vec3 dy12 = Pfy.x + jitter*oy12; 106 | vec3 dz12 = Pfz.y + jitter*oz12; 107 | 108 | vec3 dx13 = Pfx + jitter*ox13; 109 | vec3 dy13 = Pfy.x + jitter*oy13; 110 | vec3 dz13 = Pfz.z + jitter*oz13; 111 | 112 | vec3 dx21 = Pfx + jitter*ox21; 113 | vec3 dy21 = Pfy.y + jitter*oy21; 114 | vec3 dz21 = Pfz.x + jitter*oz21; 115 | 116 | vec3 dx22 = Pfx + jitter*ox22; 117 | vec3 dy22 = Pfy.y + jitter*oy22; 118 | vec3 dz22 = Pfz.y + jitter*oz22; 119 | 120 | vec3 dx23 = Pfx + jitter*ox23; 121 | vec3 dy23 = Pfy.y + jitter*oy23; 122 | vec3 dz23 = Pfz.z + jitter*oz23; 123 | 124 | vec3 dx31 = Pfx + jitter*ox31; 125 | vec3 dy31 = Pfy.z + jitter*oy31; 126 | vec3 dz31 = Pfz.x + jitter*oz31; 127 | 128 | vec3 dx32 = Pfx + jitter*ox32; 129 | vec3 dy32 = Pfy.z + jitter*oy32; 130 | vec3 dz32 = Pfz.y + jitter*oz32; 131 | 132 | vec3 dx33 = Pfx + jitter*ox33; 133 | vec3 dy33 = Pfy.z + jitter*oy33; 134 | vec3 dz33 = Pfz.z + jitter*oz33; 135 | 136 | vec3 d11 = dx11 * dx11 + dy11 * dy11 + dz11 * dz11; 137 | vec3 d12 = dx12 * dx12 + dy12 * dy12 + dz12 * dz12; 138 | vec3 d13 = dx13 * dx13 + dy13 * dy13 + dz13 * dz13; 139 | vec3 d21 = dx21 * dx21 + dy21 * dy21 + dz21 * dz21; 140 | vec3 d22 = dx22 * dx22 + dy22 * dy22 + dz22 * dz22; 141 | vec3 d23 = dx23 * dx23 + dy23 * dy23 + dz23 * dz23; 142 | vec3 d31 = dx31 * dx31 + dy31 * dy31 + dz31 * dz31; 143 | vec3 d32 = dx32 * dx32 + dy32 * dy32 + dz32 * dz32; 144 | vec3 d33 = dx33 * dx33 + dy33 * dy33 + dz33 * dz33; 145 | 146 | // Sort out the two smallest distances (F1, F2) 147 | #if 0 148 | // Cheat and sort out only F1 149 | vec3 d1 = min(min(d11,d12), d13); 150 | vec3 d2 = min(min(d21,d22), d23); 151 | vec3 d3 = min(min(d31,d32), d33); 152 | vec3 d = min(min(d1,d2), d3); 153 | d.x = min(min(d.x,d.y),d.z); 154 | return vec2(sqrt(d.x)); // F1 duplicated, no F2 computed 155 | #else 156 | // Do it right and sort out both F1 and F2 157 | vec3 d1a = min(d11, d12); 158 | d12 = max(d11, d12); 159 | d11 = min(d1a, d13); // Smallest now not in d12 or d13 160 | d13 = max(d1a, d13); 161 | d12 = min(d12, d13); // 2nd smallest now not in d13 162 | vec3 d2a = min(d21, d22); 163 | d22 = max(d21, d22); 164 | d21 = min(d2a, d23); // Smallest now not in d22 or d23 165 | d23 = max(d2a, d23); 166 | d22 = min(d22, d23); // 2nd smallest now not in d23 167 | vec3 d3a = min(d31, d32); 168 | d32 = max(d31, d32); 169 | d31 = min(d3a, d33); // Smallest now not in d32 or d33 170 | d33 = max(d3a, d33); 171 | d32 = min(d32, d33); // 2nd smallest now not in d33 172 | vec3 da = min(d11, d21); 173 | d21 = max(d11, d21); 174 | d11 = min(da, d31); // Smallest now in d11 175 | d31 = max(da, d31); // 2nd smallest now not in d31 176 | d11.xy = (d11.x < d11.y) ? d11.xy : d11.yx; 177 | d11.xz = (d11.x < d11.z) ? d11.xz : d11.zx; // d11.x now smallest 178 | d12 = min(d12, d21); // 2nd smallest now not in d21 179 | d12 = min(d12, d22); // nor in d22 180 | d12 = min(d12, d31); // nor in d31 181 | d12 = min(d12, d32); // nor in d32 182 | d11.yz = min(d11.yz,d12.xy); // nor in d12.yz 183 | d11.y = min(d11.y,d12.z); // Only two more to go 184 | d11.y = min(d11.y,d11.z); // Done! (Phew!) 185 | return sqrt(d11.xy); // F1, F2 186 | #endif 187 | } 188 | -------------------------------------------------------------------------------- /objects/GlassReflectiveFloor.hx: -------------------------------------------------------------------------------- 1 | package objects; 2 | 3 | import Structure.extend; 4 | import Structure.extendAny; 5 | import VectorMath; 6 | import material.CustomPhysicalMaterial; 7 | import math.Scalar; 8 | import rendering.PostProcess; 9 | import rendering.WebGLRenderTarget; 10 | import three.Color; 11 | import three.ShaderMaterial; 12 | import three.examples.jsm.objects.reflector.Reflector; 13 | import three.examples.jsm.objects.reflector.ReflectorOptions; 14 | 15 | class GlassReflectiveFloor extends Reflector { 16 | 17 | public var reflectorResolution: Float; 18 | public var reflectorKernel: Float; 19 | public var reflectorSigma: Float; 20 | public var reflectorPOT: Bool; 21 | public var reflectorMaterial(get, set): CustomPhysicalMaterial; 22 | 23 | public function new(geometry: three.BufferGeometry, ?options: ReflectorOptions) { 24 | var options = extend({ 25 | textureWidth: 1, 26 | textureHeight: 1, 27 | clipBias: 0., 28 | encoding: three.TextureEncoding.LinearEncoding, 29 | }, options != null ? options : {}); 30 | super(geometry, options); 31 | 32 | this.reflectorResolution = 0.5; 33 | this.reflectorKernel = 0.020; 34 | this.reflectorSigma = 0.5; 35 | this.reflectorPOT = false; 36 | 37 | this.name = 'GlassReflectiveFloor'; 38 | var reflectorRenderTarget = this.getRenderTarget(); 39 | 40 | // hijack reflector rendering to enable blurring 41 | var _onBeforeRender = this.onBeforeRender; 42 | 43 | var postProcess: Null = null; 44 | this.onBeforeRender = (renderer, scene, camera, geometry, material, group) -> { 45 | var currentTarget: WebGLRenderTarget = cast renderer.getRenderTarget(); 46 | 47 | // resize reflector target 48 | var targetSize = vec2(0); 49 | 50 | if (currentTarget != null) { 51 | targetSize.x = currentTarget.width; 52 | targetSize.y = currentTarget.height; 53 | } else { 54 | var gl = renderer.getContext(); 55 | targetSize.x = gl.drawingBufferWidth; 56 | targetSize.y = gl.drawingBufferHeight; 57 | } 58 | 59 | var targetSize = floor(targetSize * reflectorResolution); 60 | 61 | if (reflectorPOT) { 62 | targetSize.x = Scalar.floorPowerOfTwo(targetSize.x); 63 | targetSize.y = Scalar.floorPowerOfTwo(targetSize.y); 64 | } 65 | 66 | if (targetSize != vec2(reflectorRenderTarget.width, reflectorRenderTarget.height)) { 67 | reflectorRenderTarget.setSize(targetSize.x, targetSize.y); 68 | } 69 | 70 | // force linear encoding when rendering reflection 71 | var _previousEncoding = renderer.outputEncoding; 72 | var _previousToneMapping = renderer.toneMapping; 73 | var _previousExposure = renderer.toneMappingExposure; 74 | renderer.outputEncoding = LinearEncoding; 75 | 76 | _onBeforeRender(renderer, scene, camera, geometry, material, group); 77 | 78 | renderer.outputEncoding = _previousEncoding; 79 | renderer.toneMapping = _previousToneMapping; 80 | renderer.toneMappingExposure = _previousExposure; 81 | 82 | // blur 83 | if (reflectorKernel > 0) { 84 | if (postProcess == null) { 85 | postProcess = new PostProcess(renderer); 86 | } 87 | var previousTarget = renderer.getRenderTarget(); 88 | postProcess.blurSelf('reflection-blur', reflectorRenderTarget, reflectorKernel, reflectorSigma); 89 | renderer.setRenderTarget(previousTarget); 90 | } 91 | } 92 | 93 | var _originalMaterial: ShaderMaterial = cast this.material; 94 | 95 | // override material 96 | var material = new CustomPhysicalMaterial(_originalMaterial.uniforms, { 97 | name: 'ReflectiveFloor', 98 | fog: false, 99 | roughness: 0, 100 | metalness: 0.25, 101 | color: new Color(0xFFFFFF), 102 | emissiveIntensity: 0, 103 | flatShading: false, 104 | defines: { 105 | USE_UV: 1, 106 | }, 107 | vertexShader: ' 108 | #define STANDARD 109 | 110 | varying vec3 vViewPosition; 111 | #ifdef USE_TRANSMISSION 112 | varying vec3 vWorldPosition; 113 | #endif 114 | #include 115 | #include 116 | #include 117 | #include 118 | #include 119 | #include 120 | #include 121 | #include 122 | #include 123 | #include 124 | #include 125 | #include 126 | 127 | // reflector 128 | uniform mat4 textureMatrix; 129 | varying vec4 vReflectorUv; 130 | 131 | 132 | void main() { 133 | #include 134 | #include 135 | #include 136 | #include 137 | #include 138 | #include 139 | #include 140 | #include 141 | #include 142 | #include 143 | #include 144 | #include 145 | #include 146 | #include 147 | #include 148 | #include 149 | vViewPosition = - mvPosition.xyz; 150 | #include 151 | #include 152 | #include 153 | #ifdef USE_TRANSMISSION 154 | vWorldPosition = worldPosition.xyz; 155 | #endif 156 | 157 | 158 | vReflectorUv = textureMatrix * vec4( position, 1.0 ); 159 | } 160 | ', 161 | fragmentShader: ' 162 | #define STANDARD 163 | uniform float opacity; 164 | 165 | varying vec3 vViewPosition; 166 | varying vec3 vNormal; 167 | // varying vec3 vPosition; 168 | varying vec2 vUv; 169 | 170 | // reflector 171 | uniform sampler2D tDiffuse; 172 | varying vec4 vReflectorUv; 173 | 174 | float F_Schlick( const in float f0, const in float f90, const in float dotVH ) { 175 | float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); 176 | return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); 177 | } 178 | 179 | void main() { 180 | vec3 planarReflection = texture2DProj( tDiffuse, vReflectorUv ).rgb; 181 | 182 | vec3 viewDir = normalize( vViewPosition ); 183 | vec3 normal = normalize(vNormal); 184 | 185 | float mask = smoothstep(0.5, 0.2, length(0.5 - vUv)); 186 | gl_FragColor = vec4(planarReflection, F_Schlick(opacity, 1.0, dot(normal, viewDir)) * mask); 187 | 188 | #include 189 | #include 190 | #include 191 | } 192 | ' 193 | }); 194 | material.uniforms = extendAny(material.uniforms, _originalMaterial.uniforms); 195 | 196 | this.material = material; 197 | } 198 | 199 | inline function get_reflectorMaterial() return cast this.material; 200 | inline function set_reflectorMaterial(v: CustomPhysicalMaterial) { 201 | this.material = v; 202 | return v; 203 | } 204 | 205 | } -------------------------------------------------------------------------------- /control/ArcBallControl.hx: -------------------------------------------------------------------------------- 1 | package control; 2 | 3 | import Structure.extend; 4 | import animation.Spring; 5 | import event.PointerEvent; 6 | import event.ViewEventManager; 7 | import js.html.Element; 8 | import math.Quat; 9 | 10 | /** 11 | Soft arc ball camera 12 | 13 | Maintains natural drag direction when flipped upside-down (like blender viewport) 14 | 15 | strength / damping parameters 16 | https://www.desmos.com/calculator/fayu8nu1md 17 | **/ 18 | @:nullSafety 19 | class ArcBallControl { 20 | 21 | static final defaults = { 22 | strength: 700, 23 | damping: 100, 24 | dragSpeed: 6, 25 | angleAroundY: 0, 26 | angleAroundXZ: 0, 27 | radius: 1, 28 | zoomSpeed: 1, 29 | } 30 | 31 | // arc ball smoothing 32 | public var dragSpeed: Float; 33 | public var zoomSpeed: Float; 34 | 35 | public var strength(get, set): Float; 36 | inline function get_strength() return angleAroundY.strength; 37 | inline function set_strength(v: Float) return { 38 | angleAroundY.strength = v; 39 | angleAroundXZ.strength = v; 40 | radius.strength = v; 41 | return v; 42 | } 43 | 44 | public var damping(get, set): Float; 45 | inline function get_damping() return angleAroundY.damping; 46 | inline function set_damping(v: Float) return { 47 | angleAroundY.damping = v; 48 | angleAroundXZ.damping = v; 49 | radius.damping = v; 50 | return v; 51 | } 52 | 53 | // orientation (via Springs) 54 | // where angle is 0 at (x=0,z=1) and 90° at (x=1,z=0) 55 | public final angleAroundY = new Spring(0.); 56 | public final angleAroundXZ = new Spring(0.); 57 | public final axialRotation = new Spring(0.); 58 | public final radius = new Spring(1.); 59 | 60 | public final target = new Vec3(0., 0., 0.); 61 | public final position = new Vec3(0., 0., 0.); 62 | public final orientation = new Quat(0, 0, 0, 1); 63 | 64 | public function new( 65 | options: { 66 | ?interactionSurface: Element, 67 | ?viewEventManager: event.ViewEventManager, 68 | ?angleAroundY: Float, 69 | ?angleAroundXZ: Float, 70 | ?radius: Float, 71 | ?strength: Float, 72 | ?damping: Float, 73 | ?dragSpeed: Float, 74 | ?zoomSpeed: Float, 75 | } 76 | ) { 77 | 78 | var options = extend(defaults, options); 79 | 80 | this.dragSpeed = options.dragSpeed; 81 | this.zoomSpeed = options.zoomSpeed; 82 | this.strength = options.strength; 83 | this.damping = options.damping; 84 | this.angleAroundY.forceCompletion(options.angleAroundY); 85 | this.angleAroundXZ.forceCompletion(options.angleAroundXZ); 86 | this.radius.forceCompletion(options.radius); 87 | 88 | var interactionSurface = options.interactionSurface; 89 | var viewEventManager = options.viewEventManager; 90 | 91 | if (viewEventManager == null && interactionSurface != null) { 92 | viewEventManager = new ViewEventManager(interactionSurface); 93 | } 94 | 95 | if (viewEventManager != null) { 96 | viewEventManager.onPointerDown(handlePointerDown); 97 | viewEventManager.onPointerMove(handlePointerMove); 98 | viewEventManager.onPointerUp(handlePointerUp); 99 | viewEventManager.onPointerCancel(handlePointerUp); 100 | viewEventManager.onWheel((e, onView) -> { 101 | if (onView && Math.abs(zoomSpeed) > 0) { 102 | radius.target += e.deltaY * zoomSpeed / 1000; 103 | radius.target = Math.max(radius.target, 0); 104 | e.preventDefault(); 105 | e.nativeEvent.stopPropagation(); 106 | } 107 | }); 108 | } 109 | } 110 | 111 | public inline function step(dt_s: Float) { 112 | angleAroundY.step(dt_s); 113 | angleAroundXZ.step(dt_s); 114 | axialRotation.step(dt_s); 115 | radius.step(dt_s); 116 | 117 | // spherical polar 118 | position.x = radius.value * Math.sin(angleAroundY.value) * Math.cos(angleAroundXZ.value); 119 | position.z = radius.value * Math.cos(angleAroundY.value) * Math.cos(angleAroundXZ.value); 120 | position.y = radius.value * Math.sin(angleAroundXZ.value); 121 | 122 | // look at (0, 0, 0) 123 | var axial = Quat.fromAxisAngle(position.normalize(), axialRotation.value); 124 | var aY = Quat.fromAxisAngle(new Vec3(0, 1, 0), angleAroundY.value); 125 | var aXZ = Quat.fromAxisAngle(new Vec3(1, 0, 0), -angleAroundXZ.value); 126 | orientation.copyFrom(axial * (aY * aXZ)); 127 | } 128 | 129 | public inline function applyToCamera(camera: { 130 | final position: {x: Float, y: Float, z: Float}; 131 | final quaternion: {x: Float, y: Float, z: Float, w: Float}; 132 | }) { 133 | var p = position + target; 134 | var q = orientation; 135 | camera.position.x = p.x; 136 | camera.position.y = p.y; 137 | camera.position.z = p.z; 138 | camera.quaternion.x = q.x; 139 | camera.quaternion.y = q.y; 140 | camera.quaternion.z = q.z; 141 | camera.quaternion.w = q.w; 142 | } 143 | 144 | public inline function update(camera: { 145 | final position: {x: Float, y: Float, z: Float}; 146 | final quaternion: {x: Float, y: Float, z: Float, w: Float}; 147 | }, dt_s: Float) { 148 | step(dt_s); 149 | applyToCamera(camera); 150 | } 151 | 152 | var _onDown_angleAroundY: Float = 0; 153 | var _onDown_angleAroundXZ: Float = 0; 154 | var _onDown_clientXY = new Vec2(0, 0); 155 | var _drivingPointerId: Null = null; 156 | public inline function handlePointerDown(e: PointerEvent, onTargetView: Bool) { 157 | if ( 158 | onTargetView && e.button == Primary && e.isPrimary && Math.abs(dragSpeed) > 0 159 | ) { 160 | _drivingPointerId = e.pointerId; 161 | _onDown_angleAroundY = angleAroundY.target; 162 | _onDown_angleAroundXZ = angleAroundXZ.target; 163 | _onDown_clientXY.x = e.x; 164 | _onDown_clientXY.y = e.y; 165 | e.preventDefault(); 166 | e.nativeEvent.stopPropagation(); 167 | } 168 | } 169 | 170 | public inline function handlePointerMove(e: PointerEvent, _) { 171 | if (e.pointerId == _drivingPointerId) { 172 | // normalize coordinates so dragSpeed is independent of screen size 173 | var surfaceSize = new Vec2(e.viewWidth, e.viewHeight); 174 | var aspect = e.viewWidth / e.viewHeight; 175 | var clientXY = new Vec2(e.x, e.y); 176 | var normXY = clientXY / surfaceSize; 177 | var normOnDownXY = _onDown_clientXY / surfaceSize; 178 | var screenSpaceDelta = normXY - normOnDownXY; 179 | 180 | angleAroundXZ.target = _onDown_angleAroundXZ + screenSpaceDelta.y * dragSpeed; 181 | 182 | // flip y-axis rotation direction if we're upside-down 183 | // fade to 0 in and out of the flip for a smoother transition 184 | var up = orientation.transform(new Vec3(0, 1, 0)); 185 | var flip = up.y >= 0 ? 1 : -1; 186 | var fadeMultiplier = (1.0 - Math.pow(Math.abs(up.y) + 1, -4)); 187 | 188 | angleAroundY.target = _onDown_angleAroundY - fadeMultiplier * flip * screenSpaceDelta.x * dragSpeed * aspect; 189 | 190 | e.preventDefault(); 191 | e.nativeEvent.stopPropagation(); 192 | } 193 | } 194 | 195 | public inline function handlePointerUp(e: PointerEvent, _) { 196 | if (e.pointerId == _drivingPointerId) { 197 | _drivingPointerId = null; 198 | 199 | e.preventDefault(); 200 | e.nativeEvent.stopPropagation(); 201 | } 202 | } 203 | 204 | } -------------------------------------------------------------------------------- /shaders/fxaa-frag.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform sampler2D textureSampler; 4 | uniform vec2 texelSize; 5 | 6 | varying vec2 vUV; 7 | varying vec2 sampleCoordS; 8 | varying vec2 sampleCoordE; 9 | varying vec2 sampleCoordN; 10 | varying vec2 sampleCoordW; 11 | varying vec2 sampleCoordNW; 12 | varying vec2 sampleCoordSE; 13 | varying vec2 sampleCoordNE; 14 | varying vec2 sampleCoordSW; 15 | 16 | const float fxaaQualitySubpix = 1.0; 17 | const float fxaaQualityEdgeThreshold = 0.166; 18 | const float fxaaQualityEdgeThresholdMin = 0.0833; 19 | const vec3 kLumaCoefficients = vec3(0.2126, 0.7152, 0.0722); 20 | 21 | #define FxaaLuma(rgba) dot(rgba.rgb, kLumaCoefficients) 22 | 23 | void main(){ 24 | vec2 posM; 25 | 26 | posM.x = vUV.x; 27 | posM.y = vUV.y; 28 | 29 | vec4 rgbyM = texture2D(textureSampler, vUV, 0.0); 30 | float lumaM = FxaaLuma(rgbyM); 31 | float lumaS = FxaaLuma(texture2D(textureSampler, sampleCoordS, 0.0)); 32 | float lumaE = FxaaLuma(texture2D(textureSampler, sampleCoordE, 0.0)); 33 | float lumaN = FxaaLuma(texture2D(textureSampler, sampleCoordN, 0.0)); 34 | float lumaW = FxaaLuma(texture2D(textureSampler, sampleCoordW, 0.0)); 35 | float maxSM = max(lumaS, lumaM); 36 | float minSM = min(lumaS, lumaM); 37 | float maxESM = max(lumaE, maxSM); 38 | float minESM = min(lumaE, minSM); 39 | float maxWN = max(lumaN, lumaW); 40 | float minWN = min(lumaN, lumaW); 41 | float rangeMax = max(maxWN, maxESM); 42 | float rangeMin = min(minWN, minESM); 43 | float rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; 44 | float range = rangeMax - rangeMin; 45 | float rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); 46 | 47 | #ifndef MALI 48 | if(range < rangeMaxClamped) 49 | { 50 | gl_FragColor = rgbyM; 51 | return; 52 | } 53 | #endif 54 | 55 | float lumaNW = FxaaLuma(texture2D(textureSampler, sampleCoordNW, 0.0)); 56 | float lumaSE = FxaaLuma(texture2D(textureSampler, sampleCoordSE, 0.0)); 57 | float lumaNE = FxaaLuma(texture2D(textureSampler, sampleCoordNE, 0.0)); 58 | float lumaSW = FxaaLuma(texture2D(textureSampler, sampleCoordSW, 0.0)); 59 | float lumaNS = lumaN + lumaS; 60 | float lumaWE = lumaW + lumaE; 61 | float subpixRcpRange = 1.0 / range; 62 | float subpixNSWE = lumaNS + lumaWE; 63 | float edgeHorz1 = (-2.0 * lumaM) + lumaNS; 64 | float edgeVert1 = (-2.0 * lumaM) + lumaWE; 65 | float lumaNESE = lumaNE + lumaSE; 66 | float lumaNWNE = lumaNW + lumaNE; 67 | float edgeHorz2 = (-2.0 * lumaE) + lumaNESE; 68 | float edgeVert2 = (-2.0 * lumaN) + lumaNWNE; 69 | float lumaNWSW = lumaNW + lumaSW; 70 | float lumaSWSE = lumaSW + lumaSE; 71 | float edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); 72 | float edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); 73 | float edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; 74 | float edgeVert3 = (-2.0 * lumaS) + lumaSWSE; 75 | float edgeHorz = abs(edgeHorz3) + edgeHorz4; 76 | float edgeVert = abs(edgeVert3) + edgeVert4; 77 | float subpixNWSWNESE = lumaNWSW + lumaNESE; 78 | float lengthSign = texelSize.x; 79 | bool horzSpan = edgeHorz >= edgeVert; 80 | float subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; 81 | 82 | if (!horzSpan) 83 | { 84 | lumaN = lumaW; 85 | } 86 | 87 | if (!horzSpan) 88 | { 89 | lumaS = lumaE; 90 | } 91 | 92 | if (horzSpan) 93 | { 94 | lengthSign = texelSize.y; 95 | } 96 | 97 | float subpixB = (subpixA * (1.0 / 12.0)) - lumaM; 98 | float gradientN = lumaN - lumaM; 99 | float gradientS = lumaS - lumaM; 100 | float lumaNN = lumaN + lumaM; 101 | float lumaSS = lumaS + lumaM; 102 | bool pairN = abs(gradientN) >= abs(gradientS); 103 | float gradient = max(abs(gradientN), abs(gradientS)); 104 | 105 | if (pairN) 106 | { 107 | lengthSign = -lengthSign; 108 | } 109 | 110 | float subpixC = clamp(abs(subpixB) * subpixRcpRange, 0.0, 1.0); 111 | vec2 posB; 112 | 113 | posB.x = posM.x; 114 | posB.y = posM.y; 115 | 116 | vec2 offNP; 117 | 118 | offNP.x = (!horzSpan) ? 0.0 : texelSize.x; 119 | offNP.y = (horzSpan) ? 0.0 : texelSize.y; 120 | 121 | if (!horzSpan) 122 | { 123 | posB.x += lengthSign * 0.5; 124 | } 125 | 126 | if (horzSpan) 127 | { 128 | posB.y += lengthSign * 0.5; 129 | } 130 | 131 | vec2 posN; 132 | 133 | posN.x = posB.x - offNP.x * 1.5; 134 | posN.y = posB.y - offNP.y * 1.5; 135 | 136 | vec2 posP; 137 | 138 | posP.x = posB.x + offNP.x * 1.5; 139 | posP.y = posB.y + offNP.y * 1.5; 140 | 141 | float subpixD = ((-2.0) * subpixC) + 3.0; 142 | float lumaEndN = FxaaLuma(texture2D(textureSampler, posN, 0.0)); 143 | float subpixE = subpixC * subpixC; 144 | float lumaEndP = FxaaLuma(texture2D(textureSampler, posP, 0.0)); 145 | 146 | if (!pairN) 147 | { 148 | lumaNN = lumaSS; 149 | } 150 | 151 | float gradientScaled = gradient * 1.0 / 4.0; 152 | float lumaMM = lumaM - lumaNN * 0.5; 153 | float subpixF = subpixD * subpixE; 154 | bool lumaMLTZero = lumaMM < 0.0; 155 | 156 | lumaEndN -= lumaNN * 0.5; 157 | lumaEndP -= lumaNN * 0.5; 158 | 159 | bool doneN = abs(lumaEndN) >= gradientScaled; 160 | bool doneP = abs(lumaEndP) >= gradientScaled; 161 | 162 | if (!doneN) 163 | { 164 | posN.x -= offNP.x * 3.0; 165 | } 166 | 167 | if (!doneN) 168 | { 169 | posN.y -= offNP.y * 3.0; 170 | } 171 | 172 | bool doneNP = (!doneN) || (!doneP); 173 | 174 | if (!doneP) 175 | { 176 | posP.x += offNP.x * 3.0; 177 | } 178 | 179 | if (!doneP) 180 | { 181 | posP.y += offNP.y * 3.0; 182 | } 183 | 184 | if (doneNP) 185 | { 186 | if (!doneN) lumaEndN = FxaaLuma(texture2D(textureSampler, posN.xy, 0.0)); 187 | if (!doneP) lumaEndP = FxaaLuma(texture2D(textureSampler, posP.xy, 0.0)); 188 | if (!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; 189 | if (!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; 190 | 191 | doneN = abs(lumaEndN) >= gradientScaled; 192 | doneP = abs(lumaEndP) >= gradientScaled; 193 | 194 | if (!doneN) posN.x -= offNP.x * 12.0; 195 | if (!doneN) posN.y -= offNP.y * 12.0; 196 | 197 | doneNP = (!doneN) || (!doneP); 198 | 199 | if (!doneP) posP.x += offNP.x * 12.0; 200 | if (!doneP) posP.y += offNP.y * 12.0; 201 | } 202 | 203 | float dstN = posM.x - posN.x; 204 | float dstP = posP.x - posM.x; 205 | 206 | if (!horzSpan) 207 | { 208 | dstN = posM.y - posN.y; 209 | } 210 | if (!horzSpan) 211 | { 212 | dstP = posP.y - posM.y; 213 | } 214 | 215 | bool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; 216 | float spanLength = (dstP + dstN); 217 | bool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; 218 | float spanLengthRcp = 1.0 / spanLength; 219 | bool directionN = dstN < dstP; 220 | float dst = min(dstN, dstP); 221 | bool goodSpan = directionN ? goodSpanN : goodSpanP; 222 | float subpixG = subpixF * subpixF; 223 | float pixelOffset = (dst * (-spanLengthRcp)) + 0.5; 224 | float subpixH = subpixG * fxaaQualitySubpix; 225 | float pixelOffsetGood = goodSpan ? pixelOffset : 0.0; 226 | float pixelOffsetSubpix = max(pixelOffsetGood, subpixH); 227 | 228 | if (!horzSpan) 229 | { 230 | posM.x += pixelOffsetSubpix * lengthSign; 231 | } 232 | 233 | if (horzSpan) 234 | { 235 | posM.y += pixelOffsetSubpix * lengthSign; 236 | } 237 | 238 | #ifdef MALI 239 | if(range < rangeMaxClamped) 240 | { 241 | gl_FragColor = rgbyM; 242 | } 243 | else 244 | { 245 | gl_FragColor = texture2D(textureSampler, posM, 0.0); 246 | } 247 | #else 248 | gl_FragColor = texture2D(textureSampler, posM, 0.0); 249 | #endif 250 | 251 | } -------------------------------------------------------------------------------- /shaders/Blur1D.hx: -------------------------------------------------------------------------------- 1 | package shaders; 2 | 3 | import js.html.webgl.RenderingContext; 4 | import three.Texture; 5 | import three.Uniform; 6 | import three.Vector2; 7 | import three.RawShaderMaterial; 8 | 9 | /** 10 | Author: haxiomic 11 | This class was first written in TypeScript during my time at Microsoft, it was later merged into Babylon.js 12 | **/ 13 | class Blur1D extends RawShaderMaterial { 14 | 15 | static public inline function get(ctx: RenderingContext, kernel: Float, truncationSigma: Float, directionX: Float, directionY: Float, texture: Texture, width: Float, height: Float) { 16 | kernel = nearestBestKernel(kernel); 17 | var key = '$kernel@$directionX@$directionY@$truncationSigma'; 18 | var instance = instances.get(key); 19 | if (instance == null) { 20 | instance = new Blur1D(ctx, kernel, truncationSigma, directionX, directionY, true); 21 | instances.set(key, instance); 22 | } 23 | instance.uTexture.value = texture; 24 | instance.uTexelSize.value.set(1/width, 1/height); 25 | return instance; 26 | } 27 | 28 | static final instances = new Map(); 29 | 30 | public final uTexture: Uniform; 31 | public final uTexelSize: Uniform; 32 | public final kernel: Float; 33 | public final directionX: Float; 34 | public final directionY: Float; 35 | 36 | public function new(ctx: RenderingContext, kernel: Float, truncationSigma: Float, directionX: Float, directionY: Float, linearSampling: Bool) { 37 | var uTexture = new Uniform(null); 38 | var uTexelSize = new Uniform(new Vector2(1,1)); 39 | var shaderParts = generateShaderParts(ctx, kernel, truncationSigma, directionX, directionY, linearSampling); 40 | var precision = 'mediump'; 41 | super({ 42 | uniforms: { 43 | texture: uTexture, 44 | invResolution: uTexelSize, 45 | }, 46 | vertexShader: ' 47 | precision $precision float; 48 | attribute vec2 position; 49 | 50 | uniform vec2 invResolution; 51 | 52 | \n${shaderParts.varyingDeclarations.join('\n')} 53 | 54 | const vec2 madd = vec2(0.5, 0.5); 55 | 56 | void main() { 57 | vec2 texelCoord = (position * madd + madd); 58 | 59 | \n${shaderParts.varyingValues.join('\n')} 60 | 61 | gl_Position = vec4(position, 0.0, 1.); 62 | } 63 | ', 64 | fragmentShader: ' 65 | precision $precision float; 66 | uniform sampler2D texture; 67 | 68 | \n${shaderParts.fragmentDeclarations.join('\n')} 69 | 70 | \n${shaderParts.varyingDeclarations.join('\n')} 71 | 72 | void main() { 73 | \n${shaderParts.fragmentVariables.join('\n')} 74 | 75 | vec4 blend = vec4(0.0); 76 | \n${shaderParts.textureSamples.join('\n')}; 77 | gl_FragColor = blend; 78 | } 79 | ', 80 | }); 81 | 82 | this.uTexture = uTexture; 83 | this.uTexelSize = uTexelSize; 84 | this.kernel = kernel; 85 | this.directionX = directionX; 86 | this.directionY = directionY; 87 | } 88 | 89 | static function generateShaderParts(ctx: RenderingContext, kernel: Float, truncationSigma: Float, directionX: Float, directionY: Float, linearSampling: Bool) { 90 | // Generate sampling offsets and weights 91 | var N = nearestBestKernel(kernel); 92 | 93 | var centerIndex = (N - 1) / 2; 94 | 95 | // Generate Gaussian sampling weights over kernel 96 | var offsets = new Array(); 97 | var weights = new Array(); 98 | var totalWeight = 0.0; 99 | for (i in 0...N) { 100 | var u = i / (N - 1); 101 | var w = gaussianWeight(u * 2.0 - 1, truncationSigma); 102 | offsets[i] = (i - centerIndex); 103 | weights[i] = w; 104 | totalWeight += w; 105 | } 106 | 107 | // Normalize weights 108 | for (i in 0...weights.length) { 109 | weights[i] /= totalWeight; 110 | } 111 | 112 | /** 113 | Optimize: combine samples to take advantage of hardware linear sampling 114 | Let weights of two samples be A and B 115 | Then the sum of the samples: `Ax + By` 116 | Can be represented with a single lerp sample a (distance to sample x), with new weight W 117 | `Ax + By = W((1-a)x + ay)` 118 | Solving for W, a in terms of A, B: 119 | `W = A + B` 120 | `a = B/(A + B)` 121 | **/ 122 | var optimizeSamples = linearSampling; 123 | if (optimizeSamples) { 124 | var lerpSampleOffsets = new Array(); 125 | var lerpSampleWeights = new Array(); 126 | var i = 0; 127 | while(i < N) { 128 | var A = weights[i]; 129 | var leftOffset = offsets[i]; 130 | 131 | if ((i + 1) < N) { 132 | // there is a pair to combine with 133 | var B = weights[i + 1]; 134 | 135 | var lerpWeight = A + B; 136 | var alpha = B/(A + B); 137 | var lerpOffset = leftOffset + alpha; 138 | 139 | lerpSampleOffsets.push(lerpOffset); 140 | lerpSampleWeights.push(lerpWeight); 141 | } else { 142 | lerpSampleOffsets.push(leftOffset); 143 | lerpSampleWeights.push(A); 144 | } 145 | 146 | i += 2; 147 | } 148 | 149 | // replace with optimized 150 | offsets = lerpSampleOffsets; 151 | weights = lerpSampleWeights; 152 | } 153 | 154 | // Generate shaders 155 | var maxVaryingRows = ctx.getParameter(RenderingContext.MAX_VARYING_VECTORS); 156 | var maxVaryingVec2 = maxVaryingRows; // maybe more in practice? 157 | 158 | var varyingCount = Std.int(Math.min(offsets.length, maxVaryingVec2)); 159 | 160 | var varyingDeclarations = [ 161 | for (i in 0...varyingCount) 162 | 'varying vec2 sampleCoord$i;' 163 | ]; 164 | 165 | var varyingValues = [ 166 | for (i in 0...varyingCount) 167 | 'sampleCoord$i = texelCoord + vec2(${glslFloat(offsets[i] * directionX)}, ${glslFloat(offsets[i] * directionY)}) * invResolution;' 168 | ]; 169 | 170 | var fragmentVariables = [ 171 | for (i in varyingCount...offsets.length) 172 | 'vec2 sampleCoord$i = sampleCoord0 + vec2(${glslFloat((offsets[i] - offsets[0]) * directionX)}, ${glslFloat((offsets[i] - offsets[0]) * directionY)}) * invResolution;' 173 | ]; 174 | 175 | var textureSamples = [ 176 | for (i in 0...offsets.length) 177 | 'blend += texture2D(texture, sampleCoord$i) * ${glslFloat(weights[i])};' 178 | ]; 179 | 180 | return { 181 | varyingDeclarations: varyingDeclarations, 182 | varyingValues: varyingValues, 183 | fragmentDeclarations: varyingCount < offsets.length ? ['uniform vec2 invResolution;'] : [''], 184 | fragmentVariables: fragmentVariables, 185 | textureSamples: textureSamples, 186 | }; 187 | } 188 | 189 | /** 190 | Best kernels are odd numbers that when divided by 2, their integer part is even, so 5, 9 or 13. 191 | Other odd kernels optimize correctly but require proportionally more samples, even kernels are 192 | possible but will produce minor visual artifacts. Since each new kernel requires a new shader we 193 | want to minimize kernel changes, having gaps between physical kernels is helpful in that regard. 194 | The gaps between physical kernels are compensated for in the weighting of the samples 195 | **/ 196 | static function nearestBestKernel(idealKernel: Float): Int { 197 | var v = Math.round(idealKernel); 198 | 199 | for (k in [v, v - 1, v + 1, v - 2, v + 2]) { 200 | if (((k % 2) != 0) && ((Math.floor(k / 2) % 2) == 0) && k > 0) { 201 | return Std.int(Math.max(k, 3)); 202 | } 203 | } 204 | return Std.int(Math.max(v, 3)); 205 | } 206 | 207 | /** 208 | Calculates the value of a Gaussian distribution with sigma 3 at a given point. 209 | **/ 210 | static function gaussianWeight(x: Float, truncationSigma: Float) { 211 | var sigma = truncationSigma; 212 | var denominator = Math.sqrt(2.0 * Math.PI) * sigma; 213 | var exponent = -((x * x) / (2.0 * sigma * sigma)); 214 | var weight = (1.0 / denominator) * Math.exp(exponent); 215 | return weight; 216 | } 217 | 218 | /** 219 | Convert a float to a string ensuring a '.' is included 220 | **/ 221 | static function glslFloat(f: Float) { 222 | var s = Std.string(f); 223 | if (s.indexOf('.') == -1) { 224 | s += '.'; 225 | } 226 | return s; 227 | } 228 | 229 | } -------------------------------------------------------------------------------- /animation/Animator.hx: -------------------------------------------------------------------------------- 1 | package animation; 2 | 3 | #if (!macro) 4 | using Lambda; 5 | 6 | @:nullSafety 7 | class Animator { 8 | 9 | public final temporaryTweens = new Array(); 10 | public final temporarySprings = new Array(); 11 | public final springs = new Array(); 12 | var t_s = 0.0; 13 | var preStepCallbacks = new Array<(t_s: Float, dt_s: Float) -> Void>(); 14 | var postStepCallbacks = new Array<(t_s: Float, dt_s: Float) -> Void>(); 15 | 16 | public function new() {} 17 | 18 | public function step(dt_s: Float) { 19 | t_s += dt_s; 20 | 21 | for (cb in preStepCallbacks) { 22 | cb(t_s, dt_s); 23 | } 24 | 25 | for (tween in temporaryTweens) { 26 | if (!tween.isComplete()) { 27 | tween.step(dt_s); 28 | } 29 | if (tween.isComplete()) { 30 | temporaryTweens.remove(tween); 31 | } 32 | } 33 | 34 | for (spring in temporarySprings) { 35 | if (!spring.isComplete()) { 36 | spring.step(dt_s); 37 | } 38 | if (spring.isComplete()) { 39 | temporarySprings.remove(spring); 40 | } 41 | } 42 | 43 | for (spring in springs) { 44 | spring.step(dt_s); 45 | } 46 | 47 | for (cb in postStepCallbacks) { 48 | cb(t_s, dt_s); 49 | } 50 | } 51 | 52 | public macro function tweenTo(self: Expr, fieldExpression: ExprOf, toValue: ExprOf, ?options: ExprOf<{ 53 | ?duration_s: Float, 54 | ?onUpdate: (value: Float) -> Void, 55 | ?onComplete: () -> Void, 56 | ?easing: Easing, 57 | }>): ExprOf; // implementation below in macro code 58 | 59 | /** 60 | * Animate value with a spring, the spring is removed when the value is reached (if ever) 61 | */ 62 | public macro function springTo(self: Expr, fieldExpression: ExprOf, toValue: ExprOf, options: ExprOf<{ 63 | ?onUpdate: (value: Float) -> Void, 64 | ?onComplete: () -> Void, 65 | ?style: animation.Spring.SpringStyle, 66 | }>): ExprOf; 67 | 68 | /** 69 | * Add permanent spring that will not be removed when the target is reached 70 | * @param spring 71 | */ 72 | public inline function addSpring(spring: Spring) { 73 | springs.push(spring); 74 | return spring; 75 | } 76 | 77 | /** 78 | * Add a temporary spring that will be removed when the target is reached 79 | * @param spring 80 | */ 81 | public inline function addTemporarySpring(spring: Spring) { 82 | temporarySprings.push(spring); 83 | return spring; 84 | } 85 | 86 | public inline function removeSpring(spring: Spring) { 87 | springs.remove(spring); 88 | temporarySprings.remove(spring); 89 | } 90 | 91 | public inline function createSpring( 92 | initialValue: Float, 93 | ?target: Float, 94 | ?style: animation.Spring.SpringStyle, 95 | velocity: Float = 0.0, 96 | ?onUpdate: (value: Float, velocity: Float) -> Void, 97 | ?onComplete: () -> Void 98 | ) { 99 | return addSpring(new Spring(initialValue, target, style, velocity, onUpdate, onComplete)); 100 | } 101 | 102 | public function addBeforeStepCallback(callback: (t_s: Float, dt_s: Float) -> Void) { 103 | if (!preStepCallbacks.has(callback)) { 104 | preStepCallbacks.push(callback); 105 | } 106 | return {remove: () -> preStepCallbacks.remove(callback)}; 107 | } 108 | 109 | public function addAfterStepCallback(callback: (t_s: Float, dt_s: Float) -> Void) { 110 | if (!postStepCallbacks.has(callback)) { 111 | postStepCallbacks.push(callback); 112 | } 113 | return {remove: () -> postStepCallbacks.remove(callback)}; 114 | } 115 | 116 | public function removeBeforeStepCallback(callback: (t_s: Float, dt_s: Float) -> Void) { 117 | return preStepCallbacks.remove(callback); 118 | } 119 | 120 | public function removeAfterStepCallback(callback: (t_s: Float, dt_s: Float) -> Void) { 121 | return postStepCallbacks.remove(callback); 122 | } 123 | 124 | } 125 | 126 | #else // if macro 127 | 128 | import haxe.macro.Expr; 129 | import haxe.macro.Context; 130 | 131 | class Animator { 132 | 133 | public macro function tweenTo(self: Expr, fieldExpression: ExprOf, toValue: ExprOf, options: Expr): ExprOf { 134 | return macro @:privateAccess { 135 | var options: { 136 | ?duration_s: Float, 137 | ?onUpdate: (value: Float) -> Void, 138 | ?onComplete: () -> Void, 139 | ?easing: animation.Easing, 140 | } = $options; 141 | 142 | var startValue: Float = $fieldExpression; 143 | var toValue: Float = $toValue; 144 | 145 | var tween = new animation.Tween( 146 | options.easing, 147 | (__: Float) -> $fieldExpression = __, 148 | startValue, 149 | toValue, 150 | options.duration_s, 151 | options.onUpdate, 152 | options.onComplete 153 | ); 154 | 155 | $self.temporaryTweens.push(tween); 156 | 157 | tween; 158 | }; 159 | } 160 | 161 | public macro function springTo(self: Expr, fieldExpression: ExprOf, toValue: ExprOf, options: Expr): ExprOf { 162 | return macro { 163 | var options:{ 164 | ?onUpdate: (value: Float) -> Void, 165 | ?onComplete: () -> Void, 166 | ?style: animation.Spring.SpringStyle, 167 | } = $options; 168 | 169 | var startValue: Float = $fieldExpression; 170 | var toValue: Float = $toValue; 171 | var spring = new animation.Spring( 172 | startValue, 173 | toValue, 174 | options.style, 175 | 0., 176 | if (options.onUpdate == null) { 177 | (__: Float, ___) -> $fieldExpression = __; 178 | } else { 179 | (__: Float, ___) -> { 180 | $fieldExpression = __; 181 | options.onUpdate(__); 182 | } 183 | }, 184 | options.onComplete 185 | ); 186 | 187 | $self.temporarySprings.push(spring); 188 | spring; 189 | }; 190 | } 191 | 192 | } 193 | 194 | function getExpressionUniqueRef(expr: Expr): Null<{ 195 | parentObjectExpr: Expr, 196 | idExpr: Expr, 197 | }> { 198 | return switch expr.expr { 199 | // well defined 200 | case EField(e, field): 201 | { 202 | parentObjectExpr: e, 203 | idExpr: macro $v{field} 204 | } 205 | 206 | case EArray(array, index): 207 | { 208 | parentObjectExpr: array, 209 | idExpr: macro Std.string($index), 210 | } 211 | 212 | case EConst(CIdent(ident)): 213 | // we need to determine where ident is stored; 214 | // if it's a local variable we can get a unique id from the compiler 215 | // it could be a member variable or a static variable imported from some module 216 | // how can we determine? Do we support imports? 217 | var localVars = Context.getLocalTVars(); 218 | if (localVars.exists(ident)) { 219 | { 220 | parentObjectExpr: macro null, 221 | idExpr: macro $v{Std.string(localVars.get(ident).id)}, 222 | } 223 | } else { 224 | var localClass = Context.getLocalClass(); 225 | if (localClass != null) { 226 | var localClass = localClass.get(); 227 | if (Lambda.exists(localClass.fields.get(), field -> field.name == ident)) { 228 | { 229 | parentObjectExpr: macro this, 230 | idExpr: macro $v{ident}, 231 | } 232 | } else if (Lambda.exists(localClass.fields.get(), field -> field.name == ident)) { 233 | var localClassName = localClass.name; 234 | { 235 | parentObjectExpr: macro $v{localClassName}, 236 | idExpr: macro $v{ident}, 237 | } 238 | } else { 239 | // could search imports... 240 | // that gets complex 241 | Context.warning('Imported variables are not fully supported, try an object field or local variable', expr.pos); 242 | null; 243 | } 244 | } else { 245 | Context.warning('Animator cannot determine a unique reference for this variable, try an object field or local variable', expr.pos); 246 | null; 247 | } 248 | } 249 | 250 | // anonymous - no unique ref but no problem 251 | case EObjectDecl(fields): null; 252 | 253 | // unwrapping 254 | case EParenthesis(e): getExpressionUniqueRef(e); 255 | case ECheckType(e, t): getExpressionUniqueRef(e); 256 | 257 | default: 258 | Context.warning('Animator cannot determine a unique reference for this variable, try an object field or local variable', expr.pos); 259 | null; 260 | } 261 | } 262 | 263 | #end 264 | -------------------------------------------------------------------------------- /material/CustomPhysicalMaterial.hx: -------------------------------------------------------------------------------- 1 | package material; 2 | 3 | import three.Texture; 4 | import Structure.extendAny; 5 | import three.Color; 6 | import three.MeshPhysicalMaterialParameters; 7 | import three.NormalMapTypes; 8 | import three.ShaderMaterial; 9 | import three.ShaderMaterialParameters; 10 | import three.Vector2; 11 | 12 | typedef CustomPhysicalMaterialParameters = ShaderMaterialParameters & MeshPhysicalMaterialParameters & { 13 | ?transparency: Float, // missing from type definitions 14 | ?defaultAttributeValues: haxe.DynamicAccess>, // missing from type definitions 15 | } 16 | 17 | class CustomPhysicalMaterial extends ShaderMaterial { 18 | 19 | @:keep public var flatShading: Bool; 20 | 21 | @:keep public var color: Color; 22 | @:keep public var roughness: Float; 23 | @:keep public var metalness: Float; 24 | 25 | @:keep public var map: Null; 26 | 27 | @:keep public var lightMap: Null; 28 | @:keep public var lightMapIntensity: Float; 29 | 30 | @:keep public var aoMap: Null ; 31 | @:keep public var aoMapIntensity: Float; 32 | 33 | @:keep public var emissive: Color; 34 | @:keep public var emissiveIntensity: Float; 35 | @:keep public var emissiveMap: Null; 36 | 37 | @:keep public var bumpMap: Null; 38 | @:keep public var bumpScale: Float; 39 | 40 | @:keep public var normalMap: Null; 41 | @:keep public var normalMapType: NormalMapTypes; 42 | @:keep public var normalScale: Vector2; 43 | 44 | @:keep public var displacementMap: Null; 45 | @:keep public var displacementScale: Float; 46 | @:keep public var displacementBias: Float; 47 | 48 | @:keep public var roughnessMap: Null; 49 | 50 | @:keep public var metalnessMap: Null; 51 | 52 | @:keep public var alphaMap: Null; 53 | 54 | @:keep public var envMap: Null; 55 | @:keep public var envMapIntensity: Float; 56 | 57 | @:keep public var refractionRatio: Float; 58 | 59 | @:keep public var wireframeLinecap: String; 60 | @:keep public var wireframeLinejoin: String; 61 | 62 | #if (three >= "0.133.0") 63 | @:deprecated("vertex tangents have been removed") 64 | #end 65 | @:keep public var vertexTangents: Bool; 66 | 67 | @:keep public final isMeshStandardMaterial: Bool; 68 | 69 | // MeshPhysicalMaterial 70 | @:keep public var clearcoat(default, set): Float; 71 | @:keep public var clearcoatMap: Null; 72 | @:keep public var clearcoatRoughness: Float; 73 | @:keep public var clearcoatRoughnessMap: Null; 74 | @:keep public var clearcoatNormalScale: Vector2; 75 | @:keep public var clearcoatNormalMap: Null; 76 | 77 | @:isVar 78 | @:keep public var reflectivity (get, set): Float; 79 | 80 | @:keep public var sheen (default, set): Null; 81 | #if (three < "0.135.0") 82 | @:keep public var sheenTint: Color; 83 | #else 84 | @:keep public var sheenColor: Color; 85 | @:keep public var sheenColorMap: Texture; 86 | #end 87 | 88 | @:keep public var sheenRoughness: Float; 89 | @:keep public var sheenRoughnessMap: Texture; 90 | 91 | @:keep public var transparency: Float; 92 | 93 | @:keep public var transmission(default, set): Float; 94 | @:keep public var ior: Float; 95 | 96 | @:keep public var transmissionMap: Null; 97 | @:keep public var thickness: Float; 98 | @:keep public var thicknessMap: Null; 99 | @:keep public var attenuationDistance: Float; 100 | #if (three < "0.135.0") 101 | @:keep public var attenuationTint: Color; 102 | #else 103 | @:keep public var attenuationColor: Color; 104 | #end 105 | 106 | @:keep public var specularIntensity : Float; 107 | #if (three < "0.135.0") 108 | @:keep public var specularTint : Color; 109 | #else 110 | @:keep public var specularColor : Color; 111 | #end 112 | @:keep public var specularIntensityMap : Null; 113 | #if (three < "0.135.0") 114 | @:keep public var specularTintMap : Null; 115 | #else 116 | @:keep public var specularColorMap : Null; 117 | #end 118 | 119 | // @:keep public final isMaterial: Bool; 120 | @:keep public final isMeshPhysicalMaterial: Bool; 121 | @:keep public final isInitialized: Bool; 122 | 123 | public function new( 124 | ?additionalUniforms: haxe.DynamicAccess>, 125 | ?parameters: CustomPhysicalMaterialParameters 126 | ) { 127 | super(extendAny({ 128 | defines: { 129 | 'STANDARD': '', 130 | 'PHYSICAL': '', 131 | }, 132 | uniforms: extendAny(Three.ShaderLib.physical.uniforms, additionalUniforms != null ? additionalUniforms : {}), 133 | vertexShader: Three.ShaderLib.physical.vertexShader, 134 | fragmentShader: Three.ShaderLib.physical.fragmentShader, 135 | fog: true, 136 | }, parameters != null ? parameters : {})); 137 | 138 | this.flatShading = false; 139 | this.color = new Color( 0xffffff ); // diffuse 140 | this.roughness = 1.0; 141 | this.metalness = 0.0; 142 | this.map = null; 143 | this.lightMap = null; 144 | this.lightMapIntensity = 1.0; 145 | this.aoMap = null; 146 | this.aoMapIntensity = 1.0; 147 | this.emissive = new Color( 0x000000 ); 148 | this.emissiveIntensity = 1.0; 149 | this.emissiveMap = null; 150 | this.bumpMap = null; 151 | this.bumpScale = 1; 152 | this.normalMap = null; 153 | this.normalMapType = TangentSpaceNormalMap; 154 | this.normalScale = new Vector2( 1, 1 ); 155 | this.displacementMap = null; 156 | this.displacementScale = 1; 157 | this.displacementBias = 0; 158 | this.roughnessMap = null; 159 | this.metalnessMap = null; 160 | this.alphaMap = null; 161 | this.envMap = null; 162 | this.envMapIntensity = 1.0; 163 | this.refractionRatio = 0.98; 164 | this.wireframeLinecap = 'round'; 165 | this.wireframeLinejoin = 'round'; 166 | #if (three < "0.133.0") 167 | this.vertexTangents = false; 168 | #end 169 | this.isMeshStandardMaterial = true; 170 | this.clearcoat = 0.0; 171 | this.clearcoatMap = null; 172 | this.clearcoatRoughness = 0.0; 173 | this.clearcoatRoughnessMap = null; 174 | this.clearcoatNormalScale = new Vector2( 1, 1 ); 175 | this.clearcoatNormalMap = null; 176 | // this.reflectivity = 0.5; // maps to F0 = 0.04 177 | this.sheen = 0.0; // null will disable sheen bsdf 178 | #if (three < "0.135.0") 179 | this.sheenTint = new Color(0x0); 180 | #else 181 | this.sheenColor = new Color(0x0); 182 | this.sheenColorMap = null; 183 | #end 184 | this.sheenRoughness = 1.0; 185 | this.sheenRoughnessMap = null; 186 | this.transparency = 0.0; 187 | this.transmission = 0.; 188 | this.ior = 1.5; 189 | 190 | this.transmissionMap = null; 191 | 192 | this.thickness = 0.01; 193 | this.thicknessMap = null; 194 | this.attenuationDistance = 0.0; 195 | #if (three < "0.135.0") 196 | this.attenuationTint = new Color( 1, 1, 1 ); 197 | #else 198 | this.attenuationColor = new Color( 1, 1, 1 ); 199 | #end 200 | 201 | 202 | this.specularIntensity = 1.0; 203 | #if (three < "0.135.0") 204 | this.specularTint = new Color(1, 1, 1); 205 | #else 206 | this.specularColor = new Color(1, 1, 1); 207 | #end 208 | this.specularIntensityMap = null; 209 | #if (three < "0.135.0") 210 | this.specularTintMap = null; 211 | #else 212 | this.specularColorMap = null; 213 | #end 214 | 215 | this.isMeshPhysicalMaterial = true; 216 | this.isInitialized = true; 217 | 218 | if (parameters != null) { 219 | this.setValues(parameters); 220 | } 221 | } 222 | 223 | override function setValues(parameters:ShaderMaterialParameters) { 224 | // fix "is not a property of this material" by defining null values initially 225 | if (!isInitialized) { 226 | for (key in Reflect.fields(parameters)) { 227 | Reflect.setField(this, key, null); 228 | } 229 | } 230 | 231 | super.setValues(parameters); 232 | } 233 | 234 | inline function set_sheen(v: Float) { 235 | if ((this.sheen > 0) != (v > 0)) this.version++; 236 | return this.sheen = v; 237 | } 238 | 239 | inline function set_clearcoat(v: Float) { 240 | if ((this.clearcoat > 0) != (v > 0)) this.version++; 241 | return this.clearcoat = v; 242 | } 243 | 244 | inline function set_transmission(v: Float) { 245 | if ((this.transmission > 0) != (v > 0)) this.version++; 246 | return this.transmission = v; 247 | } 248 | 249 | inline function get_reflectivity() { 250 | return clamp(2.5 * ( this.ior - 1 ) / ( this.ior + 1 ), 0, 1); 251 | } 252 | inline function set_reflectivity(v: Float) { 253 | this.reflectivity = v; 254 | return this.ior = ( 1 + 0.4 * v ) / ( 1 - 0.4 * v ); 255 | } 256 | 257 | private inline function clamp(v: Float, min: Float, max: Float) { 258 | return v < min ? min : (v > max ? max : v); 259 | } 260 | 261 | } --------------------------------------------------------------------------------