├── public ├── fable.ico └── index.html ├── package.json ├── Nuget.Config ├── src ├── App.fsproj ├── App.fs └── ThreeJsTypes.fs ├── README.md ├── webpack.config.js ├── LICENSE └── .gitignore /public/fable.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w0lya/fable-three/HEAD/public/fable.ico -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "dependencies": { 4 | "@babel/core": "^7.1.2", 5 | "fable-compiler": "^2.1.6", 6 | "fable-loader": "^2.1.0", 7 | "react": "^16.6.0", 8 | "react-dom": "^16.6.0", 9 | "three": "^0.99.0", 10 | "webpack": "^4.25.1", 11 | "webpack-cli": "^3.1.2", 12 | "webpack-dev-server": "^3.1.10" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Nuget.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/App.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Three.js sample project based on Fable 2 Minimal App 2 | 3 | ## Requirements 4 | 5 | * [dotnet SDK](https://www.microsoft.com/net/download/core) 2.1 or higher 6 | * [node.js](https://nodejs.org) with [npm](https://www.npmjs.com/) 7 | 8 | ## Building and running the app 9 | 10 | * Install JS dependencies: `npm install` 11 | * Start Webpack dev server: `npx webpack-dev-server` 12 | * After the first compilation is finished, in your browser open: http://localhost:8080/ 13 | 14 | Any modification you do to the F# code will be reflected in the web page after saving. 15 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // Note this only includes basic configuration for development mode. 2 | // For a more comprehensive configuration check: 3 | // https://github.com/fable-compiler/webpack-config-template 4 | 5 | var path = require("path"); 6 | 7 | module.exports = { 8 | mode: "development", 9 | entry: "./src/App.fsproj", 10 | output: { 11 | path: path.join(__dirname, "./public"), 12 | filename: "bundle.js", 13 | }, 14 | devServer: { 15 | contentBase: "./public", 16 | port: 8080, 17 | }, 18 | module: { 19 | rules: [{ 20 | test: /\.fs(x|proj)?$/, 21 | use: "fable-loader" 22 | }] 23 | } 24 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Volha Samusik 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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (https://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # TypeScript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | # next.js build output 64 | .next 65 | -------------------------------------------------------------------------------- /src/App.fs: -------------------------------------------------------------------------------- 1 | module App 2 | 3 | open System.Collections.Generic 4 | open Fable.Core 5 | open Fable.Import 6 | open Fable.Import.Browser 7 | open ThreeJs.ThreeJsTypes 8 | 9 | // There's a bug in Fable that doesn't allow global values that are both mutable 10 | // and public, we can solve it by making them private (latest version throws error): 11 | // let [] mutable private globalCamera: ArrayCamera = jsNative 12 | 13 | // However, in this case we do want to declare the variables ourselves 14 | // so we can just do it in F# and initialize them to null 15 | let mutable globalCamera: ArrayCamera = null 16 | let mutable globalScene: Scene = null 17 | let mutable globalRenderer: WebGLRenderer = null 18 | let mutable globalMesh: Mesh = null 19 | 20 | let onWindowResize(e:Browser.UIEvent) = 21 | globalCamera.aspect <- window.innerWidth / window.innerHeight 22 | globalCamera.updateProjectionMatrix() 23 | globalRenderer.setSize( window.innerWidth, window.innerHeight) 24 | 25 | let init() = 26 | 27 | let amount = 5. 28 | let size = 1. / amount 29 | let aspectRatio = window.innerWidth / window.innerHeight 30 | let cameras = new List() 31 | 32 | for y = 0 to (int)amount do 33 | for x = 0 to (int)amount do 34 | 35 | let subcamera = Globals.PerspectiveCamera.Create( 40, aspectRatio, 0.1, 10 ) 36 | 37 | subcamera.bounds <- Globals.Vector4.Create( (float)x / amount, (float)y / amount, size, size ) 38 | 39 | subcamera.position.x <- ( (float)x / amount ) - 0.5 40 | subcamera.position.y <- 0.5 - ( (float)y / amount ) 41 | subcamera.position.z <- 1.5 42 | subcamera.position.multiplyScalar( 2.) |> ignore 43 | subcamera.lookAt( 0., 0., 0. ) 44 | subcamera.updateMatrixWorld() 45 | 46 | cameras.Add(subcamera) 47 | 48 | // Camera at a higher level (ArrayCamera) 49 | let camera = Globals.ArrayCamera.Create(cameras) 50 | 51 | camera.position.z <- 3. 52 | 53 | globalCamera <- camera 54 | 55 | let scene = Globals.Scene.Create() 56 | let ambientLight = Globals.AmbientLight.Create(Globals.Color.Create(0xFF8080)) 57 | 58 | scene.add(ambientLight) |> ignore 59 | 60 | let dirLight = Globals.DirectionalLight.Create() 61 | dirLight.position.set( 0.5, 0.5, 1. ) |> ignore 62 | dirLight.castShadow <- true 63 | dirLight.shadow.camera.zoom <- 4. 64 | scene.add( dirLight ) |> ignore 65 | 66 | let geometry = Globals.PlaneBufferGeometry.Create(100,100) 67 | let material = Globals.MeshPhongMaterial.Create(color=Globals.Color.Create(0x006600)) 68 | let background = Globals.Mesh.Create(geometry, material) 69 | background.receiveShadow <- true 70 | background.position.set( 0., 0., -1. ) |> ignore 71 | scene.add( background ) |> ignore 72 | 73 | let cbGeometry = Globals.CylinderBufferGeometry.Create(0.5, 0., 1, 45) 74 | cbGeometry.parameters.radiusTop <- 0.5 75 | let cbMaterial = Globals.MeshPhongMaterial.Create(color=Globals.Color.Create(0xFFAA00)) 76 | let mesh = Globals.Mesh.Create(cbGeometry, cbMaterial) 77 | 78 | mesh.castShadow <- true 79 | mesh.receiveShadow <- true 80 | scene.add(mesh) |> ignore 81 | 82 | globalScene <- scene 83 | globalMesh <- mesh 84 | 85 | let renderer = Globals.WebGLRenderer.Create() 86 | renderer.setPixelRatio( window.devicePixelRatio ) 87 | renderer.setSize( window.innerWidth, window.innerHeight ) 88 | renderer.shadowMap.enabled <- true 89 | document.body.appendChild( renderer.domElement ) |> ignore 90 | 91 | globalRenderer <- renderer 92 | 93 | Browser.window.addEventListener_resize(onWindowResize, false ) 94 | 95 | 96 | let rec animate _: unit = 97 | globalMesh.rotation.x <- globalMesh.rotation.x + 0.005 98 | globalMesh.rotation.z <- globalMesh.rotation.z + 0.01 99 | globalRenderer.render( globalScene, globalCamera ) 100 | 101 | Browser.window.requestAnimationFrame(animate) |> ignore 102 | 103 | init() 104 | 105 | animate 0. 106 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | three.js webgl - arraycamera 5 | 6 | 7 | 8 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 155 | 156 | 157 | 226 | 227 | -------------------------------------------------------------------------------- /src/ThreeJsTypes.fs: -------------------------------------------------------------------------------- 1 | namespace ThreeJs 2 | 3 | module ThreeJsTypes = 4 | 5 | 6 | open Fable.Core 7 | open Fable.Import.Browser 8 | 9 | type [] Matrix4 = 10 | interface end 11 | 12 | type [] Euler = 13 | abstract x : float with get,set 14 | abstract y : float with get, set 15 | abstract z : float with get, set 16 | 17 | type [] Vector3 = 18 | abstract x : float with get,set 19 | abstract y : float with get, set 20 | abstract z : float with get, set 21 | // Sets value of this vector. 22 | abstract set: x: float * y: float * z: float -> Vector3 23 | /// Multiplies this vector by scalar s. 24 | abstract multiplyScalar: s: float -> Vector3 25 | and [] Vector3Type = 26 | [] abstract Create: unit -> Vector3 27 | 28 | 29 | type [] Vector4 = 30 | abstract x : float with get,set 31 | abstract y : float with get, set 32 | abstract z : float with get, set 33 | abstract w : float with get, set 34 | and [] Vector4Type = 35 | [] abstract Create: ?p1:obj * ?p2:obj * ?p3:obj * ?p4:obj -> Vector4 36 | 37 | 38 | type [] Color = 39 | /// Red channel value between 0 and 1. Default is 1. 40 | abstract r: float with get, set 41 | /// Green channel value between 0 and 1. Default is 1. 42 | abstract g: float with get, set 43 | /// Blue channel value between 0 and 1. Default is 1. 44 | abstract b: float with get, set 45 | abstract setHex: hex: float -> Color 46 | abstract set: color: string -> Color 47 | abstract setRGB: r: float * g: float * b: float -> Color 48 | and [] ColorType = 49 | [] abstract Create: ?p1:obj -> Color 50 | 51 | 52 | 53 | type [] Object3D = 54 | abstract position: Vector3 with get, set 55 | abstract add: param : obj -> obj //ResizeArray -> Object3D 56 | abstract castShadow: bool with get, set 57 | abstract receiveShadow: bool with get, set 58 | abstract rotation: Euler with get, set 59 | 60 | type [] Camera = 61 | inherit Object3D 62 | 63 | type [] OrthographicCamera = 64 | inherit Camera 65 | abstract zoom: float with get, set 66 | 67 | 68 | type [] PerspectiveCamera = 69 | inherit Camera 70 | 71 | /// Camera frustum vertical field of view, from bottom to top of view, in degrees. 72 | abstract fov: float with get, set 73 | 74 | /// Camera frustum aspect ratio, window width divided by window height. 75 | abstract aspect: float with get, set 76 | 77 | /// Camera frustum near plane. 78 | abstract near: float with get, set 79 | 80 | /// Camera frustum far plane. 81 | abstract far: float with get, set 82 | abstract bounds : Vector4 with get, set // not found in TS 83 | abstract lookAt: p1:float * p2:float * p3:float -> unit 84 | abstract updateMatrixWorld: ?force: bool -> unit 85 | abstract updateProjectionMatrix: unit -> unit 86 | 87 | and [] PerspectiveCameraType = 88 | [] abstract Create: ?p1:obj * ?p2:obj * ?p3:obj * ?p4:obj -> PerspectiveCamera 89 | 90 | 91 | type [] ArrayCamera = 92 | inherit PerspectiveCamera 93 | abstract cameras: ResizeArray with get, set 94 | abstract isArrayCamera: obj with get, set 95 | 96 | and [] ArrayCameraType = 97 | [] abstract Create: ?p1:obj -> ArrayCamera 98 | 99 | 100 | type [] Scene = 101 | inherit Object3D 102 | and [] SceneType = 103 | [] abstract Create: unit -> Scene 104 | 105 | type [] LightShadow = 106 | abstract camera: Camera with get, set 107 | 108 | type [] DirectionalLightShadow = 109 | interface 110 | inherit LightShadow 111 | abstract camera: OrthographicCamera with get, set 112 | end 113 | 114 | type [] Light = 115 | inherit Object3D 116 | abstract color: Color with get, set 117 | 118 | type [] AmbientLight = 119 | inherit Light 120 | abstract castShadow: bool with get, set 121 | and [] AmbientLightType = 122 | [] abstract Create: ?p1:obj -> AmbientLight 123 | 124 | 125 | type [] DirectionalLight = 126 | inherit Light 127 | /// Target used for shadow camera orientation. 128 | abstract target: Object3D with get, set 129 | abstract shadow: DirectionalLightShadow with get, set 130 | and [] DirectionalLightType = 131 | [] abstract Create: unit -> DirectionalLight 132 | 133 | 134 | 135 | type [] EventDispatcher = 136 | interface end 137 | 138 | // Type to resolve parameters for Geometries. 139 | type [] GeometryParameters = 140 | abstract width : int with get, set 141 | abstract height : int with get, set 142 | abstract radiusTop : float with get, set 143 | abstract radiusBottom : float with get, set 144 | abstract radialSegments : int with get, set 145 | 146 | type [] Geometry = 147 | inherit EventDispatcher 148 | 149 | type [] BufferGeometry = 150 | inherit Geometry 151 | 152 | 153 | type [] CylinderBufferGeometry = 154 | inherit BufferGeometry 155 | abstract parameters: GeometryParameters with get, set 156 | and [] CylinderBufferGeometryType = 157 | [] abstract Create: ?p1:obj * ?p2:obj * ?p3:obj * ?p4:obj -> CylinderBufferGeometry 158 | 159 | 160 | 161 | type [] PlaneBufferGeometry = 162 | inherit BufferGeometry 163 | abstract parameters: GeometryParameters with get, set 164 | and [] PlaneBufferGeometryType = 165 | [] abstract Create: ?p1:obj * ?p:obj -> PlaneBufferGeometry 166 | 167 | 168 | type [] Material = 169 | inherit EventDispatcher 170 | 171 | type [] MeshPhongMaterial = 172 | inherit Material 173 | abstract color: Color with get, set 174 | and [] MeshPhongMaterialType = 175 | [] abstract Create: ?p1:obj -> MeshPhongMaterial 176 | 177 | 178 | type [] Mesh = 179 | inherit Object3D 180 | abstract geometry: obj with get, set 181 | abstract material: obj with get, set 182 | and [] MeshType = 183 | [] abstract Create: ?p1 :obj * ?p2:obj -> Mesh 184 | 185 | 186 | type [] Renderer = 187 | abstract domElement: HTMLCanvasElement with get, set 188 | abstract render: scene: Scene * camera: Camera -> unit 189 | abstract setSize: width: float * height: float * ?updateStyle: bool -> unit 190 | 191 | type [] WebGLShadowMap = 192 | abstract enabled: bool with get, set 193 | 194 | type [] WebGLRenderer = 195 | inherit Renderer 196 | abstract shadowMap: WebGLShadowMap with get, set 197 | abstract getPixelRatio: unit -> float 198 | abstract setPixelRatio: value: float -> unit 199 | and [] WebGLRendererType = 200 | [] abstract Create: unit -> WebGLRenderer 201 | 202 | type [] Globals = 203 | [] static member PerspectiveCamera with get(): PerspectiveCameraType = jsNative and set(v: PerspectiveCameraType): unit = jsNative 204 | [] static member ArrayCamera with get(): ArrayCameraType = jsNative and set(v: ArrayCameraType): unit = jsNative 205 | [] static member Scene with get(): SceneType = jsNative and set(v: SceneType): unit = jsNative 206 | [] static member WebGLRenderer with get(): WebGLRendererType = jsNative and set(v: WebGLRendererType): unit = jsNative 207 | [] static member Mesh with get(): MeshType = jsNative and set(v: MeshType): unit = jsNative 208 | [] static member Vector4 with get(): Vector4Type = jsNative and set(v: Vector4Type): unit = jsNative 209 | [] static member Vector3 with get(): Vector3Type = jsNative and set(v: Vector3Type): unit = jsNative 210 | [] static member AmbientLight with get(): AmbientLightType = jsNative and set(v: AmbientLightType): unit = jsNative 211 | [] static member Color with get(): ColorType = jsNative and set(v: ColorType): unit = jsNative 212 | [] static member DirectionalLight with get(): DirectionalLightType = jsNative and set(v: DirectionalLightType): unit = jsNative 213 | [] static member PlaneBufferGeometry with get(): PlaneBufferGeometryType = jsNative and set(v: PlaneBufferGeometryType): unit = jsNative 214 | [] static member MeshPhongMaterial with get(): MeshPhongMaterialType = jsNative and set(v: MeshPhongMaterialType): unit = jsNative 215 | [] static member CylinderBufferGeometry with get(): CylinderBufferGeometryType = jsNative and set(v: CylinderBufferGeometryType): unit = jsNative 216 | 217 | 218 | 219 | --------------------------------------------------------------------------------