commit 4600bcedd016e9ee86a052be726cdf59b7730ca7 Author: Gaƫtan Renaudeau Date: Fri Dec 2 10:03:51 2016 +0100 initial release diff --git a/.babelrc b/.babelrc new file mode 100755 index 0000000..0e3e3f8 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015", "stage-1", "react"] +} diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100755 index 0000000..df57f1b --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,53 @@ +{ + "parser": "babel-eslint", + "plugins": [ + "react", + "flowtype" + ], + "globals": { + "ReactClass": true + }, + "env": { + "browser": true, + "commonjs": true, + "es6": true, + "node": true + }, + "extends": [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:flowtype/recommended" + ], + "parserOptions": { + "ecmaFeatures": { + "jsx": true + }, + "sourceType": "module" + }, + "settings": { + "flowtype": { + "onlyFilesWithFlowAnnotation": false + } + }, + "rules": { + "strict": 0, + "flowtype/define-flow-type": 1, + "flowtype/use-flow-type": 1, + "indent": [ + "error", + 2 + ], + "no-console": [ + 2, + { "allow": ["warn"] } + ], + "quotes": [ + "error", + "double" + ], + "semi": [ + "error", + "always" + ] + } +} diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100755 index 0000000..0d975ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,9 @@ +### *feature request* or *bug report*? + + + +### Expected behavior + +### Actual behavior + +### Steps to reproduce the behavior diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100755 index 0000000..6bbfb0d --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,9 @@ + + +**Summary** + + + +**Test plan** + + diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..e3e1d55 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +node_modules +coverage +build +.DS_Store +.env +npm-debug.log +*.tmp +packages/*/lib +packages/gl-react/gl-react.js +packages/gl-react-dom/gl-react-dom.js +tests/node_modules_to_test/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100755 index 0000000..d04b11b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,67 @@ +# CONTRIBUTING + +Contributions are always welcome, no matter how large or small. + +## Setup + +```sh +git clone https://github.com/gre/gl-react.git +npm run install-all +npm run build +``` + +## Building + +```sh +npm run build +``` + +You must run this each time you modify one of the packages/ of the library. +It rebuild the lib/ folders and will copy the packages into the projects. + +## Testing + +```sh +npm test +``` + +if you need to regenerate the snapshots: + +```sh +npm run test-rewrite-snapshots +``` + +typecheck: + +```sh +npm run flow +``` + +Finally, Please check that ALL examples of the cookbook are working correctly. + +## Cookbook + +**Run it** + +```sh +cd cookbook +npm start +``` + +**Deploy** (only `gre` can do this at the moment!) + +```sh +npm run deploy-cookbook +``` + +## Clean up and reinstall everything + +```sh +npm run clean-all +npm run install-all +``` + +## License + +By contributing to gl-react, you agree that your contributions will be licensed +under its [MIT license](LICENSE). diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..49e4341 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +The MIT License (MIT) +Copyright (c) 2016 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100755 index 0000000..71203f5 --- /dev/null +++ b/README.md @@ -0,0 +1,130 @@ +> **NB:** This is gl-react v3. For gl-react v2, please see [ProjectSeptemberInc/gl-react](https://github.com/ProjectSeptemberInc/gl-react). + +> [gre/gl-react](https://github.com/gre/gl-react) repository is a complete rewrite of [ProjectSeptemberInc/gl-react](https://github.com/ProjectSeptemberInc/gl-react) library (gl-react v2). +It plans to be the gl-react v3. +We keep both repository at same time because (1) this work is not yet finished and (2) this repository is now a "multi libraries" repository (it's just easier to maintain that way). + +## v3 alpha: development in progress + +- [x] gl-react, universal implementation +- [x] gl-react-dom, DOM implementation +- [x] gl-react-headless, Node.js implementation + - [x] tests: 100% test coverage! +- [ ] gl-react-native, React Native implementation + +**The main remaining work of v3 is the React Native implementation**: + +The long term plan is to rely on [Exponent's WebGL implementation](https://docs.getexponent.com/versions/v11.0.0/sdk/gl-view.html) to implement the WebGL layer. The implementation is still very young and experimental (only implement a subset of WebGL), but as soon as this implementation guarantees a good conformance, the library should just work! **I encourage everyone to contribute to make Exponent WebGL implementation robust**, independently from the library you use at the end (Three.js / Pixi.js / regl / gl-react / whatever!). + +Here is the parts we would like to focus on solving in that implementation: +- Support for framebuffers. This is fundamental for gl-react. +- interoperability with React Native Image `source` prop (basically should support same format as a input for `gl.texImage2D`) +- The WebGL implementation should be a standalone implementation that we can depend as a library (shouldn't requires you to use Exponent if you just use React Native). +- interoperability with more "pixel sources" (Video, Camera, ...) + + +**Other remaining topics:** +- Flow type support: we are waiting the next flow version that should bring WebGL type support: https://github.com/facebook/flow/pull/2764 . + +icon gl-react +======== + +`gl-react` is a [React](https://facebook.github.io/react/) library to write and compose WebGL shaders. *Implement complex effects by composing React components.* + +This universal library must be coupled with one of the concrete implementations: + +- [`gl-react-dom`](packages/gl-react-dom/) for React DOM (web using WebGL). +- **unfinished** [`gl-react-native`](packages/gl-react-native/) for React Native (iOS/Android via OpenGL). +- [`gl-react-headless`](packages/gl-react-headless/) for Node.js (used for testing for now) + + +## Links + +- [Cookbook, examples, API](https://gl-react-cookbook.surge.sh) +- Chat [#gl-react on reactiflux](https://discordapp.com/channels/102860784329052160/106102146109325312) + + +## Gist + +```js +import React from "react"; +import { Shaders, Node, GLSL } from "gl-react"; +const shaders = Shaders.create({ + helloBlue: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform float blue; +void main() { + gl_FragColor = vec4(uv.x, uv.y, blue, 1.0); +}` + } +}); +class HelloBlue extends React.Component { + render() { + const { blue } = this.props; + return ; + } +} +``` + +import the correct implementation, + +```js +import {Surface} from "gl-react-dom"; // for React DOM +import {Surface} from "gl-react-native"; // for React Native +import {Surface} from "gl-react-headless"; // for Node.js! +``` + +and this code... + +```js + + + +``` + +...renders: + +![](https://cloud.githubusercontent.com/assets/211411/9386550/432492c6-475c-11e5-9328-f3d5187298c1.jpg) + + +## Features + +- **React, VDOM and immutable paradigm**: OpenGL is a low level imperative and mutable API. This library takes the best of it and exposes it in an immutable, descriptive way with React. +- **React lifecycle** allows partial GL re-rendering. Only a React Component update will trigger a redraw. Each Node holds a framebuffer state that get redrawn when component updates, then scheduling a Surface reflow. +- **Developer experience** + - React DevTools works like on DOM and allows you to inspect and debug your stack of effects. +- **Uniform bindings**: bindings from JavaScript objects to OpenGL GLSL language types (bool, int, float, vec2, vec3, vec4, mat2, mat3, mat4, sampler2D...) +- An **extensible texture loader** that allows to support any content that goes in the shader as a sampler2D texture. + - support for images + - support for videos (currently `gl-react-dom`) + - support for canvas (`gl-react-dom`) +- **flowtype** support. +- headless tests with Jest. We have reached 99.9% test coverage! +- Modular, Composable, Sharable. Write shaders once into components that you re-use everywhere! At the end, users don't need to write shaders. + +## Atom nice GLSL highlighting + +If you are using Atom Editor, you can have JS inlined GLSL syntax highlighted. + +![](https://cloud.githubusercontent.com/assets/211411/20623048/0527cce2-b306-11e6-85ee-5020be994c10.png) + +**To configure this:** + +- add `language-babel` package. +- Configure `language-babel` to add `GLSL:source.glsl` in settings "*JavaScript Tagged Template Literal Grammar Extensions*". +- (Bonus) Add this CSS to your *Atom > Stylesheet*: +```css +/* language-babel blocks */ +atom-text-editor::shadow .line .ttl-grammar { + /* NB: designed for dark theme. can be customized */ + background-color: rgba(0,0,0,0.3); +} +atom-text-editor::shadow .line .ttl-grammar:first-child:last-child { + display: block; /* force background to take full width only if ttl-grammar is alone in the line. */ +} +``` diff --git a/TRADEOFFS.md b/TRADEOFFS.md new file mode 100755 index 0000000..5b3d62b --- /dev/null +++ b/TRADEOFFS.md @@ -0,0 +1,38 @@ +# gl-react tradeoffs + +## "WebGL is a 2D API" + +The library is focused on composing fragment shaders for use-cases like 2D effects on images/videos/... +**That means Vertex shader and Vertex data are currently out of scope, `gl-react` isn't for 3D use-cases yet**. We might provide soon a escape hatch to do arbitrary gl calls in a Node. + +## Framebuffers (FBOs) + +The library uses **one framebuffer per ``** and do not re-use FBOs across Node instances. +This allows to implement Node caching (only redraw Node if necessary). + +2 exceptions: + +- The root `` do not uses any FBO because it directly draws on the `` canvas. +- The use of `backbuffering` will make a `` uses 2 FBOs: the framebuffer and the backbuffer, each draw will make them swap. + +## Surface and Node redraw + +In order to make redraw efficient, `gl-react` have 2 phases: the `redraw()` phase and the `flush()` phase (reflecting the respective methods available both on `Surface` and `Node`). In short: + +- **redraw() phase** sets a dirty flag to a Node and all its "dependents" (other nodes, buses, surface). *redraws happen generally bottom-up to the Surface.* +- **flush() phase** draws all nodes that have the redraw flag. *draws happens top-down from the Surface.* + +`redraw()` is directly hooked to React update lifecycle (re-rendering a Node will calls `redraw()` for you). +To make this system efficient, **the flush() is by default asynchronous**, i.e. `redraw()` means scheduling a new gl draw. +Surface have a main loop that runs at 60 fps and call `flush()`. This is very efficient because if Surface does not have the redraw flag, `flush()` does nothing. + +> If you want to make a `` synchronously flushing the drawing each time it renders, you can still use the `sync` prop (see in the example "`GameOfLife`"). + +## ``, a way to share computation + +[gl-react used to automatically factorize the duplicates elements of your tree](http://greweb.me/2016/06/glreactconf/), **it has been decided to remove this feature** +in order to make you fully in control. +*This was actually a pain to implement it right, a premature optimization that can have some slower performance.* + +The new gl-react embraces more the React paradigm. +There is a new equivalent way to express a Graph (and share computation): **using a ``**. diff --git a/cookbook-rn/.babelrc b/cookbook-rn/.babelrc new file mode 100755 index 0000000..2e76127 --- /dev/null +++ b/cookbook-rn/.babelrc @@ -0,0 +1,8 @@ +{ + "presets": ["react-native-stage-0/decorator-support"], + "env": { + "development": { + "plugins": ["transform-react-jsx-source"] + } + } +} diff --git a/cookbook-rn/.gitignore b/cookbook-rn/.gitignore new file mode 100755 index 0000000..5e66ba6 --- /dev/null +++ b/cookbook-rn/.gitignore @@ -0,0 +1,3 @@ +node_modules/**/* +.exponent/* +npm-debug.* diff --git a/cookbook-rn/5EOyTDQ.jpg b/cookbook-rn/5EOyTDQ.jpg new file mode 100755 index 0000000..fc9ad97 Binary files /dev/null and b/cookbook-rn/5EOyTDQ.jpg differ diff --git a/cookbook-rn/HelloGL.js b/cookbook-rn/HelloGL.js new file mode 100755 index 0000000..2e4cc98 --- /dev/null +++ b/cookbook-rn/HelloGL.js @@ -0,0 +1,28 @@ +//@flow +import React, { + Component, +} from "react"; +import {GLSL, Node, Shaders} from "gl-react"; +import {Surface} from "gl-react-native"; + +const shaders = Shaders.create({ + helloGL: { + frag: GLSL` +precision highp float; +varying vec2 uv; +void main() { + gl_FragColor = vec4(uv.x, uv.y, 0.5, 1.0); +} + ` + } +}); + +export default class GLExample1 extends Component { + render () { + return ( + + + + ); + } +} diff --git a/cookbook-rn/HelloTexture.js b/cookbook-rn/HelloTexture.js new file mode 100755 index 0000000..ad908c8 --- /dev/null +++ b/cookbook-rn/HelloTexture.js @@ -0,0 +1,34 @@ +//@flow +import React, { + Component, +} from "react"; +import {GLSL, Node, Shaders} from "gl-react"; +import {Surface} from "gl-react-native"; + +const shaders = Shaders.create({ + helloTexture: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D t; +void main() { + gl_FragColor = texture2D(t, uv); +} + ` + } +}); + +export default class GLExample1 extends Component { + render () { + return ( + + + + ); + } +} diff --git a/cookbook-rn/exp.json b/cookbook-rn/exp.json new file mode 100755 index 0000000..00fb47b --- /dev/null +++ b/cookbook-rn/exp.json @@ -0,0 +1,21 @@ +{ + "name": "glrn-ex", + "description": "An empty new project", + "slug": "glrn-ex", + "sdkVersion": "11.0.3", + "version": "1.0.0", + "orientation": "portrait", + "primaryColor": "#cccccc", + "iconUrl": "https://s3.amazonaws.com/exp-brand-assets/ExponentEmptyManifest_192.png", + "notification": { + "iconUrl": "https://s3.amazonaws.com/exp-us-standard/placeholder-push-icon-blue-circle.png", + "color": "#000000" + }, + "loading": { + "iconUrl": "https://s3.amazonaws.com/exp-brand-assets/ExponentEmptyManifest_192.png", + "hideExponentText": false + }, + "packagerOpts": { + "assetExts": ["ttf", "mp4"] + } +} diff --git a/cookbook-rn/main.js b/cookbook-rn/main.js new file mode 100755 index 0000000..14e672e --- /dev/null +++ b/cookbook-rn/main.js @@ -0,0 +1,32 @@ +import Exponent from "exponent"; +import React from "react"; +import { + StyleSheet, + Text, + View, +} from "react-native"; +import HelloGL from "./HelloGL"; +import HelloTexture from "./HelloTexture"; + +class App extends React.Component { + render() { + return ( + + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: "#fff", + alignItems: "center", + justifyContent: "center", + flexDirection: "column", + }, +}); + +Exponent.registerRootComponent(App); diff --git a/cookbook-rn/package.json b/cookbook-rn/package.json new file mode 100755 index 0000000..832e7f6 --- /dev/null +++ b/cookbook-rn/package.json @@ -0,0 +1,15 @@ +{ + "name": "glrn-ex", + "version": "0.0.0", + "description": "Hello Exponent!", + "author": null, + "private": true, + "main": "main.js", + "dependencies": { + "exponent": "^11.0.3-rc.1", + "gl-react": "file:../packages/gl-react", + "gl-react-native": "file:../packages/gl-react-native", + "react": "~15.3.2", + "react-native": "github:exponentjs/react-native#sdk-11.0.2" + } +} diff --git a/cookbook/.babelrc b/cookbook/.babelrc new file mode 100755 index 0000000..c14b282 --- /dev/null +++ b/cookbook/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["react-app"] +} diff --git a/cookbook/.flowconfig b/cookbook/.flowconfig new file mode 100755 index 0000000..4a58bdc --- /dev/null +++ b/cookbook/.flowconfig @@ -0,0 +1,7 @@ +[ignore] + +[include] + +[libs] + +[options] diff --git a/cookbook/API.json b/cookbook/API.json new file mode 100755 index 0000000..9224fa5 --- /dev/null +++ b/cookbook/API.json @@ -0,0 +1,13386 @@ +[ + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "Renders the final tree of ", + "position": { + "start": { + "line": 1, + "column": 3, + "offset": 2 + }, + "end": { + "line": 1, + "column": 29, + "offset": 28 + }, + "indent": [] + } + }, + { + "type": "link", + "title": null, + "url": "#node", + "children": [ + { + "type": "text", + "value": "Node", + "position": { + "start": { + "line": 1, + "column": 30, + "offset": 29 + }, + "end": { + "line": 1, + "column": 34, + "offset": 33 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 29, + "offset": 28 + }, + "end": { + "line": 1, + "column": 42, + "offset": 41 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " in a WebGL Canvas / OpenGLView /...", + "position": { + "start": { + "line": 1, + "column": 42, + "offset": 41 + }, + "end": { + "line": 1, + "column": 78, + "offset": 77 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 80, + "offset": 79 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 80, + "offset": 79 + }, + "indent": [] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "", + "position": { + "start": { + "line": 3, + "column": 1, + "offset": 81 + }, + "end": { + "line": 3, + "column": 12, + "offset": 92 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " performs the concrete GL draw with provided dimension (width, height).", + "position": { + "start": { + "line": 3, + "column": 12, + "offset": 92 + }, + "end": { + "line": 3, + "column": 83, + "offset": 163 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 3, + "column": 1, + "offset": 81 + }, + "end": { + "line": 3, + "column": 83, + "offset": 163 + }, + "indent": [] + } + }, + { + "type": "blockquote", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Surface is the only component that isn't \"universal\",\ntherefore ", + "position": { + "start": { + "line": 5, + "column": 3, + "offset": 167 + }, + "end": { + "line": 6, + "column": 11, + "offset": 231 + }, + "indent": [ + 1 + ] + } + }, + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "Surface is exposed by the platform implementation", + "position": { + "start": { + "line": 6, + "column": 13, + "offset": 233 + }, + "end": { + "line": 6, + "column": 62, + "offset": 282 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 6, + "column": 11, + "offset": 231 + }, + "end": { + "line": 6, + "column": 64, + "offset": 284 + }, + "indent": [] + } + }, + { + "type": "text", + "value": "\n(", + "position": { + "start": { + "line": 6, + "column": 64, + "offset": 284 + }, + "end": { + "line": 7, + "column": 2, + "offset": 286 + }, + "indent": [ + 1 + ] + } + }, + { + "type": "inlineCode", + "value": "gl-react-dom", + "position": { + "start": { + "line": 7, + "column": 2, + "offset": 286 + }, + "end": { + "line": 7, + "column": 16, + "offset": 300 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " / ", + "position": { + "start": { + "line": 7, + "column": 16, + "offset": 300 + }, + "end": { + "line": 7, + "column": 19, + "offset": 303 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "gl-react-native", + "position": { + "start": { + "line": 7, + "column": 19, + "offset": 303 + }, + "end": { + "line": 7, + "column": 36, + "offset": 320 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " / ...),\nunlike the rest of the API exposed through ", + "position": { + "start": { + "line": 7, + "column": 36, + "offset": 320 + }, + "end": { + "line": 8, + "column": 44, + "offset": 372 + }, + "indent": [ + 1 + ] + } + }, + { + "type": "inlineCode", + "value": "gl-react", + "position": { + "start": { + "line": 8, + "column": 44, + "offset": 372 + }, + "end": { + "line": 8, + "column": 54, + "offset": 382 + }, + "indent": [] + } + }, + { + "type": "text", + "value": ".\nEach platform have its own implementation but most props are shared.\nIf you write a gl-react library, you shouldn't use ", + "position": { + "start": { + "line": 8, + "column": 54, + "offset": 382 + }, + "end": { + "line": 10, + "column": 52, + "offset": 504 + }, + "indent": [ + 1, + 1 + ] + } + }, + { + "type": "inlineCode", + "value": "", + "position": { + "start": { + "line": 10, + "column": 52, + "offset": 504 + }, + "end": { + "line": 10, + "column": 63, + "offset": 515 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " but only\nlet the final user doing it. Therefore your code should remain platform-independant.", + "position": { + "start": { + "line": 10, + "column": 63, + "offset": 515 + }, + "end": { + "line": 11, + "column": 85, + "offset": 609 + }, + "indent": [ + 1 + ] + } + } + ], + "position": { + "start": { + "line": 5, + "column": 3, + "offset": 167 + }, + "end": { + "line": 11, + "column": 85, + "offset": 609 + }, + "indent": [ + 1, + 1, + 1, + 1, + 1, + 1 + ] + } + } + ], + "position": { + "start": { + "line": 5, + "column": 1, + "offset": 165 + }, + "end": { + "line": 11, + "column": 85, + "offset": 609 + }, + "indent": [ + 1, + 1, + 1, + 1, + 1, + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 11, + "column": 85, + "offset": 609 + } + } + }, + "tags": [ + { + "title": "class", + "description": null, + "lineNumber": 13, + "type": null, + "name": "Surface" + }, + { + "title": "extends", + "description": null, + "lineNumber": 14, + "type": null, + "name": "Component" + }, + { + "title": "prop", + "description": "width of the Surface. multiplied by `pixelRatio` for the actual canvas pixel size.", + "lineNumber": 15, + "type": { + "type": "NameExpression", + "name": "number" + }, + "name": "width" + }, + { + "title": "prop", + "description": "height of the Surface. multiplied by `pixelRatio` for the actual canvas pixel size.", + "lineNumber": 16, + "type": { + "type": "NameExpression", + "name": "number" + }, + "name": "height" + }, + { + "title": "prop", + "description": "a tree of React Element that renders some [Node](#node) and/or [Bus](#bus).", + "lineNumber": 17, + "type": { + "type": "NameExpression", + "name": "any" + }, + "name": "children" + }, + { + "title": "prop", + "description": "CSS styles that get passed to the underlying `` or ``", + "lineNumber": 18, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "object" + } + }, + "name": "style" + }, + { + "title": "prop", + "description": "an array of things to preload before the Surface start rendering. Help avoiding blinks and providing required textures to render an initial state.", + "lineNumber": 19, + "type": { + "type": "OptionalType", + "expression": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "NameExpression", + "name": "any" + } + ] + } + }, + "name": "preload" + }, + { + "title": "prop", + "description": "a callback called when Surface is ready and just after it rendered.", + "lineNumber": 20, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "function" + } + }, + "name": "onLoad" + }, + { + "title": "prop", + "description": "a callback called when the Surface was not able to load initially.", + "lineNumber": 21, + "type": { + "type": "OptionalType", + "expression": { + "type": "FunctionType", + "params": [ + { + "type": "ParameterType", + "name": "error", + "expression": { + "type": "NameExpression", + "name": "Error" + } + } + ], + "result": { + "type": "VoidLiteral" + } + } + }, + "name": "onLoadError" + }, + { + "title": "prop", + "description": "a callback called when the Surface context was lost.", + "lineNumber": 22, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "function" + } + }, + "name": "onContextLost" + }, + { + "title": "prop", + "description": "a callback called when the Surface was restored and ready.", + "lineNumber": 23, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "function" + } + }, + "name": "onContextRestored" + }, + { + "title": "prop", + "description": "an internal visitor used for logs and tests.", + "lineNumber": 24, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "Visitor" + } + }, + "name": "visitor" + }, + { + "title": "prop", + "description": "**(gl-react-dom only)** a optional set of attributes to init WebGL with.", + "lineNumber": 26, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "WebGLContextAttributes" + } + }, + "name": "webglContextAttributes" + }, + { + "title": "prop", + "description": "**(gl-react-dom only)** allows to override the pixelRatio. (default `devicePixelRatio`)", + "lineNumber": 27, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "number" + } + }, + "name": "pixelRatio", + "default": "window.devicePixelRatio" + }, + { + "title": "example", + "description": "\n \n ", + "lineNumber": 29 + }, + { + "title": "example", + "description": "\n \n ", + "lineNumber": 35 + }, + { + "title": "example", + "description": "\n \n \n https://i.imgur.com/wxqlQkh.jpg\n \n \n ", + "lineNumber": 41 + } + ], + "loc": { + "start": { + "line": 109, + "column": 0 + }, + "end": { + "line": 159, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 160, + "column": 0 + }, + "end": { + "line": 549, + "column": 1 + } + }, + "file": "packages/gl-react/src/createSurface.js" + }, + "kind": "class", + "name": "Surface", + "augments": [ + { + "title": "extends", + "description": null, + "lineNumber": 14, + "type": null, + "name": "Component" + } + ], + "properties": [ + { + "name": "width", + "lineNumber": 15, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "width of the Surface. multiplied by ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 37, + "offset": 36 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "pixelRatio", + "position": { + "start": { + "line": 1, + "column": 37, + "offset": 36 + }, + "end": { + "line": 1, + "column": 49, + "offset": 48 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " for the actual canvas pixel size.", + "position": { + "start": { + "line": 1, + "column": 49, + "offset": 48 + }, + "end": { + "line": 1, + "column": 83, + "offset": 82 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 83, + "offset": 82 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 83, + "offset": 82 + } + } + }, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "name": "height", + "lineNumber": 16, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "height of the Surface. multiplied by ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 38, + "offset": 37 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "pixelRatio", + "position": { + "start": { + "line": 1, + "column": 38, + "offset": 37 + }, + "end": { + "line": 1, + "column": 50, + "offset": 49 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " for the actual canvas pixel size.", + "position": { + "start": { + "line": 1, + "column": 50, + "offset": 49 + }, + "end": { + "line": 1, + "column": 84, + "offset": 83 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 84, + "offset": 83 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 84, + "offset": 83 + } + } + }, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "name": "children", + "lineNumber": 17, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "a tree of React Element that renders some ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 43, + "offset": 42 + }, + "indent": [] + } + }, + { + "type": "link", + "title": null, + "url": "#node", + "children": [ + { + "type": "text", + "value": "Node", + "position": { + "start": { + "line": 1, + "column": 44, + "offset": 43 + }, + "end": { + "line": 1, + "column": 48, + "offset": 47 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 43, + "offset": 42 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " and/or ", + "position": { + "start": { + "line": 1, + "column": 56, + "offset": 55 + }, + "end": { + "line": 1, + "column": 64, + "offset": 63 + }, + "indent": [] + } + }, + { + "type": "link", + "title": null, + "url": "#bus", + "children": [ + { + "type": "text", + "value": "Bus", + "position": { + "start": { + "line": 1, + "column": 65, + "offset": 64 + }, + "end": { + "line": 1, + "column": 68, + "offset": 67 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 64, + "offset": 63 + }, + "end": { + "line": 1, + "column": 75, + "offset": 74 + }, + "indent": [] + } + }, + { + "type": "text", + "value": ".", + "position": { + "start": { + "line": 1, + "column": 75, + "offset": 74 + }, + "end": { + "line": 1, + "column": 76, + "offset": 75 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 76, + "offset": 75 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 76, + "offset": 75 + } + } + }, + "type": { + "type": "NameExpression", + "name": "any" + } + }, + { + "name": "style", + "lineNumber": 18, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "CSS styles that get passed to the underlying ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 46, + "offset": 45 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "", + "position": { + "start": { + "line": 1, + "column": 46, + "offset": 45 + }, + "end": { + "line": 1, + "column": 57, + "offset": 56 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " or ", + "position": { + "start": { + "line": 1, + "column": 57, + "offset": 56 + }, + "end": { + "line": 1, + "column": 61, + "offset": 60 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "", + "position": { + "start": { + "line": 1, + "column": 61, + "offset": 60 + }, + "end": { + "line": 1, + "column": 70, + "offset": 69 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 70, + "offset": 69 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 70, + "offset": 69 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "object" + } + } + }, + { + "name": "preload", + "lineNumber": 19, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "an array of things to preload before the Surface start rendering. Help avoiding blinks and providing required textures to render an initial state.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 147, + "offset": 146 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 147, + "offset": 146 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 147, + "offset": 146 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "NameExpression", + "name": "any" + } + ] + } + } + }, + { + "name": "onLoad", + "lineNumber": 20, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "a callback called when Surface is ready and just after it rendered.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 68, + "offset": 67 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 68, + "offset": 67 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 68, + "offset": 67 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "function" + } + } + }, + { + "name": "onLoadError", + "lineNumber": 21, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "a callback called when the Surface was not able to load initially.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 67, + "offset": 66 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 67, + "offset": 66 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 67, + "offset": 66 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "FunctionType", + "params": [ + { + "type": "ParameterType", + "name": "error", + "expression": { + "type": "NameExpression", + "name": "Error" + } + } + ], + "result": { + "type": "VoidLiteral" + } + } + } + }, + { + "name": "onContextLost", + "lineNumber": 22, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "a callback called when the Surface context was lost.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 53, + "offset": 52 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 53, + "offset": 52 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 53, + "offset": 52 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "function" + } + } + }, + { + "name": "onContextRestored", + "lineNumber": 23, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "a callback called when the Surface was restored and ready.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 59, + "offset": 58 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 59, + "offset": 58 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 59, + "offset": 58 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "function" + } + } + }, + { + "name": "visitor", + "lineNumber": 24, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "an internal visitor used for logs and tests.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 45, + "offset": 44 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 45, + "offset": 44 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 45, + "offset": 44 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "Visitor" + } + } + }, + { + "name": "webglContextAttributes", + "lineNumber": 26, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "(gl-react-dom only)", + "position": { + "start": { + "line": 1, + "column": 3, + "offset": 2 + }, + "end": { + "line": 1, + "column": 22, + "offset": 21 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 24, + "offset": 23 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " a optional set of attributes to init WebGL with.", + "position": { + "start": { + "line": 1, + "column": 24, + "offset": 23 + }, + "end": { + "line": 1, + "column": 73, + "offset": 72 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 73, + "offset": 72 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 73, + "offset": 72 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "WebGLContextAttributes" + } + } + }, + { + "name": "pixelRatio", + "lineNumber": 27, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "(gl-react-dom only)", + "position": { + "start": { + "line": 1, + "column": 3, + "offset": 2 + }, + "end": { + "line": 1, + "column": 22, + "offset": 21 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 24, + "offset": 23 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " allows to override the pixelRatio. (default ", + "position": { + "start": { + "line": 1, + "column": 24, + "offset": 23 + }, + "end": { + "line": 1, + "column": 69, + "offset": 68 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "devicePixelRatio", + "position": { + "start": { + "line": 1, + "column": 69, + "offset": 68 + }, + "end": { + "line": 1, + "column": 87, + "offset": 86 + }, + "indent": [] + } + }, + { + "type": "text", + "value": ")", + "position": { + "start": { + "line": 1, + "column": 87, + "offset": 86 + }, + "end": { + "line": 1, + "column": 88, + "offset": 87 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 88, + "offset": 87 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 88, + "offset": 87 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "number" + } + } + } + ], + "examples": [ + { + "description": "\n \n " + }, + { + "description": "\n \n " + }, + { + "description": "\n \n \n https://i.imgur.com/wxqlQkh.jpg\n \n \n " + } + ], + "members": { + "instance": [ + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "see ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 5, + "offset": 4 + }, + "indent": [] + } + }, + { + "type": "link", + "title": null, + "url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL", + "children": [ + { + "type": "text", + "value": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL", + "position": { + "start": { + "line": 1, + "column": 5, + "offset": 4 + }, + "end": { + "line": 1, + "column": 81, + "offset": 80 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 5, + "offset": 4 + }, + "end": { + "line": 1, + "column": 81, + "offset": 80 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 81, + "offset": 80 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 81, + "offset": 80 + } + } + }, + "tags": [ + { + "title": "param", + "description": "(optional) the image MimeType", + "lineNumber": 2, + "type": { + "type": "NameExpression", + "name": "string" + }, + "name": "mimeType" + }, + { + "title": "param", + "description": "(optional) the image quality", + "lineNumber": 3, + "type": { + "type": "NameExpression", + "name": "number" + }, + "name": "quality" + }, + { + "title": "memberof", + "description": "Surface", + "lineNumber": 4 + }, + { + "title": "instance", + "description": null, + "lineNumber": 5 + } + ], + "loc": { + "start": { + "line": 278, + "column": 2 + }, + "end": { + "line": 284, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 285, + "column": 2 + }, + "end": { + "line": 288, + "column": 3 + } + }, + "file": "packages/gl-react/src/createSurface.js" + }, + "params": [ + { + "name": "mimeType", + "lineNumber": 2, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "(optional) the image MimeType", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 30, + "offset": 29 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 30, + "offset": 29 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 30, + "offset": 29 + } + } + }, + "type": { + "type": "NameExpression", + "name": "string" + } + }, + { + "name": "quality", + "lineNumber": 3, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "(optional) the image quality", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 29, + "offset": 28 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 29, + "offset": 28 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 29, + "offset": 28 + } + } + }, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "title": "param", + "name": "args", + "lineNumber": 285, + "type": { + "type": "RestType", + "expression": { + "type": "AllLiteral" + } + } + } + ], + "memberof": "Surface", + "scope": "instance", + "name": "captureAsDataURL", + "kind": "function", + "returns": [ + { + "type": { + "type": "NameExpression", + "name": "string" + } + } + ], + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Surface", + "kind": "class" + }, + { + "name": "captureAsDataURL", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Surface#captureAsDataURL" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "see ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 5, + "offset": 4 + }, + "indent": [] + } + }, + { + "type": "link", + "title": null, + "url": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob", + "children": [ + { + "type": "text", + "value": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob", + "position": { + "start": { + "line": 1, + "column": 5, + "offset": 4 + }, + "end": { + "line": 1, + "column": 78, + "offset": 77 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 5, + "offset": 4 + }, + "end": { + "line": 1, + "column": 78, + "offset": 77 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 78, + "offset": 77 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 78, + "offset": 77 + } + } + }, + "tags": [ + { + "title": "param", + "description": "(optional) the image MimeType", + "lineNumber": 2, + "type": { + "type": "NameExpression", + "name": "string" + }, + "name": "mimeType" + }, + { + "title": "param", + "description": "(optional) the image quality", + "lineNumber": 3, + "type": { + "type": "NameExpression", + "name": "number" + }, + "name": "quality" + }, + { + "title": "memberof", + "description": "Surface", + "lineNumber": 4 + }, + { + "title": "instance", + "description": null, + "lineNumber": 5 + } + ], + "loc": { + "start": { + "line": 290, + "column": 2 + }, + "end": { + "line": 296, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 297, + "column": 2 + }, + "end": { + "line": 300, + "column": 3 + } + }, + "file": "packages/gl-react/src/createSurface.js" + }, + "params": [ + { + "name": "mimeType", + "lineNumber": 2, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "(optional) the image MimeType", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 30, + "offset": 29 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 30, + "offset": 29 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 30, + "offset": 29 + } + } + }, + "type": { + "type": "NameExpression", + "name": "string" + } + }, + { + "name": "quality", + "lineNumber": 3, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "(optional) the image quality", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 29, + "offset": 28 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 29, + "offset": 28 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 29, + "offset": 28 + } + } + }, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "title": "param", + "name": "args", + "lineNumber": 297, + "type": { + "type": "RestType", + "expression": { + "type": "AllLiteral" + } + } + } + ], + "memberof": "Surface", + "scope": "instance", + "name": "captureAsBlob", + "kind": "function", + "returns": [ + { + "type": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Promise" + }, + "applications": [ + { + "type": "NameExpression", + "name": "Blob" + } + ] + } + } + ], + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Surface", + "kind": "class" + }, + { + "name": "captureAsBlob", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Surface#captureAsBlob" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "capture the root Node pixels. Make sure you have set ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 54, + "offset": 53 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "preserveDrawingBuffer: true", + "position": { + "start": { + "line": 1, + "column": 54, + "offset": 53 + }, + "end": { + "line": 1, + "column": 83, + "offset": 82 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " in ", + "position": { + "start": { + "line": 1, + "column": 83, + "offset": 82 + }, + "end": { + "line": 1, + "column": 87, + "offset": 86 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "webglContextAttributes", + "position": { + "start": { + "line": 1, + "column": 87, + "offset": 86 + }, + "end": { + "line": 1, + "column": 111, + "offset": 110 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " prop.", + "position": { + "start": { + "line": 1, + "column": 111, + "offset": 110 + }, + "end": { + "line": 1, + "column": 117, + "offset": 116 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 117, + "offset": 116 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 117, + "offset": 116 + } + } + }, + "tags": [ + { + "title": "memberof", + "description": "Surface", + "lineNumber": 2 + }, + { + "title": "instance", + "description": null, + "lineNumber": 3 + } + ], + "loc": { + "start": { + "line": 302, + "column": 2 + }, + "end": { + "line": 306, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 307, + "column": 2 + }, + "end": { + "line": 310, + "column": 3 + } + }, + "file": "packages/gl-react/src/createSurface.js" + }, + "memberof": "Surface", + "scope": "instance", + "name": "capture", + "kind": "function", + "params": [ + { + "title": "param", + "name": "x", + "lineNumber": 307, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "title": "param", + "name": "y", + "lineNumber": 307, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "title": "param", + "name": "w", + "lineNumber": 307, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "title": "param", + "name": "h", + "lineNumber": 307, + "type": { + "type": "NameExpression", + "name": "number" + } + } + ], + "returns": [ + { + "type": { + "type": "NameExpression", + "name": "NDArray" + } + } + ], + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Surface", + "kind": "class" + }, + { + "name": "capture", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Surface#capture" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Schedule a redraw of the Surface.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 34, + "offset": 33 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 34, + "offset": 33 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 34, + "offset": 33 + } + } + }, + "tags": [ + { + "title": "memberof", + "description": "Surface", + "lineNumber": 2 + }, + { + "title": "instance", + "description": null, + "lineNumber": 3 + }, + { + "title": "function", + "description": null, + "lineNumber": 4, + "name": null + } + ], + "loc": { + "start": { + "line": 312, + "column": 2 + }, + "end": { + "line": 317, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 318, + "column": 2 + }, + "end": { + "line": 320, + "column": 4 + } + }, + "file": "packages/gl-react/src/createSurface.js" + }, + "memberof": "Surface", + "scope": "instance", + "kind": "function", + "name": "redraw", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Surface", + "kind": "class" + }, + { + "name": "redraw", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Surface#redraw" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Force the redraw (if any) to happen now, synchronously.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + } + } + }, + "tags": [ + { + "title": "memberof", + "description": "Surface", + "lineNumber": 2 + }, + { + "title": "instance", + "description": null, + "lineNumber": 3 + }, + { + "title": "function", + "description": null, + "lineNumber": 4, + "name": null + } + ], + "loc": { + "start": { + "line": 322, + "column": 2 + }, + "end": { + "line": 327, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 328, + "column": 2 + }, + "end": { + "line": 330, + "column": 4 + } + }, + "file": "packages/gl-react/src/createSurface.js" + }, + "memberof": "Surface", + "scope": "instance", + "kind": "function", + "name": "flush", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Surface", + "kind": "class" + }, + { + "name": "flush", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Surface#flush" + } + ], + "static": [], + "events": [] + }, + "path": [ + { + "name": "Surface", + "kind": "class" + } + ], + "namespace": "Surface" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 9, + "offset": 8 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " is the primitive that renders a shader program into a Framebuffer.\nIt can be composed with other ", + "position": { + "start": { + "line": 1, + "column": 9, + "offset": 8 + }, + "end": { + "line": 2, + "column": 31, + "offset": 106 + }, + "indent": [ + 1 + ] + } + }, + { + "type": "inlineCode", + "value": "Node", + "position": { + "start": { + "line": 2, + "column": 31, + "offset": 106 + }, + "end": { + "line": 2, + "column": 37, + "offset": 112 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " via using a sampler2D uniforms.", + "position": { + "start": { + "line": 2, + "column": 37, + "offset": 112 + }, + "end": { + "line": 2, + "column": 69, + "offset": 144 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 69, + "offset": 144 + }, + "indent": [ + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 69, + "offset": 144 + } + } + }, + "tags": [ + { + "title": "prop", + "description": "created with `Shaders.create`", + "lineNumber": 4, + "type": { + "type": "NameExpression", + "name": "ShaderIdentifier" + }, + "name": "shader" + }, + { + "title": "prop", + "description": "uniform values that gets passed to the fragment shader.", + "lineNumber": 5, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "Uniforms" + } + }, + "name": "uniforms" + }, + { + "title": "prop", + "description": "allows to configure things like interpolation of a sampler2D texture.", + "lineNumber": 6, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "Object" + } + }, + "name": "uniformsOptions" + }, + { + "title": "prop", + "description": "the width in in real pixels unit (unlike Surface, no pixel ratio)", + "lineNumber": 7, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "number" + } + }, + "name": "width" + }, + { + "title": "prop", + "description": "the height in in real pixels unit (unlike Surface, no pixel ratio)", + "lineNumber": 8, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "number" + } + }, + "name": "height" + }, + { + "title": "prop", + "description": "If true, a React update will always force a sync redraw of the Node framebuffer.", + "lineNumber": 9, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "bool" + } + }, + "name": "sync" + }, + { + "title": "prop", + "description": "enable the backbuffering that allows to use `Backbuffer` in uniforms to get the previous framebuffer texture state in the fragment shader.", + "lineNumber": 10, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "bool" + } + }, + "name": "backbuffering" + }, + { + "title": "prop", + "description": "configure the blending function to use", + "lineNumber": 11, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "BlendFuncSrcDst" + } + }, + "name": "blendFunc" + }, + { + "title": "prop", + "description": "configure the clear to use (color,...)", + "lineNumber": 12, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "Clear" + } + }, + "name": "clear" + }, + { + "title": "prop", + "description": "in advanced use-cases, you can render things like Bus or contents to be used by Node", + "lineNumber": 13, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "any" + } + }, + "name": "children" + }, + { + "title": "example", + "description": "", + "lineNumber": 14 + } + ], + "loc": { + "start": { + "line": 278, + "column": 0 + }, + "end": { + "line": 294, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 295, + "column": 0 + }, + "end": { + "line": 954, + "column": 1 + } + }, + "file": "packages/gl-react/src/Node.js" + }, + "properties": [ + { + "name": "shader", + "lineNumber": 4, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "created with ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 14, + "offset": 13 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "Shaders.create", + "position": { + "start": { + "line": 1, + "column": 14, + "offset": 13 + }, + "end": { + "line": 1, + "column": 30, + "offset": 29 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 30, + "offset": 29 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 30, + "offset": 29 + } + } + }, + "type": { + "type": "NameExpression", + "name": "ShaderIdentifier" + } + }, + { + "name": "uniforms", + "lineNumber": 5, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "uniform values that gets passed to the fragment shader.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "Uniforms" + } + } + }, + { + "name": "uniformsOptions", + "lineNumber": 6, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "allows to configure things like interpolation of a sampler2D texture.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 70, + "offset": 69 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 70, + "offset": 69 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 70, + "offset": 69 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "Object" + } + } + }, + { + "name": "width", + "lineNumber": 7, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "the width in in real pixels unit (unlike Surface, no pixel ratio)", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 66, + "offset": 65 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 66, + "offset": 65 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 66, + "offset": 65 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "number" + } + } + }, + { + "name": "height", + "lineNumber": 8, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "the height in in real pixels unit (unlike Surface, no pixel ratio)", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 67, + "offset": 66 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 67, + "offset": 66 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 67, + "offset": 66 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "number" + } + } + }, + { + "name": "sync", + "lineNumber": 9, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "If true, a React update will always force a sync redraw of the Node framebuffer.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 81, + "offset": 80 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 81, + "offset": 80 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 81, + "offset": 80 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "bool" + } + } + }, + { + "name": "backbuffering", + "lineNumber": 10, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "enable the backbuffering that allows to use ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 45, + "offset": 44 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "Backbuffer", + "position": { + "start": { + "line": 1, + "column": 45, + "offset": 44 + }, + "end": { + "line": 1, + "column": 57, + "offset": 56 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " in uniforms to get the previous framebuffer texture state in the fragment shader.", + "position": { + "start": { + "line": 1, + "column": 57, + "offset": 56 + }, + "end": { + "line": 1, + "column": 139, + "offset": 138 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 139, + "offset": 138 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 139, + "offset": 138 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "bool" + } + } + }, + { + "name": "blendFunc", + "lineNumber": 11, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "configure the blending function to use", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 39, + "offset": 38 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 39, + "offset": 38 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 39, + "offset": 38 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "BlendFuncSrcDst" + } + } + }, + { + "name": "clear", + "lineNumber": 12, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "configure the clear to use (color,...)", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 39, + "offset": 38 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 39, + "offset": 38 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 39, + "offset": 38 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "Clear" + } + } + }, + { + "name": "children", + "lineNumber": 13, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "in advanced use-cases, you can render things like Bus or contents to be used by Node", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 85, + "offset": 84 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 85, + "offset": 84 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 85, + "offset": 84 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "any" + } + } + } + ], + "examples": [ + { + "description": "" + } + ], + "name": "Node", + "augments": [ + { + "title": "augments", + "name": "Component" + } + ], + "kind": "class", + "members": { + "instance": [ + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Capture the node pixels.\nWithout parameters, it will capture the full rectangle,\notherwise you can provide (x, y) or (x, y, w, h) to provide a subset of this rectangle.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 3, + "column": 88, + "offset": 168 + }, + "indent": [ + 1, + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 3, + "column": 88, + "offset": 168 + }, + "indent": [ + 1, + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 3, + "column": 88, + "offset": 168 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 454, + "column": 2 + }, + "end": { + "line": 458, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 459, + "column": 2 + }, + "end": { + "line": 477, + "column": 3 + } + }, + "file": "packages/gl-react/src/Node.js" + }, + "name": "capture", + "kind": "function", + "params": [ + { + "title": "param", + "name": "x", + "lineNumber": 459, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "title": "param", + "name": "y", + "lineNumber": 459, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "title": "param", + "name": "w", + "lineNumber": 459, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "title": "param", + "name": "h", + "lineNumber": 459, + "type": { + "type": "NameExpression", + "name": "number" + } + } + ], + "returns": [ + { + "type": { + "type": "NameExpression", + "name": "NDArray" + } + } + ], + "memberof": "Node", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Node", + "kind": "class" + }, + { + "name": "capture", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Node#capture" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Schedule a redraw of this node and all dependent nodes.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + } + } + }, + "tags": [ + { + "title": "function", + "description": null, + "lineNumber": 3, + "name": null + } + ], + "loc": { + "start": { + "line": 479, + "column": 2 + }, + "end": { + "line": 483, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 484, + "column": 2 + }, + "end": { + "line": 489, + "column": 4 + } + }, + "file": "packages/gl-react/src/Node.js" + }, + "kind": "function", + "name": "redraw", + "memberof": "Node", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Node", + "kind": "class" + }, + { + "name": "redraw", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Node#redraw" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Force the redraw (if any) to happen now, synchronously.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + } + } + }, + "tags": [ + { + "title": "function", + "description": null, + "lineNumber": 3, + "name": null + } + ], + "loc": { + "start": { + "line": 491, + "column": 2 + }, + "end": { + "line": 495, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 496, + "column": 2 + }, + "end": { + "line": 498, + "column": 4 + } + }, + "file": "packages/gl-react/src/Node.js" + }, + "kind": "function", + "name": "flush", + "memberof": "Node", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Node", + "kind": "class" + }, + { + "name": "flush", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Node#flush" + } + ], + "static": [], + "events": [] + }, + "path": [ + { + "name": "Node", + "kind": "class" + } + ], + "namespace": "Node" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Define shaders statically.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 27, + "offset": 26 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 27, + "offset": 26 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 27, + "offset": 26 + } + } + }, + "tags": [ + { + "title": "namespace", + "description": null, + "lineNumber": 2, + "name": null + } + ], + "loc": { + "start": { + "line": 104, + "column": 0 + }, + "end": { + "line": 107, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 108, + "column": 0 + }, + "end": { + "line": 149, + "column": 2 + } + }, + "file": "packages/gl-react/src/Shaders.js" + }, + "kind": "namespace", + "name": "Shaders", + "members": { + "instance": [], + "static": [ + { + "description": "", + "tags": [ + { + "title": "memberof", + "description": "Shaders", + "lineNumber": 1 + }, + { + "title": "param", + "description": "an object that statically define all shaders.", + "lineNumber": 2, + "type": { + "type": "NameExpression", + "name": "ShadersDefinition" + }, + "name": "shadersDef" + }, + { + "title": "returns", + "description": ", an object map that returns a ShaderIdentifier for each shader key defined in the shaders definition.", + "lineNumber": 3, + "type": { + "type": "NameExpression", + "name": "ShadersSheet" + } + }, + { + "title": "example", + "description": "const shaders = Shaders.create({\n helloGL: {\n frag: GLSL`...`\n }\n });\n ...\n ", + "lineNumber": 4 + } + ], + "loc": { + "start": { + "line": 109, + "column": 2 + }, + "end": { + "line": 121, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 122, + "column": 2 + }, + "end": { + "line": 138, + "column": 3 + } + }, + "file": "packages/gl-react/src/Shaders.js" + }, + "memberof": "Shaders", + "params": [ + { + "name": "shadersDef", + "lineNumber": 2, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "an object that statically define all shaders.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 46, + "offset": 45 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 46, + "offset": 45 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 46, + "offset": 45 + } + } + }, + "type": { + "type": "NameExpression", + "name": "ShadersDefinition" + } + } + ], + "returns": [ + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": ", an object map that returns a ShaderIdentifier for each shader key defined in the shaders definition.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 103, + "offset": 102 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 103, + "offset": 102 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 103, + "offset": 102 + } + } + }, + "type": { + "type": "NameExpression", + "name": "ShadersSheet" + } + } + ], + "examples": [ + { + "description": "const shaders = Shaders.create({\n helloGL: {\n frag: GLSL`...`\n }\n });\n ...\n " + } + ], + "name": "create", + "kind": "function", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Shaders", + "kind": "namespace" + }, + { + "name": "create", + "kind": "function" + } + ], + "namespace": "Shaderscreate" + } + ] + }, + "path": [ + { + "name": "Shaders", + "kind": "namespace" + } + ], + "namespace": "Shaders" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "copy pixel with a linear interpolation", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 39, + "offset": 38 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 39, + "offset": 38 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 39, + "offset": 38 + } + } + }, + "tags": [ + { + "title": "prop", + "description": "content to render", + "lineNumber": 2, + "type": { + "type": "NameExpression", + "name": "any" + }, + "name": "children" + } + ], + "loc": { + "start": { + "line": 10, + "column": 0 + }, + "end": { + "line": 13, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 14, + "column": 0 + }, + "end": { + "line": 24, + "column": 1 + } + }, + "file": "packages/gl-react/src/LinearCopy.js" + }, + "properties": [ + { + "name": "children", + "lineNumber": 2, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "content to render", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 18, + "offset": 17 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 18, + "offset": 17 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 18, + "offset": 17 + } + } + }, + "type": { + "type": "NameExpression", + "name": "any" + } + } + ], + "name": "LinearCopy", + "augments": [ + { + "title": "augments", + "name": "Component" + } + ], + "kind": "class", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "LinearCopy", + "kind": "class" + } + ], + "namespace": "LinearCopy" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "copy pixel with no interpolation (nearest pixel)", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 49, + "offset": 48 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 49, + "offset": 48 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 49, + "offset": 48 + } + } + }, + "tags": [ + { + "title": "prop", + "description": "content to render", + "lineNumber": 2, + "type": { + "type": "NameExpression", + "name": "any" + }, + "name": "children" + } + ], + "loc": { + "start": { + "line": 10, + "column": 0 + }, + "end": { + "line": 13, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 14, + "column": 0 + }, + "end": { + "line": 25, + "column": 1 + } + }, + "file": "packages/gl-react/src/NearestCopy.js" + }, + "properties": [ + { + "name": "children", + "lineNumber": 2, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "content to render", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 18, + "offset": 17 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 18, + "offset": 17 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 18, + "offset": 17 + } + } + }, + "type": { + "type": "NameExpression", + "name": "any" + } + } + ], + "name": "NearestCopy", + "augments": [ + { + "title": "augments", + "name": "Component" + } + ], + "kind": "class", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "NearestCopy", + "kind": "class" + } + ], + "namespace": "NearestCopy" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "a ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 3, + "offset": 2 + }, + "indent": [] + } + }, + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "Bus is a container to \"cache\" and re-use content", + "position": { + "start": { + "line": 1, + "column": 5, + "offset": 4 + }, + "end": { + "line": 1, + "column": 53, + "offset": 52 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 3, + "offset": 2 + }, + "end": { + "line": 1, + "column": 55, + "offset": 54 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " (tree of Node, canvas, video,...) somewhere else in your GL graph.\nTo use it, use the Bus ", + "position": { + "start": { + "line": 1, + "column": 55, + "offset": 54 + }, + "end": { + "line": 2, + "column": 24, + "offset": 145 + }, + "indent": [ + 1 + ] + } + }, + { + "type": "inlineCode", + "value": "ref", + "position": { + "start": { + "line": 2, + "column": 24, + "offset": 145 + }, + "end": { + "line": 2, + "column": 29, + "offset": 150 + }, + "indent": [] + } + }, + { + "type": "text", + "value": ":", + "position": { + "start": { + "line": 2, + "column": 29, + "offset": 150 + }, + "end": { + "line": 2, + "column": 30, + "offset": 151 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 30, + "offset": 151 + }, + "indent": [ + 1 + ] + } + }, + { + "type": "list", + "ordered": false, + "start": null, + "loose": false, + "children": [ + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "provide it in another Node texture uniform so you can share computation (send a Node texture to multiple Nodes dependent) (more exactly, a working pattern is to give a ", + "position": { + "start": { + "line": 3, + "column": 3, + "offset": 154 + }, + "end": { + "line": 3, + "column": 171, + "offset": 322 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "()=>ref", + "position": { + "start": { + "line": 3, + "column": 171, + "offset": 322 + }, + "end": { + "line": 3, + "column": 180, + "offset": 331 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " function that will be resolved in ", + "position": { + "start": { + "line": 3, + "column": 180, + "offset": 331 + }, + "end": { + "line": 3, + "column": 215, + "offset": 366 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "DidUpdate", + "position": { + "start": { + "line": 3, + "column": 215, + "offset": 366 + }, + "end": { + "line": 3, + "column": 226, + "offset": 377 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " lifecycle)", + "position": { + "start": { + "line": 3, + "column": 226, + "offset": 377 + }, + "end": { + "line": 3, + "column": 237, + "offset": 388 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 3, + "column": 3, + "offset": 154 + }, + "end": { + "line": 3, + "column": 237, + "offset": 388 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 3, + "column": 1, + "offset": 152 + }, + "end": { + "line": 3, + "column": 237, + "offset": 388 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "You have a ", + "position": { + "start": { + "line": 4, + "column": 3, + "offset": 391 + }, + "end": { + "line": 4, + "column": 14, + "offset": 402 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "capture()", + "position": { + "start": { + "line": 4, + "column": 14, + "offset": 402 + }, + "end": { + "line": 4, + "column": 25, + "offset": 413 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " method to snapshot the underlying Node (because Node can be hidden being nested React components).", + "position": { + "start": { + "line": 4, + "column": 25, + "offset": 413 + }, + "end": { + "line": 4, + "column": 124, + "offset": 512 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 4, + "column": 3, + "offset": 391 + }, + "end": { + "line": 4, + "column": 124, + "offset": 512 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 4, + "column": 1, + "offset": 389 + }, + "end": { + "line": 4, + "column": 124, + "offset": 512 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 3, + "column": 1, + "offset": 152 + }, + "end": { + "line": 4, + "column": 124, + "offset": 512 + }, + "indent": [ + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 4, + "column": 124, + "offset": 512 + } + } + }, + "tags": [ + { + "title": "prop", + "description": "the content to render. It can also be a function that takes a redraw function and render an element.", + "lineNumber": 7, + "type": { + "type": "NameExpression", + "name": "any" + }, + "name": "children" + }, + { + "title": "prop", + "description": "In case you want to explicitely draw Bus directly into a uniform, you can give the uniform name of the parent node.\nIf this prop is not used, the Bus does not directly belong to a Node and a ref can be used to indirectly give a texture to a node.\n`uniform` is equivalent to directly pass your VDOM inside the Node uniforms prop.\n\n**Usage Example**\n\n[![](https://github.com/gre/gl-react/raw/master/docs/examples/blur.gif)](/blurmapmouse)", + "lineNumber": 8, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "string" + } + }, + "name": "uniform" + }, + { + "title": "example", + "description": "\n \n //here, glEffects or content like a canvas/video...\n \n this.refs.myBus\n }} ... />\n", + "lineNumber": 16 + } + ], + "loc": { + "start": { + "line": 16, + "column": 0 + }, + "end": { + "line": 43, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 44, + "column": 0 + }, + "end": { + "line": 201, + "column": 1 + } + }, + "file": "packages/gl-react/src/Bus.js" + }, + "properties": [ + { + "name": "children", + "lineNumber": 7, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "the content to render. It can also be a function that takes a redraw function and render an element.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 101, + "offset": 100 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 101, + "offset": 100 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 101, + "offset": 100 + } + } + }, + "type": { + "type": "NameExpression", + "name": "any" + } + }, + { + "name": "uniform", + "lineNumber": 8, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "In case you want to explicitely draw Bus directly into a uniform, you can give the uniform name of the parent node.\nIf this prop is not used, the Bus does not directly belong to a Node and a ref can be used to indirectly give a texture to a node.\n", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 3, + "column": 1, + "offset": 247 + }, + "indent": [ + 1, + 1 + ] + } + }, + { + "type": "inlineCode", + "value": "uniform", + "position": { + "start": { + "line": 3, + "column": 1, + "offset": 247 + }, + "end": { + "line": 3, + "column": 10, + "offset": 256 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " is equivalent to directly pass your VDOM inside the Node uniforms prop.", + "position": { + "start": { + "line": 3, + "column": 10, + "offset": 256 + }, + "end": { + "line": 3, + "column": 82, + "offset": 328 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 3, + "column": 82, + "offset": 328 + }, + "indent": [ + 1, + 1 + ] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "Usage Example", + "position": { + "start": { + "line": 5, + "column": 3, + "offset": 332 + }, + "end": { + "line": 5, + "column": 16, + "offset": 345 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 5, + "column": 1, + "offset": 330 + }, + "end": { + "line": 5, + "column": 18, + "offset": 347 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 5, + "column": 1, + "offset": 330 + }, + "end": { + "line": 5, + "column": 18, + "offset": 347 + }, + "indent": [] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "link", + "title": null, + "url": "/blurmapmouse", + "children": [ + { + "type": "image", + "title": null, + "url": "https://github.com/gre/gl-react/raw/master/docs/examples/blur.gif", + "alt": null, + "position": { + "start": { + "line": 7, + "column": 2, + "offset": 350 + }, + "end": { + "line": 7, + "column": 72, + "offset": 420 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 7, + "column": 1, + "offset": 349 + }, + "end": { + "line": 7, + "column": 88, + "offset": 436 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 7, + "column": 1, + "offset": 349 + }, + "end": { + "line": 7, + "column": 88, + "offset": 436 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 7, + "column": 88, + "offset": 436 + } + } + }, + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "string" + } + } + } + ], + "examples": [ + { + "description": "\n \n //here, glEffects or content like a canvas/video...\n \n this.refs.myBus\n }} ... />\n" + } + ], + "name": "Bus", + "augments": [ + { + "title": "augments", + "name": "Component" + } + ], + "kind": "class", + "members": { + "instance": [ + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Capture the underlying Node pixels.\nNB it only works for nodes, not for content like video/canvas.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 63, + "offset": 98 + }, + "indent": [ + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 63, + "offset": 98 + }, + "indent": [ + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 63, + "offset": 98 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 150, + "column": 2 + }, + "end": { + "line": 153, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 154, + "column": 2 + }, + "end": { + "line": 157, + "column": 3 + } + }, + "file": "packages/gl-react/src/Bus.js" + }, + "name": "capture", + "kind": "function", + "params": [ + { + "title": "param", + "name": "x", + "lineNumber": 154, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "title": "param", + "name": "y", + "lineNumber": 154, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "title": "param", + "name": "w", + "lineNumber": 154, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "title": "param", + "name": "h", + "lineNumber": 154, + "type": { + "type": "NameExpression", + "name": "number" + } + } + ], + "returns": [ + { + "type": { + "type": "NameExpression", + "name": "NDArray" + } + } + ], + "memberof": "Bus", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Bus", + "kind": "class" + }, + { + "name": "capture", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Bus#capture" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Schedule a redraw of all nodes that depends on this Bus.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 57, + "offset": 56 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 57, + "offset": 56 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 57, + "offset": 56 + } + } + }, + "tags": [ + { + "title": "function", + "description": null, + "lineNumber": 3, + "name": null + } + ], + "loc": { + "start": { + "line": 164, + "column": 2 + }, + "end": { + "line": 168, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 169, + "column": 2 + }, + "end": { + "line": 171, + "column": 4 + } + }, + "file": "packages/gl-react/src/Bus.js" + }, + "kind": "function", + "name": "redraw", + "memberof": "Bus", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Bus", + "kind": "class" + }, + { + "name": "redraw", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Bus#redraw" + } + ], + "static": [], + "events": [] + }, + "path": [ + { + "name": "Bus", + "kind": "class" + } + ], + "namespace": "Bus" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "A High Order Component (HOC) function that provides\nthe contextual ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 16, + "offset": 67 + }, + "indent": [ + 1 + ] + } + }, + { + "type": "inlineCode", + "value": "width", + "position": { + "start": { + "line": 2, + "column": 16, + "offset": 67 + }, + "end": { + "line": 2, + "column": 23, + "offset": 74 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " and ", + "position": { + "start": { + "line": 2, + "column": 23, + "offset": 74 + }, + "end": { + "line": 2, + "column": 28, + "offset": 79 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "height", + "position": { + "start": { + "line": 2, + "column": 28, + "offset": 79 + }, + "end": { + "line": 2, + "column": 36, + "offset": 87 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " props to a GL Component.\nIt also merge optional width,height props to override the contextual size", + "position": { + "start": { + "line": 2, + "column": 36, + "offset": 87 + }, + "end": { + "line": 3, + "column": 74, + "offset": 186 + }, + "indent": [ + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 3, + "column": 74, + "offset": 186 + }, + "indent": [ + 1, + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 3, + "column": 74, + "offset": 186 + } + } + }, + "tags": [ + { + "title": "function", + "description": null, + "lineNumber": 4, + "name": "connectSize" + }, + { + "title": "param", + "description": "a React Component that receives width and height props", + "lineNumber": 5, + "type": null, + "name": "GLComponent" + }, + { + "title": "returns", + "description": "a Component that merge width and height props\nwith context and renders `GLComponent`.", + "lineNumber": 6, + "type": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "ReactClass" + }, + "applications": [ + { + "type": "AllLiteral" + } + ] + } + }, + { + "title": "example", + "description": "const Foo = ({ width, height }) => ;\n const FooConnected = connectSize(Foo);\n // you don't have to provide width, height.\n // If you do, you override width,height in the context as well, so is implicitly receiving the new width/height.", + "lineNumber": 8 + } + ], + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 17, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 18, + "column": 0 + }, + "end": { + "line": 56, + "column": 2 + } + }, + "file": "packages/gl-react/src/connectSize.js" + }, + "kind": "function", + "name": "connectSize", + "params": [ + { + "name": "GLComponent", + "lineNumber": 5, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "a React Component that receives width and height props", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 55, + "offset": 54 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 55, + "offset": 54 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 55, + "offset": 54 + } + } + }, + "type": { + "type": "UnionType", + "elements": [ + { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "ReactClass" + }, + "applications": [ + null + ] + }, + { + "type": "FunctionType", + "params": [ + { + "type": "ParameterType", + "name": "props", + "expression": { + "type": "AllLiteral" + } + } + ], + "result": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression" + }, + "applications": [ + null + ] + } + } + ] + } + } + ], + "returns": [ + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "a Component that merge width and height props\nwith context and renders ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 26, + "offset": 71 + }, + "indent": [ + 1 + ] + } + }, + { + "type": "inlineCode", + "value": "GLComponent", + "position": { + "start": { + "line": 2, + "column": 26, + "offset": 71 + }, + "end": { + "line": 2, + "column": 39, + "offset": 84 + }, + "indent": [] + } + }, + { + "type": "text", + "value": ".", + "position": { + "start": { + "line": 2, + "column": 39, + "offset": 84 + }, + "end": { + "line": 2, + "column": 40, + "offset": 85 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 40, + "offset": 85 + }, + "indent": [ + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 40, + "offset": 85 + } + } + }, + "type": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "ReactClass" + }, + "applications": [ + { + "type": "AllLiteral" + } + ] + } + } + ], + "examples": [ + { + "description": "const Foo = ({ width, height }) => ;\n const FooConnected = connectSize(Foo);\n // you don't have to provide width, height.\n // If you do, you override width,height in the context as well, so is implicitly receiving the new width/height." + } + ], + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "connectSize", + "kind": "function" + } + ], + "namespace": "connectSize" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "GLSL string template to write your shader.\nThe library use a string template for esthetic reason (e.g. syntax color support) but also so we can more strongly type things.\nNote that later, we might do static analysis to generate at compile time the uniform types for instance.\nSee ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 4, + "column": 5, + "offset": 280 + }, + "indent": [ + 1, + 1, + 1 + ] + } + }, + { + "type": "link", + "title": null, + "url": "http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf", + "children": [ + { + "type": "text", + "value": "GLSL spec", + "position": { + "start": { + "line": 4, + "column": 6, + "offset": 281 + }, + "end": { + "line": 4, + "column": 15, + "offset": 290 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 4, + "column": 5, + "offset": 280 + }, + "end": { + "line": 4, + "column": 97, + "offset": 372 + }, + "indent": [] + } + }, + { + "type": "text", + "value": ".", + "position": { + "start": { + "line": 4, + "column": 97, + "offset": 372 + }, + "end": { + "line": 4, + "column": 98, + "offset": 373 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 4, + "column": 98, + "offset": 373 + }, + "indent": [ + 1, + 1, + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 4, + "column": 98, + "offset": 373 + } + } + }, + "tags": [ + { + "title": "return", + "description": ", an object you can give to `Shaders.create`'s `frag`.", + "lineNumber": 6, + "type": { + "type": "NameExpression", + "name": "GLSLCode" + } + }, + { + "title": "example", + "description": "GLSL`\nprecision highp float;\nvarying vec2 uv;\nvoid main() {\n gl_FragColor = vec4(uv.x, uv.y, 0.5, 1.0);\n}\n`", + "lineNumber": 7 + } + ], + "loc": { + "start": { + "line": 10, + "column": 0 + }, + "end": { + "line": 25, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 26, + "column": 0 + }, + "end": { + "line": 32, + "column": 1 + } + }, + "file": "packages/gl-react/src/GLSL.js" + }, + "returns": [ + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": ", an object you can give to ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 29, + "offset": 28 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "Shaders.create", + "position": { + "start": { + "line": 1, + "column": 29, + "offset": 28 + }, + "end": { + "line": 1, + "column": 45, + "offset": 44 + }, + "indent": [] + } + }, + { + "type": "text", + "value": "'s ", + "position": { + "start": { + "line": 1, + "column": 45, + "offset": 44 + }, + "end": { + "line": 1, + "column": 48, + "offset": 47 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "frag", + "position": { + "start": { + "line": 1, + "column": 48, + "offset": 47 + }, + "end": { + "line": 1, + "column": 54, + "offset": 53 + }, + "indent": [] + } + }, + { + "type": "text", + "value": ".", + "position": { + "start": { + "line": 1, + "column": 54, + "offset": 53 + }, + "end": { + "line": 1, + "column": 55, + "offset": 54 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 55, + "offset": 54 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 55, + "offset": 54 + } + } + }, + "type": { + "type": "NameExpression", + "name": "GLSLCode" + } + } + ], + "examples": [ + { + "description": "GLSL`\nprecision highp float;\nvarying vec2 uv;\nvoid main() {\n gl_FragColor = vec4(uv.x, uv.y, 0.5, 1.0);\n}\n`" + } + ], + "name": "GLSL", + "kind": "class", + "params": [ + { + "title": "param", + "name": "strings", + "lineNumber": 26, + "type": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "NameExpression", + "name": "string" + } + ] + } + }, + { + "title": "param", + "name": "values", + "lineNumber": 26, + "type": { + "type": "RestType", + "expression": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "NameExpression", + "name": "string" + } + ] + } + } + } + ], + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "GLSL", + "kind": "class" + } + ], + "namespace": "GLSL" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "object created by ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 19, + "offset": 18 + }, + "indent": [] + } + }, + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "GLSL", + "position": { + "start": { + "line": 1, + "column": 21, + "offset": 20 + }, + "end": { + "line": 1, + "column": 25, + "offset": 24 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "", + "position": { + "start": { + "line": 1, + "column": 25, + "offset": 24 + }, + "end": { + "line": 1, + "column": 27, + "offset": 26 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 19, + "offset": 18 + }, + "end": { + "line": 1, + "column": 29, + "offset": 28 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " string template", + "position": { + "start": { + "line": 1, + "column": 29, + "offset": 28 + }, + "end": { + "line": 1, + "column": 45, + "offset": 44 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 45, + "offset": 44 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 45, + "offset": 44 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 6, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 7, + "column": 23 + } + }, + "file": "packages/gl-react/src/GLSL.js" + }, + "name": "GLSLCode", + "kind": "typedef", + "type": { + "type": "NameExpression", + "name": "string" + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "GLSLCode", + "kind": "typedef" + } + ], + "namespace": "GLSLCode" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "An object map from a key string to a ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 38, + "offset": 37 + }, + "indent": [] + } + }, + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "ShaderDefinition", + "position": { + "start": { + "line": 1, + "column": 40, + "offset": 39 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 38, + "offset": 37 + }, + "end": { + "line": 1, + "column": 58, + "offset": 57 + }, + "indent": [] + } + }, + { + "type": "text", + "value": ".", + "position": { + "start": { + "line": 1, + "column": 58, + "offset": 57 + }, + "end": { + "line": 1, + "column": 59, + "offset": 58 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 59, + "offset": 58 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 59, + "offset": 58 + } + } + }, + "tags": [ + { + "title": "example", + "description": "{\n helloGL: {\n frag: GLSL`...`\n }\n }", + "lineNumber": 2 + } + ], + "loc": { + "start": { + "line": 35, + "column": 0 + }, + "end": { + "line": 43, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 44, + "column": 0 + }, + "end": { + "line": 46, + "column": 2 + } + }, + "file": "packages/gl-react/src/Shaders.js" + }, + "examples": [ + { + "description": "{\n helloGL: {\n frag: GLSL`...`\n }\n }" + } + ], + "name": "ShadersDefinition", + "kind": "typedef", + "type": { + "type": "RecordType", + "fields": [] + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "ShadersDefinition", + "kind": "typedef" + } + ], + "namespace": "ShadersDefinition" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "An object that contains a ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 27, + "offset": 26 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "frag", + "position": { + "start": { + "line": 1, + "column": 27, + "offset": 26 + }, + "end": { + "line": 1, + "column": 33, + "offset": 32 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " GLSLCode.", + "position": { + "start": { + "line": 1, + "column": 33, + "offset": 32 + }, + "end": { + "line": 1, + "column": 43, + "offset": 42 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 43, + "offset": 42 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 43, + "offset": 42 + } + } + }, + "tags": [ + { + "title": "example", + "description": "{\n frag: GLSL`...`\n }", + "lineNumber": 2 + } + ], + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 14, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 15, + "column": 0 + }, + "end": { + "line": 17, + "column": 3 + } + }, + "file": "packages/gl-react/src/Shaders.js" + }, + "examples": [ + { + "description": "{\n frag: GLSL`...`\n }" + } + ], + "name": "ShaderDefinition", + "kind": "typedef", + "properties": [ + { + "title": "property", + "name": "frag", + "lineNumber": 16, + "type": { + "type": "NameExpression", + "name": "GLSLCode" + } + } + ], + "type": { + "type": "RecordType", + "fields": [ + { + "type": "FieldType", + "key": "frag", + "value": { + "type": "NameExpression", + "name": "GLSLCode" + } + } + ] + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "ShaderDefinition", + "kind": "typedef" + } + ], + "namespace": "ShaderDefinition" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "An object map from a key string to a ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 38, + "offset": 37 + }, + "indent": [] + } + }, + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "ShaderIdentifier", + "position": { + "start": { + "line": 1, + "column": 40, + "offset": 39 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 38, + "offset": 37 + }, + "end": { + "line": 1, + "column": 58, + "offset": 57 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " that you can pass to ", + "position": { + "start": { + "line": 1, + "column": 58, + "offset": 57 + }, + "end": { + "line": 1, + "column": 80, + "offset": 79 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "", + "position": { + "start": { + "line": 1, + "column": 80, + "offset": 79 + }, + "end": { + "line": 1, + "column": 95, + "offset": 94 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 95, + "offset": 94 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 95, + "offset": 94 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 48, + "column": 0 + }, + "end": { + "line": 50, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 51, + "column": 0 + }, + "end": { + "line": 53, + "column": 2 + } + }, + "file": "packages/gl-react/src/Shaders.js" + }, + "name": "ShadersSheet", + "kind": "typedef", + "type": { + "type": "RecordType", + "fields": [] + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "ShadersSheet", + "kind": "typedef" + } + ], + "namespace": "ShadersSheet" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 21, + "column": 0 + }, + "end": { + "line": 23, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 24, + "column": 0 + }, + "end": { + "line": 27, + "column": 2 + } + }, + "file": "packages/gl-react/src/Shaders.js" + }, + "name": "ShaderIdentifier", + "kind": "typedef", + "properties": [ + { + "title": "property", + "name": "type", + "lineNumber": 25 + }, + { + "title": "property", + "name": "id", + "lineNumber": 26, + "type": { + "type": "NameExpression", + "name": "string" + } + } + ], + "type": { + "type": "RecordType", + "fields": [ + { + "type": "FieldType", + "key": "type" + }, + { + "type": "FieldType", + "key": "id", + "value": { + "type": "NameExpression", + "name": "string" + } + } + ] + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "ShaderIdentifier", + "kind": "typedef" + } + ], + "namespace": "ShaderIdentifier" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Uniforms is an map object from uniform name to a value.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 56, + "offset": 55 + }, + "indent": [] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "The library support numerous uniform types via different formats.\nLet's describe them:", + "position": { + "start": { + "line": 3, + "column": 3, + "offset": 59 + }, + "end": { + "line": 4, + "column": 21, + "offset": 145 + }, + "indent": [ + 1 + ] + } + } + ], + "position": { + "start": { + "line": 3, + "column": 1, + "offset": 57 + }, + "end": { + "line": 4, + "column": 23, + "offset": 147 + }, + "indent": [ + 1 + ] + } + } + ], + "position": { + "start": { + "line": 3, + "column": 1, + "offset": 57 + }, + "end": { + "line": 4, + "column": 23, + "offset": 147 + }, + "indent": [ + 1 + ] + } + }, + { + "type": "heading", + "depth": 3, + "children": [ + { + "type": "text", + "value": "int or float", + "position": { + "start": { + "line": 6, + "column": 5, + "offset": 153 + }, + "end": { + "line": 6, + "column": 17, + "offset": 165 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 6, + "column": 1, + "offset": 149 + }, + "end": { + "line": 6, + "column": 17, + "offset": 165 + }, + "indent": [] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "a JavaScript number", + "position": { + "start": { + "line": 8, + "column": 1, + "offset": 167 + }, + "end": { + "line": 8, + "column": 20, + "offset": 186 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 8, + "column": 1, + "offset": 167 + }, + "end": { + "line": 8, + "column": 20, + "offset": 186 + }, + "indent": [] + } + }, + { + "type": "heading", + "depth": 3, + "children": [ + { + "type": "text", + "value": "bool", + "position": { + "start": { + "line": 10, + "column": 5, + "offset": 192 + }, + "end": { + "line": 10, + "column": 9, + "offset": 196 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 10, + "column": 1, + "offset": 188 + }, + "end": { + "line": 10, + "column": 9, + "offset": 196 + }, + "indent": [] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "a JavaScript Boolean", + "position": { + "start": { + "line": 12, + "column": 1, + "offset": 198 + }, + "end": { + "line": 12, + "column": 21, + "offset": 218 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 12, + "column": 1, + "offset": 198 + }, + "end": { + "line": 12, + "column": 21, + "offset": 218 + }, + "indent": [] + } + }, + { + "type": "heading", + "depth": 3, + "children": [ + { + "type": "text", + "value": "int[], float[], bool[] arrays", + "position": { + "start": { + "line": 14, + "column": 5, + "offset": 224 + }, + "end": { + "line": 14, + "column": 34, + "offset": 253 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 14, + "column": 1, + "offset": 220 + }, + "end": { + "line": 14, + "column": 34, + "offset": 253 + }, + "indent": [] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "an array of the number (0/1 for bool can be used as well as bools)", + "position": { + "start": { + "line": 16, + "column": 1, + "offset": 255 + }, + "end": { + "line": 16, + "column": 67, + "offset": 321 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 16, + "column": 1, + "offset": 255 + }, + "end": { + "line": 16, + "column": 67, + "offset": 321 + }, + "indent": [] + } + }, + { + "type": "heading", + "depth": 3, + "children": [ + { + "type": "text", + "value": "vec2, vec3, vec4", + "position": { + "start": { + "line": 18, + "column": 5, + "offset": 327 + }, + "end": { + "line": 18, + "column": 21, + "offset": 343 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 18, + "column": 1, + "offset": 323 + }, + "end": { + "line": 18, + "column": 21, + "offset": 343 + }, + "indent": [] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "an array of number, of size respectively 2, 3 and 4.", + "position": { + "start": { + "line": 20, + "column": 1, + "offset": 345 + }, + "end": { + "line": 20, + "column": 53, + "offset": 397 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 20, + "column": 1, + "offset": 345 + }, + "end": { + "line": 20, + "column": 53, + "offset": 397 + }, + "indent": [] + } + }, + { + "type": "blockquote", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "same is available for ivec* variants.", + "position": { + "start": { + "line": 22, + "column": 3, + "offset": 401 + }, + "end": { + "line": 22, + "column": 40, + "offset": 438 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 22, + "column": 3, + "offset": 401 + }, + "end": { + "line": 22, + "column": 40, + "offset": 438 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 22, + "column": 1, + "offset": 399 + }, + "end": { + "line": 22, + "column": 40, + "offset": 438 + }, + "indent": [] + } + }, + { + "type": "heading", + "depth": 3, + "children": [ + { + "type": "text", + "value": "mat2, mat3, mat4", + "position": { + "start": { + "line": 24, + "column": 5, + "offset": 444 + }, + "end": { + "line": 24, + "column": 21, + "offset": 460 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 24, + "column": 1, + "offset": 440 + }, + "end": { + "line": 24, + "column": 21, + "offset": 460 + }, + "indent": [] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Similarly to vectorial types, you can pass an array of numbers.\nFor matrix, you actually define them in a flatten way (not arrays of arrays).", + "position": { + "start": { + "line": 26, + "column": 1, + "offset": 462 + }, + "end": { + "line": 27, + "column": 78, + "offset": 603 + }, + "indent": [ + 1 + ] + } + } + ], + "position": { + "start": { + "line": 26, + "column": 1, + "offset": 462 + }, + "end": { + "line": 27, + "column": 78, + "offset": 603 + }, + "indent": [ + 1 + ] + } + }, + { + "type": "heading", + "depth": 3, + "children": [ + { + "type": "text", + "value": "sampler2D type (aka texture)", + "position": { + "start": { + "line": 29, + "column": 5, + "offset": 609 + }, + "end": { + "line": 29, + "column": 33, + "offset": 637 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 29, + "column": 1, + "offset": 605 + }, + "end": { + "line": 29, + "column": 33, + "offset": 637 + }, + "indent": [] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "The library support numerous and extensible uniform value format.", + "position": { + "start": { + "line": 31, + "column": 1, + "offset": 639 + }, + "end": { + "line": 31, + "column": 66, + "offset": 704 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 31, + "column": 1, + "offset": 639 + }, + "end": { + "line": 31, + "column": 66, + "offset": 704 + }, + "indent": [] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "FIXME: to be documented.", + "position": { + "start": { + "line": 33, + "column": 3, + "offset": 708 + }, + "end": { + "line": 33, + "column": 27, + "offset": 732 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 33, + "column": 1, + "offset": 706 + }, + "end": { + "line": 33, + "column": 29, + "offset": 734 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 33, + "column": 1, + "offset": 706 + }, + "end": { + "line": 33, + "column": 29, + "offset": 734 + }, + "indent": [] + } + }, + { + "type": "heading", + "depth": 3, + "children": [ + { + "type": "text", + "value": "struct types", + "position": { + "start": { + "line": 35, + "column": 5, + "offset": 740 + }, + "end": { + "line": 35, + "column": 17, + "offset": 752 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 35, + "column": 1, + "offset": 736 + }, + "end": { + "line": 35, + "column": 17, + "offset": 752 + }, + "indent": [] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Consider it unsupported even though it ", + "position": { + "start": { + "line": 37, + "column": 1, + "offset": 754 + }, + "end": { + "line": 37, + "column": 40, + "offset": 793 + }, + "indent": [] + } + }, + { + "type": "emphasis", + "children": [ + { + "type": "text", + "value": "might", + "position": { + "start": { + "line": 37, + "column": 41, + "offset": 794 + }, + "end": { + "line": 37, + "column": 46, + "offset": 799 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 37, + "column": 40, + "offset": 793 + }, + "end": { + "line": 37, + "column": 47, + "offset": 800 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " work since gl-react is based on ", + "position": { + "start": { + "line": 37, + "column": 47, + "offset": 800 + }, + "end": { + "line": 37, + "column": 80, + "offset": 833 + }, + "indent": [] + } + }, + { + "type": "inlineCode", + "value": "gl-texture2d", + "position": { + "start": { + "line": 37, + "column": 80, + "offset": 833 + }, + "end": { + "line": 37, + "column": 94, + "offset": 847 + }, + "indent": [] + } + }, + { + "type": "text", + "value": ".", + "position": { + "start": { + "line": 37, + "column": 94, + "offset": 847 + }, + "end": { + "line": 37, + "column": 95, + "offset": 848 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 37, + "column": 1, + "offset": 754 + }, + "end": { + "line": 37, + "column": 95, + "offset": 848 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 37, + "column": 95, + "offset": 848 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 122, + "column": 0 + }, + "end": { + "line": 161, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 162, + "column": 0 + }, + "end": { + "line": 164, + "column": 2 + } + }, + "file": "packages/gl-react/src/Node.js" + }, + "name": "Uniforms", + "kind": "typedef", + "type": { + "type": "RecordType", + "fields": [] + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Uniforms", + "kind": "typedef" + } + ], + "namespace": "Uniforms" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Options on a texture.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 22, + "offset": 21 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 22, + "offset": 21 + }, + "indent": [] + } + }, + { + "type": "list", + "ordered": false, + "start": null, + "loose": false, + "children": [ + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "interpolation define how the pixel lookup get mapped to screen.", + "position": { + "start": { + "line": 2, + "column": 3, + "offset": 24 + }, + "end": { + "line": 2, + "column": 66, + "offset": 87 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 2, + "column": 3, + "offset": 24 + }, + "end": { + "line": 2, + "column": 66, + "offset": 87 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 2, + "column": 1, + "offset": 22 + }, + "end": { + "line": 2, + "column": 66, + "offset": 87 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "wrap define how the edge lookup behaves. It can be either a ", + "position": { + "start": { + "line": 3, + "column": 3, + "offset": 90 + }, + "end": { + "line": 3, + "column": 63, + "offset": 150 + }, + "indent": [] + } + }, + { + "type": "linkReference", + "identifier": "x,y", + "referenceType": "shortcut", + "children": [ + { + "type": "text", + "value": "x,y", + "position": { + "start": { + "line": 3, + "column": 64, + "offset": 151 + }, + "end": { + "line": 3, + "column": 67, + "offset": 154 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 3, + "column": 63, + "offset": 150 + }, + "end": { + "line": 3, + "column": 68, + "offset": 155 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " wrap or a wrap value for both.", + "position": { + "start": { + "line": 3, + "column": 68, + "offset": 155 + }, + "end": { + "line": 3, + "column": 99, + "offset": 186 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 3, + "column": 3, + "offset": 90 + }, + "end": { + "line": 3, + "column": 99, + "offset": 186 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 3, + "column": 1, + "offset": 88 + }, + "end": { + "line": 3, + "column": 99, + "offset": 186 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 2, + "column": 1, + "offset": 22 + }, + "end": { + "line": 3, + "column": 99, + "offset": 186 + }, + "indent": [ + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 3, + "column": 99, + "offset": 186 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 64, + "column": 0 + }, + "end": { + "line": 68, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 69, + "column": 0 + }, + "end": { + "line": 72, + "column": 2 + } + }, + "file": "packages/gl-react/src/Node.js" + }, + "name": "TextureOptions", + "kind": "typedef", + "properties": [ + { + "title": "property", + "name": "interpolation", + "lineNumber": 70, + "type": { + "type": "NameExpression", + "name": "Interpolation" + } + }, + { + "title": "property", + "name": "wrap", + "lineNumber": 71, + "type": { + "type": "UnionType", + "elements": [ + { + "type": "ArrayType", + "elements": [ + { + "type": "NameExpression", + "name": "WrapMode" + }, + { + "type": "NameExpression", + "name": "WrapMode" + } + ] + }, + { + "type": "NameExpression", + "name": "WrapMode" + } + ] + } + } + ], + "type": { + "type": "RecordType", + "fields": [ + { + "type": "FieldType", + "key": "interpolation", + "value": { + "type": "NameExpression", + "name": "Interpolation" + } + }, + { + "type": "FieldType", + "key": "wrap", + "value": { + "type": "UnionType", + "elements": [ + { + "type": "ArrayType", + "elements": [ + { + "type": "NameExpression", + "name": "WrapMode" + }, + { + "type": "NameExpression", + "name": "WrapMode" + } + ] + }, + { + "type": "NameExpression", + "name": "WrapMode" + } + ] + } + } + ] + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "TextureOptions", + "kind": "typedef" + } + ], + "namespace": "TextureOptions" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "The texture pixel interpolation mode.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 38, + "offset": 37 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 38, + "offset": 37 + }, + "indent": [] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "One of:", + "position": { + "start": { + "line": 3, + "column": 1, + "offset": 39 + }, + "end": { + "line": 3, + "column": 8, + "offset": 46 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 3, + "column": 1, + "offset": 39 + }, + "end": { + "line": 3, + "column": 8, + "offset": 46 + }, + "indent": [] + } + }, + { + "type": "list", + "ordered": false, + "start": null, + "loose": false, + "children": [ + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "linear", + "position": { + "start": { + "line": 4, + "column": 3, + "offset": 49 + }, + "end": { + "line": 4, + "column": 11, + "offset": 57 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 4, + "column": 3, + "offset": 49 + }, + "end": { + "line": 4, + "column": 11, + "offset": 57 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 4, + "column": 1, + "offset": 47 + }, + "end": { + "line": 4, + "column": 11, + "offset": 57 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "nearest", + "position": { + "start": { + "line": 5, + "column": 3, + "offset": 60 + }, + "end": { + "line": 5, + "column": 12, + "offset": 69 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 5, + "column": 3, + "offset": 60 + }, + "end": { + "line": 5, + "column": 12, + "offset": 69 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 5, + "column": 1, + "offset": 58 + }, + "end": { + "line": 5, + "column": 12, + "offset": 69 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 4, + "column": 1, + "offset": 47 + }, + "end": { + "line": 5, + "column": 12, + "offset": 69 + }, + "indent": [ + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 5, + "column": 12, + "offset": 69 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 42, + "column": 0 + }, + "end": { + "line": 48, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 49, + "column": 0 + }, + "end": { + "line": 49, + "column": 42 + } + }, + "file": "packages/gl-react/src/Node.js" + }, + "name": "Interpolation", + "kind": "typedef", + "type": { + "type": "UnionType", + "elements": [ + { + "type": "StringLiteralType", + "value": "linear" + }, + { + "type": "StringLiteralType", + "value": "nearest" + } + ] + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Interpolation", + "kind": "typedef" + } + ], + "namespace": "Interpolation" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "A texture wrap mode define how the texture lookup repeat over edges.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 69, + "offset": 68 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 69, + "offset": 68 + }, + "indent": [] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "One of:", + "position": { + "start": { + "line": 3, + "column": 1, + "offset": 70 + }, + "end": { + "line": 3, + "column": 8, + "offset": 77 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 3, + "column": 1, + "offset": 70 + }, + "end": { + "line": 3, + "column": 8, + "offset": 77 + }, + "indent": [] + } + }, + { + "type": "list", + "ordered": false, + "start": null, + "loose": false, + "children": [ + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "clamp to edge", + "position": { + "start": { + "line": 4, + "column": 3, + "offset": 80 + }, + "end": { + "line": 4, + "column": 18, + "offset": 95 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 4, + "column": 3, + "offset": 80 + }, + "end": { + "line": 4, + "column": 18, + "offset": 95 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 4, + "column": 1, + "offset": 78 + }, + "end": { + "line": 4, + "column": 18, + "offset": 95 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "repeat", + "position": { + "start": { + "line": 5, + "column": 3, + "offset": 98 + }, + "end": { + "line": 5, + "column": 11, + "offset": 106 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 5, + "column": 3, + "offset": 98 + }, + "end": { + "line": 5, + "column": 11, + "offset": 106 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 5, + "column": 1, + "offset": 96 + }, + "end": { + "line": 5, + "column": 11, + "offset": 106 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "mirrored repeat", + "position": { + "start": { + "line": 6, + "column": 3, + "offset": 109 + }, + "end": { + "line": 6, + "column": 20, + "offset": 126 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 6, + "column": 3, + "offset": 109 + }, + "end": { + "line": 6, + "column": 20, + "offset": 126 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 6, + "column": 1, + "offset": 107 + }, + "end": { + "line": 6, + "column": 20, + "offset": 126 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 4, + "column": 1, + "offset": 78 + }, + "end": { + "line": 6, + "column": 20, + "offset": 126 + }, + "indent": [ + 1, + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 6, + "column": 20, + "offset": 126 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 51, + "column": 0 + }, + "end": { + "line": 58, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 59, + "column": 0 + }, + "end": { + "line": 62, + "column": 20 + } + }, + "file": "packages/gl-react/src/Node.js" + }, + "name": "WrapMode", + "kind": "typedef", + "type": { + "type": "UnionType", + "elements": [ + { + "type": "StringLiteralType", + "value": "clamp to edge" + }, + { + "type": "StringLiteralType", + "value": "repeat" + }, + { + "type": "StringLiteralType", + "value": "mirrored repeat" + } + ] + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "WrapMode", + "kind": "typedef" + } + ], + "namespace": "WrapMode" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 96, + "column": 0 + }, + "end": { + "line": 98, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 99, + "column": 0 + }, + "end": { + "line": 102, + "column": 3 + } + }, + "file": "packages/gl-react/src/Node.js" + }, + "name": "BlendFuncSrcDst", + "kind": "typedef", + "properties": [ + { + "title": "property", + "name": "src", + "lineNumber": 100, + "type": { + "type": "NameExpression", + "name": "BlendFunc" + } + }, + { + "title": "property", + "name": "dst", + "lineNumber": 101, + "type": { + "type": "NameExpression", + "name": "BlendFunc" + } + } + ], + "type": { + "type": "RecordType", + "fields": [ + { + "type": "FieldType", + "key": "src", + "value": { + "type": "NameExpression", + "name": "BlendFunc" + } + }, + { + "type": "FieldType", + "key": "dst", + "value": { + "type": "NameExpression", + "name": "BlendFunc" + } + } + ] + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "BlendFuncSrcDst", + "kind": "typedef" + } + ], + "namespace": "BlendFuncSrcDst" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "The GL blending function.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 26, + "offset": 25 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 26, + "offset": 25 + }, + "indent": [] + } + }, + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "One of:", + "position": { + "start": { + "line": 3, + "column": 1, + "offset": 27 + }, + "end": { + "line": 3, + "column": 8, + "offset": 34 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 3, + "column": 1, + "offset": 27 + }, + "end": { + "line": 3, + "column": 8, + "offset": 34 + }, + "indent": [] + } + }, + { + "type": "list", + "ordered": false, + "start": null, + "loose": false, + "children": [ + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "zero", + "position": { + "start": { + "line": 4, + "column": 3, + "offset": 37 + }, + "end": { + "line": 4, + "column": 9, + "offset": 43 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 4, + "column": 3, + "offset": 37 + }, + "end": { + "line": 4, + "column": 9, + "offset": 43 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 4, + "column": 1, + "offset": 35 + }, + "end": { + "line": 4, + "column": 9, + "offset": 43 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "one", + "position": { + "start": { + "line": 5, + "column": 3, + "offset": 46 + }, + "end": { + "line": 5, + "column": 8, + "offset": 51 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 5, + "column": 3, + "offset": 46 + }, + "end": { + "line": 5, + "column": 8, + "offset": 51 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 5, + "column": 1, + "offset": 44 + }, + "end": { + "line": 5, + "column": 8, + "offset": 51 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "src color", + "position": { + "start": { + "line": 6, + "column": 3, + "offset": 54 + }, + "end": { + "line": 6, + "column": 14, + "offset": 65 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 6, + "column": 3, + "offset": 54 + }, + "end": { + "line": 6, + "column": 14, + "offset": 65 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 6, + "column": 1, + "offset": 52 + }, + "end": { + "line": 6, + "column": 14, + "offset": 65 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "one minus src color", + "position": { + "start": { + "line": 7, + "column": 3, + "offset": 68 + }, + "end": { + "line": 7, + "column": 24, + "offset": 89 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 7, + "column": 3, + "offset": 68 + }, + "end": { + "line": 7, + "column": 24, + "offset": 89 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 7, + "column": 1, + "offset": 66 + }, + "end": { + "line": 7, + "column": 24, + "offset": 89 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "src alpha", + "position": { + "start": { + "line": 8, + "column": 3, + "offset": 92 + }, + "end": { + "line": 8, + "column": 14, + "offset": 103 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 8, + "column": 3, + "offset": 92 + }, + "end": { + "line": 8, + "column": 14, + "offset": 103 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 8, + "column": 1, + "offset": 90 + }, + "end": { + "line": 8, + "column": 14, + "offset": 103 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "one minus src alpha", + "position": { + "start": { + "line": 9, + "column": 3, + "offset": 106 + }, + "end": { + "line": 9, + "column": 24, + "offset": 127 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 9, + "column": 3, + "offset": 106 + }, + "end": { + "line": 9, + "column": 24, + "offset": 127 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 9, + "column": 1, + "offset": 104 + }, + "end": { + "line": 9, + "column": 24, + "offset": 127 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "dst color", + "position": { + "start": { + "line": 10, + "column": 3, + "offset": 130 + }, + "end": { + "line": 10, + "column": 14, + "offset": 141 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 10, + "column": 3, + "offset": 130 + }, + "end": { + "line": 10, + "column": 14, + "offset": 141 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 10, + "column": 1, + "offset": 128 + }, + "end": { + "line": 10, + "column": 14, + "offset": 141 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "one minus dst color", + "position": { + "start": { + "line": 11, + "column": 3, + "offset": 144 + }, + "end": { + "line": 11, + "column": 24, + "offset": 165 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 11, + "column": 3, + "offset": 144 + }, + "end": { + "line": 11, + "column": 24, + "offset": 165 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 11, + "column": 1, + "offset": 142 + }, + "end": { + "line": 11, + "column": 24, + "offset": 165 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "dst alpha", + "position": { + "start": { + "line": 12, + "column": 3, + "offset": 168 + }, + "end": { + "line": 12, + "column": 14, + "offset": 179 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 12, + "column": 3, + "offset": 168 + }, + "end": { + "line": 12, + "column": 14, + "offset": 179 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 12, + "column": 1, + "offset": 166 + }, + "end": { + "line": 12, + "column": 14, + "offset": 179 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "one minus dst alpha", + "position": { + "start": { + "line": 13, + "column": 3, + "offset": 182 + }, + "end": { + "line": 13, + "column": 24, + "offset": 203 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 13, + "column": 3, + "offset": 182 + }, + "end": { + "line": 13, + "column": 24, + "offset": 203 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 13, + "column": 1, + "offset": 180 + }, + "end": { + "line": 13, + "column": 24, + "offset": 203 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "constant color", + "position": { + "start": { + "line": 14, + "column": 3, + "offset": 206 + }, + "end": { + "line": 14, + "column": 19, + "offset": 222 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 14, + "column": 3, + "offset": 206 + }, + "end": { + "line": 14, + "column": 19, + "offset": 222 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 14, + "column": 1, + "offset": 204 + }, + "end": { + "line": 14, + "column": 19, + "offset": 222 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "one minus constant color", + "position": { + "start": { + "line": 15, + "column": 3, + "offset": 225 + }, + "end": { + "line": 15, + "column": 29, + "offset": 251 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 15, + "column": 3, + "offset": 225 + }, + "end": { + "line": 15, + "column": 29, + "offset": 251 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 15, + "column": 1, + "offset": 223 + }, + "end": { + "line": 15, + "column": 29, + "offset": 251 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "constant alpha", + "position": { + "start": { + "line": 16, + "column": 3, + "offset": 254 + }, + "end": { + "line": 16, + "column": 19, + "offset": 270 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 16, + "column": 3, + "offset": 254 + }, + "end": { + "line": 16, + "column": 19, + "offset": 270 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 16, + "column": 1, + "offset": 252 + }, + "end": { + "line": 16, + "column": 19, + "offset": 270 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "one minus constant alpha", + "position": { + "start": { + "line": 17, + "column": 3, + "offset": 273 + }, + "end": { + "line": 17, + "column": 29, + "offset": 299 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 17, + "column": 3, + "offset": 273 + }, + "end": { + "line": 17, + "column": 29, + "offset": 299 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 17, + "column": 1, + "offset": 271 + }, + "end": { + "line": 17, + "column": 29, + "offset": 299 + }, + "indent": [] + } + }, + { + "type": "listItem", + "loose": false, + "checked": null, + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "src alpha saturate", + "position": { + "start": { + "line": 18, + "column": 3, + "offset": 302 + }, + "end": { + "line": 18, + "column": 23, + "offset": 322 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 18, + "column": 3, + "offset": 302 + }, + "end": { + "line": 18, + "column": 23, + "offset": 322 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 18, + "column": 1, + "offset": 300 + }, + "end": { + "line": 18, + "column": 23, + "offset": 322 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 4, + "column": 1, + "offset": 35 + }, + "end": { + "line": 18, + "column": 23, + "offset": 322 + }, + "indent": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 18, + "column": 23, + "offset": 322 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 74, + "column": 0 + }, + "end": { + "line": 93, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 94, + "column": 0 + }, + "end": { + "line": 94, + "column": 48 + } + }, + "file": "packages/gl-react/src/Node.js" + }, + "name": "BlendFunc", + "kind": "typedef", + "type": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "$Keys" + }, + "applications": [ + null + ] + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "BlendFunc", + "kind": "typedef" + } + ], + "namespace": "BlendFunc" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "The GL clear mode.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 19, + "offset": 18 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 19, + "offset": 18 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 19, + "offset": 18 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 114, + "column": 0 + }, + "end": { + "line": 116, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 117, + "column": 0 + }, + "end": { + "line": 119, + "column": 3 + } + }, + "file": "packages/gl-react/src/Node.js" + }, + "name": "Clear", + "kind": "typedef", + "properties": [ + { + "title": "property", + "name": "color", + "lineNumber": 118, + "type": { + "type": "NameExpression", + "name": "Vec4" + } + } + ], + "type": { + "type": "RecordType", + "fields": [ + { + "type": "FieldType", + "key": "color", + "value": { + "type": "NameExpression", + "name": "Vec4" + } + } + ] + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Clear", + "kind": "typedef" + } + ], + "namespace": "Clear" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Array of 4 numbers. Useful to represent colors. ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 49, + "offset": 48 + }, + "indent": [] + } + }, + { + "type": "emphasis", + "children": [ + { + "type": "linkReference", + "identifier": " r, g, b, a ", + "referenceType": "shortcut", + "children": [ + { + "type": "text", + "value": " r, g, b, a ", + "position": { + "start": { + "line": 1, + "column": 51, + "offset": 50 + }, + "end": { + "line": 1, + "column": 63, + "offset": 62 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 50, + "offset": 49 + }, + "end": { + "line": 1, + "column": 64, + "offset": 63 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 49, + "offset": 48 + }, + "end": { + "line": 1, + "column": 65, + "offset": 64 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 65, + "offset": 64 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 65, + "offset": 64 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 104, + "column": 0 + }, + "end": { + "line": 106, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 107, + "column": 0 + }, + "end": { + "line": 112, + "column": 2 + } + }, + "file": "packages/gl-react/src/Node.js" + }, + "name": "Vec4", + "kind": "typedef", + "type": { + "type": "ArrayType", + "elements": [ + { + "type": "NameExpression", + "name": "number" + }, + { + "type": "NameExpression", + "name": "number" + }, + { + "type": "NameExpression", + "name": "number" + }, + { + "type": "NameExpression", + "name": "number" + } + ] + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Vec4", + "kind": "typedef" + } + ], + "namespace": "Vec4" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Multidimensional Arrays object from library\n", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 1, + "offset": 44 + }, + "indent": [ + 1 + ] + } + }, + { + "type": "link", + "title": null, + "url": "https://www.npmjs.com/package/ndarray", + "children": [ + { + "type": "text", + "value": "ndarray", + "position": { + "start": { + "line": 2, + "column": 2, + "offset": 45 + }, + "end": { + "line": 2, + "column": 9, + "offset": 52 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 2, + "column": 1, + "offset": 44 + }, + "end": { + "line": 2, + "column": 49, + "offset": 92 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 49, + "offset": 92 + }, + "indent": [ + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 49, + "offset": 92 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 6, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 13, + "column": 2 + } + }, + "file": "packages/gl-react/flow/ndarray.js" + }, + "name": "NDArray", + "kind": "typedef", + "properties": [ + { + "title": "property", + "name": "shape", + "lineNumber": 8, + "type": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "NameExpression", + "name": "number" + } + ] + } + }, + { + "title": "property", + "name": "data", + "lineNumber": 9, + "type": { + "type": "UnionType", + "elements": [ + { + "type": "NameExpression", + "name": "$TypedArray" + }, + { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "NameExpression", + "name": "number" + } + ] + } + ] + } + }, + { + "title": "property", + "name": "stride", + "lineNumber": 10, + "type": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "NameExpression", + "name": "number" + } + ] + } + }, + { + "title": "property", + "name": "transpose", + "lineNumber": 11, + "type": { + "type": "FunctionType", + "params": [], + "result": { + "type": "NameExpression", + "name": "NDArray" + } + } + }, + { + "title": "property", + "name": "step", + "lineNumber": 12, + "type": { + "type": "FunctionType", + "params": [], + "result": { + "type": "NameExpression", + "name": "NDArray" + } + } + } + ], + "type": { + "type": "RecordType", + "fields": [ + { + "type": "FieldType", + "key": "shape", + "value": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "NameExpression", + "name": "number" + } + ] + } + }, + { + "type": "FieldType", + "key": "data", + "value": { + "type": "UnionType", + "elements": [ + { + "type": "NameExpression", + "name": "$TypedArray" + }, + { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "NameExpression", + "name": "number" + } + ] + } + ] + } + }, + { + "type": "FieldType", + "key": "stride", + "value": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "NameExpression", + "name": "number" + } + ] + } + }, + { + "type": "FieldType", + "key": "transpose", + "value": { + "type": "FunctionType", + "params": [], + "result": { + "type": "NameExpression", + "name": "NDArray" + } + } + }, + { + "type": "FieldType", + "key": "step", + "value": { + "type": "FunctionType", + "params": [], + "result": { + "type": "NameExpression", + "name": "NDArray" + } + } + } + ] + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "NDArray", + "kind": "typedef" + } + ], + "namespace": "NDArray" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "This type is provided by the library ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 38, + "offset": 37 + }, + "indent": [] + } + }, + { + "type": "link", + "title": null, + "url": "https://github.com/stackgl/gl-texture2d", + "children": [ + { + "type": "text", + "value": "gl-texture2d", + "position": { + "start": { + "line": 1, + "column": 39, + "offset": 38 + }, + "end": { + "line": 1, + "column": 51, + "offset": 50 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 38, + "offset": 37 + }, + "end": { + "line": 1, + "column": 93, + "offset": 92 + }, + "indent": [] + } + }, + { + "type": "text", + "value": ".", + "position": { + "start": { + "line": 1, + "column": 93, + "offset": 92 + }, + "end": { + "line": 1, + "column": 94, + "offset": 93 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 94, + "offset": 93 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 94, + "offset": 93 + } + } + }, + "tags": [ + { + "title": "type", + "description": "Texture", + "lineNumber": 2, + "type": null, + "errors": [ + "Missing or invalid tag type" + ] + } + ], + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 6, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 15, + "column": 2 + } + }, + "file": "packages/gl-react/flow/gl-texture2d.js" + }, + "name": "Texture", + "kind": "typedef", + "properties": [ + { + "title": "property", + "name": "dispose", + "lineNumber": 8, + "type": { + "type": "FunctionType", + "params": [], + "result": { + "type": "VoidLiteral" + } + } + }, + { + "title": "property", + "name": "setPixels", + "lineNumber": 9, + "type": { + "type": "FunctionType", + "params": [ + { + "type": "ParameterType", + "name": "d", + "expression": { + "type": "AllLiteral" + } + } + ], + "result": { + "type": "VoidLiteral" + } + } + }, + { + "title": "property", + "name": "bind", + "lineNumber": 10, + "type": { + "type": "FunctionType", + "params": [ + { + "type": "ParameterType", + "name": "unit", + "expression": { + "type": "NameExpression", + "name": "number" + } + } + ], + "result": { + "type": "NameExpression", + "name": "number" + } + } + }, + { + "title": "property", + "name": "minFilter", + "lineNumber": 11, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "title": "property", + "name": "magFilter", + "lineNumber": 12, + "type": { + "type": "NameExpression", + "name": "number" + } + }, + { + "title": "property", + "name": "wrap", + "lineNumber": 13, + "type": { + "type": "ArrayType", + "elements": [ + { + "type": "NameExpression", + "name": "number" + }, + { + "type": "NameExpression", + "name": "number" + } + ] + } + }, + { + "title": "property", + "name": "shape", + "lineNumber": 14, + "type": { + "type": "ArrayType", + "elements": [ + { + "type": "NameExpression", + "name": "number" + }, + { + "type": "NameExpression", + "name": "number" + } + ] + } + } + ], + "type": { + "type": "RecordType", + "fields": [ + { + "type": "FieldType", + "key": "dispose", + "value": { + "type": "FunctionType", + "params": [], + "result": { + "type": "VoidLiteral" + } + } + }, + { + "type": "FieldType", + "key": "setPixels", + "value": { + "type": "FunctionType", + "params": [ + { + "type": "ParameterType", + "name": "d", + "expression": { + "type": "AllLiteral" + } + } + ], + "result": { + "type": "VoidLiteral" + } + } + }, + { + "type": "FieldType", + "key": "bind", + "value": { + "type": "FunctionType", + "params": [ + { + "type": "ParameterType", + "name": "unit", + "expression": { + "type": "NameExpression", + "name": "number" + } + } + ], + "result": { + "type": "NameExpression", + "name": "number" + } + } + }, + { + "type": "FieldType", + "key": "minFilter", + "value": { + "type": "NameExpression", + "name": "number" + } + }, + { + "type": "FieldType", + "key": "magFilter", + "value": { + "type": "NameExpression", + "name": "number" + } + }, + { + "type": "FieldType", + "key": "wrap", + "value": { + "type": "ArrayType", + "elements": [ + { + "type": "NameExpression", + "name": "number" + }, + { + "type": "NameExpression", + "name": "number" + } + ] + } + }, + { + "type": "FieldType", + "key": "shape", + "value": { + "type": "ArrayType", + "elements": [ + { + "type": "NameExpression", + "name": "number" + }, + { + "type": "NameExpression", + "name": "number" + } + ] + } + } + ] + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Texture", + "kind": "typedef" + } + ], + "namespace": "Texture" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "A texture loader is an extensible way to add more \"renderable texture\" into gl-react.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 86, + "offset": 85 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 86, + "offset": 85 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 86, + "offset": 85 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 9, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 10, + "column": 0 + }, + "end": { + "line": 51, + "column": 1 + } + }, + "file": "packages/gl-react/src/TextureLoader.js" + }, + "name": "TextureLoader", + "kind": "class", + "members": { + "instance": [ + { + "description": "", + "tags": [ + { + "title": "property", + "description": "the contextual rendering context", + "lineNumber": 1, + "type": { + "type": "NameExpression", + "name": "WebGLRenderingContext" + }, + "name": "gl" + } + ], + "loc": { + "start": { + "line": 12, + "column": 2 + }, + "end": { + "line": 14, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 15, + "column": 2 + }, + "end": { + "line": 15, + "column": 28 + } + }, + "file": "packages/gl-react/src/TextureLoader.js" + }, + "properties": [ + { + "name": "gl", + "lineNumber": 1, + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "the contextual rendering context", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 33, + "offset": 32 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 33, + "offset": 32 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 33, + "offset": 32 + } + } + }, + "type": { + "type": "NameExpression", + "name": "WebGLRenderingContext" + } + } + ], + "name": "gl", + "kind": "member", + "memberof": "TextureLoader", + "scope": "instance", + "type": { + "type": "NameExpression", + "name": "WebGLRenderingContext" + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "TextureLoader", + "kind": "class" + }, + { + "name": "gl", + "kind": "member", + "scope": "instance" + } + ], + "namespace": "TextureLoader#gl" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 17, + "column": 2 + }, + "end": { + "line": 19, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 20, + "column": 2 + }, + "end": { + "line": 22, + "column": 3 + } + }, + "file": "packages/gl-react/src/TextureLoader.js" + }, + "name": "constructor", + "kind": "function", + "params": [ + { + "title": "param", + "name": "gl", + "lineNumber": 20, + "type": { + "type": "NameExpression", + "name": "WebGLRenderingContext" + } + } + ], + "memberof": "TextureLoader", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "TextureLoader", + "kind": "class" + }, + { + "name": "constructor", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "TextureLoader#constructor" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "You must free everything you have done and stop all pending load() calls.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 74, + "offset": 73 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 74, + "offset": 73 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 74, + "offset": 73 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 24, + "column": 2 + }, + "end": { + "line": 26, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 27, + "column": 2 + }, + "end": { + "line": 27, + "column": 21 + } + }, + "file": "packages/gl-react/src/TextureLoader.js" + }, + "name": "dispose", + "kind": "member", + "memberof": "TextureLoader", + "scope": "instance", + "type": { + "type": "FunctionType", + "params": [], + "result": { + "type": "VoidLiteral" + } + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "TextureLoader", + "kind": "class" + }, + { + "name": "dispose", + "kind": "member", + "scope": "instance" + } + ], + "namespace": "TextureLoader#dispose" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Check if the loader should handle a given input", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 48, + "offset": 47 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 48, + "offset": 47 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 48, + "offset": 47 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 29, + "column": 2 + }, + "end": { + "line": 31, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 32, + "column": 2 + }, + "end": { + "line": 32, + "column": 34 + } + }, + "file": "packages/gl-react/src/TextureLoader.js" + }, + "name": "canLoad", + "kind": "member", + "memberof": "TextureLoader", + "scope": "instance", + "type": { + "type": "FunctionType", + "params": [ + { + "type": "ParameterType", + "name": "input", + "expression": { + "type": "AllLiteral" + } + } + ], + "result": { + "type": "NameExpression", + "name": "boolean" + } + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "TextureLoader", + "kind": "class" + }, + { + "name": "canLoad", + "kind": "member", + "scope": "instance" + } + ], + "namespace": "TextureLoader#canLoad" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "try to get in sync the texture for a given output. otherwise null.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 67, + "offset": 66 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 67, + "offset": 66 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 67, + "offset": 66 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 34, + "column": 2 + }, + "end": { + "line": 36, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 37, + "column": 2 + }, + "end": { + "line": 37, + "column": 29 + } + }, + "file": "packages/gl-react/src/TextureLoader.js" + }, + "name": "get", + "kind": "member", + "memberof": "TextureLoader", + "scope": "instance", + "type": { + "type": "FunctionType", + "params": [ + { + "type": "ParameterType", + "name": "input", + "expression": { + "type": "NameExpression", + "name": "T" + } + } + ], + "result": { + "type": "NullableType", + "expression": { + "type": "NameExpression", + "name": "Texture" + } + } + }, + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "TextureLoader", + "kind": "class" + }, + { + "name": "get", + "kind": "member", + "scope": "instance" + } + ], + "namespace": "TextureLoader#get" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "load() called if get() was null. it returns a promise and a dispose function.\nIt is your responsability to cache the disposable per input.\nIf load() is called twice, same value should be returned. but you can drop it when it's loaded.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 3, + "column": 96, + "offset": 234 + }, + "indent": [ + 1, + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 3, + "column": 96, + "offset": 234 + }, + "indent": [ + 1, + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 3, + "column": 96, + "offset": 234 + } + } + }, + "tags": [], + "loc": { + "start": { + "line": 39, + "column": 2 + }, + "end": { + "line": 43, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 44, + "column": 2 + }, + "end": { + "line": 50, + "column": 3 + } + }, + "file": "packages/gl-react/src/TextureLoader.js" + }, + "name": "load", + "kind": "function", + "params": [ + { + "title": "param", + "name": "input", + "lineNumber": 44, + "type": { + "type": "NameExpression", + "name": "T" + } + } + ], + "returns": [ + { + "type": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "DisposablePromise" + }, + "applications": [ + { + "type": "NameExpression", + "name": "Texture" + } + ] + } + } + ], + "memberof": "TextureLoader", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "TextureLoader", + "kind": "class" + }, + { + "name": "load", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "TextureLoader#load" + } + ], + "static": [], + "events": [] + }, + "path": [ + { + "name": "TextureLoader", + "kind": "class" + } + ], + "namespace": "TextureLoader" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Utility to visit the Surface & Node draw lifecycle (used for logging and testing)", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 82, + "offset": 81 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 82, + "offset": 81 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 82, + "offset": 81 + } + } + }, + "tags": [ + { + "title": "namespace", + "description": null, + "lineNumber": 2, + "name": null + } + ], + "loc": { + "start": { + "line": 6, + "column": 0 + }, + "end": { + "line": 9, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 10, + "column": 0 + }, + "end": { + "line": 28, + "column": 2 + } + }, + "file": "packages/gl-react/src/Visitors.js" + }, + "kind": "namespace", + "name": "Visitors", + "members": { + "instance": [], + "static": [ + { + "description": "", + "tags": [ + { + "title": "memberof", + "description": "Visitors", + "lineNumber": 1 + } + ], + "loc": { + "start": { + "line": 11, + "column": 2 + }, + "end": { + "line": 13, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 14, + "column": 2 + }, + "end": { + "line": 16, + "column": 3 + } + }, + "file": "packages/gl-react/src/Visitors.js" + }, + "memberof": "Visitors", + "name": "add", + "kind": "function", + "params": [ + { + "title": "param", + "name": "visitor", + "lineNumber": 14, + "type": { + "type": "NameExpression", + "name": "VisitorLike" + } + } + ], + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Visitors", + "kind": "namespace" + }, + { + "name": "add", + "kind": "function" + } + ], + "namespace": "Visitorsadd" + }, + { + "description": "", + "tags": [ + { + "title": "memberof", + "description": "Visitors", + "lineNumber": 1 + } + ], + "loc": { + "start": { + "line": 17, + "column": 2 + }, + "end": { + "line": 19, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 20, + "column": 2 + }, + "end": { + "line": 23, + "column": 3 + } + }, + "file": "packages/gl-react/src/Visitors.js" + }, + "memberof": "Visitors", + "name": "remove", + "kind": "function", + "params": [ + { + "title": "param", + "name": "visitor", + "lineNumber": 20, + "type": { + "type": "NameExpression", + "name": "VisitorLike" + } + } + ], + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Visitors", + "kind": "namespace" + }, + { + "name": "remove", + "kind": "function" + } + ], + "namespace": "Visitorsremove" + } + ] + }, + "path": [ + { + "name": "Visitors", + "kind": "namespace" + } + ], + "namespace": "Visitors" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Extensible way to support more sampler2D uniform texture formats.\nYou must call the methods statically before the ", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 49, + "offset": 114 + }, + "indent": [ + 1 + ] + } + }, + { + "type": "inlineCode", + "value": "", + "position": { + "start": { + "line": 2, + "column": 49, + "offset": 114 + }, + "end": { + "line": 2, + "column": 60, + "offset": 125 + }, + "indent": [] + } + }, + { + "type": "text", + "value": " is instanciated.", + "position": { + "start": { + "line": 2, + "column": 60, + "offset": 125 + }, + "end": { + "line": 2, + "column": 77, + "offset": 142 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 77, + "offset": 142 + }, + "indent": [ + 1 + ] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 2, + "column": 77, + "offset": 142 + } + } + }, + "tags": [ + { + "title": "namespace", + "description": null, + "lineNumber": 3, + "name": null + } + ], + "loc": { + "start": { + "line": 9, + "column": 0 + }, + "end": { + "line": 13, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 14, + "column": 0 + }, + "end": { + "line": 33, + "column": 2 + } + }, + "file": "packages/gl-react/src/TextureLoaders.js" + }, + "kind": "namespace", + "name": "TextureLoaders", + "members": { + "instance": [], + "static": [ + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Add a TextureLoader class to extend texture format support.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 60, + "offset": 59 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 60, + "offset": 59 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 60, + "offset": 59 + } + } + }, + "tags": [ + { + "title": "memberof", + "description": "TextureLoaders", + "lineNumber": 2 + } + ], + "loc": { + "start": { + "line": 15, + "column": 2 + }, + "end": { + "line": 18, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 19, + "column": 2 + }, + "end": { + "line": 21, + "column": 3 + } + }, + "file": "packages/gl-react/src/TextureLoaders.js" + }, + "memberof": "TextureLoaders", + "name": "add", + "kind": "function", + "params": [ + { + "title": "param", + "name": "loader", + "lineNumber": 19, + "type": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Class" + }, + "applications": [ + { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "TextureLoader" + }, + "applications": [ + { + "type": "AllLiteral" + } + ] + } + ] + } + } + ], + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "TextureLoaders", + "kind": "namespace" + }, + { + "name": "add", + "kind": "function" + } + ], + "namespace": "TextureLoadersadd" + }, + { + "description": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Remove a previously added TextureLoader class.", + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 47, + "offset": 46 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 47, + "offset": 46 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1, + "offset": 0 + }, + "end": { + "line": 1, + "column": 47, + "offset": 46 + } + } + }, + "tags": [ + { + "title": "memberof", + "description": "TextureLoaders", + "lineNumber": 2 + } + ], + "loc": { + "start": { + "line": 22, + "column": 2 + }, + "end": { + "line": 25, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 26, + "column": 2 + }, + "end": { + "line": 29, + "column": 3 + } + }, + "file": "packages/gl-react/src/TextureLoaders.js" + }, + "memberof": "TextureLoaders", + "name": "remove", + "kind": "function", + "params": [ + { + "title": "param", + "name": "loader", + "lineNumber": 26, + "type": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Class" + }, + "applications": [ + { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "TextureLoader" + }, + "applications": [ + { + "type": "AllLiteral" + } + ] + } + ] + } + } + ], + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "TextureLoaders", + "kind": "namespace" + }, + { + "name": "remove", + "kind": "function" + } + ], + "namespace": "TextureLoadersremove" + } + ] + }, + "path": [ + { + "name": "TextureLoaders", + "kind": "namespace" + } + ], + "namespace": "TextureLoaders" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 17, + "column": 0 + }, + "end": { + "line": 19, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 20, + "column": 0 + }, + "end": { + "line": 100, + "column": 1 + } + }, + "file": "packages/gl-react/src/VisitorLogger.js" + }, + "name": "VisitorLogger", + "augments": [ + { + "title": "augments", + "name": "Visitor" + } + ], + "kind": "class", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "VisitorLogger", + "kind": "class" + } + ], + "namespace": "VisitorLogger" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 21, + "column": 0 + }, + "end": { + "line": 23, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 24, + "column": 0 + }, + "end": { + "line": 64, + "column": 1 + } + }, + "file": "packages/gl-react/src/Visitor.js" + }, + "name": "Visitor", + "kind": "class", + "members": { + "instance": [ + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 25, + "column": 2 + }, + "end": { + "line": 27, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 28, + "column": 2 + }, + "end": { + "line": 28, + "column": 37 + } + }, + "file": "packages/gl-react/src/Visitor.js" + }, + "name": "onSurfaceMount", + "kind": "function", + "params": [ + { + "title": "param", + "name": "surface", + "lineNumber": 28, + "type": { + "type": "NameExpression", + "name": "Surface" + } + } + ], + "memberof": "Visitor", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Visitor", + "kind": "class" + }, + { + "name": "onSurfaceMount", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Visitor#onSurfaceMount" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 29, + "column": 2 + }, + "end": { + "line": 31, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 32, + "column": 2 + }, + "end": { + "line": 32, + "column": 39 + } + }, + "file": "packages/gl-react/src/Visitor.js" + }, + "name": "onSurfaceUnmount", + "kind": "function", + "params": [ + { + "title": "param", + "name": "surface", + "lineNumber": 32, + "type": { + "type": "NameExpression", + "name": "Surface" + } + } + ], + "memberof": "Visitor", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Visitor", + "kind": "class" + }, + { + "name": "onSurfaceUnmount", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Visitor#onSurfaceUnmount" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 33, + "column": 2 + }, + "end": { + "line": 35, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 36, + "column": 2 + }, + "end": { + "line": 36, + "column": 75 + } + }, + "file": "packages/gl-react/src/Visitor.js" + }, + "name": "onSurfaceGLContextChange", + "kind": "function", + "params": [ + { + "title": "param", + "name": "surface", + "lineNumber": 36, + "type": { + "type": "NameExpression", + "name": "Surface" + } + }, + { + "title": "param", + "name": "gl", + "lineNumber": 36, + "type": { + "type": "NullableType", + "expression": { + "type": "NameExpression", + "name": "WebGLRenderingContext" + } + } + } + ], + "memberof": "Visitor", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Visitor", + "kind": "class" + }, + { + "name": "onSurfaceGLContextChange", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Visitor#onSurfaceGLContextChange" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 37, + "column": 2 + }, + "end": { + "line": 38, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 39, + "column": 2 + }, + "end": { + "line": 39, + "column": 43 + } + }, + "file": "packages/gl-react/src/Visitor.js" + }, + "name": "onSurfaceDrawSkipped", + "kind": "function", + "params": [ + { + "title": "param", + "name": "surface", + "lineNumber": 39, + "type": { + "type": "NameExpression", + "name": "Surface" + } + } + ], + "memberof": "Visitor", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Visitor", + "kind": "class" + }, + { + "name": "onSurfaceDrawSkipped", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Visitor#onSurfaceDrawSkipped" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 40, + "column": 2 + }, + "end": { + "line": 41, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 42, + "column": 2 + }, + "end": { + "line": 42, + "column": 41 + } + }, + "file": "packages/gl-react/src/Visitor.js" + }, + "name": "onSurfaceDrawStart", + "kind": "function", + "params": [ + { + "title": "param", + "name": "surface", + "lineNumber": 42, + "type": { + "type": "NameExpression", + "name": "Surface" + } + } + ], + "memberof": "Visitor", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Visitor", + "kind": "class" + }, + { + "name": "onSurfaceDrawStart", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Visitor#onSurfaceDrawStart" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 43, + "column": 2 + }, + "end": { + "line": 44, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 45, + "column": 2 + }, + "end": { + "line": 45, + "column": 34 + } + }, + "file": "packages/gl-react/src/Visitor.js" + }, + "name": "onSurfaceDrawError", + "kind": "function", + "params": [ + { + "title": "param", + "name": "e", + "lineNumber": 45, + "type": { + "type": "NameExpression", + "name": "Error" + } + } + ], + "memberof": "Visitor", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Visitor", + "kind": "class" + }, + { + "name": "onSurfaceDrawError", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Visitor#onSurfaceDrawError" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 46, + "column": 2 + }, + "end": { + "line": 47, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 48, + "column": 2 + }, + "end": { + "line": 48, + "column": 39 + } + }, + "file": "packages/gl-react/src/Visitor.js" + }, + "name": "onSurfaceDrawEnd", + "kind": "function", + "params": [ + { + "title": "param", + "name": "surface", + "lineNumber": 48, + "type": { + "type": "NameExpression", + "name": "Surface" + } + } + ], + "memberof": "Visitor", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Visitor", + "kind": "class" + }, + { + "name": "onSurfaceDrawEnd", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Visitor#onSurfaceDrawEnd" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 49, + "column": 2 + }, + "end": { + "line": 50, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 51, + "column": 2 + }, + "end": { + "line": 51, + "column": 34 + } + }, + "file": "packages/gl-react/src/Visitor.js" + }, + "name": "onNodeDrawSkipped", + "kind": "function", + "params": [ + { + "title": "param", + "name": "node", + "lineNumber": 51, + "type": { + "type": "NameExpression", + "name": "Node" + } + } + ], + "memberof": "Visitor", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Visitor", + "kind": "class" + }, + { + "name": "onNodeDrawSkipped", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Visitor#onNodeDrawSkipped" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 52, + "column": 2 + }, + "end": { + "line": 53, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 54, + "column": 2 + }, + "end": { + "line": 54, + "column": 32 + } + }, + "file": "packages/gl-react/src/Visitor.js" + }, + "name": "onNodeDrawStart", + "kind": "function", + "params": [ + { + "title": "param", + "name": "node", + "lineNumber": 54, + "type": { + "type": "NameExpression", + "name": "Node" + } + } + ], + "memberof": "Visitor", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Visitor", + "kind": "class" + }, + { + "name": "onNodeDrawStart", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Visitor#onNodeDrawStart" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 55, + "column": 2 + }, + "end": { + "line": 56, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 57, + "column": 2 + }, + "end": { + "line": 57, + "column": 92 + } + }, + "file": "packages/gl-react/src/Visitor.js" + }, + "name": "onNodeSyncDeps", + "kind": "function", + "params": [ + { + "title": "param", + "name": "node", + "lineNumber": 57, + "type": { + "type": "NameExpression", + "name": "Node" + } + }, + { + "title": "param", + "name": "additions", + "lineNumber": 57, + "type": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "UnionType", + "elements": [ + { + "type": "NameExpression", + "name": "Node" + }, + { + "type": "NameExpression", + "name": "Bus" + } + ] + } + ] + } + }, + { + "title": "param", + "name": "deletions", + "lineNumber": 57, + "type": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "UnionType", + "elements": [ + { + "type": "NameExpression", + "name": "Node" + }, + { + "type": "NameExpression", + "name": "Bus" + } + ] + } + ] + } + } + ], + "memberof": "Visitor", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Visitor", + "kind": "class" + }, + { + "name": "onNodeSyncDeps", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Visitor#onNodeSyncDeps" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 58, + "column": 2 + }, + "end": { + "line": 59, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 60, + "column": 2 + }, + "end": { + "line": 60, + "column": 55 + } + }, + "file": "packages/gl-react/src/Visitor.js" + }, + "name": "onNodeDraw", + "kind": "function", + "params": [ + { + "title": "param", + "name": "node", + "lineNumber": 60, + "type": { + "type": "NameExpression", + "name": "Node" + } + }, + { + "title": "param", + "name": "preparedUniforms", + "lineNumber": 60, + "type": { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + null + ] + } + } + ], + "memberof": "Visitor", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Visitor", + "kind": "class" + }, + { + "name": "onNodeDraw", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Visitor#onNodeDraw" + }, + { + "description": "", + "tags": [], + "loc": { + "start": { + "line": 61, + "column": 2 + }, + "end": { + "line": 62, + "column": 5 + } + }, + "context": { + "loc": { + "start": { + "line": 63, + "column": 2 + }, + "end": { + "line": 63, + "column": 30 + } + }, + "file": "packages/gl-react/src/Visitor.js" + }, + "name": "onNodeDrawEnd", + "kind": "function", + "params": [ + { + "title": "param", + "name": "node", + "lineNumber": 63, + "type": { + "type": "NameExpression", + "name": "Node" + } + } + ], + "memberof": "Visitor", + "scope": "instance", + "members": { + "instance": [], + "static": [] + }, + "path": [ + { + "name": "Visitor", + "kind": "class" + }, + { + "name": "onNodeDrawEnd", + "kind": "function", + "scope": "instance" + } + ], + "namespace": "Visitor#onNodeDrawEnd" + } + ], + "static": [], + "events": [] + }, + "path": [ + { + "name": "Visitor", + "kind": "class" + } + ], + "namespace": "Visitor" + } +] \ No newline at end of file diff --git a/cookbook/DocIntro.md b/cookbook/DocIntro.md new file mode 100755 index 0000000..01db04e --- /dev/null +++ b/cookbook/DocIntro.md @@ -0,0 +1,21 @@ + +gl-react API documentation +========================== + +`gl-react` is a [React](https://facebook.github.io/react/) library to write and compose WebGL shaders. + +This universal library must be coupled with one of the concrete implementations: + +- [`gl-react-dom`](https://github.com/gre/gl-react/tree/master/packages/gl-react-dom/) for React DOM (web using WebGL). +- [`gl-react-native`](https://github.com/gre/gl-react/tree/master/packages/gl-react-native/) for React Native (iOS/Android via OpenGL). +- [`gl-react-headless`](https://github.com/gre/gl-react/tree/master/packages/gl-react-headless/) for Node.js (used for testing for now) + +[![](https://cloud.githubusercontent.com/assets/211411/9386550/432492c6-475c-11e5-9328-f3d5187298c1.jpg)](/hellogl) + +```js + + + +``` + +There are two primitive components: [Surface](#surface) and [Node](#node). diff --git a/cookbook/package.json b/cookbook/package.json new file mode 100755 index 0000000..fb89651 --- /dev/null +++ b/cookbook/package.json @@ -0,0 +1,45 @@ +{ + "name": "gl-react-cookbook", + "version": "3.0.0-alpha", + "private": true, + "devDependencies": { + "babel-preset-react-app": "^1.0.0", + "raw-loader": "^0.5.1", + "react-scripts": "0.6.1" + }, + "cookbook": { + "githubprefix": "https://github.com/gre/gl-react/tree/master/cookbook/" + }, + "dependencies": { + "animated": "^0.1.3", + "github-slugger": "^1.1.1", + "gl-react": "file:../packages/gl-react", + "gl-react-dom": "file:../packages/gl-react-dom", + "gl-texture2d": "^2.0.12", + "glsl-transitions": "^2016.10.24", + "hoist-non-react-statics": "^1.2.0", + "lodash": "^4.17.2", + "ndarray": "^1.0.18", + "ndarray-ops": "^1.2.2", + "prism-theme-one-dark": "^1.0.0", + "prismjs": "^1.5.1", + "raf": "^3.3.0", + "react": "^15.4.0", + "react-addons-perf": "^15.4.0", + "react-color": "^2.4.0", + "react-dom": "^15.4.0", + "react-json2d": "^0.1.0", + "react-motion": "^0.4.5", + "react-prism": "^4.0.0", + "react-router": "^2.8.1", + "react-sidebar": "^2.2.1", + "remark": "^6.2.0", + "remark-react": "^3.1.0" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "eject": "react-scripts eject", + "generate-examples": "cd src/examples; ./gen.sh 1> index.js" + } +} diff --git a/cookbook/public/favicon.ico b/cookbook/public/favicon.ico new file mode 100755 index 0000000..92b24d7 Binary files /dev/null and b/cookbook/public/favicon.ico differ diff --git a/cookbook/public/index.html b/cookbook/public/index.html new file mode 100755 index 0000000..edd7fdc --- /dev/null +++ b/cookbook/public/index.html @@ -0,0 +1,13 @@ + + + + + + + + gl-react cookbook + + +
+ + diff --git a/cookbook/src/App.css b/cookbook/src/App.css new file mode 100755 index 0000000..f8d66e2 --- /dev/null +++ b/cookbook/src/App.css @@ -0,0 +1,236 @@ +a { + color: inherit; +} +a:hover, a:focus { + color: inherit; +} +.menu { + background: #fff; + padding: 10px; + width: 280px; +} +.menu ul { + margin: 0; + padding: 0; +} +.menu li { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding: 4px; +} +.menu li a { + text-decoration: none; +} +.menu li .example-link.active { + opacity: 0.5; +} +.menu li .example-link strong { + text-decoration: underline; +} +.menu li .example-link.active strong { + text-decoration: none; +} +.App { + flex: 1; + display: flex; + flex-direction: column; + position: relative; +} +.App > header { + height: 50px; + display: flex; + flexDirection: row; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid #000; + position: relative; +} +.App > header .sidebar-opener { + font-size: 32px; + padding: 0px 15px; + cursor: pointer; + user-select: none; + text-decoration: none; +} +.App > header .inspector-opener { + font-size: 32px; + padding: 0px 15px; + cursor: pointer; + user-select: none; + text-decoration: none; +} +.App > header .logo { + margin-left: 20px; + text-decoration: none; + font-weight: bold; + font-size: 20px; + white-space: nowrap; +} +.App > header .logo .t1 { + color: #E24; +} +.App > header .logo .t2 { + color: #000; +} +.App > header .logo .t3 { + color: #E24; +} +.App > header .logo .v { + font-weight: normal; + font-size: 0.6em; + margin-left: 10px; + color: #333; +} +.App > header .logo img { + height: 22px; + vertical-align: middle; + margin-right: 10px; +} +.App > header nav { + margin: 0 20px; +} +.App > header nav a { + margin: 0 4px; + padding: 0 4px; +} +.App > header h1 { + font-size: 1.4em; + font-weight: normal; + margin: 0; + padding: 0; + flex: 1; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + text-align: center; +} +.App > header select { + margin: 0 20px; +} +a.left, a.right { + font-size: 1.6em; + padding: 0.6em 0.4em; + background: #fff; + color: #000; + text-decoration: none; + opacity: 0.2; + cursor: pointer; + user-select: none; +} +a.left:hover, a.right:hover { + opacity: 1; +} +a.left { + position: absolute; + top: 50%; + left: 0; +} +a.right { + position: absolute; + top: 50%; + right: 0; +} +.App .container { + flex: 1; + padding: 10px 40px; + display: flex; + flex-direction: row; + align-items: flex-start; + justify-content: space-around; + flex-wrap: wrap; + background: #111; + overflow: auto; +} +.App .container .source { + font-size: 12px; + width: 600px; + overflow: auto; + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; +} +.App .container .source .viewsource { + color: #fff; + text-align: right; + opacity: 0.8; + padding: 4px 0; +} + +.App .container .example { + min-height: 100%; + display: flex; + flex-direction: column; + justify-content: space-around; + user-select: none; +} +.desc { + font-size: 1.1em; + text-align: center; + color: #666; + white-space: pre-line; + display: inline-block; + padding: 10px 0; +} +.desc em { + +} +.desc pre { + display: inline-block; + text-align: left; +} +.desc blockquote { + padding: 0; + margin: 0; + padding-left: 20px; + text-align: left; + border-left: 2px solid #333; + opacity: 0.5; +} +.desc strong { + color: #999; +} +.desc p { + margin: 0; +} +.App .container .rendering { + border: 1px solid #000; + display: inline-block; + background: #eee url(https://i.imgur.com/RE6MGrd.png) repeat; + background-size: 20px 20px; + line-height: 0px; + font-family: sans-serif; + font-size: 14px; + align-self: center; +} +.App .container .toolbox { + background: #fff; + display: flex; + flex-direction: column; + align-items: stretch; + padding-bottom: 10px; +} +.App .container .toolbox h3 { + padding: 0; + margin: 4px 0; +} +.App .container .toolbox > * { + padding: 5px 10px; + display: flex; + flex-direction: column; + align-items: stretch; +} +.App .container .example .buttons { + text-align: center; + padding: 1.8em; + background: #fff; +} +.App .container .example .buttons button { + font-size: 1.4em; +} +.inspector { + width: 600px; + height: 100%; + display: flex; +} diff --git a/cookbook/src/App.js b/cookbook/src/App.js new file mode 100755 index 0000000..2901afc --- /dev/null +++ b/cookbook/src/App.js @@ -0,0 +1,212 @@ +//@flow +import React, { PureComponent, Component, PropTypes } from "react"; +import { findDOMNode } from "react-dom"; +import "./App.css"; +import {Link} from "react-router"; +import logopng from "./logo.png"; +import Code from "./Code"; +import Sidebar from "react-sidebar"; +import Inspector from "./Inspector"; +import pkg from "../package.json"; + +function triggerLink (linkRef) { + const dom = linkRef && findDOMNode(linkRef); + if (dom) dom.click(); +} + +const lenseSidebar = ({ location: {query: {menu, inspector}} }) => +({ + menu, + inspector, +}); + +class MenuContext extends PureComponent { + props: { + examples: Array<*>, + menu: bool, + inspector: bool, + currentExample: ?Object, + }; + render() { + const { examples, menu, inspector } = this.props; + return
+

{examples.length} Examples

+
    + {examples.map((ex, i) => +
  • + + {ex.path} +   + {ex.title} + +
  • )} +
+
; + } +} + +class Header extends Component { + render() { + const { + currentExample, + toToggleMenu, + toToggleInspector, + } = this.props; + return
+ { currentExample + ? ☰ + : null } + + + gl + - + react + {pkg.version} + + +

+ {currentExample && currentExample.title} +

+ { currentExample + ? ☰ + : null } +
; + } +} + +class App extends Component { + static contextTypes = { + router: PropTypes.object.isRequired, + }; + + componentDidMount() { + document.addEventListener("keydown", this.keydown, false); + } + + componentWillUnmount() { + document.removeEventListener("keydown", this.keydown); + } + + keydown = (e: KeyboardEvent) => { + switch(e.keyCode) { + case 37: return e.metaKey && e.shiftKey && triggerLink(this.refs.left); + case 39: return e.metaKey && e.shiftKey && triggerLink(this.refs.right); + default: + } + }; + + render() { + const { children, location, routes } = this.props; + const { menu, inspector } = lenseSidebar(this.props); + const currentExample = routes[1].isExample ? routes[1] : null; + const {LeftSidebar} = routes[1]; + const menuOpened = (LeftSidebar && !currentExample) || !!menu; + const inspectorOpened = !!currentExample && !!inspector; + const examples = this.props.route.childRoutes; + const index = examples.indexOf(currentExample); + let prev = examples[index - 1]; + if (prev && !prev.isExample) prev = null; + let next = examples[index + 1]; + if (next && !next.isExample) next = null; + return ( + + : + }> + + + : + LeftSidebar ? : + }> + +
+ + { prev + ? + ā® + : null } + + { next + ? āÆ + : null } + +
+ + {children} + + { currentExample + ? +
+ {currentExample.source} + + view source + +
+ : null } +
+ + + + ); + } +} + +export default App; diff --git a/cookbook/src/Code.css b/cookbook/src/Code.css new file mode 100755 index 0000000..e5fece1 --- /dev/null +++ b/cookbook/src/Code.css @@ -0,0 +1,13 @@ +pre.cookbook-code a { + color: inherit; +} +pre.cookbook-code .js-template-string-glsl { + background: rgba(0,0,0,0.3); + padding: 4px 8px; + display: block; + max-height: 65vh; + overflow-y: scroll; +} +pre.cookbook-code { + margin: 0; +} diff --git a/cookbook/src/Code.js b/cookbook/src/Code.js new file mode 100755 index 0000000..f8d1f8c --- /dev/null +++ b/cookbook/src/Code.js @@ -0,0 +1,41 @@ +//@flow +import React, { PureComponent } from "react"; +import "prismjs"; +const {Prism} = window; +import "prismjs/plugins/autolinker/prism-autolinker"; +import "prismjs/components/prism-jsx"; +import "prismjs/components/prism-glsl"; +import "prism-theme-one-dark/prism-onedark.css"; +import { PrismCode } from "react-prism"; +import "./Code.css"; + +// add GLSL synthax for GLSL blocks. +Prism.languages.insertBefore("jsx", "string", { + "template-string": { + pattern: /GLSL`(?:\\\\|\\?[^\\])*?`/, + greedy: true, + inside: { + "string": /GLSL`[\n]?|`/, + "js-template-string-glsl": { + pattern: /[^`]*/, + inside: { + rest: Prism.languages.glsl, + }, + }, + }, + } +}); + +export default class Code extends PureComponent { + props: { + children?: any, + }; + render() { + const {children} = this.props; + return ( +

+        {children}
+      
+ ); + } +} diff --git a/cookbook/src/Dashboard.css b/cookbook/src/Dashboard.css new file mode 100755 index 0000000..183f874 --- /dev/null +++ b/cookbook/src/Dashboard.css @@ -0,0 +1,63 @@ +.dashboard { + color: #eee; + margin: 10px auto; + width: 650px; +} + +.dashboard h2 { + font-weight: normal; + margin: 0.6em 0 1.2em 0; +} + +.dashboard nav { + margin: 2em 0; + font-size: 1.4em; + letter-spacing: 0.08em; +} +.dashboard nav a { + display: block; + padding: 0.2em 0; + text-decoration: none; + opacity: 0.6; +} +.dashboard nav a:hover { + text-decoration: underline; + opacity: 1; +} + +.dashboard .ex { + display: flex; + flex-direction: column; + position: relative; +} +.dashboard .showcode, .dashboard .showunderthehood { + opacity: 0.5; + cursor: pointer; + padding: 10px; + font-size: 10px; +} +.dashboard .showcode:hover, .dashboard .showunderthehood:hover { + opacity: 1; +} +.dashboard .showcode { + text-align: center; + transform: rotate(-90deg) translateX(-100%); + transform-origin: top left; + width: 200px; + position: absolute; + left: 200px; +} +.dashboard .showunderthehood { + text-align: center; +} +.dashboard .ex > header { + display: flex; + flex-direction: row; +} +.dashboard .ex pre { + flex: 1; +} +.dashboard .gl-react-inspector { + background: #fff; + height: 400px; +} diff --git a/cookbook/src/Dashboard.js b/cookbook/src/Dashboard.js new file mode 100755 index 0000000..31c79d1 --- /dev/null +++ b/cookbook/src/Dashboard.js @@ -0,0 +1,157 @@ +import React, {Component} from "react"; +import {Link} from "react-router"; +import Code from "./Code"; +import {Surface} from "gl-react-dom"; +import {Node, Shaders, GLSL, Backbuffer, LinearCopy} from "gl-react"; +import timeLoop from "./HOC/timeLoop"; +import "./Dashboard.css"; +import Inspector from "./Inspector"; + +const shaders = Shaders.create({ + Persistence: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D t, back; +uniform float persistence; +void main () { + gl_FragColor = vec4(mix( + texture2D(t, uv), + texture2D(back, uv), + persistence + ).rgb, 1.0); +}` + }, + hello: { + // uniforms are variables from JS. We pipe blue uniform into blue output color + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform float red; +void main() { + gl_FragColor = vec4(red, uv.x, uv.y, 1.0); +}` }, + Rotating: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform float angle, scale; +uniform sampler2D children; +void main() { + mat2 rotation = mat2(cos(angle), -sin(angle), sin(angle), cos(angle)); + vec2 p = (uv - vec2(0.5)) * rotation / scale + vec2(0.5); + gl_FragColor = + p.x < 0.0 || p.x > 1.0 || p.y < 0.0 || p.y > 1.0 + ? vec4(0.0) + : texture2D(children, p); +}` } +}); + +const MotionBlur = ({ children: t, persistence }) => + ; + +// We can make a that will render the concrete +class HelloGL extends Component { + props: { + red: number, + }; + render() { + const { red } = this.props; + return ; + } +} + +class Rotate extends Component { + props: { + scale: number, + angle: number, + children: any, + }; + render() { + const { angle, scale, children } = this.props; + return ; + } +} + +class Ex1 extends Component { + props: { time: number }; + state = { + showCode: false, + showInspector: false, + }; + onShowCode = () => { + this.setState({ showCode: true }); + }; + onShowInspector = () => { + this.setState({ showInspector: true }); + }; + render() { + const { time } = this.props; + const { showCode, showInspector } = this.state; + const persistence = 0.75 - 0.20 * Math.cos(0.0005 * time); + const red = 0.6 + 0.4 * Math.cos(0.004 * time); + const scale = 0.70 + 0.40 * Math.cos(0.001 * time); + const angle = 2 * Math.PI * (0.5 + 0.5 * Math.cos(0.001 * time)); + return ( +
+
+ + + + + + + + + + { !showCode + ?
SHOW ME THE CODE!
+ : + { + ` + + + + + + ` + } } +
+ { showCode + ? + !showInspector + ?
SHOW ME UNDER THE HOOD!
+ : + : null } +
+ ); + } +} + +const Ex1Loop = timeLoop(Ex1); + +export default class Dashboard extends Component { + render() { + return
+

+ gl-react is a React library to write and compose WebGL shaders. +

+ + +
; + } +} diff --git a/cookbook/src/Docs/index.js b/cookbook/src/Docs/index.js new file mode 100755 index 0000000..fbb50cf --- /dev/null +++ b/cookbook/src/Docs/index.js @@ -0,0 +1,541 @@ +//@flow +// NB This is our custom version of documentation html renderer. +// everything is intentionally inlined here so we can do custom things. + +import React, { + Component, + PureComponent, +} from "react"; +import GithubSlugger from "github-slugger"; +import remark from "remark"; +import remarkReactRenderer from "remark-react"; +import API from "../../API.json"; +import "./style.css"; +import Code from "../Code"; +import DocIntroMD from "raw!../../DocIntro.md"; + +const paths = { + "Component": "https://facebook.github.io/react/docs/react-component.html", + "WebGLRenderingContext": "https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14", + "WebGLContextAttributes": "https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.2", + +}; +API.forEach(doc => { + paths[doc.name] = "#"+slug(doc.name); +}); + +function getHref (text: string) { + return text in paths ? paths[text] : null; +} + +function t (text) { + return {text}; +} + +function visit(tree, type, visitor, reverse) { + if (typeof type === "function") { + reverse = visitor; + visitor = type; + type = null; + } + function all(children, parent) { + var step = reverse ? -1 : 1; + var max = children.length; + var min = -1; + var index = (reverse ? max : min) + step; + var child; + while (index > min && index < max) { + child = children[index]; + if (child && one(child, index, parent) === false) { + return false; + } + index += step; + } + return true; + } + function one(node, index, parent) { + var result; + index = index || (parent ? 0 : null); + if (!type || node.type === type) { + result = visitor(node, index, parent || null); + } + if (node.children && result !== false) { + return all(node.children, node); + } + return result; + } + one(tree); +} + +function rerouteLinks (ast) { + visit(ast, "link", function (node) { + if (node.jsdoc && !node.url.match(/^(http|https|\.)/) && getHref(node.url)) { + node.url = getHref(node.url); + } + }); + return ast; +} + +function autolink(text) { + var href = getHref(text); + if (href) { + return {text}; + } + return t(text); +} + +function commaList(getHref, items, start, end, sep) { + var res = []; + if (start) { + res.push(t(start)); + } + for (var i = 0, iz = items.length; i < iz; ++i) { + res = res.concat(formatType(items[i])); + if (i + 1 !== iz) { + res.push(t(sep || ", ")); + } + } + if (end) { + res.push(t(end)); + } + return res; +} + +function decorate(formatted, str, prefix) { + if (prefix) { + return [t(str)].concat(formatted); + } + return formatted.concat(t(str)); +} + +function formatType(node) { + var result = []; + + if (!node) { + return [t("any")]; + } + + switch (node.type) { + case "NullableLiteral": + return [t("?")]; + case "AllLiteral": + return [t("any")]; + case "NullLiteral": + return [t("null")]; + case "VoidLiteral": + return [t("void")]; + case "UndefinedLiteral": + return [autolink("undefined")]; + case "NameExpression": + return [autolink(node.name)]; + case "ParameterType": + return [t(node.name + ": ")].concat(formatType(node.expression)); + + case "TypeApplication": + return formatType(node.expression) + .concat(commaList(getHref, node.applications, "<", ">")); + case "UnionType": + return commaList(getHref, node.elements, "(", ")", " | "); + case "ArrayType": + return commaList(getHref, node.elements, "[", "]"); + case "RecordType": + return commaList(getHref, node.fields, "{", "}"); + + case "FieldType": + if (node.value) { + return [t(node.key + ": ")].concat(formatType(node.value)); + } + return [t(node.key)]; + + case "FunctionType": + result = [t("function (")]; + if (node["this"]) { + if (node["new"]) { + result.push(t("new: ")); + } else { + result.push(t("this: ")); + } + result = result.concat(formatType(node["this"])); + if (node.params.length !== 0) { + result.push(t(", ")); + } + } + result = result.concat(commaList(getHref, node.params, "", ")")); + if (node.result) { + result = result.concat([t(": ")].concat(formatType(node.result))); + } + return result; + + case "RestType": + return [ "...", formatType(node.expression) ]; + case "OptionalType": + return ["optional ", formatType(node.expression) ].concat( + node.default ? t("(default " + node.default + ")") : []); + case "NonNullableType": + return decorate(formatType(node.expression), "!", node.prefix); + case "NullableType": + return decorate(formatType(node.expression), "?", node.prefix); + case "StringLiteralType": + return [{JSON.stringify(node.value)}]; + case "NumericLiteralType": + case "BooleanLiteralType": + return [{String(node.value)}]; + + default: + throw new Error("Unknown type " + node.type); + } +} + +function parameter (param, short) { + if (short) { + return (param.type && param.type.type === "OptionalType") ? + "[" + param.name + "]" : param.name; + } + return [param.name+": ", formatType(param.type)]; +} + +function parameters (section, short) { + if (section.params) { + return ["("].concat(section.params.map((param, i) => + [i===0?"":", ", parameter(param, short)])).concat([")"]); + } + return "()"; +} + +function slug (str) { + var slugger = new GithubSlugger(); + return slugger.slug(str); +} + +const MD = remark().use(remarkReactRenderer); + +function md (ast, inline) { + if (inline && ast && ast.children.length && ast.children[0].type === "paragraph") { + ast = { + type: "root", + children: ast.children[0].children.concat(ast.children.slice(1)) + }; + } + if (ast) ast = rerouteLinks(ast); + if (!ast) return; + const root = MD.stringify(ast); + if (inline) { + return {root.props.children}; + } + return
{root}
; +} + +function signature (section) { + var returns = ""; + var prefix = ""; + if (section.kind === "class") { + prefix = "new "; + } else if (section.kind !== "function") { + return section.name; + } + if (section.returns) { + returns = [": ", formatType(section.returns[0].type)]; + } + return [prefix + section.name, parameters(section), returns]; +} + +function isReactComponent (section) { + return section.augments && section.augments.some(a => a.name==="Component"); +} + +function shortSignature (section) { + var prefix = ""; + if (section.kind === "class") { + if (isReactComponent(section)) { + const props = + (section.properties||[]) + .filter(p => p.type && p.type.type !== "OptionalType") + .map(p => p.name); + const attrs = + props + .filter(name => name!=="children") + .map(name => " "+name+"={..}") + .join(""); + const hasChildren = props.some(name => name==="children"); + return "<"+section.name+attrs+(hasChildren ? ">\n ...\n" : " />"); + } + else { + prefix = "new "; + } + } else if (section.kind !== "function") { + return; + } + return [prefix + section.name, parameters(section, true)]; +} + +function highlight (code) { + return code ? {code} : null; +} + +class DocSectionList extends PureComponent { + render() { + const { members } = this.props; + return ( +
+ {members.map((member, i) => +
+
+ + .{signature(member)} + +
+ +
)} +
+ ); + } +} + +class DocSection extends PureComponent { + render() { + const { section, nested } = this.props; + return ( +
+ {(!nested || (section.context && section.context.github)) + ? +
+ {!nested + ? +

+ {section.name} + + {section.augments + ? + +  extends  + { section.augments + ? section.augments.map((tag, i) => + {i===0 ? "" : ", "} + {autolink(tag.name)} + ) + : null } + + : null } + + { section.context && section.context.github + ? + + {section.context.path} + + : null } +

+ : null } + + { !nested + ? highlight(shortSignature(section)) + : null } + +
+ : null } + + {md(section.description)} + + { section.version ?
Version: {section.version}
: null } + { section.license ?
License: {section.license}
: null } + { section.author ?
Author: {section.author}
: null } + { section.copyright ?
Copyright: {section.copyright}
: null } + { section.since ?
Since: {section.since}
: null } + + {section.params + ? +
+

Parameters

+
+ {section.params.map((param, i) => +
+
+ {param.name}  + + ({formatType(param.type)} + {param.default + ? (default {param.default}) + : null } + ) + {md(param.description, true)} +
+
+ )} +
+
+ : null } + + {section.properties + ? +
+

+ {isReactComponent(section) ? "Props" : "Properties"} +

+
    + {section.properties.map((property, i) => +
  • + {property.name}  + ({formatType(property.type)}) + { property.default + ? (default {property.default}) + : null } + { property.description + ? [": ", md(property.description, true)] + : null } + { property.properties + ? +
      + {property.properties.map((property, i) => +
    • + {property.name} +   + {formatType(property.type)} + {property.default + ? (default {property.default}) + : null} + {md(property.description)} +
    • )} +
    + : null } +
  • )} +
+
+ : null } + + {section.returns + ? section.returns.map((ret, i) => +
+

Returns

+ {formatType(ret.type)} + { ret.description + ? md(ret.description, true) + : null } +
) + : null } + + {section.throws + ? +
+

Throws

+
    + {section.throws.map((throws, i) => +
  • + {formatType(throws.type)} + {": "} + {md(throws.description, true)} +
  • )} +
+
+ : null } + + { section.examples + ? +
+

Example{section.examples.length>1?"s":""}

+ {section.examples.map((example, i) => +
+ { example.caption ?

{md(example.caption)}

: null } + {highlight(example.description)} +
)} +
+ : null } + + { section.members.static && section.members.static.length + ? +
+

Static Members

+ +
+ : null } + + { section.members.instance && section.members.instance.length + ? +
+

Instance Members

+ +
+ : null } + + { section.members.events && section.members.events.length + ? +
+

Events

+ +
+ : null } +
+ ); + } +} + +class DocBody extends Component { + render() { + return ( +
+
+ +
+ {API.map((s, i) => + s.kind !== "note" + ? + : null)} +
+ ); + } +} + + +class DocIntro extends Component { + render() { + return MD.process(DocIntroMD).contents; + } +} + +class Doc extends PureComponent { + render() { + return ( +
+ +
+ ); + } +} +export class DocToc extends Component { + render () { + return ( +
+ + gl-react + + +
+ ); + } +} + +export default class Screen extends Component { + render() { + return ; + } +} diff --git a/cookbook/src/Docs/style.css b/cookbook/src/Docs/style.css new file mode 100755 index 0000000..6cfd03b --- /dev/null +++ b/cookbook/src/Docs/style.css @@ -0,0 +1,157 @@ +.documentation { + width: 100%; + min-width: 600px; + max-width: 900px; + color: #555; + line-height: 1.3em; +} + +.documentation a[href]:hover { + text-decoration: underline; +} + +.documentation-toc ul { + padding: 20px 5px; + overflow: visible; + list-style: none; +} +.documentation-toc li { + padding: 0; + overflow: visible; + +} +.documentation-toc li a { + padding: 4px 20px; + position: relative; + display: block; + +} +.documentation-toc li.react-component a { + color: #f56; +} +.documentation-toc li.react-component a:before { + content: "<"; +} +.documentation-toc li.react-component a:after { + content: " />"; +} + +.documentation-toc li.kind-typedef a { + color: #69c; +} +.documentation-toc li.kind-typedef a:before { + left: 0px; + color: #69c; + font-size: 10px; + content: "type"; + margin-right: 4px; + margin-left: -20px; + vertical-align: middle; +} + +a:hover { + cursor: pointer; +} + +.documentation strong { + color: #333; +} + +.documentation .intro { + padding: 20px 40px; + background: #fff; +} + +.documentation .section { + padding: 20px 40px; + background: #fff; + margin: 40px 0; +} + +.documentation .section.nested { + margin: 0; + padding: 0; +} + +.documentation .section h3 { + color: #000; + font-size: 1.8em; + margin: 0.4em 0; + display: flex; + flex-direction: row; + align-items: baseline; +} +.documentation .section h3 .augments { + font-weight: normal; + font-size: 0.6em; + margin-left: 4px; +} +.documentation .section h3 .github-link { + text-align: right; + font-size: 12px; + flex: 1; + font-weight: normal; +} + +.documentation .section h4 { + margin: 0; + margin-left: -0.8em; + margin-top: 1em; + margin-bottom: 0.8em; + font-size: 1.4em; + font-weight: normal; +} + +.documentation .type { + opacity: 0.7; +} + +.documentation .props { + padding: 0; + margin: 0; + list-style: none; +} +.documentation .props li { + padding: 0.4em 0; +} + +.documentation .member { + padding: 20px 0; +} + +.documentation .member .member-title { + color: #000; + font-weight: bold; + margin-left: -20px; + padding: 1em 0; +} + +.documentation .member > .section { + padding-left: 40px; +} + +.documentation .md { + +} + +.documentation .md blockquote { + opacity: 0.5; + margin: 0.2em 0; + padding: 0 0 0 20px; + border-left: 3px solid #666; +} + +.documentation .md h1 { + font-size: 1.6em; +} +.documentation .md h2 { + font-size: 1.4em; +} +.documentation .md h3 { + font-size: 1.2em; +} +.documentation .md h4, +.documentation .md h5, +.documentation .md h6 { + font-size: 1em; +} diff --git a/cookbook/src/ExamplePage.js b/cookbook/src/ExamplePage.js new file mode 100755 index 0000000..3446e16 --- /dev/null +++ b/cookbook/src/ExamplePage.js @@ -0,0 +1,87 @@ +import React, {PropTypes, Component} from "react"; + +const encodeQueryValue = value => JSON.stringify(value); +const decodeQueryValue = value => JSON.parse(value); +const encodeQuery = obj => { + const values = {}; + for (let k in obj) { + if (obj.hasOwnProperty(k)) { + values[k] = encodeQueryValue(obj[k]); + } + } + return values; +}; +const decodeQuery = query => { + query = { ...query }; + for (let k in query) { + if (query.hasOwnProperty(k)) { + try { + query[k] = decodeQueryValue(query[k]); + } + catch (e) { + console.warn(e); + delete query[k]; + } + } + } + return query; +}; + +export default class ExamplePage extends Component { + static contextTypes = { + router: PropTypes.object.isRequired, + }; + + setToolState = (obj: any) => { + const { location: { pathname, query } } = this.props; + this.context.router.replace({ + pathname, + query: { ...query, ...encodeQuery(obj) } + }); + }; + + onChangeField = prop => value => this.setToolState({ [prop]: value }); + + render() { + const { + route: { path, toolbox, ToolboxFooter, Example, desc, descAfter }, + location: { query } + } = this.props; + const props = { + setToolState: this.setToolState, + ...Example.defaultProps, + ...decodeQuery(query), + }; + return
+
{desc}
+
+ +
+ { toolbox + ?
+ {toolbox.map((field, i) => +
+ { field.title + ?

{ + typeof field.title==="function" + ? field.title(props[field.prop]) + : field.title + }

+ : null } + { field.Editor + ? + : null } +
)} + { ToolboxFooter + ? + : null} +
+ : null } +
{descAfter}
+
; + } +} diff --git a/cookbook/src/HOC/timeLoop.js b/cookbook/src/HOC/timeLoop.js new file mode 100755 index 0000000..8db07f5 --- /dev/null +++ b/cookbook/src/HOC/timeLoop.js @@ -0,0 +1,50 @@ +//@flow +import React, { PureComponent } from "react"; +import raf from "raf"; +import hoistNonReactStatics from "hoist-non-react-statics"; + +// NB this is only an utility for the examples +export default ( + C: ReactClass<*>, + { refreshRate = 60 }: { refreshRate?: number } = {} +): ReactClass<*> => { + class TL extends PureComponent { + static displayName = `timeLoop(${C.displayName||C.name||""})`; + state: { time: number }; + state = { + time: 0, + tick: 0, + }; + _r: any; + componentDidMount() { + let startTime: number, lastTime: number; + let interval = 1000 / refreshRate; + lastTime = -interval; + const loop = (t: number) => { + this._r = raf(loop); + if (!startTime) startTime = t; + if (t - lastTime > interval) { + lastTime = t; + this.setState({ + time: t - startTime, + tick: this.state.tick + 1, + }); + } + }; + this._r = raf(loop); + } + componentWillUnmount() { + raf.cancel(this._r); + } + render() { + return ; + } + }; + + hoistNonReactStatics(TL, C); + + return TL; +} diff --git a/cookbook/src/Inspector/index.css b/cookbook/src/Inspector/index.css new file mode 100755 index 0000000..acb7310 --- /dev/null +++ b/cookbook/src/Inspector/index.css @@ -0,0 +1,334 @@ +.gl-react-inspector { + display: flex; + flex-direction: column; + flex: 1; + font-size: 12px; + background: #fff; + color: #000; +} +.gl-react-inspector h2 { + font-weight: normal; +} +.gl-react-inspector .no-surface { + padding: 20px; +} +.gl-react-inspector .no-surface ul { + font-size: 16px; +} +.gl-react-inspector .no-surface li span { + text-decoration: underline; + cursor: pointer; +} +.gl-react-inspector > header { + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 4px; + background: #fff; +} + +.gl-react-inspector > header label { + margin-right: 5px; +} + +.gl-react-inspector .btn { + cursor: pointer; + text-decoration: underline; + user-select: none; + display: inline-block; + text-align: right; +} + +.gl-react-inspector .body { + flex: 1; + background: #f3f3f3; + overflow: auto; + display: flex; + position: relative; + flex-direction: column; +} + +.gl-react-inspector .body .nodes { + position: relative; + width: 100%; + height: 100%; + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AscDA0JcudXmQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABIElEQVR42u3cMQqAQAxE0YkK3v/Ago3gEULIe43lwvLZzqn0Ob/v4/wWd5IcYTUBCAABIAAEgAAQAAJAAAgAASAABIAAEAACQAAIAAEgAASAABAAAkAACAABIABmqfx/qbLL5QUg1Xi2fYDe8+0DIAABuAIBIAAEgAAQAAJAAAgAASAABIAAEAACQAAIAAEgAASAABAAAkAACAABIACmsQ+wl30A7ANsPt8+AAIQgCsQAAJAAAgAASAABIAAEAACQAAIAAEgAASAABAAAkAACAABIAAEgAAQAAJgGvsAe9kHwD7A5vPtAyAAAbgCASAABIAAEAACQAAIAAEgAASAABAAAkAACAABIAAEgAAQAAJAAAgAASAApql8f4niBWChF2e+DTF+hz42AAAAAElFTkSuQmCC'); + background-size: 64px 64px; +} + +.gl-react-inspector .body .hook-drawer { + position: absolute; + top: 0; + left: 0; + overflow: visible; + pointer-events: none; + user-select: none; +} + +.gl-react-inspector .box { + position: absolute;; + display: flex; + flex-direction: column; + width: 180px; + border-radius: 2px; + box-shadow: 0px 0px 3px rgba(0,0,0,0); + border: 1px solid #eee; + background: #fff; + transition: 0.2s border-color; +} +.gl-react-inspector .box.recent-draw { + border-color: #999; + transition: 0.1s border-color; +} + +/* +.gl-react-inspector .box.node header .name { + color: #C30; +} +.gl-react-inspector .box.bus header .name { + color: #093; +} +*/ + +.gl-react-inspector .box header { + display: flex; + flex-direction: row; + font-size: 12px; + padding: 0px 4px; + margin-bottom: 4px; + line-height: 24px; + cursor: move; + border-bottom: 1px solid #eee; +} + +.gl-react-inspector .box header .name { + flex: 1; + font-weight: bold; + word-wrap: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.gl-react-inspector .box header .redraw { + padding-left: 10px; + padding: 0px 6px; + min-width: 30px; + line-height: 24px; + opacity: 0.5; + cursor: pointer; + text-align: right; +} +.gl-react-inspector .box header .redraw:hover { + opacity: 1; +} +.gl-react-inspector .box header .drawCount { + font-size: 10px; + min-width: 20px; + text-align: right; +} + +.gl-react-inspector .box .content-html { + max-height: 80px; + overflow: auto; + color: #aaa; + padding: 2px 8px; + font-size: 9px; + white-space: normal; + word-wrap: break-word; +} + +.gl-react-inspector .box.minimized { + width: 80px; +} +.gl-react-inspector .box.minimized header { + border-bottom: none; + margin-bottom: -2px; +} +.gl-react-inspector .box.minimized footer { + border-top: none; + margin-top: 0; +} +.gl-react-inspector .box.minimized .redraw, +.gl-react-inspector .box.minimized .content-html { + display: none; +} +.gl-react-inspector .box.minimized .uniform { + line-height: 12px; +} +.gl-react-inspector .box.minimized .uniform .anchor-hook { + height: 12px; +} +.gl-react-inspector .box.minimized .uniform:not(.type-sampler2D):not(.type-array-sampler2D) { + display: none; +} + +.gl-react-inspector .box footer { + font-size: 10px; + display: flex; + flex-direction: row; + align-items: baseline; + justify-content: space-between; + padding: 2px 4px; + margin-top: 4px; + border-top: 1px solid #eee; + cursor: pointer; + user-select: none; + position: relative; +} + +.gl-react-inspector .box footer .minimize { + opacity: 0; + position: absolute; + left: -12px; + padding: 1px 4px; +} +.gl-react-inspector .box:hover footer .minimize { + opacity: 0.2; +} +.gl-react-inspector .box footer:hover .minimize { + opacity: 1; +} +.gl-react-inspector .box footer .dim { + color: #888; + font-size: 8px; +} +.gl-react-inspector .box footer .mode { + padding-left: 4px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + + +.gl-react-inspector .box .anchor-hook { + position: absolute; + left: -2px; + height: 16px; +} + +.gl-react-inspector .box .hook { + position: absolute; + right: 0px; + height: 20px; +} + +.gl-react-inspector .uniforms { + display: flex; + flex-direction: column; + overflow: hidden; +} + +.gl-react-inspector .uniforms .uniform { + line-height: 18px; + font-size: 11px; + display: flex; + flex-direction: row; + justify-content: space-between; +} +.gl-react-inspector .uniforms .name { + color: #000; + display: inline-block; + padding: 0 4px; +} +.gl-react-inspector .uniforms .value-container { + /*white-space: nowrap;*/ + word-break: break-all; + padding-right: 6px; + overflow: hidden; +} +.gl-react-inspector .uniforms .val { + color: #850; + background: #ffc; + padding: 1px 3px; + border: 1px solid #ed9; +} +.gl-react-inspector .uniforms .value.type-sampler2D { + text-align: right; +} +.gl-react-inspector .uniforms .value.type-sampler2D .val { + display: none; +} + +.gl-react-inspector .uniforms .value-array .value-array, +.gl-react-inspector .uniforms .value.type-sampler2D { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.gl-react-inspector .uniforms .value-array > :first-child:before { + content: "["; + color: #aaa; +} +.gl-react-inspector .uniforms .value-array > :after { + content: ","; + color: #aaa; +} +.gl-react-inspector .uniforms .value-array > :last-child:after { + content: "]"; + color: #aaa; +} + +.gl-react-inspector .uniforms .meta-info { + color: #ec6; + padding: 0 2px; +} + +.gl-react-inspector .preview { +} + +.gl-react-inspector .preview { + background: #fff; + position: relative; + flex: 1; + display: flex; + justify-content: center; + align-items: center; + padding: 10px; +} + +.gl-react-inspector .connection circle { + fill: #666; + r: 1; +} + +.gl-react-inspector circle.hook, +.gl-react-inspector circle.anchor { + r: 2; + fill: #666; +} +.gl-react-inspector circle.standalone-output { + r: 2; + fill: #000; + opacity: 1; +} + +.gl-react-inspector path.connection-line { + opacity: 0.1; + stroke: #000; + fill: none; + stroke-width: 1; +} + +.gl-react-inspector .box.grabbed { + border-color: #000; + transition: none; +} +.gl-react-inspector .anchor-group.grabbed circle.hook, +.gl-react-inspector .anchor-group.grabbed circle.anchor { + fill: #000; +} +.gl-react-inspector .anchor-group.grabbed path.connection-line { + stroke: #000; + opacity: 0.3; +} + +.gl-react-inspector .preview canvas { + background: #f3f3f3 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAARklEQVRYw+3XwQkAIAwEwYvYf8uxBcE8fMwWEAbuleruDDd6cOXzAAEBAQEBAQEBAQFf2tM/RJIyMSAgICAgICAgICDgZQeYxgVOKu5KXQAAAABJRU5ErkJggg==) repeat; + background-size: 20px 20px; + border: 1px solid #666; + max-width: 100%; + min-height: 64px; + box-sizing: border-box; + image-rendering: pixelated; +} diff --git a/cookbook/src/Inspector/index.js b/cookbook/src/Inspector/index.js new file mode 100755 index 0000000..fcaab7a --- /dev/null +++ b/cookbook/src/Inspector/index.js @@ -0,0 +1,1476 @@ +//@flow +import React, { + PureComponent, + Component, + PropTypes, +} from "react"; +import {Visitors, VisitorLogger, Node, Bus, Backbuffer, listSurfaces} from "gl-react"; +import raf from "raf"; +import type {Surface} from "gl-react-dom"; +import createTexture from "gl-texture2d"; +import createShader from "gl-shader"; +import throttle from "lodash/throttle"; +import "./index.css"; + +const drawTimeWindow = 1000; +const anchorYOff = 30; + +const formatNumber = (n: number) => { + const str = String(n); + const i = str.indexOf("."); + if (i === -1) return str; + return str.slice(0, i + 1 + Math.max(1, 5-i)); +}; +const formatObject = (o: any) => { + if (typeof o === "object") { + const name = o && o.constructor && o.constructor.name; + if (name) { + return "[object "+String(name)+"]"; + } + } + return String(o); +}; + +const primitiveTypeAlias = { + vec2: Array(2).fill("float"), + vec3: Array(3).fill("float"), + vec4: Array(4).fill("float"), + ivec2: Array(2).fill("int"), + ivec3: Array(3).fill("int"), + ivec4: Array(4).fill("int"), + bvec2: Array(2).fill("bool"), + bvec3: Array(3).fill("bool"), + bvec4: Array(4).fill("bool"), + mat2: Array(4).fill("float"), + mat3: Array(9).fill("float"), + mat4: Array(16).fill("float"), +}; + + +const classType = type => { + if (Array.isArray(type)) return "type-array-"+type[0]; + return "type-"+type; +}; + +const inferSize = obj => { + if (obj) { + if (obj.shape) { + return obj.shape; + } + if ("videoWidth" in obj) { + return [obj.videoWidth, obj.videoHeight]; + } + if ("width" in obj) { + return [obj.width, obj.height]; + } + } + return [0,0]; +}; + +class PreviewRenderer { + canvas: HTMLCanvasElement; + copyShader: any; + gl: ?WebGLRenderingContext; + texture: any; + constructor () { + const canvas = document.createElement("canvas"); + canvas.width = canvas.height = 2; + const opts = { preserveDrawingBuffer: true }; + const gl = canvas.getContext("webgl", opts); + if (!gl) return; + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); + const texture = createTexture(gl, [2,2]); + const buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + -1.0, -1.0, + 1.0, -1.0, + -1.0, 1.0, + -1.0, 1.0, + 1.0, -1.0, + 1.0, 1.0 + ]), gl.STATIC_DRAW); + const copyShader = createShader(gl, ` +attribute vec2 _p; +varying vec2 uv; +void main() { + gl_Position = vec4(_p,0.0,1.0); + uv = vec2(0.5, 0.5) * (_p+vec2(1.0, 1.0)); +} +`, ` +precision highp float; +varying vec2 uv; +uniform sampler2D t; +void main () { + vec4 c = texture2D(t, uv); + gl_FragColor = c; +}`); + copyShader.bind(); + copyShader.attributes._p.pointer(); + this.copyShader = copyShader; + this.texture = texture; + this.canvas = canvas; + this.gl = gl; + } + setSize (newWidth, newHeight) { + const { gl, canvas, texture } = this; + if (!gl) return; + if (gl.drawingBufferWidth === newWidth && + gl.drawingBufferHeight === newHeight) return; + texture.shape = [ newWidth, newHeight ]; + canvas.width = newWidth; + canvas.height = newHeight; + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + } + draw (obj) { + const { texture, gl, copyShader } = this; + if (!gl) return; + const [w, h] = inferSize(obj); + if (!w || !h) return; + this.setSize(w, h); + texture.setPixels(obj); + copyShader.uniforms.t = texture.bind(); + gl.drawArrays(gl.TRIANGLES, 0, 6); + } + copyToCanvas2D (ctx) { + const { canvas } = this; + ctx.canvas.width = canvas.width; + ctx.canvas.height = canvas.height; + ctx.drawImage(canvas, 0, 0); + } +} + +class UniformValue extends Component { + props: { + id: string, + node: Object, + value: any, + type: any, + info: any, + }; + render () { + let { id, node, value, type, info } = this.props; + if (type in primitiveTypeAlias) { + type = primitiveTypeAlias[type]; + } + return ( + Array.isArray(type) + ? + {type.map((type, i) => + )} + + : + + { typeof value === "number" + ? formatNumber(value) + : String(value) } + + {info ? : null} + + ); + } +} + +class Btn extends Component { + props: { + onClick: ?()=>void, + children?: any, + }; + render() { + const {onClick,children} = this.props; + return {children}; + } +} + +/** + * an anchor is the top-right Dot attached on a InspectorNode/Bus + * it is the start of a connection to another node + */ +class Anchor extends Component { + props: { + id: number, + drawCount: number, + }; + drawHistoryDates: Array = []; + static contextTypes = { + inspector: PropTypes.object.isRequired, + }; + componentDidMount() { + this.context.inspector.addAnchor(this.props.id, this); + } + componentWillReceiveProps({ id, drawCount }) { + if (id !== this.props.id) { + this.context.inspector.removeAnchor(this.props.id, this); + this.context.inspector.addAnchor(id, this); + } + for (let i=this.props.drawCount; i 200) { + this.drawHistoryDates.shift(); + } + } + } + componentWillUnmount() { + this.context.inspector.removeAnchor(this.props.id, this); + } + getDrawCountsForTimeWindow = (w: number) => { + const now = Date.now(); + const {drawHistoryDates} = this; + const values = []; + for (let i=drawHistoryDates.length-1; i>0; i--) { + const t = (now - drawHistoryDates[i]) / w; + if (t > 1) break; + values.push(t); + } + return values; + }; + getXY = () => { + const {root} = this.refs; + const {top,left,height} = root.getBoundingClientRect(); + return [left,top+height/2]; + }; + render() { + return ; + } +} + +/** + * AnchorHook is the dot shown at the left of a texture uniform. + * It is the end of the connection with an Anchor. + */ +class AnchorHook extends Component { + props: { + id: string, + nodeId: number, + anchorId: number, + }; + static contextTypes = { + inspector: PropTypes.object.isRequired, + }; + componentDidMount() { + this.context.inspector.addAnchorHook(this.props.anchorId, this); + } + componentWillReceiveProps({ anchorId }) { + if (anchorId !== this.props.anchorId) { + this.context.inspector.removeAnchorHook(this.props.anchorId, this); + this.context.inspector.addAnchorHook(anchorId, this); + } + } + componentWillUnmount() { + this.context.inspector.removeAnchorHook(this.props.anchorId, this); + } + getId = () => { + return this.props.id; + }; + getNodeId = () => { + return this.props.nodeId; + }; + getAnchorId = () => { + return this.props.anchorId; + }; + getXY = () => { + const {root} = this.refs; + const {top,left,height} = root.getBoundingClientRect(); + return [left,top+height/2]; + }; + render() { + return ; + } +} + +class MetaInfo extends Component { + props: { + id: string, + node: Object, + info: { + initialObj: ?Object, + dependency: ?Object, + textureOptions: ?Object, + } + }; + render() { + const { id, info, node } = this.props; + const { dependency, obj } = info; + return ( + + { dependency + ? + : obj===Backbuffer + ? + : null } + { dependency + ? dependency.getGLShortName() + : typeof obj === "string" + ? {obj} + : formatObject(obj) } + + ); + } +} + +const sharedRenderer = new PreviewRenderer(); + +class Preview extends Component { + props: { + capture: Function, + }; + interval: number; + canvas: ?HTMLCanvasElement; + ctx: ?CanvasRenderingContext2D; + componentWillReceiveProps() { + this.capture(); + } + componentWillUnmount () { + this.capture.cancel(); + } + capture = throttle(() => { + const {ctx} = this; + if (!ctx) return; + const {capture} = this.props; + const snap = capture(); + if (snap) { + sharedRenderer.draw(snap); + sharedRenderer.copyToCanvas2D(ctx); + } + }, 100); + onCanvasRef = canvas => { + if (!canvas || this.ctx) return; + const ctx = canvas.getContext("2d"); + if ("imageSmoothingEnabled" in ctx) { + ctx.imageSmoothingEnabled = false; + } + else { + ctx.imageSmoothingEnabled = false; + ctx.mozImageSmoothingEnabled = false; + ctx.webkitImageSmoothingEnabled = false; + ctx.msImageSmoothingEnabled = false; + } + this.canvas = canvas; + this.ctx = ctx; + this.capture(); + }; + render() { + return
+ +
; + } +} + +class PreviewNode extends PureComponent { + props: { + node: Object, + }; + capture = () => { + const {node} = this.props; + try { + return node.capture(); + } + catch(e) {} // eslint-disable-line no-empty + }; + render() { + return ; + } +} + +class PreviewContent extends Component { + props: { + content: Object, + }; + capture = () => { + const {content} = this.props; + return content; + }; + render() { + return ; + } +} + +class DrawCount extends PureComponent { + props: { + drawCount: number, + }; + render() { + const { drawCount } = this.props; + return + {drawCount} + ; + } +} + +class InspectorBox extends Component { + props: { + animated: boolean, + drawCount: number, + cls: string, + pos: [number, number], + glObject: Bus | Node, + mode?: any, + width: number, + height: number, + children?: any, + root?: boolean, + grabbed?: boolean, + minimized: boolean, + onGrabStart: (id: number, e: MouseEvent)=>void, + onMinimizeChange: (id: number, minimized: boolean)=>void, + }; + static contextTypes = { + inspector: PropTypes.object.isRequired, + }; + state = { + recentDraw: false, + }; + lastDrawCountTime: number; + _timeout: number; + componentDidMount() { + this.context.inspector.addBox(this.props.glObject.id, this); + this.onNewDraw(); + } + componentWillReceiveProps({ drawCount, glObject: {id} }) { + if (id !== this.props.glObject.id) { + this.context.inspector.removeBox(id, this); + this.context.inspector.addBox(id, this); + } + if (drawCount !== this.props.drawCount) { + this.onNewDraw(); + } + } + componentWillUnmount() { + clearTimeout(this._timeout); + this.context.inspector.removeBox(this.props.glObject.id, this); + } + getSize() { + const { width, height } = this.refs.box.getBoundingClientRect(); + return [ width, height ]; + } + onNewDraw = () => { + if (!this.state.recentDraw) { + this.setState({ recentDraw: true }, () => { + clearTimeout(this._timeout); + this._timeout = setTimeout(() => { + if (this.state.recentDraw) { + this.setState({ recentDraw: false }); + } + }, 20); + }); + } + }; + onMouseDown = e => { + e.preventDefault(); + this.props.onGrabStart(this.props.glObject.id, e); + }; + onClickMinimize = e => { + e.preventDefault(); + this.props.onMinimizeChange(this.props.glObject.id, !this.props.minimized); + }; + onRedraw = () => this.props.glObject.redraw(); + render() { + const { + recentDraw + } = this.state; + const { + pos, + drawCount, + glObject, + width, + height, + mode, + children, + grabbed, + cls, + minimized, + } = this.props; + return ( +
+
+ + + {glObject.getGLShortName()} + + + + +
+ {children} +
+ ↕ + {width}⨉{height} + {mode} +
+
+ ); + } +} + +const formatType = t => { + if (Array.isArray(t)) return t[0]+"[]"; + return t; +}; + +class Uniforms extends PureComponent { + props: { + node: Object, + preparedUniforms: ?Object, + }; + render() { + const { node, preparedUniforms } = this.props; + return
+ { preparedUniforms && + preparedUniforms.map(u => +
+ + {u.key} + + + + +
+ )} +
; + } +} + +class SVGConnectionLine extends PureComponent { + props: { + hookX: number, + hookY: number, + anchorX: number, + anchorY: number, + onPathRef: Function, + reversedHook?: boolean, + recursive?: boolean, + }; + render() { + const { + anchorX, + anchorY, + hookX, + hookY, + recursive, + onPathRef, + reversedHook, + } = this.props; + const dx = hookX - anchorX; + const dy = hookY - anchorY; + const factor = Math.abs(dx) + Math.abs(dy); + const t = Math.round((recursive ? 0.1 : 0.3) * factor); + const s = Math.round((recursive ? 0.02 : 0.05) * factor, 8); + return ; + } +} + +class SVGConnection extends Component { + props: { + anchor: Object, + hookX: number, + hookY: number, + anchorX: number, + anchorY: number, + tension: number, + solid: number, + recursive?: boolean, + reversedHook?: boolean, + animated?: boolean, + }; + state = { + draws: [], + }; + path: any; + _raf: any; + componentDidMount() { + const {path} = this; + const loop = () => { + this._raf = raf(loop); + if (!this.props.animated) return; + const values = this.props.anchor.getDrawCountsForTimeWindow(drawTimeWindow); + if (values.length===0) { + if (this.state.draws.length!==0) { + this.setState({ draws: [] }); + } + return; + } + const length = path.getTotalLength(); + this.setState({ + draws: values.map(v => { + const { x, y } = path.getPointAtLength(v * length); + return [ x.toFixed(1), y.toFixed(1) ]; // round is too aggressive and great jump. but using full float is also more consuming + }), + }); + }; + this._raf = raf(loop); + } + componentWillReceiveProps({ animated }) { + if (this.props.animated && !animated) { + this.setState({ draws: [] }); + } + } + componentWillUnmount() { + raf.cancel(this._raf); + } + onPathRef = ref => { + this.path = ref; + }; + render () { + const { hookX, hookY, anchorX, anchorY, tension, solid, recursive, reversedHook } = this.props; + const { draws } = this.state; + return ( + + + {draws.map(([x,y], i) => + )} + + ); + } +} + +class SVGStandaloneConnection extends Component { + props: { + animated: boolean, + anchor: Anchor, + anchorX: number, + anchorY: number, + hookX: number, + hookY: number, + }; + render() { + const {animated, anchor, anchorX, anchorY, hookX, hookY} = this.props; + return + + + ; + } +} + +class HookDrawer extends Component { + props: { + animated: boolean, + anchorHooksByAnchorId: *, + anchorsById: *, + anchorPositions: *, + anchorHookPositions: *, + boxSizes: *, + grabbing: ?Object, + }; + render() { + const { + animated, + anchorHooksByAnchorId, + anchorsById, + anchorPositions, + anchorHookPositions, + boxSizes, + grabbing, + } = this.props; + return { + [...anchorsById].map(([ anchorId, anchor ]) => { + const hooks = anchorHooksByAnchorId.get(anchorId) || []; + const anchorPosition = anchorPositions.get(anchorId); + const size = boxSizes.get(anchorId); + const anchorIsGrabbed = grabbing && grabbing.id===anchorId; + if (!anchorPosition || !size) return null; + return + { hooks.length===0 + ? + : hooks.map(hook => { + const hookId = hook.getId(); + const hookNodeId = hook.getNodeId(); + const hookIsGrabbed = grabbing && grabbing.id===hookNodeId; + const hookPosition = anchorHookPositions.get(hook); + if (!hookPosition) return null; + return ( + + + + + ); + })} + + ; + }) + }; + } +} + +const inspectorVisitorLogger = new VisitorLogger(); + +export default class Inspector extends Component { + state = { + capture: false, + animated: true, + physics: false, + minimizeAll: false, + }; + static childContextTypes = { + inspector: PropTypes.object.isRequired, + } + getChildContext() { + return { inspector: this }; + } + + // We don't use React state here for better perf. + surface: ?Surface; + preparedUniformsMap: WeakMap = new WeakMap(); + nodeDrawCounts: WeakMap = new WeakMap(); + busDrawCounts: WeakMap = new WeakMap(); + anchorsById: Map = new Map(); + anchorHooksByAnchorId: Map> = new Map(); + boxesById: Map = new Map(); + + // FIXME anchorPositions will become relative to nodePositions when it's stable + anchorPositions: Map = new Map(); + anchorHookPositions: Map = new Map(); + + boxMinimized: Map = new Map(); + boxPos: Map = new Map(); + boxVel: Map = new Map(); + boxAcc: Map = new Map(); + boxSizes: Map = new Map(); + _startupTimeout: number; + _raf: any; + grabbing: ?{ + id: number, + initialPos: [number, number], + initialEventPos: [number, number], + }; + + componentDidMount() { + Visitors.add(this); + this._startupTimeout = setTimeout(() => this.detectSurface(), 0); + let lastT; + const loop = t => { + this._raf = raf(loop); + if (!lastT) lastT = t; + const delta = Math.min(100, t - lastT); + lastT = t; + const syncChanged = this.syncFromDom(); + const physicsChanged = this.state.physics && this.physicsStep(delta); + if (syncChanged || physicsChanged) { + this.forceUpdate(); + } + }; + this._raf = raf(loop); + } + + componentWillUnmount() { + raf.cancel(this._raf); + clearTimeout(this._startupTimeout); + Visitors.remove(this); + } + + setSurface (surface) { + if (surface === this.surface) return; + this.preparedUniformsMap = new WeakMap(); + this.surface = surface; + if (surface) surface.rebootForDebug(); + this.forceUpdate(); + } + + detectSurface() { + const surface = listSurfaces()[0]; + this.setSurface(surface); + } + + onSurfaceMount () { + if (!this.surface) { + this.detectSurface(); + } + } + + onSurfaceUnmount (surface: Surface) { + if (surface===this.surface) { + this.setSurface(null); + } + } + + onSurfaceGLContextChange () { + } + + onSurfaceDrawSkipped() { + } + + onSurfaceDrawStart() { + } + + onSurfaceDrawError () { + + } + + onSurfaceDrawEnd(surface: Surface) { + if (surface === this.surface) { + this.forceUpdate(); + } + } + + onNodeDrawSkipped() { + + } + + onNodeDrawStart() { + + } + + onNodeSyncDeps() { + + } + + onNodeDraw(node: Node, preparedUniforms: Array<*>) { + this.preparedUniformsMap.set(node, preparedUniforms); + } + + onNodeDrawEnd(node: Node) { + this.nodeDrawCounts.set(node, (this.nodeDrawCounts.get(node)||0)+1); + node.dependencies.forEach(obj => { + if (obj instanceof Bus) { + this.busDrawCounts.set(obj, (this.busDrawCounts.get(obj)||0)+1); + } + }); + } + + addAnchorHook(id: number, hook: AnchorHook) { + let hooks = this.anchorHooksByAnchorId.get(id); + if (!hooks) { + this.anchorHooksByAnchorId.set(id, [hook]); + } + else { + hooks.push(hook); + } + this.forceUpdate(); + } + + removeAnchorHook(id: number, hook: AnchorHook) { + const hooks = this.anchorHooksByAnchorId.get(id); + if (hooks) { + const i = hooks.indexOf(hook); + hooks.splice(i, 1); + if (hooks.length===0) { + this.anchorHooksByAnchorId.delete(id); + } + this.forceUpdate(); + } + } + + addAnchor(id: number, hook: Anchor) { + this.anchorsById.set(id, hook); + this.forceUpdate(); + } + + removeAnchor(id: number) { + this.anchorsById.delete(id); + this.forceUpdate(); + } + + addBox(id: number, box: InspectorBox) { + this.boxesById.set(id, box); + const i = this.boxPos.size; + const pos = [ // FIXME TMP + 50 + 240 * ((i+1) % 2), + 40 + 200 * Math.floor(i / 2), + ]; + this.boxPos.set(id, pos); + this.boxMinimized.set(id, this.state.minimizeAll); + } + + removeBox(id: number) { + this.boxesById.delete(id); + this.boxPos.delete(id); + this.boxSizes.delete(id); + this.boxMinimized.delete(id); + this.boxVel.delete(id); + this.boxAcc.delete(id); + } + + bounds: any; + syncFromDom () { + const {nodes, body} = this.refs; + let hasChanged = false; + if (nodes) { + this.bounds = body.getBoundingClientRect(); + const { top: offY, left: offX } = nodes.getBoundingClientRect(); + const { + anchorsById, + anchorPositions, + anchorHooksByAnchorId, + anchorHookPositions, + boxesById, + boxSizes, + } = this; + // FIXME only the Anchor and AnchorHook should be allow to "sync this" + // as soon as we make this local position, not global... + anchorsById.forEach((anchor, id) => { + let [x,y] = anchor.getXY(); + x = Math.round(x - offX); + y = Math.round(y - offY); + const old = anchorPositions.get(id); + if (!old || x!==old[0] || y!==old[1]) { + hasChanged = true; + anchorPositions.set(id, [x, y]); + } + }); + anchorHooksByAnchorId.forEach(anchorHooks => { + anchorHooks.forEach(anchorHook => { + let [x,y] = anchorHook.getXY(); + x = Math.round(x - offX); + y = Math.round(y - offY); + const old = anchorHookPositions.get(anchorHook); + if (!old || x!==old[0] || y!==old[1]) { + hasChanged = true; + anchorHookPositions.set(anchorHook, [x, y]); + } + }); + }); + // FIXME: same, the box size should only be sync when InspectorBox did update/mount + boxesById.forEach((box, id) => { + const size = box.getSize(); + const old = boxSizes.get(id); + if (!old || size[0]!==old[0] || size[1]!==old[1]) { + hasChanged = true; + boxSizes.set(id, size); + } + }); + } + return hasChanged; + } + + applyForce(id, force) { + const {boxAcc} = this; + const mass = 1; + const acc = boxAcc.get(id) || [0,0]; + boxAcc.set(id, [ + acc[0] + force[0] / mass, + acc[1] + force[1] / mass, + ]); + } + + spring() { + // a bit inspired from https://github.com/dhotson/springy/blob/master/springy.js + const { + anchorPositions, + anchorHookPositions, + anchorsById, + anchorHooksByAnchorId, + boxPos, + boxSizes, + } = this; + anchorsById.forEach((anchor, anchorId) => { + const hooks = anchorHooksByAnchorId.get(anchorId) || []; + const anchorPos = anchorPositions.get(anchorId); + hooks.forEach(hook => { + const hookId = hook.getId(); + const hookNodeId = hook.getNodeId(); + if (anchorId !== hookNodeId) { + const hookPos = anchorHookPositions.get(hook); + const dx = anchorPos[0] - hookPos[0]; + const dy = anchorPos[1] - hookPos[1]; + const magn = Math.sqrt(dx * dx + dy * dy); + const dist = magn + 0.1; + const dir = [ dx / magn, dy / magn ]; + // Coulombs law + const m = 8 / (dist * dist); + this.applyForce(anchorId, [ + m * dir[0], + m * dir[1] + ]); + this.applyForce(hookId, [ + -m * dir[0], + -m * dir[1] + ]); + // Hookes law + const length = 1.0; // FIXME mysterious constant xD + const disp = length - magn; + const n = 0.5 * 0.00001 * disp; + this.applyForce(hookId, [ + -n * dir[0], + -n * dir[1] + ]); + this.applyForce(anchorId, [ + n * dir[0], + n * dir[1] + ]); + } + }); + }); + + // a box push other boxes + const boxIds = [...boxPos.keys()]; + for (let i=0; i { + if (id === grabId) return; + let a = boxAcc.get(id) || [0,0]; + let v = boxVel.get(id) || [0,0]; + const size = boxSizes.get(id) || [0,0]; + v = [ + (v[0] + a[0] * timestep) * damping, + (v[1] + a[1] * timestep) * damping, + ]; + p = [ + p[0] + v[0] * timestep, + p[1] + v[1] * timestep, + ]; + if (bounds) { + const pad = 20; + p[0] = Math.max(pad, Math.min(p[0], bounds.width - size[0] - pad)); + p[1] = Math.max(pad, Math.min(p[1], bounds.height - size[1] - pad)); + } + + a = [0, 0]; + boxVel.set(id, v); + boxAcc.set(id, a); + boxPos.set(id, p); + }); + } + + physicsStep (timestep) { + this.spring(); + this.updateVelocityPosition(timestep); + var energy = 0.0; + this.boxVel.forEach(v => { + energy += v[0] * v[0] + v[1] * v[1]; + }); + if (energy > 0.00001) { + this.forceUpdate(); + } + } + + onGrabStart = (id, e) => { + const boxPos = this.boxPos.get(id) || [0, 0]; + this.grabbing = { + id, + initialPos: boxPos, + initialEventPos: [e.clientX, e.clientY], + }; + this.forceUpdate(); + }; + + onMouseMove = e => { + if (this.grabbing) { + const { id, initialPos, initialEventPos } = this.grabbing; + const dx = e.clientX - initialEventPos[0]; + const dy = e.clientY - initialEventPos[1]; + const pos = [ + initialPos[0] + dx, + initialPos[1] + dy, + ]; + this.boxPos.set(id, pos); + this.forceUpdate();// NB for better perf we should only make the box to update.. same for the physicsStep + } + }; + + onMouseLeave = () => { + if (this.grabbing) { + this.grabbing = 0; + this.forceUpdate(); + } + }; + + onMouseUp = () => { + if (this.grabbing) { + this.grabbing = 0; + this.forceUpdate(); + } + }; + + onMinimizeChange = (id, minimized) => { + this.boxMinimized.set(id, minimized); + this.forceUpdate(); + }; + + onCaptureChange = e => { + this.setState({ capture: e.target.checked }); + }; + + onAnimatedChange = e => { + this.setState({ animated: e.target.checked }); + }; + + onMinimizeAllChange = e => { + const minimizeAll = e.target.checked; + this.setState({ minimizeAll }); + const {boxPos, boxMinimized} = this; + boxPos.forEach((p, id) => { + boxMinimized.set(id, minimizeAll); + }); + }; + + onPhysicsChange = e => { + this.setState({ physics: e.target.checked }); + }; + + onSelectChange = e => { + const {selectedIndex} = e.target; + this.setSurface( + selectedIndex===0 + ? undefined + : listSurfaces()[selectedIndex - 1] + ); + }; + + rebootSurface = () => { + const { surface } = this.state; + if (surface) surface.rebootForDebug(); + }; + + loseContextLatestExtension: any; + loseContext = () => { + const {surface} = this; + if (surface) { + const {gl} = surface; + if (gl) { + this.loseContextLatestExtension = gl.getExtension("WEBGL_lose_context"); + this.loseContextLatestExtension.loseContext(); + } + } + this.forceUpdate(); + }; + + restoreContext = () => { + const {loseContextLatestExtension} = this; + if (loseContextLatestExtension) { + loseContextLatestExtension.restoreContext(); + this.loseContextLatestExtension = null; + } + this.forceUpdate(); + }; + + onVisitorLoggerChange = (e) => { + Visitors.remove(inspectorVisitorLogger); + if (e.target.checked) { + Visitors.add(inspectorVisitorLogger); + } + } + + render () { + const {surface, boxPos, grabbing} = this; + const {capture, animated, minimizeAll, physics} = this.state; + + const key = surface && surface.id; + let headerBody, body; + + if (surface) { + const root = surface.root; + const nodeElements = []; + // when you over a graph line it should tell info about uniforms around the node? + // when you over a uniform, it should bold the connected lines. + + if (root) { + let stack: Array = [ root ]; + const explored = {}; + while (stack.length > 0) { + const n: Node | Bus = stack.pop(); + const deps = n instanceof Node ? n.dependencies : []; + stack = deps.concat(stack); + const {id} = n; + if (explored[id]) continue; // FIXME for some reason i can't yet figure out, it can't be before the stacking in advanced examples like Behind Asteroids + explored[id] = true; + const pos = boxPos.get(id) || [0,0]; + const minimized = this.boxMinimized.get(id) || false; + const props = { + animated, + key: id, + pos, + glObject: n, + grabbed: grabbing ? grabbing.id===id : false, + minimized, + onMinimizeChange: this.onMinimizeChange, + onGrabStart: this.onGrabStart, + }; + if (n instanceof Node) { + const [ width, height ] = n.getGLSize(); + const drawCount = this.nodeDrawCounts.get(n); + nodeElements.push( + + + { capture && !minimized + ? + : null } + + ); + } + else { + const content = n.getGLRenderableContent(); + const [width, height] = inferSize(content); + const drawCount = this.busDrawCounts.get(n); + nodeElements.push( + +
+ { (content && content.outerHTML) || null } +
+ { capture && !minimized + ? + : null} +
+ ); + } + } + + const lost = !surface.gl || this.loseContextLatestExtension; + headerBody = +
+ + + + + { lost + ? + + restore GL context + + : + + lose GL context + } +
; + body = +
+
+ {nodeElements} +
+ +
; + } + } + + else { + body = +
+
+

No Surface is currently inspected. Select one of these:

+
    + {listSurfaces().map(surface => +
  • + this.setSurface(surface)}> + {surface.getGLName()} + +
  • )} +
+
+
; + } + + return ( +
+
+
+ + +
+ {headerBody} +
+ {body} +
+ ); + } +} diff --git a/cookbook/src/examples/animated/index.js b/cookbook/src/examples/animated/index.js new file mode 100755 index 0000000..a2cf99d --- /dev/null +++ b/cookbook/src/examples/animated/index.js @@ -0,0 +1,46 @@ +//@flow +import React, { Component } from "react"; +import { Shaders, Node, GLSL } from "gl-react"; +import { Surface } from "gl-react-dom"; +import Animated from "animated"; +const shaders = Shaders.create({ + cursor: { frag: GLSL` +precision lowp float; varying vec2 uv; uniform vec2 style; +void main() { + float dist = pow(1.0 - distance(style, uv), 8.0); + gl_FragColor = vec4(smoothstep(2.0, 0.2, distance(style, uv)) * vec3( + 1.0 * dist + pow(1.0 - distance(style.y, uv.y), 16.0), + 0.5 * dist + pow(1.0 - distance(style.y, uv.y), 32.0), + 0.2 * dist + pow(1.0 - distance(style.x, uv.x), 32.0)), 1.0); +}` } +}); +class Cursor extends Component { + render() { + const { style: { x, y } } = this.props; + return ; + } +} +// using "style" is a hack. see https://github.com/animatedjs/animated/issues/45 +const AnimatedCursor = Animated.createAnimatedComponent(Cursor); + +export default class Example extends Component { + state = { + style: new Animated.ValueXY({ x: 0.5, y: 0.5 }) + }; + onMouseMove = (e: any) => { + const rect = e.target.getBoundingClientRect(); + Animated.spring(this.state.style, { + toValue: { + x: (e.clientX - rect.left) / rect.width, + y: (rect.bottom - e.clientY) / rect.height, + } + }).start(); + }; + render() { + return ( + + + + ); + } +}; diff --git a/cookbook/src/examples/animated/meta.js b/cookbook/src/examples/animated/meta.js new file mode 100755 index 0000000..19be503 --- /dev/null +++ b/cookbook/src/examples/animated/meta.js @@ -0,0 +1,5 @@ +import markdown from "../../markdown"; +export const title = "Cursor spring effect with animated"; +export const desc = markdown` +We can also use \`Animated\` animation library. +`; diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/package.json b/cookbook/src/examples/behindasteroids/behind-asteroids/package.json new file mode 100755 index 0000000..51a4fef --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/package.json @@ -0,0 +1,55 @@ +{ + "private": true, + "name": "gre-js13k-2014", + "version": "0.0.0", + "description": "", + "scripts": { + "clean": "rm -rf build/; mkdir -p build target", + "compileglsl": "./scripts/compileglslfiles.sh src/shaders build", + "concat": "./scripts/concat.sh > build/build.js", + "minify": "uglifyjs build/build.js -c --screw-ie8 -m -o build/build.min.js", + "nominify": "cp build/build.js build/build.min.js", + "gen": "cp src/target.html target/index.html && cp build/build.min.js target/b.js", + "build": "export NODE_ENV=production; npm run clean && npm run compileglsl && npm run concat && npm run minify && npm run gen && npm run zip", + "build-nominify": "npm run clean && npm run compileglsl && npm run concat && npm run nominify && npm run gen", + "watch": "npm run build-nominify; wr 'npm run build-nominify' src/ scripts/", + "liveserver": "mkdir -p target; cd target; live-server --no-browser", + "zip": "cd target; zip -r ../target.zip .; cd ..; wc -c target.zip", + "start": "budo index.js:bundle.js | garnish" + }, + "browserify": { + "transform": [ + "babelify", + "glslify" + ] + }, + "repository": { + "type": "git", + "url": "git://github.com/gre/js13k-2014.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/gre/js13k-2014/issues" + }, + "homepage": "https://github.com/gre/js13k-2014", + "devDependencies": { + "babelify": "^6.4.0", + "browserify": "^11.0.1", + "budo": "^5.1.5", + "eslint": "^1.3.0", + "garnish": "^3.2.1", + "glslify": "^2.3.1", + "glslmin": "0.0.0", + "live-server": "^0.8.1", + "uglify-js": "^2.4.24", + "uglifycss": "^0.0.17", + "wr": "^1.3.1" + }, + "dependencies": { + "gl-react": "^1.2.5", + "gl-react-inspector": "0.0.2", + "react": "^0.14.0", + "react-dom": "^0.14.0" + } +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/scripts/compileglslfiles.sh b/cookbook/src/examples/behindasteroids/behind-asteroids/scripts/compileglslfiles.sh new file mode 100755 index 0000000..3aa9e43 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/scripts/compileglslfiles.sh @@ -0,0 +1,22 @@ + +if [ "$#" -ne 2 ]; then + echo "Invalid arguments. Usage: $0 fromDir toDir" >&2; + exit 1; +fi; +if [ "$1" == "$2" ]; then + echo "fromDir and toDir must be different" >&2; + exit 2; +fi; +if [ ! -d "$1" ]; then + echo "fromDir must be a directory" >&2; + exit 3; +fi; +if [ ! -d "$2" ]; then + echo "toDir must be a directory" >&2; + exit 4; +fi; + +for glsl in $1/*.frag $1/*.vert; do + name=`basename $glsl`; + cat $glsl | glslmin > $2/$name; +done; diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/scripts/concat.sh b/cookbook/src/examples/behindasteroids/behind-asteroids/scripts/concat.sh new file mode 100755 index 0000000..94ad4d2 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/scripts/concat.sh @@ -0,0 +1,47 @@ + +cat src/pre.js + +if [ "$NODE_ENV" == "production" ]; then + cat src/env_prod.js +else + cat src/env_dev.js +fi; + +# libs + +cat src/lib/math.js +cat src/lib/path.js +cat src/lib/asteroids.font.js +cat src/lib/webgl.js +cat src/lib/jsfxr.js +cat src/lib/audio.js + +# shaders + +cd build; +for glsl in *.frag *.vert; do + name=`echo $glsl | tr '.' '_' | tr '[:lower:]' '[:upper:]'` + cat $glsl | ../scripts/wrapjs.sh $name + echo +done +cd ..; + +# game + +cat src/setup.js +cat src/state.js +cat src/sounds.js +cat src/input.js +cat src/behaviors.js +cat src/ai.js +cat src/asteroids.js +cat src/asteroidsIncoming.js +cat src/bullets.js +cat src/particles.js +cat src/spaceship.js +cat src/ufo.js +cat src/ui.js +cat src/effects.js +cat src/game.js + +cat src/post.js diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/scripts/wrapjs.sh b/cookbook/src/examples/behindasteroids/behind-asteroids/scripts/wrapjs.sh new file mode 100755 index 0000000..4f1f704 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/scripts/wrapjs.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# usage: cat glslfile | ./wrapjs.sh varname + +echo -n "var $1 ='" +perl -p -e 's/\n/\\n/'; +echo -ne "';" diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/ai.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/ai.js new file mode 100755 index 0000000..0fca967 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/ai.js @@ -0,0 +1,328 @@ +/* global +DEBUG +AIrotate: true +AIboost: true +AIshoot: true +AIexcitement: true +spaceship +t dt +asteroids +bullets +W H +dist normAngle +ufos +playingSince +ctx +*/ + +/* +if (DEBUG) { + /* eslint-disable no-inner-declarations + var AIdebug = [], AIdebugCircle = []; + function drawAIDebug () { + AIdebug.forEach(function (debug, i) { + ctx.save(); + ctx.lineWidth = 2; + ctx.fillStyle = ctx.strokeStyle = "hsl("+Math.floor(360*i/AIdebug.length)+",80%,50%)"; + ctx.beginPath(); + ctx.moveTo(debug[0], debug[1]); + ctx.lineTo(debug[2], debug[3]); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(debug[0], debug[1], 2, 0, 2*Math.PI); + ctx.fill(); + ctx.restore(); + }); + AIdebugCircle.forEach(function (debug, i) { + ctx.save(); + ctx.lineWidth = 2; + ctx.fillStyle = ctx.strokeStyle = "hsl("+Math.floor(360*i/AIdebugCircle.length)+",80%,50%)"; + ctx.beginPath(); + ctx.arc(debug[0], debug[1], Math.max(0, debug[2] * debug[3]), 0, 2*Math.PI); + ctx.stroke(); + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.arc(debug[0], debug[1], debug[3], 0, 2*Math.PI); + ctx.stroke(); + ctx.textAlign = "center"; + ctx.textBaseline = "bottom"; + ctx.fillText(debug[2].toFixed(2), debug[0], debug[1]-debug[3]-2); + ctx.restore(); + }); + } + function clearDebug () { + AIdebug = []; + AIdebugCircle = []; + } + function addDebugCircle (p, value, radius) { + AIdebugCircle.push([ p[0], p[1], value, radius ]); + } + function addDebug (p, v) { + var d = 200; + AIdebug.push([ p[0], p[1], p[0]+(v?d*v[0]:0), p[1]+(v?d*v[1]:0) ]); + } + function addPolarDebug (p, ang, vel) { + var v = [ + vel * Math.cos(ang), + vel * Math.sin(ang) + ]; + addDebug(p, v); + } + /* eslint-enable +} +*/ + +var closestAsteroidMemory, targetShootMemory, closestAsteroidMemoryT, targetShootMemoryT; + +// AI states +function aiLogic (smart) { // set the 3 AI inputs (rotate, shoot, boost) + var i; + + // DEBUG && clearDebug(); + + // first part is data extraction / analysis + + //var ax = Math.cos(spaceship[4]); + //var ay = Math.sin(spaceship[4]); + var vel = Math.sqrt(spaceship[2]*spaceship[2]+spaceship[3]*spaceship[3]); + var velAng = Math.atan2(spaceship[3], spaceship[2]); + + //var spaceshipVel = [ ax * vel, ay * vel ]; + + + // utilities + + function orient (ang) { + var stableAng = normAngle(ang - spaceship[4]); + AIrotate = stableAng < 0 ? -1 : 1; + return stableAng; + } + + function move (ang, vel) { + var stableAng = normAngle(ang - spaceship[4]); + var abs = Math.abs(stableAng); + if (abs > Math.PI/2) { + if (vel) AIboost = abs>Math.PI/2-0.4 ? vel>0?-1:1 : 0; + AIrotate = stableAng > 0 ? -1 : 1; + } + else { + if (vel) AIboost = abs<0.4 ? vel<0?-1:1 : 0; + AIrotate = stableAng < 0 ? -1 : 1; + } + } + + // take actions to move and stabilize to a point + function moveToPoint (p, minDist) { + var dx = p[0]-spaceship[0]; + var dy = p[1]-spaceship[1]; + if (dx*dx+dy*dy 0.003 * dist && Math.abs(normAngle(ang - velAng)) acceptDist) return; + //DEBUG && addDebug(p, v); + moveAwayFromPoint(p, v); + } + + function predictShootIntersection (bulletVel, pos, target, targetVel) { + // http://gamedev.stackexchange.com/a/25292 + var totarget = [ + target[0] - pos[0], + target[1] - pos[1] + ]; + var a = dot(targetVel, targetVel) - bulletVel * bulletVel; + var b = 2 * dot(targetVel, totarget); + var c = dot(totarget, totarget); + var p = -b / (2 * a); + var q = Math.sqrt((b * b) - 4 * a * c) / (2 * a); + var t1 = p - q; + var t2 = p + q; + var t = t1 > t2 && t2 > 0 ? t2 : t1; + + return [t, [ + target[0] + targetVel[0] * t, + target[1] + targetVel[1] * t + ]]; + } + + var middle = [W/2,H/2]; + + var closestAsteroid, targetShoot, danger = 0; + var closestAsteroidScore = 0.3, targetShootScore = 0.1; + var incomingBullet, incomingBulletScore = 0; + + for (i = 0; i < asteroids.length; ++i) { + var ast = asteroids[i]; + // FIXME: take velocity of spaceship into account? + var v = [ + ast[3] * Math.cos(ast[2]), + ast[3] * Math.sin(ast[2]) + ]; + var timeBeforeImpact = dot([ spaceship[0]-ast[0], spaceship[1]-ast[1] ],v)/dot(v,v); + var impact = [ + ast[0] + timeBeforeImpact * v[0], + ast[1] + timeBeforeImpact * v[1] + ]; + var distToImpact = dist(spaceship, impact); + var distWithSize = distToImpact - 10 - 10 * ast[5]; + + var score = + Math.exp(-distWithSize/40) + + Math.exp(-distWithSize/120) + + timeBeforeImpact > 0 ? Math.exp(-timeBeforeImpact/1000) : 0; + + if (score > closestAsteroidScore) { + closestAsteroidScore = score; + closestAsteroid = ast; + danger ++; + } + + score = + Math.exp(-(ast[5]-1)) * + Math.exp(-distWithSize/200); + + if (score > targetShootScore) { + var res = predictShootIntersection(0.3, spaceship, ast, v); + var t = res[0]; + var p = res[1]; + if (0 incomingBulletScore) { + incomingBulletScore = score; + incomingBullet = impact; + } + } + + for (i = 0; i < ufos.length; ++i) { + var u = ufos[i]; + res = predictShootIntersection(0.3, spaceship, u, u.slice(2)); + t = res[0]; + p = res[1]; + targetShoot = p; + } + + AIexcitement = + (1 - Math.exp(-asteroids.length/10)) + // total asteroids + (1 - Math.exp(-danger/3)) // danger + ; + + // Now we implement the spaceship reaction + // From the least to the most important reactions + + // Dump random changes + + AIshoot = playingSince > 3000 && Math.random() < 0.001*dt*(1-smart); + + AIrotate = (playingSince > 1000 && Math.random()<0.002*dt) ? + (Math.random()<0.6 ? 0 : Math.random() < 0.5 ? -1 : 1) : AIrotate; + + AIboost = (playingSince > 2000 && Math.random()<0.004*dt) ? + (Math.random()<0.7 ? 0 : Math.random() < 0.5 ? -1 : 1) : AIboost; + + // Stay in center area + + if (0.1 + smart > Math.random()) moveToPoint(middle, 30); + + // Shot the target + + if (smart > Math.random()) { + if (targetShoot) { + AIshoot = + Math.abs(orient(Math.atan2( + targetShoot[1] - spaceship[1], + targetShoot[0] - spaceship[0]))) < 0.1 && + Math.random() < 0.04 * dt; + targetShootMemory = targetShoot; + targetShootMemoryT = t; + } + else { + AIshoot = 0; + } + } + + // Avoid dangers + if (smart > Math.random()) { + if (closestAsteroid) { + moveAwayFromAsteroid(closestAsteroid); + closestAsteroidMemory = closestAsteroid; + closestAsteroidMemoryT = closestAsteroid; + } + + if (incomingBullet) moveAwayFromPoint(incomingBullet); + } + + //DEBUG && targetShoot && addPolarDebug(targetShoot, 0, 0); + //DEBUG && closestAsteroid && addPolarDebug(closestAsteroid, closestAsteroid[2], closestAsteroid[3]); +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/asteroids.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/asteroids.js new file mode 100755 index 0000000..b4c0229 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/asteroids.js @@ -0,0 +1,102 @@ +/* global +ctx path W H asteroids:true rotatingLetters incPosition incRotation MOBILE play +Asend AsendFail +*/ + +// Logic + +function randomAsteroidShape (lvl) { + var n = 4 + lvl * 2; + var size = lvl * 10; + var pts = []; + for (var i = 0; i < n; ++i) { + var l = size*(0.4 + 0.6 * Math.random()); + var a = 2 * Math.PI * i / n; + pts.push([ + l * Math.cos(a), + l * Math.sin(a) + ]); + } + return pts; +} + +function randomAsteroids () { + asteroids = []; + for (var i=0; i<8; ++i) { + var lvl = Math.floor(1.5 + 3 * Math.random()); + asteroids[i] = [ + W * Math.random(), + H * Math.random(), + 2 * Math.PI * Math.random(), + 0.02 + 0.02 * Math.random(), + randomAsteroidShape(lvl), + lvl + ]; + } +} + + +function explodeAsteroid (j) { + var aster = asteroids[j]; + asteroids.splice(j, 1); + var lvl = aster[5]; + if (lvl > 1) { + var nb = Math.round(2+1.5*Math.random()); + for (var k=0; k diffMax) diffMin *= Math.random(); + + + var pRotAmp = diffMin + Math.random() * (diffMax-diffMin); + var pRotAmpRatio = diffMin + Math.random() * (diffMax-diffMin); + var pRotSpeed = diffMin + Math.random() * (diffMax-diffMin); + + var lvl = Math.floor(2 + 3 * Math.random() * Math.random() + 4 * Math.random() * Math.random() * Math.random()); + var ampRot = player<2 ? 0 : Math.PI * (0.8 * Math.random() + 0.05 * lvl) * pRotAmp; + if (ampRot < 0.2) ampRot = 0; + var ampRotRatio = + player > 2 && + ampRot > Math.exp(-player/4) && + Math.random() > 0.5 + 0.4 * ((player-3)%8)/8 - 0.5 * (1 - Math.exp(-player/10)) ? + 0.9 - 0.5 * pRotAmpRatio - 0.2 * pRotAmp : + 1; + + if (player == 2) { + ampRot = 0.2 + Math.random(); + } + + if (player == 3) { + ampRot = 0.2 + Math.random(); + ampRotRatio = 0.5 + 0.4 * Math.random(); + } + + incomingObjects.push([ + pos, + // velocity + 0.1 + 0.002 * player, + // initial angle + 2*Math.PI*Math.random(), + // initial force + 10 + 40*Math.random(), + // rot velocity + 0.002 + 0.001 * (Math.random() + 0.5 * lvl * Math.random() + Math.random() * player / 30) * pRotSpeed - 0.001 * pRotAmp, + // shape + randomAsteroidShape(lvl), + // level + lvl, + // key + key, + // amplitude rotation + ampRot, + // amplitude rotation valid ratio + ampRotRatio, + // explode time + 0 + ]); + return 1; +} + +function applyIncLogic (o) { + if (!o[10]) { + o[0] += o[1] * dt; + o[2] += o[4] * dt; + o[3] = o[3] < 10 ? 60 : o[3] - 0.02 * dt; + } +} + +// RENDERING + +function drawInc (o) { + var rotC = incRotationCenter(o); + var phase = Math.cos(o[2]); + var rot = phase * o[8] + rotC; + var w = 10 * o[6]; + var valid = Math.abs(phase) < o[9]; + + if (playingSince>0 && lifes && !dying && !o[10]) { + ctx.lineWidth = 1+o[3]/60; + ctx.strokeStyle = valid ? "#7cf" : "#f66"; + + if (o[8] > 0.1) { + ctx.save(); + ctx.rotate(rotC); + ctx.strokeStyle = "#f66"; + ctx.beginPath(); + ctx.arc(0, 0, w+10, -o[8], -o[8]*o[9]); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(0, 0, w+10, o[8]*o[9], o[8]); + ctx.stroke(); + ctx.strokeStyle = "#7cf"; + ctx.beginPath(); + ctx.arc(0, 0, w+10, -o[8] * o[9], o[8] * o[9]); + ctx.stroke(); + path([ + [w+8, 0], + [w+12, 0] + ]); + ctx.stroke(); + ctx.restore(); + } + + ctx.save(); + ctx.rotate(rot); + ctx.save(); + var mx = 60 + w; + var x = o[3] + w; + ctx.globalAlpha = 0.2; + path([ + [0,0], + [mx,0] + ]); + ctx.stroke(); + ctx.restore(); + path([ + [0,0], + [x,0] + ]); + ctx.stroke(); + var r = 6; + path([ + [ mx - r, r ], + [ mx, 0], + [ mx - r, -r ] + ], 1); + ctx.stroke(); + ctx.restore(); + } + else { + ctx.strokeStyle = o[10] ? "#f66" : "#999"; + } + + ctx.save(); + path(o[5]); + ctx.fillStyle = "#000"; + ctx.fill(); + ctx.stroke(); + ctx.restore(); + + var sum = [0, 0]; + o[5].forEach(function (p) { + sum[0] += p[0]; + sum[1] += p[1]; + }); + + if (!MOBILE && playingSince>0) { + if (helpVisible()) { + ctx.strokeStyle = "#f7c"; + } + ctx.translate(sum[0]/o[5].length+1, sum[1]/o[5].length-5); + font(String.fromCharCode(o[7]), 1); + } +} + +function drawIncHelp () { + if (!helpVisible()) return; + ctx.strokeStyle = "#f7c"; + ctx.lineWidth = 4; + incomingObjects.forEach(function (o) { + var p = incPosition(o); + ctx.beginPath(); + ctx.arc(p[0], p[1], 80 + 40 * Math.cos(0.005 * t), 0, 2*Math.PI); + ctx.stroke(); + }); +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/behaviors.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/behaviors.js new file mode 100755 index 0000000..ee35283 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/behaviors.js @@ -0,0 +1,41 @@ +/* global dt W H */ + +function euclidPhysics (obj) { + obj[0] += obj[2] * dt; + obj[1] += obj[3] * dt; +} + +function polarPhysics (obj) { + var x = Math.cos(obj[2]); + var y = Math.sin(obj[2]); + var s = dt * obj[3]; + obj[0] += s * x; + obj[1] += s * y; +} + +function destroyOutOfBox (obj, i, arr) { + if (obj[0] < -100 || obj[1] < -100 || obj[0] > W+100 || obj[1] > H+100) { + arr.splice(i, 1); + } +} + +function applyLife (obj, i, arr) { + if ((obj[4] -= dt) < 0) { + arr.splice(i, 1); + } +} + +function loopOutOfBox (obj) { + if (obj[0] < 0) { + obj[0] += W; + } + else if (obj[0] > W) { + obj[0] -= W; + } + if (obj[1] < 0) { + obj[1] += H; + } + else if (obj[1] > H) { + obj[1] -= H; + } +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/bullets.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/bullets.js new file mode 100755 index 0000000..68b2d6f --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/bullets.js @@ -0,0 +1,27 @@ +/* global +ctx bullets +*/ + +function shoot (obj, vel, ang) { + var ax = Math.cos(ang); + var ay = Math.sin(ang); + bullets.push([ + obj[0] + 14 * ax, + obj[1] + 14 * ay, + obj[2] + vel * ax, + obj[3] + vel * ay, + 1000, + 0 + ]); +} + +// RENDERING + + +function drawBullet () { + ctx.globalAlpha = 1 - Math.random()*Math.random(); + ctx.fillStyle = "#00f"; + ctx.beginPath(); + ctx.arc(0, 0, 2+2.5*Math.random(), 0, 2*Math.PI); + ctx.fill(); +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/effects.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/effects.js new file mode 100755 index 0000000..f2f0463 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/effects.js @@ -0,0 +1,139 @@ +/* global +g +gl +textureGame +smoothstep +glSetTexture +glBindFBO +glGetFBOTexture +glUniformLocation +glBindTexture +laserFbo +playerFbo +glareFbo +fbo1 fbo2 +persistenceFbo +copyShader +glBindShader +laserShader +playerShader +blur1dShader +gameShader +glareShader +persistenceShader +t +excitementSmoothed +gameOver +player +playingSince +lifes +lastLoseShot +shaking +jumping +dying +*/ + +function drawPostProcessing () { + glSetTexture(textureGame, g); + + // Laser + glBindFBO(laserFbo); + glBindShader(laserShader); + gl.uniform1i(glUniformLocation(laserShader, "t"), glBindTexture(textureGame, 0)); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + // Player / env + glBindFBO(playerFbo); + glBindShader(playerShader); + gl.uniform1f(glUniformLocation(playerShader, "pt"), playingSince / 1000); + gl.uniform1f(glUniformLocation(playerShader, "pl"), player); + gl.uniform1f(glUniformLocation(playerShader, "ex"), gameOver || excitementSmoothed); + gl.uniform1f(glUniformLocation(playerShader, "J"), jumping); + gl.uniform1f(glUniformLocation(playerShader, "P"), playingSince<0 || gameOver || dying ? 0 : 1); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(fbo1); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(playerFbo), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 2, 2 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(fbo2); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(fbo1), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), -2, 2 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(fbo1); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(fbo2), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 6, 0 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(playerFbo); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(fbo1), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 0, 2 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + // Glare + glBindFBO(glareFbo); + glBindShader(glareShader); + gl.uniform1i(glUniformLocation(glareShader, "t"), glBindTexture(glGetFBOTexture(laserFbo), 0)); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(fbo1); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(glareFbo), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 2, -4 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(glareFbo); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(fbo1), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 4, -8 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + // Blur + glBindFBO(fbo1); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(laserFbo), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 0.5, 0.5 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(fbo2); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(fbo1), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), -0.5, 0.5 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(fbo1); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(fbo2), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 1, 0 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(fbo2); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(fbo1), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 0, 1 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + // Persistence + glBindFBO(fbo1); + glBindShader(persistenceShader); + gl.uniform1i(glUniformLocation(persistenceShader, "t"), glBindTexture(glGetFBOTexture(fbo2), 0)); + gl.uniform1i(glUniformLocation(persistenceShader, "r"), glBindTexture(glGetFBOTexture(persistenceFbo), 1)); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(persistenceFbo); + glBindShader(copyShader); + gl.uniform1i(glUniformLocation(copyShader, "t"), glBindTexture(glGetFBOTexture(fbo1), 0)); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + // Final draw + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + glBindShader(gameShader); + gl.uniform1i(glUniformLocation(gameShader, "G"), glBindTexture(glGetFBOTexture(laserFbo), 0)); + gl.uniform1i(glUniformLocation(gameShader, "R"), glBindTexture(glGetFBOTexture(persistenceFbo), 1)); + gl.uniform1i(glUniformLocation(gameShader, "B"), glBindTexture(glGetFBOTexture(fbo2), 2)); + gl.uniform1i(glUniformLocation(gameShader, "L"), glBindTexture(glGetFBOTexture(glareFbo), 3)); + gl.uniform1i(glUniformLocation(gameShader, "E"), glBindTexture(glGetFBOTexture(playerFbo), 4)); + gl.uniform1f(glUniformLocation(gameShader, "s"), + !player ? smoothstep(-4000, -3000, playingSince) : 1); + gl.uniform1f(glUniformLocation(gameShader, "F"), + smoothstep(300, 0, t-lastLoseShot) + + !gameOver && lifes>4 ? 0.5 * smoothstep(-1, 1, Math.cos(0.01*t)) : 0); + gl.uniform2f(glUniformLocation(gameShader, "k"), shaking[0], shaking[1]); + gl.drawArrays(gl.TRIANGLES, 0, 6); +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/env_dev.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/env_dev.js new file mode 100755 index 0000000..1a9de2d --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/env_dev.js @@ -0,0 +1,2 @@ +var DEBUG = true; // eslint-disable-line no-unused-vars +var MOBILE = "ontouchstart" in document; // eslint-disable-line no-unused-vars diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/env_prod.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/env_prod.js new file mode 100755 index 0000000..222e5ac --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/env_prod.js @@ -0,0 +1,2 @@ +var DEBUG = false; // eslint-disable-line no-unused-vars +var MOBILE = "ontouchstart" in document; // eslint-disable-line no-unused-vars diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/game.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/game.js new file mode 100755 index 0000000..3586407 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/game.js @@ -0,0 +1,411 @@ +/* eslint-disable no-undef */ +/* eslint-enable no-unused-vars */ + +randomAsteroids(); +raf=requestAnimationFrame(render); + +if (DEBUG) { + /* eslint-disable */ +/* + // DEBUG the game over screen + setTimeout(function () { + playingSince = -1; + awaitingContinue = 0; + player = 42; + achievements = [123, 45, 6]; + gameOver = 1; + }, 1000); +*/ + // Debug the levels + addEventListener("resize", function () { + playingSince = -1; + awaitingContinue = 0; + player ++; + incomingObjects = []; + console.log("player=", player); +/* + ufos.push([ + 0, 0, 0, 0, 0 + ]); + */ + + }); + +/* + setTimeout(function () { + killSmoothed ++; + }, 100); + setTimeout(function () { + killSmoothed ++; + }, 2000); + */ + + // Debug the incomingObjects + +/* + setInterval(function () { + createInc(); + if (incomingObjects[0]) sendAsteroid(incomingObjects[0]); + incomingObjects.splice(0, 1); + }, 800); +*/ + + /* eslint-enable */ +} + +window._behindAsteroids_send = function () { + createInc(); + if (incomingObjects[0]) sendAsteroid(incomingObjects[0]); + incomingObjects.splice(0, 1); +}; + +// Game Render Loop + +var _lastT, _lastCheckSize = -9999; +function render (_t) { + raf=requestAnimationFrame(render); + if (!_lastT) _lastT = _t; + dt = Math.min(100, _t-_lastT); + _lastT = _t; + + if (t-_lastCheckSize>200) checkSize(); + + t += dt; // accumulate the game time (that is not the same as _t) + + // UPDATE game + update(); + + // RENDER game + + // Game rendering + + ctx = gameCtx; + + ctx.save(); + + drawGame(); + + ctx.restore(); + + RENDER_CB(); +} + +// Game Update Loop + +function update () { + playingSince += dt; + + if (t-ufoMusicTime>1200) { + ufoMusicTime = t; + if (ufos[0]) + play(Aufo); + } + + if(!gameOver && !awaitingContinue) { + + if (playingSince > 0 && !achievements) { + achievements = [0,0,0]; + } + + var i; + var nbSpaceshipBullets = 0; + + if (!dying && playingSince>0 && t-musicPaused>5000 && player > 2 && !ufos.length) { + + combosTarget = Math.floor(30 - 25 * Math.exp(-(player-3)/15)); + var musicFreq = 3*combos/combosTarget; + if (combos > combosTarget) { + musicPaused = t; + neverUFOs = combos = 0; + ufos.push([ + W * Math.random(), + H * Math.random(), + 0, + 0, + 0 + ]); + achievements[2] ++; + } + + musicPhase += musicFreq*2*Math.PI*dt/1000; + if ((Math.sin(musicPhase) > 0) !== musicTick) { + musicTick = !musicTick; + play(musicTick ? Amusic1 : Amusic2); + } + } + + // randomly send some asteroids + /* + if (Math.random() < 0.001 * dt) + randomInGameAsteroid(); + */ + + // player lifecycle + + if (lifes == 0 && playingSince > 0) { + // player enter + resurrectionTime = t; + lifes = 4; + player++; + score = 0; + scoreForLife = 10000; + jumpingAmp = 0; + jumpingFreq = 0; + asteroids = []; + ufos = []; + play(Acoin); + if (player > 1) { + //localStorage.ba_pl = player; + //localStorage.ba_ach = achievements; + } + } + + // inc lifecycle + + if (playingSince > 1000 && !dying) { + for (i = 0; i < incomingObjects.length; i++) { + var o = incomingObjects[i]; + if (!o[10]) { + var p = incPosition(o); + var matchingTap = tap && circleCollides(tap, p, (MOBILE ? 60 : 20) + 10 * o[6]); + if (keys[o[7]] || matchingTap) { + // send an asteroid + neverPlayed = tap = keys[o[7]] = 0; + if (sendAsteroid(o)) { + achievements[0] ++; + if (player > 3) combos ++; + incomingObjects.splice(i--, 1); + } + else { + // failed to aim (red aiming) + score += 5000; + combos = 0; + lastLoseShot = o[10] = t; + } + } + } + else { + if (t-o[10] > 1000) + incomingObjects.splice(i--, 1); + } + } + tap = 0; + + while(maybeCreateInc()); + } + + // spaceship lifecycle + + if (dying && t-dying > 2000 + (lifes>1 ? 0 : 2000)) { + dying = 0; + spaceship = [ W/2, H/2, 0, 0, 0 ]; + if (--lifes) { + resurrectionTime = t; + } + else { + // Player lost. game over + playingSince = -5000; + randomAsteroids(); + ufos = []; + setTimeout(function(){ play(Aleave); }, 1000); + } + } + + // score lifecycle + + if (score >= scoreForLife) { + lastExtraLife = t; + lifes ++; + scoreForLife += 10000; + play(Alife); + if (lifes > 5) { + gameOver = 1; + incomingObjects = []; + ufos = []; + randomAsteroids(); + //localStorage.ba_pl=0; + } + } + + if (!dying && playingSince>0 && t - lastScoreIncrement > 100) { + score += 10; + lastScoreIncrement = t; + } + best = Math.max(best, score); + + // collision + + bullets.forEach(function (bull, i) { + if (!bull[5]) nbSpaceshipBullets ++; + var j; + + if (bull[4]<900) { + // bullet-spaceship collision + if (!dying && circleCollides(bull, spaceship, 20)) { + explose(bull); + bullets.splice(i, 1); + spaceshipDie(); + return; + } + + // bullet-ufo collision + for (j = 0; j < ufos.length; ++j) { + var ufo = ufos[j]; + if (circleCollides(bull, ufo, 20)) { + explose(bull); + bullets.splice(i, 1); + ufos.splice(j, 1); + return; + } + } + } + + for (j = 0; j < asteroids.length; ++j) { + var aster = asteroids[j]; + var lvl = aster[5]; + // bullet-asteroid collision + if (circleCollides(bull, aster, 10 * lvl)) { + explose(bull); + bullets.splice(i, 1); + explodeAsteroid(j); + score += 50 * Math.floor(0.4 * (6 - lvl) * (6 - lvl)); + return; + } + } + }); + + if (!dying && playingSince > 0) asteroids.forEach(function (aster, j) { + // asteroid-spaceship collision + if (circleCollides(aster, spaceship, 10 + 10 * aster[5])) { + if (t - resurrectionTime < 200) { + // if spaceship just resurect, will explode the asteroid + explodeAsteroid(j); + } + else { + // otherwise, player die + explose(spaceship); + spaceshipDie(); + } + } + }); + + // run spaceship AI + AIexcitement = 0; + if (!dying && playingSince > 0) { + var ax = Math.cos(spaceship[4]); + var ay = Math.sin(spaceship[4]); + + // ai logic (determine the 3 inputs) + aiLogic(1-Math.exp(-(player-0.8)/14)); + + // apply ai inputs with game logic + + var rotSpeed = 0.004 + 0.003 * (1-Math.exp(-player/40)); + var accSpeed = 0.0003 - 0.0002 * Math.exp(-(player-1)/5) + 0.00001 * player; + var shotRate = 100 + 1000 * Math.exp(-(player-1)/8) + 300 * Math.exp(-player/20); + + spaceship[2] += AIboost * dt * accSpeed * ax; + spaceship[3] += AIboost * dt * accSpeed * ay; + spaceship[4] = normAngle(spaceship[4] + AIrotate * dt * rotSpeed); + if (nbSpaceshipBullets < 3) { + if (AIshoot && t-lastBulletShoot > shotRate) { + lastBulletShoot = t; + play(Ashot); + shoot(spaceship, 0.3, spaceship[4]); + } + } + } + } + + euclidPhysics(spaceship); + asteroids.forEach(polarPhysics); + ufos.forEach(euclidPhysics); + bullets.forEach(euclidPhysics); + particles.forEach(polarPhysics); + + ufos.forEach(applyUFOlogic); + incomingObjects.forEach(applyIncLogic); + + particles.forEach(applyLife); + loopOutOfBox(spaceship); + asteroids.forEach(playingSince > 0 && !awaitingContinue && !gameOver ? destroyOutOfBox : loopOutOfBox); + ufos.forEach(loopOutOfBox); + bullets.forEach(applyLife); + bullets.forEach(loopOutOfBox); + + excitementSmoothed += 0.04 * (AIexcitement - excitementSmoothed); + AIboostSmoothed += 0.04 * (AIboost - AIboostSmoothed); + + // handling jumping / shaking + killSmoothed -= dt * 0.0003 * killSmoothed; + jumpingAmpSmoothed += 0.04 * (jumpingAmp - jumpingAmpSmoothed); + jumpingFreqSmoothed += 0.04 * (jumpingFreq - jumpingFreqSmoothed); + if (killSmoothed > 1.3) { + if (jumpingAmp < 0.5) { + jumpingFreq = 1 + Math.random(); + jumpingAmp ++; + } + } + if (killSmoothed < 0.8) { + jumpingAmp = 0; + } + var prevPhase = jumpingPhase; + jumpingPhase += jumpingFreq *2*Math.PI*dt/1000; + jumping = jumpingAmpSmoothed * Math.pow(Math.cos(jumpingPhase), 2.0); + if (Math.cos(prevPhase) < 0 && 0 < Math.cos(jumpingPhase)) { + jumpingFreq = 1 + 3 * Math.random() * Math.random(); + } + if (jumpingAmp < 0.5) { + jumpingAmpSmoothed += 0.04 * (jumpingAmp - jumpingAmpSmoothed); + } + + var shake = jumpingAmp * Math.pow(smoothstep(0.2, 0.0, jumping), 0.5); + if (shake > 0.5 && t-lastJump>100) { + play(Ajump); + lastJump = t; + } + shaking = [ + 30 * shake * (Math.random()-0.5) / FW, + 30 * shake * (Math.random()-0.5) / FH + ]; +} + + +// Game DRAWING + +function drawGame () { + ctx.save(); + ctx.fillStyle = "#000"; + ctx.fillRect(0, 0, W, H); + ctx.restore(); + + renderCollection(asteroids, drawAsteroid); + renderCollection(ufos, drawUFO); + renderCollection(bullets, drawBullet); + renderCollection(particles, drawParticle); + + if (playingSince > 0 && !awaitingContinue && !gameOver) { + ctx.save(); + translateTo(spaceship); + drawSpaceship(spaceship); + ctx.restore(); + } + + drawGameUI(); + + drawGlitch(); +} + + +function translateTo (p) { + ctx.translate(p[0], p[1]); +} + +function renderCollection (coll, draw) { + for (var i=0; i0 && 1700 ? 0 : -l) : -l/2, 0); + for (var i=0; i 0 ? -.9 : 10); + _changeTime = 0; + _changeLimit = p['m'] == 1 ? 0 : (1 - p['m']) * (1 - p['m']) * 20000 + 32; + } + + // I split the r() function into two functions for better readability + this.tr = function() { + this.r(); + + // Shorter reference + var p = this._p; + + // Calculating the length is all that remained here, everything else moved somewhere + _envelopeLength0 = p['b'] * p['b'] * 100000; + _envelopeLength1 = p['c'] * p['c'] * 100000; + _envelopeLength2 = p['e'] * p['e'] * 100000 + 12; + // Full length of the volume envelop (and therefore sound) + // Make sure the length can be divided by 3 so we will not need the padding "==" after base64 encode + return ((_envelopeLength0 + _envelopeLength1 + _envelopeLength2) / 3 | 0) * 3; + } + + /** + * Writes the wave to the supplied buffer ByteArray + * @param buffer A ByteArray to write the wave to + * @return If the wave is finished + */ + this.sw = function(buffer, length) { + // Shorter reference + var p = this._p; + + // If the filters are active + var _filters = p['s'] != 1 || p['v'], + // Cutoff multiplier which adjusts the amount the wave position can move + _hpFilterCutoff = p['v'] * p['v'] * .1, + // Speed of the high-pass cutoff multiplier + _hpFilterDeltaCutoff = 1 + p['w'] * .0003, + // Cutoff multiplier which adjusts the amount the wave position can move + _lpFilterCutoff = p['s'] * p['s'] * p['s'] * .1, + // Speed of the low-pass cutoff multiplier + _lpFilterDeltaCutoff = 1 + p['t'] * .0001, + // If the low pass filter is active + _lpFilterOn = p['s'] != 1, + // masterVolume * masterVolume (for quick calculations) + _masterVolume = p['x'] * p['x'], + // Minimum frequency before stopping + _minFreqency = p['g'], + // If the phaser is active + _phaser = p['q'] || p['r'], + // Change in phase offset + _phaserDeltaOffset = p['r'] * p['r'] * p['r'] * .2, + // Phase offset for phaser effect + _phaserOffset = p['q'] * p['q'] * (p['q'] < 0 ? -1020 : 1020), + // Once the time reaches this limit, some of the iables are r + _repeatLimit = p['p'] ? ((1 - p['p']) * (1 - p['p']) * 20000 | 0) + 32 : 0, + // The punch factor (louder at begining of sustain) + _sustainPunch = p['d'], + // Amount to change the period of the wave by at the peak of the vibrato wave + _vibratoAmplitude = p['j'] / 2, + // Speed at which the vibrato phase moves + _vibratoSpeed = p['k'] * p['k'] * .01, + // The type of wave to generate + _waveType = p['a']; + + var _envelopeLength = _envelopeLength0, // Length of the current envelope stage + _envelopeOverLength0 = 1 / _envelopeLength0, // (for quick calculations) + _envelopeOverLength1 = 1 / _envelopeLength1, // (for quick calculations) + _envelopeOverLength2 = 1 / _envelopeLength2; // (for quick calculations) + + // Damping muliplier which restricts how fast the wave position can move + var _lpFilterDamping = 5 / (1 + p['u'] * p['u'] * 20) * (.01 + _lpFilterCutoff); + if (_lpFilterDamping > .8) { + _lpFilterDamping = .8; + } + _lpFilterDamping = 1 - _lpFilterDamping; + + var _finished = false, // If the sound has finished + _envelopeStage = 0, // Current stage of the envelope (attack, sustain, decay, end) + _envelopeTime = 0, // Current time through current enelope stage + _envelopeVolume = 0, // Current volume of the envelope + _hpFilterPos = 0, // Adjusted wave position after high-pass filter + _lpFilterDeltaPos = 0, // Change in low-pass wave position, as allowed by the cutoff and damping + _lpFilterOldPos, // Previous low-pass wave position + _lpFilterPos = 0, // Adjusted wave position after low-pass filter + _periodTemp, // Period modified by vibrato + _phase = 0, // Phase through the wave + _phaserInt, // Integer phaser offset, for bit maths + _phaserPos = 0, // Position through the phaser buffer + _pos, // Phase expresed as a Number from 0-1, used for fast sin approx + _repeatTime = 0, // Counter for the repeats + _sample, // Sub-sample calculated 8 times per actual sample, averaged out to get the super sample + _superSample, // Actual sample writen to the wave + _vibratoPhase = 0; // Phase through the vibrato sine wave + + // Buffer of wave values used to create the out of phase second wave + var _phaserBuffer = new Array(1024), + // Buffer of random values used to generate noise + _noiseBuffer = new Array(32); + for (var i = _phaserBuffer.length; i--; ) { + _phaserBuffer[i] = 0; + } + for (var i = _noiseBuffer.length; i--; ) { + _noiseBuffer[i] = Math.random() * 2 - 1; + } + + for (var i = 0; i < length; i++) { + if (_finished) { + return i; + } + + // Repeats every _repeatLimit times, partially rting the sound parameters + if (_repeatLimit) { + if (++_repeatTime >= _repeatLimit) { + _repeatTime = 0; + this.r(); + } + } + + // If _changeLimit is reached, shifts the pitch + if (_changeLimit) { + if (++_changeTime >= _changeLimit) { + _changeLimit = 0; + _period *= _changeAmount; + } + } + + // Acccelerate and apply slide + _slide += _deltaSlide; + _period *= _slide; + + // Checks for frequency getting too low, and stops the sound if a minFrequency was set + if (_period > _maxPeriod) { + _period = _maxPeriod; + if (_minFreqency > 0) { + _finished = true; + } + } + + _periodTemp = _period; + + // Applies the vibrato effect + if (_vibratoAmplitude > 0) { + _vibratoPhase += _vibratoSpeed; + _periodTemp *= 1 + Math.sin(_vibratoPhase) * _vibratoAmplitude; + } + + _periodTemp |= 0; + if (_periodTemp < 8) { + _periodTemp = 8; + } + + // Sweeps the square duty + if (!_waveType) { + _squareDuty += _dutySweep; + if (_squareDuty < 0) { + _squareDuty = 0; + } else if (_squareDuty > .5) { + _squareDuty = .5; + } + } + + // Moves through the different stages of the volume envelope + if (++_envelopeTime > _envelopeLength) { + _envelopeTime = 0; + + switch (++_envelopeStage) { + case 1: + _envelopeLength = _envelopeLength1; + break; + case 2: + _envelopeLength = _envelopeLength2; + } + } + + // Sets the volume based on the position in the envelope + switch (_envelopeStage) { + case 0: + _envelopeVolume = _envelopeTime * _envelopeOverLength0; + break; + case 1: + _envelopeVolume = 1 + (1 - _envelopeTime * _envelopeOverLength1) * 2 * _sustainPunch; + break; + case 2: + _envelopeVolume = 1 - _envelopeTime * _envelopeOverLength2; + break; + case 3: + _envelopeVolume = 0; + _finished = true; + } + + // Moves the phaser offset + if (_phaser) { + _phaserOffset += _phaserDeltaOffset; + _phaserInt = _phaserOffset | 0; + if (_phaserInt < 0) { + _phaserInt = -_phaserInt; + } else if (_phaserInt > 1023) { + _phaserInt = 1023; + } + } + + // Moves the high-pass filter cutoff + if (_filters && _hpFilterDeltaCutoff) { + _hpFilterCutoff *= _hpFilterDeltaCutoff; + if (_hpFilterCutoff < .00001) { + _hpFilterCutoff = .00001; + } else if (_hpFilterCutoff > .1) { + _hpFilterCutoff = .1; + } + } + + _superSample = 0; + for (var j = 8; j--; ) { + // Cycles through the period + _phase++; + if (_phase >= _periodTemp) { + _phase %= _periodTemp; + + // Generates new random noise for this period + if (_waveType == 3) { + for (var n = _noiseBuffer.length; n--; ) { + _noiseBuffer[n] = Math.random() * 2 - 1; + } + } + } + + // Gets the sample from the oscillator + switch (_waveType) { + case 0: // Square wave + _sample = ((_phase / _periodTemp) < _squareDuty) ? .5 : -.5; + break; + case 1: // Saw wave + _sample = 1 - _phase / _periodTemp * 2; + break; + case 2: // Sine wave (fast and accurate approx) + _pos = _phase / _periodTemp; + _pos = (_pos > .5 ? _pos - 1 : _pos) * 6.28318531; + _sample = 1.27323954 * _pos + .405284735 * _pos * _pos * (_pos < 0 ? 1 : -1); + _sample = .225 * ((_sample < 0 ? -1 : 1) * _sample * _sample - _sample) + _sample; + break; + case 3: // Noise + _sample = _noiseBuffer[Math.abs(_phase * 32 / _periodTemp | 0)]; + } + + // Applies the low and high pass filters + if (_filters) { + _lpFilterOldPos = _lpFilterPos; + _lpFilterCutoff *= _lpFilterDeltaCutoff; + if (_lpFilterCutoff < 0) { + _lpFilterCutoff = 0; + } else if (_lpFilterCutoff > .1) { + _lpFilterCutoff = .1; + } + + if (_lpFilterOn) { + _lpFilterDeltaPos += (_sample - _lpFilterPos) * _lpFilterCutoff; + _lpFilterDeltaPos *= _lpFilterDamping; + } else { + _lpFilterPos = _sample; + _lpFilterDeltaPos = 0; + } + + _lpFilterPos += _lpFilterDeltaPos; + + _hpFilterPos += _lpFilterPos - _lpFilterOldPos; + _hpFilterPos *= 1 - _hpFilterCutoff; + _sample = _hpFilterPos; + } + + // Applies the phaser effect + if (_phaser) { + _phaserBuffer[_phaserPos % 1024] = _sample; + _sample += _phaserBuffer[(_phaserPos - _phaserInt + 1024) % 1024]; + _phaserPos++; + } + + _superSample += _sample; + } + + // Averages out the super samples and applies volumes + _superSample *= .125 * _envelopeVolume * _masterVolume; + + // Clipping if too loud + buffer[i] = _superSample >= 1 ? 32767 : _superSample <= -1 ? -32768 : _superSample * 32767 | 0; + } + + return length; + } +} + +// Adapted from http://codebase.es/riffwave/ +var synth = new SfxrSynth(); +// Export for the Closure Compiler +function jsfxr (settings, audioCtx, cb) { + // Initialize SfxrParams + synth._p.ss(settings); + // Synthesize Wave + var envelopeFullLength = synth.tr(); + var data = new Uint8Array(((envelopeFullLength + 1) / 2 | 0) * 4 + 44); + + var used = synth.sw(new Uint16Array(data.buffer, 44), envelopeFullLength) * 2; + + var dv = new Uint32Array(data.buffer, 0, 44); + // Initialize header + dv[0] = 0x46464952; // "RIFF" + dv[1] = used + 36; // put total size here + dv[2] = 0x45564157; // "WAVE" + dv[3] = 0x20746D66; // "fmt " + dv[4] = 0x00000010; // size of the following + dv[5] = 0x00010001; // Mono: 1 channel, PCM format + dv[6] = 0x0000AC44; // 44,100 samples per second + dv[7] = 0x00015888; // byte rate: two bytes per sample + dv[8] = 0x00100002; // 16 bits per sample, aligned on every two bytes + dv[9] = 0x61746164; // "data" + dv[10] = used; // put number of samples here + + // Base64 encoding written by me, @maettig + used += 44; + var i = 0, + base64Characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', + output = 'data:audio/wav;base64,'; + for (; i < used; i += 3) + { + var a = data[i] << 16 | data[i + 1] << 8 | data[i + 2]; + output += base64Characters[a >> 18] + base64Characters[a >> 12 & 63] + base64Characters[a >> 6 & 63] + base64Characters[a & 63]; + } + + audioCtx && audioCtx.decodeAudioData(data.buffer, cb); + + return output; +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/lib/math.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/lib/math.js new file mode 100755 index 0000000..90fae32 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/lib/math.js @@ -0,0 +1,31 @@ + +// normalize radian angle between -PI and PI (assuming it is not too far) +function normAngle (a) { + return a < -Math.PI ? a + 2*Math.PI : + a>Math.PI ? a - 2*Math.PI : a; +} + +function smoothstep (min, max, value) { + var x = Math.max(0, Math.min(1, (value-min)/(max-min))); + return x*x*(3 - 2*x); +} + +function scoreTxt (s) { + return (s<=9?"0":"")+s; +} + +function dist (a, b) { + var x = a[0]-b[0]; + var y = a[1]-b[1]; + return Math.sqrt(x * x + y * y); +} + +function length (v) { + return Math.sqrt(v[0]*v[0]+v[1]*v[1]); +} + +function circleCollides (a, b, r) { + var x = a[0] - b[0]; + var y = a[1] - b[1]; + return x*x+y*y < r*r; +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/lib/path.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/lib/path.js new file mode 100755 index 0000000..cca2f51 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/lib/path.js @@ -0,0 +1,16 @@ +/* global ctx */ + +function path (pts, noclose) { // eslint-disable-line no-unused-vars + ctx.beginPath(); + var mv = 1; + for (var i = 0; pts && i4 ? 0.5 * smoothstep(-1, 1, Math.cos(0.01*t)) : 0; + var k = [ shaking[0], shaking[1] ]; + return { + pt: pt, + pl: pl, + ex: ex, + J: J, + P: P, + s: s, + F: F, + k: k, + W: W, + H: H, + S: SEED + }; + } +}; + +}); diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/pre.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/pre.js new file mode 100755 index 0000000..40ae31d --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/pre.js @@ -0,0 +1,3 @@ +/* eslint-disable */ +module.exports = (function(d,g,RENDER_CB){ + var raf; diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/setup.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/setup.js new file mode 100755 index 0000000..6ea97cc --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/setup.js @@ -0,0 +1,37 @@ +/* global g d MOBILE +gameScale: true +glCreateFBO glCreateShader glCreateTexture glUniformLocation +STATIC_VERT +BLUR1D_FRAG +COPY_FRAG +GAME_FRAG +GLARE_FRAG +LASER_FRAG +PERSISTENCE_FRAG +PLAYER_FRAG +*/ + +var ctx, + gameCtx = g.getContext("2d"), + FW = MOBILE ? 480 : 800, + FH = MOBILE ? 660 : 680, + GAME_MARGIN = MOBILE ? 50 : 120, + GAME_Y_MARGIN = MOBILE ? 140 : GAME_MARGIN, + GAME_INC_PADDING = MOBILE ? 40 : 80, + W = FW - 2 * GAME_MARGIN, + H = FH - 2 * GAME_Y_MARGIN, + borderLength = 2*(W+H+2*GAME_INC_PADDING), + SEED = Math.random(); + +// DOM setup + +d.style.webkitTransformOrigin = d.style.transformOrigin = "0 0"; + +g.width = W; +g.height = H; + +var uiScale = 1; + +function checkSize () { + +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/blur1d.frag b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/blur1d.frag new file mode 100755 index 0000000..10fd4a1 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/blur1d.frag @@ -0,0 +1,18 @@ +precision highp float; + +varying vec2 uv; +uniform sampler2D t; +uniform vec2 dim; +uniform vec2 dir; + +void main() { + vec4 color = vec4(0.0); + vec2 off1 = vec2(1.3846153846) * dir; + vec2 off2 = vec2(3.2307692308) * dir; + color += texture2D(t, uv) * 0.2270270270; + color += texture2D(t, uv + (off1 / dim)) * 0.3162162162; + color += texture2D(t, uv - (off1 / dim)) * 0.3162162162; + color += texture2D(t, uv + (off2 / dim)) * 0.0702702703; + color += texture2D(t, uv - (off2 / dim)) * 0.0702702703; + gl_FragColor = color; +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/copy.frag b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/copy.frag new file mode 100755 index 0000000..e6ab7f2 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/copy.frag @@ -0,0 +1,8 @@ +precision highp float; + +varying vec2 uv; +uniform sampler2D t; + +void main() { + gl_FragColor = texture2D(t, uv); +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/game.frag b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/game.frag new file mode 100755 index 0000000..1f8e95e --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/game.frag @@ -0,0 +1,46 @@ +precision highp float; + +varying vec2 uv; +uniform sampler2D G; // game +uniform sampler2D R; // persistence +uniform sampler2D B; // blur +uniform sampler2D L; // glare +uniform sampler2D E; // env (player) +uniform float s; // starting +uniform float F; // fail factor (red effect) +uniform vec2 k; + +float squircleDist (vec2 a, vec2 b) { + float p = 10.0; + vec2 c = a-b; + return pow(abs(pow(abs(c.x), p)+pow(abs(c.y), p)), 1.0/p); +} + +void main() { + vec2 UV = uv + k; + vec2 pos = (UV/0.98)-0.01; + float d = squircleDist(UV, vec2(0.5)); + float dd = smoothstep(0.45, 0.51, d); + pos = mix(pos, vec2(0.5), 0.2 * (0.6 - d) - 0.02 * d); + + vec3 gc = texture2D(G, pos).rgb; + + gl_FragColor = + step(0.0, UV.x) * + step(UV.x, 1.0) * + step(0.0, UV.y) * + step(UV.y, 1.0) * + vec4(( + vec3(0.03 + 0.1 * F, 0.04, 0.05) + + mix(vec3(0.05, 0.1, 0.15) - gc, 2.0 * gc, s) + + s * ( + texture2D(L, pos).rgb + + vec3(0.3 + F, 0.6, 1.0) * ( + texture2D(R, pos).rgb + + 3.0 * texture2D(B, pos).rgb + ) + + 0.5 * texture2D(E, pos).rgb + ) + ) + * mix(1.0, smoothstep(1.0, 0.0, dd), 0.6), 1.0); +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/glare.frag b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/glare.frag new file mode 100755 index 0000000..72b91e7 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/glare.frag @@ -0,0 +1,8 @@ +precision highp float; + +varying vec2 uv; +uniform sampler2D t; + +void main() { + gl_FragColor = vec4(step(0.9, texture2D(t, uv).r)); +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/laser.frag b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/laser.frag new file mode 100755 index 0000000..0badd4d --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/laser.frag @@ -0,0 +1,15 @@ +precision highp float; + +varying vec2 uv; +uniform sampler2D t; + +void main() { + vec3 c = texture2D(t, uv).rgb; + vec2 off = 0.003 * vec2( + cos(47.0 * uv.y), + sin(67.0 * uv.x) + ); + gl_FragColor = vec4( + c.r + c.g + c.b + texture2D(t, uv+off).b + ); +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/persistence.frag b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/persistence.frag new file mode 100755 index 0000000..f4305b3 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/persistence.frag @@ -0,0 +1,13 @@ +precision highp float; + +varying vec2 uv; +uniform sampler2D t; +uniform sampler2D r; + +void main() { + vec3 b = texture2D(r, uv).rgb; + gl_FragColor = vec4( + b * (0.82 - 0.3 * b.r * b.r) + + texture2D(t, uv).rgb, + 1.0); +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/player.frag b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/player.frag new file mode 100755 index 0000000..cbbc54e --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/player.frag @@ -0,0 +1,107 @@ +precision highp float; + +varying vec2 uv; + +uniform float pt; // playing since time +uniform float pl; // player number +uniform float S; // Seed +uniform float ex; // excitement +uniform float J; // jump +uniform float P; // playing + +float disc (vec2 c, vec2 r) { + return step(length((uv - c) / r), 1.0); +} +float squircle (vec2 c, vec2 r, float p) { + vec2 v = (uv - c) / r; + return step(pow(abs(v.x), p) + pow(abs(v.y), p), 1.0); +} + +vec3 env () { + return 0.1 + + 0.3 * vec3(1.0, 0.9, 0.7) * smoothstep(0.4, 0.1, distance(uv, vec2(0.2, 1.2))) + + 0.4 * vec3(0.8, 0.6, 1.0) * smoothstep(0.5, 0.2, distance(uv, vec2(1.3, 0.7))); +} + +vec4 player (float p, float dx) { + vec4 c = vec4(0.0); + + vec2 e = vec2( + min(ex, 1.0), + mix(min(ex, 1.0), min(ex-1.0, 1.0), 0.5)); + + // variable params + vec4 skin = 0.2 + 0.4 * pow(abs(cos(4.*p+S)), 2.0) * vec4(1.0, 0.7, 0.3, 1.0); + vec4 hair = vec4(0.5, 0.3, 0.3, 1.0); + vec4 sweater = vec4( + 0.3 * (1.0 + cos(3.*p + 6.*S)), + 0.2 * (1.0 + cos(7.*p + 7.*S)), + 0.1+0.2 * (1.0 + sin(7.*p + 8.*S)), + 1.0); + float feminity = step(sin(9.0*p+S), 0.0); + float hairSize = 0.02 + 0.02 * feminity * cos(p+S); + float walk = step(dx, -0.01) + step(0.01, dx); + float play = (1.0 - walk) * step(0.0, pt); + vec2 pos = vec2(0.5) + + // jumping cycle + J * vec2(0.0, 0.2) + + // walking cycle + walk * vec2( + 0.03 * cos(4.0*pt + sin(pt)), + 0.05 * abs(sin(3.0*pt))) + + // playing cycle + e * play * (1.0 - P) * vec2( + 0.05 * cos(pt * (1.0 + 0.1 * sin(pt))), + 0.05 * abs(sin(pt))); + vec2 pos2 = mix(pos, vec2(0.5), 0.5); + pos.x += dx; + pos2.x += dx; + + // face skin + c += skin * disc(pos, vec2(0.06, 0.1)); + // mouth + c *= 1.0 - (0.5 + 0.5 * feminity) * disc(pos - vec2(0.0, 0.04), vec2(0.03, 0.01)); + // left eye + c *= 1.0 - disc(pos + vec2(0.03, 0.03), vec2(0.02, 0.01)); + // right eye + c *= 1.0 - disc(pos + vec2(-0.03, 0.03), vec2(0.02, 0.01)); + // nose + c *= 1.0 - 0.6 * disc(pos, vec2(0.01, 0.02)); + // hair (also contrib to face skin color) + c += hair * disc(pos + vec2(0.0, hairSize), vec2(0.07, 0.1 + hairSize)); + // left hand + c += play * (hair + skin) * disc(pos2 - vec2( + -0.2 + 0.01 * cos(5.0*pt), + 0.45 - 0.1 * e.y * step(0.0, pt) * P * pow(abs(sin(8.0 * pt * (1.0 + 0.2 * cos(pt)))), 4.0) + ), vec2(0.055, 0.05)); + // right hand + c += play * (hair + skin) * disc(pos2 - vec2( + 0.2 + 0.01 * cos(5.0*pt), + 0.45 - 0.1 * e.x * step(2.0, pt) * P * pow(abs(cos(7.0 * pt)), 4.0) + ), vec2(0.055, 0.05)); + // neck + c += step(c.a, 0.0) * (hair + skin) * + squircle(pos - vec2(0.0, 0.10 + 0.02 * feminity), + vec2(0.05 - 0.01 * feminity, 0.03), 4.0); + // sweater + vec2 sr = vec2( + 0.16 + 0.04 * sin(9.*p), + 0.27 + 0.02 * cos(9.*p)); + c += step(c.r+c.g+c.b, 0.0) * sweater * step(1.0, + squircle(pos - vec2(0.0, 0.35), sr * (1.0 - 0.1 * feminity), 4.0) + + disc(pos - vec2(0.0, 0.35), sr)); + return c; +} + +void main() { + float light = 0.6 + 0.4 * smoothstep(2.0, 0.0, distance(pt, -2.0)); + vec4 c = vec4(0.0); + // main player + c += (1.0 - smoothstep(-0.0, -5.0, pt)) * + player(pl+step(pt, 0.0), -0.6 * smoothstep(-1., -5., pt)); + // prev player + c += step (1.0, pl) * + player(pl+step(pt, 0.0)-1.0, 2.0 *smoothstep(-4., -1., pt)); + c *= 1.0 - 1.3 * distance(uv, vec2(0.5)); + gl_FragColor = vec4(light * mix(env(), c.rgb, clamp(c.a, 0.0, 1.0)), 1.0); +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/static.vert b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/static.vert new file mode 100755 index 0000000..f718f47 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/shaders/static.vert @@ -0,0 +1,7 @@ +attribute vec2 p; +varying vec2 uv; + +void main() { + gl_Position = vec4(p,0.0,1.0); + uv = 0.5 * (p+1.0); +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/sounds.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/sounds.js new file mode 100755 index 0000000..a2e5b8d --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/sounds.js @@ -0,0 +1,20 @@ + /* global audio */ + +var Ashot = audio([0,0.06,0.18,,0.33,0.5,0.23,-0.04,-0.24,,,-0.02,,0.37,-0.22,,,,0.8,,,,,0.3]), + + Amusic1 = audio([,,0.12,,0.13,0.16,,,,,,,,,,,,,0.7,,,,,0.5]), + Amusic2 = audio([,,0.12,,0.13,0.165,,,,,,,,,,,,,0.7,,,,,0.5]), + + Aexplosion1 = audio([3,,0.35,0.5369,0.5,0.15,,-0.02,,,,-0.7444,0.78,,,0.7619,,,0.1,,,,,0.5]), + Aexplosion2 = audio([3,,0.38,0.5369,0.52,0.18,,-0.02,,,,-0.7444,0.78,,,0.7619,,,0.1,,,,,0.5]), + + Asend = audio([2,0.07,0.04,,0.24,0.25,,0.34,-0.1999,,,-0.02,,0.3187,,,-0.14,0.04,0.85,,0.28,0.63,,0.5]), + AsendFail = audio([1,,0.04,,0.45,0.14,0.06,-0.06,0.02,0.87,0.95,-0.02,,0.319,,,-0.14,0.04,0.5,,,,,0.4]), + + Alost = audio([0,0.11,0.37,,0.92,0.15,,-0.06,-0.04,0.3,0.14,0.1,,0.5047,,,,,0.16,-0.02,,0.5,,1]), + Aleave = audio([0,0.11,0.36,,0.66,0.19,,0.06,-0.06,0.05,0.8,-0.12,0.3,0.19,-0.06,,,-0.02,0.23,-0.02,,0.4,,0.4]), + Acoin = audio([0,,0.094,0.29,0.42,0.563,,,,,,0.4399,0.5658,,,,,,1,,,,,0.5]), + Amsg = audio([2,0.07,0.1,,0.2,0.75,0.35,-0.1,0.12,,,-0.02,,,,,-0.06,-0.0377,0.26,,,0.8,,0.7]), + Aufo = audio([2,0.05,0.74,,0.33,0.5,,,,0.46,0.29,,,,,,,,1,,,,,0.3]), + Alife = audio([0,0.12,0.8,0.48,0.77,0.92,,-0.12,-0.0999,,,-0.4,0.2,0.34,,0.65,,,0.93,-0.02,,,,0.38]), + Ajump = audio([3,,0.12,0.56,0.27,0.07,,-0.12,0.02,,,-0.02,0.68,,,,-0.04,-0.022,0.06,,,0.06,,0.5]); diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/spaceship.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/spaceship.js new file mode 100755 index 0000000..f9d592d --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/spaceship.js @@ -0,0 +1,96 @@ +/* global +ctx t path lifes play Alost AIboostSmoothed dying:true deads:true achievements killSmoothed:true +*/ + +function spaceshipDie() { + if (dying) return; + dying = t; + if (lifes == 1) { + play(Alost); + } + deads ++; + achievements[1] ++; + killSmoothed ++; +} + +/* +function resetSpaceship () { + var x = W * (0.25 + 0.5 * Math.random()); + var y = H * (0.25 + 0.5 * Math.random()); + spaceship = [x, y, 0, 0]; +} +*/ + +// RENDERING + +function drawSpaceship (o) { + ctx.strokeStyle = "#f00"; + ctx.globalAlpha = 0.4; + ctx.rotate(o[4]); + if (dying) { + ctx.lineWidth = 2; + var delta = (t-dying)/200; + + path([ + [-6, -6 - 0.5*delta], + [3, -3 - 0.9*delta] + ]); + ctx.stroke(); + + if (delta < 8) { + path([ + [3 + 0.4*delta, -3 - 0.8*delta], + [12 + 0.4*delta, 0 - 0.5*delta] + ]); + ctx.stroke(); + } + + path([ + [12, 0+0.4*delta], + [3, 3+delta] + ]); + ctx.stroke(); + + if (delta < 9) { + path([ + [1, 5 + delta], + [-6, 6 + delta] + ]); + ctx.stroke(); + } + + if (delta < 7) { + path([ + [-6 - delta, -6], + [-6 - delta, 6] + ]); + ctx.stroke(); + } + } + else { + path([ + [-6, -6], + [ 12, 0], + [ -6, 6], + [ -5, 0] + ]); + ctx.stroke(); + if (AIboostSmoothed>0.2) { + path([ + [-7, 2*Math.random()-1], + [-7 - 5*AIboostSmoothed, 4*Math.random()-2] + ]); + ctx.stroke(); + } + if (AIboostSmoothed<-0.2) { + path([ + [2, -5], + [2 - 5 * AIboostSmoothed, -7], + , + [2, 5], + [2 - 5 * AIboostSmoothed, 7] + ]); + ctx.stroke(); + } + } +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/state.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/state.js new file mode 100755 index 0000000..fdb3f7d --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/state.js @@ -0,0 +1,69 @@ +/* global W H */ + +// N.B: constants don't live here + +var t = 0, dt, + + spaceship = [ W/2, H/2, 0, 0, 0 ], // [x, y, velx, vely, rot] + asteroids = [], // array of [x, y, rot, vel, shape, lvl] + ufos = [], // array of [x, y, vx, vy, timeBeforeShot] + bullets = [], // array of [x, y, velx, vely, life, isAlien] + incomingObjects = [], // array of: [pos, vel, ang, force, rotVel, shape, lvl, key, rotAmp, rotAmpValid, explodeTime] + particles = [], // array of [x, y, rot, vel, life] + + dying = 0, + resurrectionTime = 0, + best = 0, + score = 0, // current asteroids player score + scoreForLife, // will track the next score to win a life (10000, 20000, ...) + playingSince = -10000, + deads = 0, + player = 0, + lifes = 0, + + AIshoot = 0, AIboost = 0, AIrotate = 0, AIexcitement = 0, + AIboostSmoothed = 0, + + shaking = [0,0], + jumping = 0, + jumpingFreq = 0, + jumpingPhase = 0, + jumpingFreqSmoothed = 0, + jumpingAmp = 0, + jumpingAmpSmoothed = 0, + killSmoothed = 0, + + musicPhase = 0, + musicTick = 0, + musicPaused = 0, + ufoMusicTime = 0, + + excitementSmoothed = 0, + neverPlayed = 1, + neverUFOs = 1, + combos = 0, + combosTarget, + gameOver, + awaitingContinue = 0,//localStorage.ba_pl && parseInt(localStorage.ba_pl), + // achievements: [nbAsteroids, nbKills, nbUfos] + achievements, + + lastScoreIncrement = 0, + lastJump = 0, + lastBulletShoot = 0, + lastExtraLife = 0, + lastLoseShot = 0, + + // Input state : updated by user events, handled & emptied by the update loop + keys = {}, + tap, + + // variables related to setup + gameScale; + + +function helpVisible () { + return neverPlayed && + incomingObjects[0] && + playingSince>8000; +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/target.html b/cookbook/src/examples/behindasteroids/behind-asteroids/src/target.html new file mode 100755 index 0000000..f6f9ae7 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/target.html @@ -0,0 +1,29 @@ + + +Behind Asteroids + + + + + + + + +
+ + + diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/ufo.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/ufo.js new file mode 100755 index 0000000..149fbe2 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/ufo.js @@ -0,0 +1,56 @@ +/* global +dt dying spaceship shoot ctx path +*/ + +function applyUFOlogic (o) { + o[4] -= dt; + if (o[4]<0) { + o[4] = 500 + 300 * Math.random(); + if (!dying) { + var target = Math.atan2(spaceship[1] - o[1], spaceship[0] - o[0]); + if (!o[2] || Math.random()<0.2) { + var randomAngle = 2*Math.PI*Math.random(); + o[2] = 0.08 * Math.cos(randomAngle); + o[3] = 0.08 * Math.sin(randomAngle); + } + shoot(o, 0.3+0.1*Math.random(), target + 0.6 * Math.random() - 0.3); + } + } +} + +// RENDERING + +var UFOa = [ + [8,0], + [7,5], + [0,9], + [7,14] +]; +var UFOb = [ + [15,14], + [22,9], + [15,5], + [14,0] +]; + +var UFO = + UFOa + .concat(UFOb) + .concat(UFOa) + .concat([,]) + .concat(UFOb) + .concat([ + , + [7,5], + [15,5], + , + [0,9], + [22,9] + ]); + +function drawUFO () { + ctx.globalAlpha = 0.4; + ctx.strokeStyle = "#f00"; + path(UFO); + ctx.stroke(); +} diff --git a/cookbook/src/examples/behindasteroids/behind-asteroids/src/ui.js b/cookbook/src/examples/behindasteroids/behind-asteroids/src/ui.js new file mode 100755 index 0000000..cca4f1f --- /dev/null +++ b/cookbook/src/examples/behindasteroids/behind-asteroids/src/ui.js @@ -0,0 +1,430 @@ +/* global +ctx path font gameOver W H player playingSince:true awaitingContinue scoreTxt +lifes dying MOBILE best score t UFO neverPlayed lastExtraLife neverUFOs dt play +Amsg GAME_MARGIN FW FH combos achievements musicTick helpVisible */ + +// IN GAME UI + +function button (t1, t2) { + ctx.globalAlpha = 1; + path([ + [0, 0], + [160, 0], + [160, 120], + [0, 120] + ]); + ctx.translate(80, 30); + ctx.stroke(); + ctx.fillStyle = "#000"; + ctx.fill(); + ctx.save(); + font(t1, 2); + ctx.restore(); + ctx.save(); + ctx.translate(0, 40); + font(t2, 2); + ctx.restore(); +} + +function drawGameUI () { + ctx.save(); + ctx.fillStyle = ctx.strokeStyle = "#0f0"; + ctx.globalAlpha = 0.3; + + if (gameOver) { + ctx.save(); + ctx.strokeStyle = "#0f0"; + ctx.globalAlpha = 0.3; + ctx.save(); + ctx.translate((W-340)/2, H/8); + font("YOU EARNED ", 2, 1); + ctx.globalAlpha = 0.5; + font((player*25)+"Ā¢", 2, 1); + ctx.restore(); + ctx.save(); + ctx.translate(W/2, H/4); + font("FROM "+player+" PLAYERS", 2); + ctx.restore(); + ctx.save(); + ctx.globalAlpha = 0.5; + ctx.translate((W-200)/2, H/2); + drawAchievements(2); + ctx.restore(); + + ctx.save(); + ctx.translate(W/2 - 180, H - 160); + button("TWEET", "SCORE"); + ctx.restore(); + + ctx.save(); + ctx.translate(W/2 + 20, H - 160); + button("PLAY", "AGAIN"); + ctx.restore(); + + ctx.restore(); + } + else if (playingSince < 0 || awaitingContinue) { + ctx.save(); + ctx.translate(W-50, 20); + font(scoreTxt(0), 1.5, -1); + ctx.restore(); + + ctx.save(); + ctx.translate(50, 70); + if ((!awaitingContinue || playingSince>0) && t%1000<500) + font("PLAYER "+(awaitingContinue||player+1), 2, 1); + ctx.restore(); + + ctx.save(); + ctx.translate(W/2 - 160, 0.7*H); + path([ + [0,2], + [0,18] + ]); + ctx.stroke(); + ctx.translate(40,0); + font("COIN", 2, 1); + ctx.translate(40,0); + path([ + [0,2], + [0,18] + ]); + ctx.stroke(); + ctx.translate(40,0); + font("PLAY", 2, 1); + ctx.restore(); + } + else { + for (var i=1; i 0) { + ctx.save(); + ctx.globalAlpha = 1; + ctx.translate(W/2, 140); + font("CONTINUE ?", 3); + ctx.restore(); + ctx.save(); + ctx.globalAlpha = 1; + ctx.translate(W/4, 210); + font("YES", MOBILE ? 4 : 6); + ctx.restore(); + ctx.save(); + ctx.globalAlpha = 1; + ctx.translate(3*W/4, 210); + font("NO", MOBILE ? 4 : 6); + ctx.restore(); + } + ctx.save(); + ctx.translate(W/2, H-14); + font("2015 GREWEB INC", .6); + ctx.restore(); + + if (!gameOver) { + ctx.save(); + ctx.translate(W/2, 20); + font(scoreTxt(best), .6); + ctx.restore(); + + ctx.save(); + ctx.translate(50, 20); + font(scoreTxt(score), 1.5, 1); + ctx.restore(); + } + + if (gameOver || playingSince<0 && t%1000<800) { + ctx.save(); + ctx.translate(W-20, H-24); + font(MOBILE ? "MOBILE" : "DESKTOP", .6, -1); + ctx.restore(); + ctx.save(); + ctx.translate(W-20, H-14); + font("VERSION", .6, -1); + ctx.restore(); + } + + ctx.restore(); +} + +function drawGlitch () { + ctx.save(); + ctx.fillStyle = + ctx.strokeStyle = "#f00"; + ctx.globalAlpha = 0.03; + ctx.translate(W/2, H/2); + ctx.beginPath(); + ctx.arc(0, 0, 4, 0, 2*Math.PI); + ctx.fill(); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(0, 0, 12, 0, 2*Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(0, 0, 12, 4, 6); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(0, 0, 12, 1, 2); + ctx.stroke(); + ctx.restore(); +} + + +// EXTERNAL UI + +var badgesIcons = [ + [ + [-11, -11], + [4, -13], + [6, -6], + [14, 0], + [14, 8], + [6, 8], + [-6, 14], + [-14, 0] + ], + [ + [-8, 13], + [0, -13], + [8, 13], + [0, 11], + [-8, 13], + , + [-10, -2], + [10, 2], + , + [10, -2], + [-10, 2], + , + ], + UFO.map(function (p) { + return p ? [p[0]-11,p[1]-7] : p; + }) +]; + +var lastStatement, lastStatementTime = 0; + +var lastMessage2; + +function drawUI () { + var currentMessage = "", + currentMessage2 = "", + currentMessageClr = "#f7c", + currentMessageClr2 = "#7fc"; + + function announcePlayer (player) { + currentMessage = "PLAYER "+player; + currentMessage2 = [ + "GENIOUS PLAYER!!", + "EXPERIENCED PLAYER!!", + "GOOD PLAYER. GET READY", + "NICE PLAYER.", + "BEGINNER.", + "VERY BEGINNER. EASY KILL" + ][Math.floor(Math.exp((-player)/8)*6)]; + } + + if (gameOver) { + currentMessage = "PLAYER MASTERED THE GAME"; + currentMessage2 = "REACHED ᐃᐃᐃᐃᐃ"; + } + else if (!player) { + if (playingSince<-7000) { + currentMessage = "BEHIND ASTEROIDS"; + currentMessage2 = "THE DARK SIDE"; + } + else if (playingSince<-3500) { + currentMessageClr = currentMessageClr2 = "#7cf"; + currentMessage = "SEND ASTEROIDS TO MAKE"; + currentMessage2 = "PLAYERS WASTE THEIR MONEY"; + } + else if (!awaitingContinue) { + var nb = Math.min(25, Math.floor((playingSince+3500)/80)); + for (var i=0; i-2000) + currentMessage2 = "A NEW PLAYER!"; + } + else { + if (playingSince<0) playingSince = 0; // jump to skip the "player coming" + announcePlayer(awaitingContinue); + } + } + else if (dying) { + if (lifes==1) { + currentMessageClr2 = "#f66"; + currentMessage = "GOOD JOB !!!"; + currentMessage2 = "THE DUDE IS BROKE"; + } + else if (lifes==2) { + currentMessageClr2 = "#f66"; + currentMessage = "OK..."; + currentMessage2 = "ONE MORE TIME !"; + } + else { + if (lastStatement && t - lastStatementTime > 3000) { // lastStatementTime is not used here + currentMessage = lastStatement; + } + else { + currentMessage = ["!!!", "GREAT!", "COOL!", "OMG!", "AHAH!", "RUDE!", "EPIC!", "WICKED!", "SHAME!", "HEHEHE!", "BWAHAHA!"]; + lastStatement = currentMessage = currentMessage[Math.floor(Math.random() * currentMessage.length)]; + lastStatementTime = 0; + } + } + } + else { + if (playingSince<0) { + currentMessage = "INCOMING NEW PLAYER..."; + currentMessage2 = "25Ā¢ 25Ā¢ 25Ā¢ 25Ā¢ 25Ā¢"; + } + else if (playingSince<6000 && lifes==4) { + announcePlayer(player); + } + else { + currentMessageClr2 = "#f66"; + if (lastStatement && t - lastStatementTime < 3000) { + currentMessage2 = lastStatement; + } + else { + if (neverPlayed) { + if (helpVisible()) { + currentMessageClr = currentMessageClr2 = "#f7c"; + currentMessage = MOBILE ? "TAP ON ASTEROIDS" : "PRESS ASTEROIDS LETTER"; + currentMessage2 = "TO SEND THEM TO THE GAME"; + } + } + else if (lifes > 4 && t - lastExtraLife > 5000) { + currentMessageClr = currentMessageClr2 = "#f66"; + currentMessage = "DON'T LET PLAYER"; + currentMessage2 = "REACH ᐃᐃᐃᐃᐃ !!!"; + } + else if (score > 10000 && t - lastExtraLife < 4500) { + currentMessageClr = currentMessageClr2 = "#f66"; + currentMessage = "OH NO! PLAYER JUST"; + currentMessage2 = "WON AN EXTRA LIFE!"; + } + else if (player==2 && 5000 5) { + lastStatement = 0; + if (Math.random() < 0.0001 * dt && t - lastStatementTime > 8000) { + currentMessage2 = [ + "COME ON! KILL IT!", + "JUST DO IT!", + "I WANT ¢¢¢", + "GIVE ME SOME ¢¢¢", + "DO IT!", + "DESTROY IT!" + ]; + lastStatement = currentMessage2 = currentMessage2[Math.floor(Math.random() * currentMessage2.length)]; + lastStatementTime = t; + } + } + } + } + } + + if (currentMessage2 && lastMessage2 !== currentMessage2 && + (currentMessageClr2 == "#f66" || currentMessageClr2 == "#f7c")) { + play(Amsg); + } + + ctx.save(); + ctx.translate(GAME_MARGIN, MOBILE ? 40 : 2); + ctx.lineWidth = (t%600>300) ? 2 : 1; + ctx.save(); + ctx.strokeStyle = currentMessageClr; + font(currentMessage, MOBILE ? 1.5 : 2, 1); + ctx.restore(); + ctx.save(); + ctx.strokeStyle = currentMessageClr2; + ctx.translate(0, MOBILE ? 30 : 40); + font(lastMessage2 = currentMessage2, MOBILE ? 1.5 : 2, 1); + ctx.restore(); + ctx.restore(); + + if (gameOver) return; + + ctx.save(); + ctx.translate(FW - GAME_MARGIN, 2); + ctx.lineWidth = 2; + ctx.strokeStyle = "#7cf"; + font(((playingSince>0&&awaitingContinue||player)*25)+"Ā¢", 2, -1); + ctx.restore(); + + + ctx.save(); + ctx.globalAlpha = musicTick ? 1 : 0.6; + ctx.strokeStyle = "#7cf"; + ctx.translate(FW - GAME_MARGIN, FH - 30); + if (combos) font(combos+"x", 1.5, -1); + ctx.restore(); + + /* + if (combos && combosTarget-combos < 9) { + ctx.save(); + ctx.strokeStyle = "#7cf"; + ctx.globalAlpha = musicTick ? 1 : 0.5; + ctx.translate(FW - GAME_MARGIN, FH - 50); + font((1+combosTarget-combos)+" ", 1, -1); + ctx.translate(0, 0); + path(UFO); + ctx.stroke(); + ctx.restore(); + } + */ + + if (achievements) { + ctx.save(); + ctx.translate(GAME_MARGIN + 50, FH - 20); + ctx.strokeStyle = "#fc7"; + drawAchievements(1); + ctx.restore(); + } +} + +function drawAchievements (fontSize) { + for (var j = 0; j < 3; j++) { + var badge = achievements[j]; + if (badge) { + ctx.save(); + ctx.translate(100 * j, 0); + path(badgesIcons[j]); + ctx.stroke(); + ctx.translate(0, -20 - 10 * fontSize); + font(""+badge, fontSize); + ctx.restore(); + } + } +} diff --git a/cookbook/src/examples/behindasteroids/build.js b/cookbook/src/examples/behindasteroids/build.js new file mode 100644 index 0000000..7e6ac19 --- /dev/null +++ b/cookbook/src/examples/behindasteroids/build.js @@ -0,0 +1,3180 @@ +/* eslint-disable */ +module.exports = (function(d,g,RENDER_CB){ + var raf; +var DEBUG = true; // eslint-disable-line no-unused-vars +var MOBILE = "ontouchstart" in document; // eslint-disable-line no-unused-vars + +// normalize radian angle between -PI and PI (assuming it is not too far) +function normAngle (a) { + return a < -Math.PI ? a + 2*Math.PI : + a>Math.PI ? a - 2*Math.PI : a; +} + +function smoothstep (min, max, value) { + var x = Math.max(0, Math.min(1, (value-min)/(max-min))); + return x*x*(3 - 2*x); +} + +function scoreTxt (s) { + return (s<=9?"0":"")+s; +} + +function dist (a, b) { + var x = a[0]-b[0]; + var y = a[1]-b[1]; + return Math.sqrt(x * x + y * y); +} + +function length (v) { + return Math.sqrt(v[0]*v[0]+v[1]*v[1]); +} + +function circleCollides (a, b, r) { + var x = a[0] - b[0]; + var y = a[1] - b[1]; + return x*x+y*y < r*r; +} +/* global ctx */ + +function path (pts, noclose) { // eslint-disable-line no-unused-vars + ctx.beginPath(); + var mv = 1; + for (var i = 0; pts && i0 ? 0 : -l) : -l/2, 0); + for (var i=0; i 0 ? -.9 : 10); + _changeTime = 0; + _changeLimit = p['m'] == 1 ? 0 : (1 - p['m']) * (1 - p['m']) * 20000 + 32; + } + + // I split the r() function into two functions for better readability + this.tr = function() { + this.r(); + + // Shorter reference + var p = this._p; + + // Calculating the length is all that remained here, everything else moved somewhere + _envelopeLength0 = p['b'] * p['b'] * 100000; + _envelopeLength1 = p['c'] * p['c'] * 100000; + _envelopeLength2 = p['e'] * p['e'] * 100000 + 12; + // Full length of the volume envelop (and therefore sound) + // Make sure the length can be divided by 3 so we will not need the padding "==" after base64 encode + return ((_envelopeLength0 + _envelopeLength1 + _envelopeLength2) / 3 | 0) * 3; + } + + /** + * Writes the wave to the supplied buffer ByteArray + * @param buffer A ByteArray to write the wave to + * @return If the wave is finished + */ + this.sw = function(buffer, length) { + // Shorter reference + var p = this._p; + + // If the filters are active + var _filters = p['s'] != 1 || p['v'], + // Cutoff multiplier which adjusts the amount the wave position can move + _hpFilterCutoff = p['v'] * p['v'] * .1, + // Speed of the high-pass cutoff multiplier + _hpFilterDeltaCutoff = 1 + p['w'] * .0003, + // Cutoff multiplier which adjusts the amount the wave position can move + _lpFilterCutoff = p['s'] * p['s'] * p['s'] * .1, + // Speed of the low-pass cutoff multiplier + _lpFilterDeltaCutoff = 1 + p['t'] * .0001, + // If the low pass filter is active + _lpFilterOn = p['s'] != 1, + // masterVolume * masterVolume (for quick calculations) + _masterVolume = p['x'] * p['x'], + // Minimum frequency before stopping + _minFreqency = p['g'], + // If the phaser is active + _phaser = p['q'] || p['r'], + // Change in phase offset + _phaserDeltaOffset = p['r'] * p['r'] * p['r'] * .2, + // Phase offset for phaser effect + _phaserOffset = p['q'] * p['q'] * (p['q'] < 0 ? -1020 : 1020), + // Once the time reaches this limit, some of the iables are r + _repeatLimit = p['p'] ? ((1 - p['p']) * (1 - p['p']) * 20000 | 0) + 32 : 0, + // The punch factor (louder at begining of sustain) + _sustainPunch = p['d'], + // Amount to change the period of the wave by at the peak of the vibrato wave + _vibratoAmplitude = p['j'] / 2, + // Speed at which the vibrato phase moves + _vibratoSpeed = p['k'] * p['k'] * .01, + // The type of wave to generate + _waveType = p['a']; + + var _envelopeLength = _envelopeLength0, // Length of the current envelope stage + _envelopeOverLength0 = 1 / _envelopeLength0, // (for quick calculations) + _envelopeOverLength1 = 1 / _envelopeLength1, // (for quick calculations) + _envelopeOverLength2 = 1 / _envelopeLength2; // (for quick calculations) + + // Damping muliplier which restricts how fast the wave position can move + var _lpFilterDamping = 5 / (1 + p['u'] * p['u'] * 20) * (.01 + _lpFilterCutoff); + if (_lpFilterDamping > .8) { + _lpFilterDamping = .8; + } + _lpFilterDamping = 1 - _lpFilterDamping; + + var _finished = false, // If the sound has finished + _envelopeStage = 0, // Current stage of the envelope (attack, sustain, decay, end) + _envelopeTime = 0, // Current time through current enelope stage + _envelopeVolume = 0, // Current volume of the envelope + _hpFilterPos = 0, // Adjusted wave position after high-pass filter + _lpFilterDeltaPos = 0, // Change in low-pass wave position, as allowed by the cutoff and damping + _lpFilterOldPos, // Previous low-pass wave position + _lpFilterPos = 0, // Adjusted wave position after low-pass filter + _periodTemp, // Period modified by vibrato + _phase = 0, // Phase through the wave + _phaserInt, // Integer phaser offset, for bit maths + _phaserPos = 0, // Position through the phaser buffer + _pos, // Phase expresed as a Number from 0-1, used for fast sin approx + _repeatTime = 0, // Counter for the repeats + _sample, // Sub-sample calculated 8 times per actual sample, averaged out to get the super sample + _superSample, // Actual sample writen to the wave + _vibratoPhase = 0; // Phase through the vibrato sine wave + + // Buffer of wave values used to create the out of phase second wave + var _phaserBuffer = new Array(1024), + // Buffer of random values used to generate noise + _noiseBuffer = new Array(32); + for (var i = _phaserBuffer.length; i--; ) { + _phaserBuffer[i] = 0; + } + for (var i = _noiseBuffer.length; i--; ) { + _noiseBuffer[i] = Math.random() * 2 - 1; + } + + for (var i = 0; i < length; i++) { + if (_finished) { + return i; + } + + // Repeats every _repeatLimit times, partially rting the sound parameters + if (_repeatLimit) { + if (++_repeatTime >= _repeatLimit) { + _repeatTime = 0; + this.r(); + } + } + + // If _changeLimit is reached, shifts the pitch + if (_changeLimit) { + if (++_changeTime >= _changeLimit) { + _changeLimit = 0; + _period *= _changeAmount; + } + } + + // Acccelerate and apply slide + _slide += _deltaSlide; + _period *= _slide; + + // Checks for frequency getting too low, and stops the sound if a minFrequency was set + if (_period > _maxPeriod) { + _period = _maxPeriod; + if (_minFreqency > 0) { + _finished = true; + } + } + + _periodTemp = _period; + + // Applies the vibrato effect + if (_vibratoAmplitude > 0) { + _vibratoPhase += _vibratoSpeed; + _periodTemp *= 1 + Math.sin(_vibratoPhase) * _vibratoAmplitude; + } + + _periodTemp |= 0; + if (_periodTemp < 8) { + _periodTemp = 8; + } + + // Sweeps the square duty + if (!_waveType) { + _squareDuty += _dutySweep; + if (_squareDuty < 0) { + _squareDuty = 0; + } else if (_squareDuty > .5) { + _squareDuty = .5; + } + } + + // Moves through the different stages of the volume envelope + if (++_envelopeTime > _envelopeLength) { + _envelopeTime = 0; + + switch (++_envelopeStage) { + case 1: + _envelopeLength = _envelopeLength1; + break; + case 2: + _envelopeLength = _envelopeLength2; + } + } + + // Sets the volume based on the position in the envelope + switch (_envelopeStage) { + case 0: + _envelopeVolume = _envelopeTime * _envelopeOverLength0; + break; + case 1: + _envelopeVolume = 1 + (1 - _envelopeTime * _envelopeOverLength1) * 2 * _sustainPunch; + break; + case 2: + _envelopeVolume = 1 - _envelopeTime * _envelopeOverLength2; + break; + case 3: + _envelopeVolume = 0; + _finished = true; + } + + // Moves the phaser offset + if (_phaser) { + _phaserOffset += _phaserDeltaOffset; + _phaserInt = _phaserOffset | 0; + if (_phaserInt < 0) { + _phaserInt = -_phaserInt; + } else if (_phaserInt > 1023) { + _phaserInt = 1023; + } + } + + // Moves the high-pass filter cutoff + if (_filters && _hpFilterDeltaCutoff) { + _hpFilterCutoff *= _hpFilterDeltaCutoff; + if (_hpFilterCutoff < .00001) { + _hpFilterCutoff = .00001; + } else if (_hpFilterCutoff > .1) { + _hpFilterCutoff = .1; + } + } + + _superSample = 0; + for (var j = 8; j--; ) { + // Cycles through the period + _phase++; + if (_phase >= _periodTemp) { + _phase %= _periodTemp; + + // Generates new random noise for this period + if (_waveType == 3) { + for (var n = _noiseBuffer.length; n--; ) { + _noiseBuffer[n] = Math.random() * 2 - 1; + } + } + } + + // Gets the sample from the oscillator + switch (_waveType) { + case 0: // Square wave + _sample = ((_phase / _periodTemp) < _squareDuty) ? .5 : -.5; + break; + case 1: // Saw wave + _sample = 1 - _phase / _periodTemp * 2; + break; + case 2: // Sine wave (fast and accurate approx) + _pos = _phase / _periodTemp; + _pos = (_pos > .5 ? _pos - 1 : _pos) * 6.28318531; + _sample = 1.27323954 * _pos + .405284735 * _pos * _pos * (_pos < 0 ? 1 : -1); + _sample = .225 * ((_sample < 0 ? -1 : 1) * _sample * _sample - _sample) + _sample; + break; + case 3: // Noise + _sample = _noiseBuffer[Math.abs(_phase * 32 / _periodTemp | 0)]; + } + + // Applies the low and high pass filters + if (_filters) { + _lpFilterOldPos = _lpFilterPos; + _lpFilterCutoff *= _lpFilterDeltaCutoff; + if (_lpFilterCutoff < 0) { + _lpFilterCutoff = 0; + } else if (_lpFilterCutoff > .1) { + _lpFilterCutoff = .1; + } + + if (_lpFilterOn) { + _lpFilterDeltaPos += (_sample - _lpFilterPos) * _lpFilterCutoff; + _lpFilterDeltaPos *= _lpFilterDamping; + } else { + _lpFilterPos = _sample; + _lpFilterDeltaPos = 0; + } + + _lpFilterPos += _lpFilterDeltaPos; + + _hpFilterPos += _lpFilterPos - _lpFilterOldPos; + _hpFilterPos *= 1 - _hpFilterCutoff; + _sample = _hpFilterPos; + } + + // Applies the phaser effect + if (_phaser) { + _phaserBuffer[_phaserPos % 1024] = _sample; + _sample += _phaserBuffer[(_phaserPos - _phaserInt + 1024) % 1024]; + _phaserPos++; + } + + _superSample += _sample; + } + + // Averages out the super samples and applies volumes + _superSample *= .125 * _envelopeVolume * _masterVolume; + + // Clipping if too loud + buffer[i] = _superSample >= 1 ? 32767 : _superSample <= -1 ? -32768 : _superSample * 32767 | 0; + } + + return length; + } +} + +// Adapted from http://codebase.es/riffwave/ +var synth = new SfxrSynth(); +// Export for the Closure Compiler +function jsfxr (settings, audioCtx, cb) { + // Initialize SfxrParams + synth._p.ss(settings); + // Synthesize Wave + var envelopeFullLength = synth.tr(); + var data = new Uint8Array(((envelopeFullLength + 1) / 2 | 0) * 4 + 44); + + var used = synth.sw(new Uint16Array(data.buffer, 44), envelopeFullLength) * 2; + + var dv = new Uint32Array(data.buffer, 0, 44); + // Initialize header + dv[0] = 0x46464952; // "RIFF" + dv[1] = used + 36; // put total size here + dv[2] = 0x45564157; // "WAVE" + dv[3] = 0x20746D66; // "fmt " + dv[4] = 0x00000010; // size of the following + dv[5] = 0x00010001; // Mono: 1 channel, PCM format + dv[6] = 0x0000AC44; // 44,100 samples per second + dv[7] = 0x00015888; // byte rate: two bytes per sample + dv[8] = 0x00100002; // 16 bits per sample, aligned on every two bytes + dv[9] = 0x61746164; // "data" + dv[10] = used; // put number of samples here + + // Base64 encoding written by me, @maettig + used += 44; + var i = 0, + base64Characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', + output = 'data:audio/wav;base64,'; + for (; i < used; i += 3) + { + var a = data[i] << 16 | data[i + 1] << 8 | data[i + 2]; + output += base64Characters[a >> 18] + base64Characters[a >> 12 & 63] + base64Characters[a >> 6 & 63] + base64Characters[a & 63]; + } + + audioCtx && audioCtx.decodeAudioData(data.buffer, cb); + + return output; +} +/* global jsfxr */ + +var audioCtx, audioDest, audio, play; // eslint-disable-line + +var AudioContext = window.AudioContext || window.webkitAudioContext; + +if (AudioContext) { + audioCtx = new AudioContext(); + audioDest = audioCtx.createDynamicsCompressor(); + var gain = audioCtx.createGain(); + gain.gain.value = 0.1; + audioDest.connect(gain); + gain.connect(audioCtx.destination); + + audio = function (conf) { // eslint-disable-line no-unused-vars + var o = []; + jsfxr(conf, audioCtx, function (buf) { + o.push(buf); + }); + return o; + }; + play = function (o) { // eslint-disable-line no-unused-vars + if (!o[0]) return; + var source = audioCtx.createBufferSource(); + source.buffer = o[0]; + source.start(0); + source.connect(audioDest); + setTimeout(function () { + source.disconnect(audioDest); + }, o[0].duration * 1000 + 300); + }; +} +else { + audio = play = function(){}; +} +var BLUR1D_FRAG ='precision highp float;varying vec2 uv;uniform sampler2D t;uniform vec2 dim;uniform vec2 dir;void main(){vec4 a=vec4(0.0);vec2 b=vec2(1.3846153846)*dir;vec2 c=vec2(3.2307692308)*dir;a+=texture2D(t,uv)*0.2270270270;a+=texture2D(t,uv+(b/dim))*0.3162162162;a+=texture2D(t,uv-(b/dim))*0.3162162162;a+=texture2D(t,uv+(c/dim))*0.0702702703;a+=texture2D(t,uv-(c/dim))*0.0702702703;gl_FragColor=a;}'; +var COPY_FRAG ='precision highp float;varying vec2 uv;uniform sampler2D t;void main(){gl_FragColor=texture2D(t,uv);}'; +var GAME_FRAG ='precision highp float;varying vec2 uv;uniform sampler2D G;uniform sampler2D R;uniform sampler2D B;uniform sampler2D L;uniform sampler2D E;uniform float s;uniform float F;uniform vec2 k;float a(vec2 b,vec2 c){float d=10.0;vec2 e=b-c;return pow(abs(pow(abs(e.x),d)+pow(abs(e.y),d)),1.0/d);}void main(){vec2 f=uv+k;vec2 g=(f/0.98)-0.01;float h=a(f,vec2(0.5));float i=smoothstep(0.45,0.51,h);g=mix(g,vec2(0.5),0.2*(0.6-h)-0.02*h);vec3 j=texture2D(G,g).rgb;gl_FragColor=step(0.0,f.x)*step(f.x,1.0)*step(0.0,f.y)*step(f.y,1.0)*vec4((vec3(0.03+0.1*F,0.04,0.05)+mix(vec3(0.05,0.1,0.15)-j,2.0*j,s)+s*(texture2D(L,g).rgb+vec3(0.3+F,0.6,1.0)*(texture2D(R,g).rgb+3.0*texture2D(B,g).rgb)+0.5*texture2D(E,g).rgb))*mix(1.0,smoothstep(1.0,0.0,i),0.6),1.0);}'; +var GLARE_FRAG ='precision highp float;varying vec2 uv;uniform sampler2D t;void main(){gl_FragColor=vec4(step(0.9,texture2D(t,uv).r));}'; +var LASER_FRAG ='precision highp float;varying vec2 uv;uniform sampler2D t;void main(){vec3 a=texture2D(t,uv).rgb;vec2 b=0.003*vec2(cos(47.0*uv.y),sin(67.0*uv.x));gl_FragColor=vec4(a.r+a.g+a.b+texture2D(t,uv+b).b);}'; +var PERSISTENCE_FRAG ='precision highp float;varying vec2 uv;uniform sampler2D t;uniform sampler2D r;void main(){vec3 a=texture2D(r,uv).rgb;gl_FragColor=vec4(a*(0.82-0.3*a.r*a.r)+texture2D(t,uv).rgb,1.0);}'; +var PLAYER_FRAG ='precision highp float;varying vec2 uv;uniform float pt;uniform float pl;uniform float S;uniform float ex;uniform float J;uniform float P;float a(vec2 b,vec2 c){return step(length((uv-b)/c),1.0);}float d(vec2 b,vec2 c,float e){vec2 f=(uv-b)/c;return step(pow(abs(f.x),e)+pow(abs(f.y),e),1.0);}vec3 g(){return 0.1+0.3*vec3(1.0,0.9,0.7)*smoothstep(0.4,0.1,distance(uv,vec2(0.2,1.2)))+0.4*vec3(0.8,0.6,1.0)*smoothstep(0.5,0.2,distance(uv,vec2(1.3,0.7)));}vec4 h(float e,float i){vec4 b=vec4(0.0);vec2 j=vec2(min(ex,1.0),mix(min(ex,1.0),min(ex-1.0,1.0),0.5));vec4 k=0.2+0.4*pow(abs(cos(4.*e+S)),2.0)*vec4(1.0,0.7,0.3,1.0);vec4 l=vec4(0.5,0.3,0.3,1.0);vec4 m=vec4(0.3*(1.0+cos(3.*e+6.*S)),0.2*(1.0+cos(7.*e+7.*S)),0.1+0.2*(1.0+sin(7.*e+8.*S)),1.0);float n=step(sin(9.0*e+S),0.0);float o=0.02+0.02*n*cos(e+S);float p=step(i,-0.01)+step(0.01,i);float q=(1.0-p)*step(0.0,pt);vec2 r=vec2(0.5)+J*vec2(0.0,0.2)+p*vec2(0.03*cos(4.0*pt+sin(pt)),0.05*abs(sin(3.0*pt)))+j*q*(1.0-P)*vec2(0.05*cos(pt*(1.0+0.1*sin(pt))),0.05*abs(sin(pt)));vec2 s=mix(r,vec2(0.5),0.5);r.x+=i;s.x+=i;b+=k*a(r,vec2(0.06,0.1));b*=1.0-(0.5+0.5*n)*a(r-vec2(0.0,0.04),vec2(0.03,0.01));b*=1.0-a(r+vec2(0.03,0.03),vec2(0.02,0.01));b*=1.0-a(r+vec2(-0.03,0.03),vec2(0.02,0.01));b*=1.0-0.6*a(r,vec2(0.01,0.02));b+=l*a(r+vec2(0.0,o),vec2(0.07,0.1+o));b+=q*(l+k)*a(s-vec2(-0.2+0.01*cos(5.0*pt),0.45-0.1*j.y*step(0.0,pt)*P*pow(abs(sin(8.0*pt*(1.0+0.2*cos(pt)))),4.0)),vec2(0.055,0.05));b+=q*(l+k)*a(s-vec2(0.2+0.01*cos(5.0*pt),0.45-0.1*j.x*step(2.0,pt)*P*pow(abs(cos(7.0*pt)),4.0)),vec2(0.055,0.05));b+=step(b.a,0.0)*(l+k)*d(r-vec2(0.0,0.10+0.02*n),vec2(0.05-0.01*n,0.03),4.0);vec2 t=vec2(0.16+0.04*sin(9.*e),0.27+0.02*cos(9.*e));b+=step(b.r+b.g+b.b,0.0)*m*step(1.0,d(r-vec2(0.0,0.35),t*(1.0-0.1*n),4.0)+a(r-vec2(0.0,0.35),t));return b;}void main(){float u=0.6+0.4*smoothstep(2.0,0.0,distance(pt,-2.0));vec4 b=vec4(0.0);b+=(1.0-smoothstep(-0.0,-5.0,pt))*h(pl+step(pt,0.0),-0.6*smoothstep(-1.,-5.,pt));b+=step(1.0,pl)*h(pl+step(pt,0.0)-1.0,2.0*smoothstep(-4.,-1.,pt));b*=1.0-1.3*distance(uv,vec2(0.5));gl_FragColor=vec4(u*mix(g(),b.rgb,clamp(b.a,0.0,1.0)),1.0);}'; +var STATIC_VERT ='attribute vec2 p;varying vec2 uv;void main(){gl_Position=vec4(p,0.0,1.0);uv=0.5*(p+1.0);}'; +/* global g d MOBILE +gameScale: true +glCreateFBO glCreateShader glCreateTexture glUniformLocation +STATIC_VERT +BLUR1D_FRAG +COPY_FRAG +GAME_FRAG +GLARE_FRAG +LASER_FRAG +PERSISTENCE_FRAG +PLAYER_FRAG +*/ + +var ctx, + gameCtx = g.getContext("2d"), + FW = MOBILE ? 480 : 800, + FH = MOBILE ? 660 : 680, + GAME_MARGIN = MOBILE ? 50 : 120, + GAME_Y_MARGIN = MOBILE ? 140 : GAME_MARGIN, + GAME_INC_PADDING = MOBILE ? 40 : 80, + W = FW - 2 * GAME_MARGIN, + H = FH - 2 * GAME_Y_MARGIN, + borderLength = 2*(W+H+2*GAME_INC_PADDING), + SEED = Math.random(); + +// DOM setup + +d.style.webkitTransformOrigin = d.style.transformOrigin = "0 0"; + +g.width = W; +g.height = H; + +var uiScale = 1; + +function checkSize () { + +} +/* global W H */ + +// N.B: constants don't live here + +var t = 0, dt, + + spaceship = [ W/2, H/2, 0, 0, 0 ], // [x, y, velx, vely, rot] + asteroids = [], // array of [x, y, rot, vel, shape, lvl] + ufos = [], // array of [x, y, vx, vy, timeBeforeShot] + bullets = [], // array of [x, y, velx, vely, life, isAlien] + incomingObjects = [], // array of: [pos, vel, ang, force, rotVel, shape, lvl, key, rotAmp, rotAmpValid, explodeTime] + particles = [], // array of [x, y, rot, vel, life] + + dying = 0, + resurrectionTime = 0, + best = 0, + score = 0, // current asteroids player score + scoreForLife, // will track the next score to win a life (10000, 20000, ...) + playingSince = -10000, + deads = 0, + player = 0, + lifes = 0, + + AIshoot = 0, AIboost = 0, AIrotate = 0, AIexcitement = 0, + AIboostSmoothed = 0, + + shaking = [0,0], + jumping = 0, + jumpingFreq = 0, + jumpingPhase = 0, + jumpingFreqSmoothed = 0, + jumpingAmp = 0, + jumpingAmpSmoothed = 0, + killSmoothed = 0, + + musicPhase = 0, + musicTick = 0, + musicPaused = 0, + ufoMusicTime = 0, + + excitementSmoothed = 0, + neverPlayed = 1, + neverUFOs = 1, + combos = 0, + combosTarget, + gameOver, + awaitingContinue = 0,//localStorage.ba_pl && parseInt(localStorage.ba_pl), + // achievements: [nbAsteroids, nbKills, nbUfos] + achievements, + + lastScoreIncrement = 0, + lastJump = 0, + lastBulletShoot = 0, + lastExtraLife = 0, + lastLoseShot = 0, + + // Input state : updated by user events, handled & emptied by the update loop + keys = {}, + tap, + + // variables related to setup + gameScale; + + +function helpVisible () { + return neverPlayed && + incomingObjects[0] && + playingSince>8000; +} + /* global audio */ + +var Ashot = audio([0,0.06,0.18,,0.33,0.5,0.23,-0.04,-0.24,,,-0.02,,0.37,-0.22,,,,0.8,,,,,0.3]), + + Amusic1 = audio([,,0.12,,0.13,0.16,,,,,,,,,,,,,0.7,,,,,0.5]), + Amusic2 = audio([,,0.12,,0.13,0.165,,,,,,,,,,,,,0.7,,,,,0.5]), + + Aexplosion1 = audio([3,,0.35,0.5369,0.5,0.15,,-0.02,,,,-0.7444,0.78,,,0.7619,,,0.1,,,,,0.5]), + Aexplosion2 = audio([3,,0.38,0.5369,0.52,0.18,,-0.02,,,,-0.7444,0.78,,,0.7619,,,0.1,,,,,0.5]), + + Asend = audio([2,0.07,0.04,,0.24,0.25,,0.34,-0.1999,,,-0.02,,0.3187,,,-0.14,0.04,0.85,,0.28,0.63,,0.5]), + AsendFail = audio([1,,0.04,,0.45,0.14,0.06,-0.06,0.02,0.87,0.95,-0.02,,0.319,,,-0.14,0.04,0.5,,,,,0.4]), + + Alost = audio([0,0.11,0.37,,0.92,0.15,,-0.06,-0.04,0.3,0.14,0.1,,0.5047,,,,,0.16,-0.02,,0.5,,1]), + Aleave = audio([0,0.11,0.36,,0.66,0.19,,0.06,-0.06,0.05,0.8,-0.12,0.3,0.19,-0.06,,,-0.02,0.23,-0.02,,0.4,,0.4]), + Acoin = audio([0,,0.094,0.29,0.42,0.563,,,,,,0.4399,0.5658,,,,,,1,,,,,0.5]), + Amsg = audio([2,0.07,0.1,,0.2,0.75,0.35,-0.1,0.12,,,-0.02,,,,,-0.06,-0.0377,0.26,,,0.8,,0.7]), + Aufo = audio([2,0.05,0.74,,0.33,0.5,,,,0.46,0.29,,,,,,,,1,,,,,0.3]), + Alife = audio([0,0.12,0.8,0.48,0.77,0.92,,-0.12,-0.0999,,,-0.4,0.2,0.34,,0.65,,,0.93,-0.02,,,,0.38]), + Ajump = audio([3,,0.12,0.56,0.27,0.07,,-0.12,0.02,,,-0.02,0.68,,,,-0.04,-0.022,0.06,,,0.06,,0.5]); +/* global +keys +tap:true +MOBILE +c +d +gameOver +W +gameScale +achievements:true +player:true +playingSince:true +awaitingContinue:true +*/ + +/* +for (var i=0; i<99; ++i) keys[i] = 0; + +var fullScreenRequested = 0; +function onTap (e) { + if (MOBILE && !fullScreenRequested && d.webkitRequestFullScreen){ + d.webkitRequestFullScreen(); + fullScreenRequested = 1; + } + + var r = c.getBoundingClientRect(), + x = (e.clientX - r.left) / gameScale, + y = (e.clientY - r.top) / gameScale; + if (gameOver) { + if(280 < y && y < 400) { + if (W/2 - 180 < x && x < W/2 - 20) { + open("https://twitter.com/intent/tweet?via=greweb&url="+ + encodeURIComponent(location.href)+ + "&text="+ + encodeURIComponent( + "Reached Level "+player+ + " ("+(player*25)+"Ā¢) with "+ + achievements[0]+"⬠ "+ + achievements[1]+"ᐃ "+ + achievements[2]+"šŸž" + )); + } + else if (W/2 + 20 < x && x < W/2 + 180) { + location.reload(); + } + } + } + else if (awaitingContinue) { + if (playingSince>0 && 170 W+100 || obj[1] > H+100) { + arr.splice(i, 1); + } +} + +function applyLife (obj, i, arr) { + if ((obj[4] -= dt) < 0) { + arr.splice(i, 1); + } +} + +function loopOutOfBox (obj) { + if (obj[0] < 0) { + obj[0] += W; + } + else if (obj[0] > W) { + obj[0] -= W; + } + if (obj[1] < 0) { + obj[1] += H; + } + else if (obj[1] > H) { + obj[1] -= H; + } +} +/* global +DEBUG +AIrotate: true +AIboost: true +AIshoot: true +AIexcitement: true +spaceship +t dt +asteroids +bullets +W H +dist normAngle +ufos +playingSince +ctx +*/ + +/* +if (DEBUG) { + /* eslint-disable no-inner-declarations + var AIdebug = [], AIdebugCircle = []; + function drawAIDebug () { + AIdebug.forEach(function (debug, i) { + ctx.save(); + ctx.lineWidth = 2; + ctx.fillStyle = ctx.strokeStyle = "hsl("+Math.floor(360*i/AIdebug.length)+",80%,50%)"; + ctx.beginPath(); + ctx.moveTo(debug[0], debug[1]); + ctx.lineTo(debug[2], debug[3]); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(debug[0], debug[1], 2, 0, 2*Math.PI); + ctx.fill(); + ctx.restore(); + }); + AIdebugCircle.forEach(function (debug, i) { + ctx.save(); + ctx.lineWidth = 2; + ctx.fillStyle = ctx.strokeStyle = "hsl("+Math.floor(360*i/AIdebugCircle.length)+",80%,50%)"; + ctx.beginPath(); + ctx.arc(debug[0], debug[1], Math.max(0, debug[2] * debug[3]), 0, 2*Math.PI); + ctx.stroke(); + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.arc(debug[0], debug[1], debug[3], 0, 2*Math.PI); + ctx.stroke(); + ctx.textAlign = "center"; + ctx.textBaseline = "bottom"; + ctx.fillText(debug[2].toFixed(2), debug[0], debug[1]-debug[3]-2); + ctx.restore(); + }); + } + function clearDebug () { + AIdebug = []; + AIdebugCircle = []; + } + function addDebugCircle (p, value, radius) { + AIdebugCircle.push([ p[0], p[1], value, radius ]); + } + function addDebug (p, v) { + var d = 200; + AIdebug.push([ p[0], p[1], p[0]+(v?d*v[0]:0), p[1]+(v?d*v[1]:0) ]); + } + function addPolarDebug (p, ang, vel) { + var v = [ + vel * Math.cos(ang), + vel * Math.sin(ang) + ]; + addDebug(p, v); + } + /* eslint-enable +} +*/ + +var closestAsteroidMemory, targetShootMemory, closestAsteroidMemoryT, targetShootMemoryT; + +// AI states +function aiLogic (smart) { // set the 3 AI inputs (rotate, shoot, boost) + var i; + + // DEBUG && clearDebug(); + + // first part is data extraction / analysis + + //var ax = Math.cos(spaceship[4]); + //var ay = Math.sin(spaceship[4]); + var vel = Math.sqrt(spaceship[2]*spaceship[2]+spaceship[3]*spaceship[3]); + var velAng = Math.atan2(spaceship[3], spaceship[2]); + + //var spaceshipVel = [ ax * vel, ay * vel ]; + + + // utilities + + function orient (ang) { + var stableAng = normAngle(ang - spaceship[4]); + AIrotate = stableAng < 0 ? -1 : 1; + return stableAng; + } + + function move (ang, vel) { + var stableAng = normAngle(ang - spaceship[4]); + var abs = Math.abs(stableAng); + if (abs > Math.PI/2) { + if (vel) AIboost = abs>Math.PI/2-0.4 ? vel>0?-1:1 : 0; + AIrotate = stableAng > 0 ? -1 : 1; + } + else { + if (vel) AIboost = abs<0.4 ? vel<0?-1:1 : 0; + AIrotate = stableAng < 0 ? -1 : 1; + } + } + + // take actions to move and stabilize to a point + function moveToPoint (p, minDist) { + var dx = p[0]-spaceship[0]; + var dy = p[1]-spaceship[1]; + if (dx*dx+dy*dy 0.003 * dist && Math.abs(normAngle(ang - velAng)) acceptDist) return; + //DEBUG && addDebug(p, v); + moveAwayFromPoint(p, v); + } + + function predictShootIntersection (bulletVel, pos, target, targetVel) { + // http://gamedev.stackexchange.com/a/25292 + var totarget = [ + target[0] - pos[0], + target[1] - pos[1] + ]; + var a = dot(targetVel, targetVel) - bulletVel * bulletVel; + var b = 2 * dot(targetVel, totarget); + var c = dot(totarget, totarget); + var p = -b / (2 * a); + var q = Math.sqrt((b * b) - 4 * a * c) / (2 * a); + var t1 = p - q; + var t2 = p + q; + var t = t1 > t2 && t2 > 0 ? t2 : t1; + + return [t, [ + target[0] + targetVel[0] * t, + target[1] + targetVel[1] * t + ]]; + } + + var middle = [W/2,H/2]; + + var closestAsteroid, targetShoot, danger = 0; + var closestAsteroidScore = 0.3, targetShootScore = 0.1; + var incomingBullet, incomingBulletScore = 0; + + for (i = 0; i < asteroids.length; ++i) { + var ast = asteroids[i]; + // FIXME: take velocity of spaceship into account? + var v = [ + ast[3] * Math.cos(ast[2]), + ast[3] * Math.sin(ast[2]) + ]; + var timeBeforeImpact = dot([ spaceship[0]-ast[0], spaceship[1]-ast[1] ],v)/dot(v,v); + var impact = [ + ast[0] + timeBeforeImpact * v[0], + ast[1] + timeBeforeImpact * v[1] + ]; + var distToImpact = dist(spaceship, impact); + var distWithSize = distToImpact - 10 - 10 * ast[5]; + + var score = + Math.exp(-distWithSize/40) + + Math.exp(-distWithSize/120) + + timeBeforeImpact > 0 ? Math.exp(-timeBeforeImpact/1000) : 0; + + if (score > closestAsteroidScore) { + closestAsteroidScore = score; + closestAsteroid = ast; + danger ++; + } + + score = + Math.exp(-(ast[5]-1)) * + Math.exp(-distWithSize/200); + + if (score > targetShootScore) { + var res = predictShootIntersection(0.3, spaceship, ast, v); + var t = res[0]; + var p = res[1]; + if (0 incomingBulletScore) { + incomingBulletScore = score; + incomingBullet = impact; + } + } + + for (i = 0; i < ufos.length; ++i) { + var u = ufos[i]; + res = predictShootIntersection(0.3, spaceship, u, u.slice(2)); + t = res[0]; + p = res[1]; + targetShoot = p; + } + + AIexcitement = + (1 - Math.exp(-asteroids.length/10)) + // total asteroids + (1 - Math.exp(-danger/3)) // danger + ; + + // Now we implement the spaceship reaction + // From the least to the most important reactions + + // Dump random changes + + AIshoot = playingSince > 3000 && Math.random() < 0.001*dt*(1-smart); + + AIrotate = (playingSince > 1000 && Math.random()<0.002*dt) ? + (Math.random()<0.6 ? 0 : Math.random() < 0.5 ? -1 : 1) : AIrotate; + + AIboost = (playingSince > 2000 && Math.random()<0.004*dt) ? + (Math.random()<0.7 ? 0 : Math.random() < 0.5 ? -1 : 1) : AIboost; + + // Stay in center area + + if (0.1 + smart > Math.random()) moveToPoint(middle, 30); + + // Shot the target + + if (smart > Math.random()) { + if (targetShoot) { + AIshoot = + Math.abs(orient(Math.atan2( + targetShoot[1] - spaceship[1], + targetShoot[0] - spaceship[0]))) < 0.1 && + Math.random() < 0.04 * dt; + targetShootMemory = targetShoot; + targetShootMemoryT = t; + } + else { + AIshoot = 0; + } + } + + // Avoid dangers + if (smart > Math.random()) { + if (closestAsteroid) { + moveAwayFromAsteroid(closestAsteroid); + closestAsteroidMemory = closestAsteroid; + closestAsteroidMemoryT = closestAsteroid; + } + + if (incomingBullet) moveAwayFromPoint(incomingBullet); + } + + //DEBUG && targetShoot && addPolarDebug(targetShoot, 0, 0); + //DEBUG && closestAsteroid && addPolarDebug(closestAsteroid, closestAsteroid[2], closestAsteroid[3]); +} +/* global +ctx path W H asteroids:true rotatingLetters incPosition incRotation MOBILE play +Asend AsendFail +*/ + +// Logic + +function randomAsteroidShape (lvl) { + var n = 4 + lvl * 2; + var size = lvl * 10; + var pts = []; + for (var i = 0; i < n; ++i) { + var l = size*(0.4 + 0.6 * Math.random()); + var a = 2 * Math.PI * i / n; + pts.push([ + l * Math.cos(a), + l * Math.sin(a) + ]); + } + return pts; +} + +function randomAsteroids () { + asteroids = []; + for (var i=0; i<8; ++i) { + var lvl = Math.floor(1.5 + 3 * Math.random()); + asteroids[i] = [ + W * Math.random(), + H * Math.random(), + 2 * Math.PI * Math.random(), + 0.02 + 0.02 * Math.random(), + randomAsteroidShape(lvl), + lvl + ]; + } +} + + +function explodeAsteroid (j) { + var aster = asteroids[j]; + asteroids.splice(j, 1); + var lvl = aster[5]; + if (lvl > 1) { + var nb = Math.round(2+1.5*Math.random()); + for (var k=0; k diffMax) diffMin *= Math.random(); + + + var pRotAmp = diffMin + Math.random() * (diffMax-diffMin); + var pRotAmpRatio = diffMin + Math.random() * (diffMax-diffMin); + var pRotSpeed = diffMin + Math.random() * (diffMax-diffMin); + + var lvl = Math.floor(2 + 3 * Math.random() * Math.random() + 4 * Math.random() * Math.random() * Math.random()); + var ampRot = player<2 ? 0 : Math.PI * (0.8 * Math.random() + 0.05 * lvl) * pRotAmp; + if (ampRot < 0.2) ampRot = 0; + var ampRotRatio = + player > 2 && + ampRot > Math.exp(-player/4) && + Math.random() > 0.5 + 0.4 * ((player-3)%8)/8 - 0.5 * (1 - Math.exp(-player/10)) ? + 0.9 - 0.5 * pRotAmpRatio - 0.2 * pRotAmp : + 1; + + if (player == 2) { + ampRot = 0.2 + Math.random(); + } + + if (player == 3) { + ampRot = 0.2 + Math.random(); + ampRotRatio = 0.5 + 0.4 * Math.random(); + } + + incomingObjects.push([ + pos, + // velocity + 0.1 + 0.002 * player, + // initial angle + 2*Math.PI*Math.random(), + // initial force + 10 + 40*Math.random(), + // rot velocity + 0.002 + 0.001 * (Math.random() + 0.5 * lvl * Math.random() + Math.random() * player / 30) * pRotSpeed - 0.001 * pRotAmp, + // shape + randomAsteroidShape(lvl), + // level + lvl, + // key + key, + // amplitude rotation + ampRot, + // amplitude rotation valid ratio + ampRotRatio, + // explode time + 0 + ]); + return 1; +} + +function applyIncLogic (o) { + if (!o[10]) { + o[0] += o[1] * dt; + o[2] += o[4] * dt; + o[3] = o[3] < 10 ? 60 : o[3] - 0.02 * dt; + } +} + +// RENDERING + +function drawInc (o) { + var rotC = incRotationCenter(o); + var phase = Math.cos(o[2]); + var rot = phase * o[8] + rotC; + var w = 10 * o[6]; + var valid = Math.abs(phase) < o[9]; + + if (playingSince>0 && lifes && !dying && !o[10]) { + ctx.lineWidth = 1+o[3]/60; + ctx.strokeStyle = valid ? "#7cf" : "#f66"; + + if (o[8] > 0.1) { + ctx.save(); + ctx.rotate(rotC); + ctx.strokeStyle = "#f66"; + ctx.beginPath(); + ctx.arc(0, 0, w+10, -o[8], -o[8]*o[9]); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(0, 0, w+10, o[8]*o[9], o[8]); + ctx.stroke(); + ctx.strokeStyle = "#7cf"; + ctx.beginPath(); + ctx.arc(0, 0, w+10, -o[8] * o[9], o[8] * o[9]); + ctx.stroke(); + path([ + [w+8, 0], + [w+12, 0] + ]); + ctx.stroke(); + ctx.restore(); + } + + ctx.save(); + ctx.rotate(rot); + ctx.save(); + var mx = 60 + w; + var x = o[3] + w; + ctx.globalAlpha = 0.2; + path([ + [0,0], + [mx,0] + ]); + ctx.stroke(); + ctx.restore(); + path([ + [0,0], + [x,0] + ]); + ctx.stroke(); + var r = 6; + path([ + [ mx - r, r ], + [ mx, 0], + [ mx - r, -r ] + ], 1); + ctx.stroke(); + ctx.restore(); + } + else { + ctx.strokeStyle = o[10] ? "#f66" : "#999"; + } + + ctx.save(); + path(o[5]); + ctx.fillStyle = "#000"; + ctx.fill(); + ctx.stroke(); + ctx.restore(); + + var sum = [0, 0]; + o[5].forEach(function (p) { + sum[0] += p[0]; + sum[1] += p[1]; + }); + + if (!MOBILE && playingSince>0) { + if (helpVisible()) { + ctx.strokeStyle = "#f7c"; + } + ctx.translate(sum[0]/o[5].length+1, sum[1]/o[5].length-5); + font(String.fromCharCode(o[7]), 1); + } +} + +function drawIncHelp () { + if (!helpVisible()) return; + ctx.strokeStyle = "#f7c"; + ctx.lineWidth = 4; + incomingObjects.forEach(function (o) { + var p = incPosition(o); + ctx.beginPath(); + ctx.arc(p[0], p[1], 80 + 40 * Math.cos(0.005 * t), 0, 2*Math.PI); + ctx.stroke(); + }); +} +/* global +ctx bullets +*/ + +function shoot (obj, vel, ang) { + var ax = Math.cos(ang); + var ay = Math.sin(ang); + bullets.push([ + obj[0] + 14 * ax, + obj[1] + 14 * ay, + obj[2] + vel * ax, + obj[3] + vel * ay, + 1000, + 0 + ]); +} + +// RENDERING + + +function drawBullet () { + ctx.globalAlpha = 1 - Math.random()*Math.random(); + ctx.fillStyle = "#00f"; + ctx.beginPath(); + ctx.arc(0, 0, 2+2.5*Math.random(), 0, 2*Math.PI); + ctx.fill(); +} +/* global +particles play Aexplosion1 Aexplosion2 ctx +*/ +function explose (o) { + play(Math.random()<0.5 ? Aexplosion1 : Aexplosion2); + var n = Math.floor(19 + 9 * Math.random()); + for (var i = 0; i < n; ++i) { + var l = 30 * Math.random() - 10; + var a = (Math.random() + 2 * Math.PI * i) / n; + particles.push([ + o[0] + l * Math.cos(a), + o[1] + l * Math.sin(a), + a, + 0.06, + Math.random()<0.3 ? 0 : 1000 + ]); + } +} + +// RENDERING + + +function drawParticle () { + ctx.globalAlpha = 0.8; + ctx.fillStyle = "#f00"; + ctx.beginPath(); + ctx.arc(0, 0, 1, 0, 2*Math.PI); + ctx.fill(); +} +/* global +ctx t path lifes play Alost AIboostSmoothed dying:true deads:true achievements killSmoothed:true +*/ + +function spaceshipDie() { + if (dying) return; + dying = t; + if (lifes == 1) { + play(Alost); + } + deads ++; + achievements[1] ++; + killSmoothed ++; +} + +/* +function resetSpaceship () { + var x = W * (0.25 + 0.5 * Math.random()); + var y = H * (0.25 + 0.5 * Math.random()); + spaceship = [x, y, 0, 0]; +} +*/ + +// RENDERING + +function drawSpaceship (o) { + ctx.strokeStyle = "#f00"; + ctx.globalAlpha = 0.4; + ctx.rotate(o[4]); + if (dying) { + ctx.lineWidth = 2; + var delta = (t-dying)/200; + + path([ + [-6, -6 - 0.5*delta], + [3, -3 - 0.9*delta] + ]); + ctx.stroke(); + + if (delta < 8) { + path([ + [3 + 0.4*delta, -3 - 0.8*delta], + [12 + 0.4*delta, 0 - 0.5*delta] + ]); + ctx.stroke(); + } + + path([ + [12, 0+0.4*delta], + [3, 3+delta] + ]); + ctx.stroke(); + + if (delta < 9) { + path([ + [1, 5 + delta], + [-6, 6 + delta] + ]); + ctx.stroke(); + } + + if (delta < 7) { + path([ + [-6 - delta, -6], + [-6 - delta, 6] + ]); + ctx.stroke(); + } + } + else { + path([ + [-6, -6], + [ 12, 0], + [ -6, 6], + [ -5, 0] + ]); + ctx.stroke(); + if (AIboostSmoothed>0.2) { + path([ + [-7, 2*Math.random()-1], + [-7 - 5*AIboostSmoothed, 4*Math.random()-2] + ]); + ctx.stroke(); + } + if (AIboostSmoothed<-0.2) { + path([ + [2, -5], + [2 - 5 * AIboostSmoothed, -7], + , + [2, 5], + [2 - 5 * AIboostSmoothed, 7] + ]); + ctx.stroke(); + } + } +} +/* global +dt dying spaceship shoot ctx path +*/ + +function applyUFOlogic (o) { + o[4] -= dt; + if (o[4]<0) { + o[4] = 500 + 300 * Math.random(); + if (!dying) { + var target = Math.atan2(spaceship[1] - o[1], spaceship[0] - o[0]); + if (!o[2] || Math.random()<0.2) { + var randomAngle = 2*Math.PI*Math.random(); + o[2] = 0.08 * Math.cos(randomAngle); + o[3] = 0.08 * Math.sin(randomAngle); + } + shoot(o, 0.3+0.1*Math.random(), target + 0.6 * Math.random() - 0.3); + } + } +} + +// RENDERING + +var UFOa = [ + [8,0], + [7,5], + [0,9], + [7,14] +]; +var UFOb = [ + [15,14], + [22,9], + [15,5], + [14,0] +]; + +var UFO = + UFOa + .concat(UFOb) + .concat(UFOa) + .concat([,]) + .concat(UFOb) + .concat([ + , + [7,5], + [15,5], + , + [0,9], + [22,9] + ]); + +function drawUFO () { + ctx.globalAlpha = 0.4; + ctx.strokeStyle = "#f00"; + path(UFO); + ctx.stroke(); +} +/* global +ctx path font gameOver W H player playingSince:true awaitingContinue scoreTxt +lifes dying MOBILE best score t UFO neverPlayed lastExtraLife neverUFOs dt play +Amsg GAME_MARGIN FW FH combos achievements musicTick helpVisible */ + +// IN GAME UI + +function button (t1, t2) { + ctx.globalAlpha = 1; + path([ + [0, 0], + [160, 0], + [160, 120], + [0, 120] + ]); + ctx.translate(80, 30); + ctx.stroke(); + ctx.fillStyle = "#000"; + ctx.fill(); + ctx.save(); + font(t1, 2); + ctx.restore(); + ctx.save(); + ctx.translate(0, 40); + font(t2, 2); + ctx.restore(); +} + +function drawGameUI () { + ctx.save(); + ctx.fillStyle = ctx.strokeStyle = "#0f0"; + ctx.globalAlpha = 0.3; + + if (gameOver) { + ctx.save(); + ctx.strokeStyle = "#0f0"; + ctx.globalAlpha = 0.3; + ctx.save(); + ctx.translate((W-340)/2, H/8); + font("YOU EARNED ", 2, 1); + ctx.globalAlpha = 0.5; + font((player*25)+"Ā¢", 2, 1); + ctx.restore(); + ctx.save(); + ctx.translate(W/2, H/4); + font("FROM "+player+" PLAYERS", 2); + ctx.restore(); + ctx.save(); + ctx.globalAlpha = 0.5; + ctx.translate((W-200)/2, H/2); + drawAchievements(2); + ctx.restore(); + + ctx.save(); + ctx.translate(W/2 - 180, H - 160); + button("TWEET", "SCORE"); + ctx.restore(); + + ctx.save(); + ctx.translate(W/2 + 20, H - 160); + button("PLAY", "AGAIN"); + ctx.restore(); + + ctx.restore(); + } + else if (playingSince < 0 || awaitingContinue) { + ctx.save(); + ctx.translate(W-50, 20); + font(scoreTxt(0), 1.5, -1); + ctx.restore(); + + ctx.save(); + ctx.translate(50, 70); + if ((!awaitingContinue || playingSince>0) && t%1000<500) + font("PLAYER "+(awaitingContinue||player+1), 2, 1); + ctx.restore(); + + ctx.save(); + ctx.translate(W/2 - 160, 0.7*H); + path([ + [0,2], + [0,18] + ]); + ctx.stroke(); + ctx.translate(40,0); + font("COIN", 2, 1); + ctx.translate(40,0); + path([ + [0,2], + [0,18] + ]); + ctx.stroke(); + ctx.translate(40,0); + font("PLAY", 2, 1); + ctx.restore(); + } + else { + for (var i=1; i 0) { + ctx.save(); + ctx.globalAlpha = 1; + ctx.translate(W/2, 140); + font("CONTINUE ?", 3); + ctx.restore(); + ctx.save(); + ctx.globalAlpha = 1; + ctx.translate(W/4, 210); + font("YES", MOBILE ? 4 : 6); + ctx.restore(); + ctx.save(); + ctx.globalAlpha = 1; + ctx.translate(3*W/4, 210); + font("NO", MOBILE ? 4 : 6); + ctx.restore(); + } + ctx.save(); + ctx.translate(W/2, H-14); + font("2015 GREWEB INC", .6); + ctx.restore(); + + if (!gameOver) { + ctx.save(); + ctx.translate(W/2, 20); + font(scoreTxt(best), .6); + ctx.restore(); + + ctx.save(); + ctx.translate(50, 20); + font(scoreTxt(score), 1.5, 1); + ctx.restore(); + } + + if (gameOver || playingSince<0 && t%1000<800) { + ctx.save(); + ctx.translate(W-20, H-24); + font(MOBILE ? "MOBILE" : "DESKTOP", .6, -1); + ctx.restore(); + ctx.save(); + ctx.translate(W-20, H-14); + font("VERSION", .6, -1); + ctx.restore(); + } + + ctx.restore(); +} + +function drawGlitch () { + ctx.save(); + ctx.fillStyle = + ctx.strokeStyle = "#f00"; + ctx.globalAlpha = 0.03; + ctx.translate(W/2, H/2); + ctx.beginPath(); + ctx.arc(0, 0, 4, 0, 2*Math.PI); + ctx.fill(); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(0, 0, 12, 0, 2*Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(0, 0, 12, 4, 6); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(0, 0, 12, 1, 2); + ctx.stroke(); + ctx.restore(); +} + + +// EXTERNAL UI + +var badgesIcons = [ + [ + [-11, -11], + [4, -13], + [6, -6], + [14, 0], + [14, 8], + [6, 8], + [-6, 14], + [-14, 0] + ], + [ + [-8, 13], + [0, -13], + [8, 13], + [0, 11], + [-8, 13], + , + [-10, -2], + [10, 2], + , + [10, -2], + [-10, 2], + , + ], + UFO.map(function (p) { + return p ? [p[0]-11,p[1]-7] : p; + }) +]; + +var lastStatement, lastStatementTime = 0; + +var lastMessage2; + +function drawUI () { + var currentMessage = "", + currentMessage2 = "", + currentMessageClr = "#f7c", + currentMessageClr2 = "#7fc"; + + function announcePlayer (player) { + currentMessage = "PLAYER "+player; + currentMessage2 = [ + "GENIOUS PLAYER!!", + "EXPERIENCED PLAYER!!", + "GOOD PLAYER. GET READY", + "NICE PLAYER.", + "BEGINNER.", + "VERY BEGINNER. EASY KILL" + ][Math.floor(Math.exp((-player)/8)*6)]; + } + + if (gameOver) { + currentMessage = "PLAYER MASTERED THE GAME"; + currentMessage2 = "REACHED ᐃᐃᐃᐃᐃ"; + } + else if (!player) { + if (playingSince<-7000) { + currentMessage = "BEHIND ASTEROIDS"; + currentMessage2 = "THE DARK SIDE"; + } + else if (playingSince<-3500) { + currentMessageClr = currentMessageClr2 = "#7cf"; + currentMessage = "SEND ASTEROIDS TO MAKE"; + currentMessage2 = "PLAYERS WASTE THEIR MONEY"; + } + else if (!awaitingContinue) { + var nb = Math.min(25, Math.floor((playingSince+3500)/80)); + for (var i=0; i-2000) + currentMessage2 = "A NEW PLAYER!"; + } + else { + if (playingSince<0) playingSince = 0; // jump to skip the "player coming" + announcePlayer(awaitingContinue); + } + } + else if (dying) { + if (lifes==1) { + currentMessageClr2 = "#f66"; + currentMessage = "GOOD JOB !!!"; + currentMessage2 = "THE DUDE IS BROKE"; + } + else if (lifes==2) { + currentMessageClr2 = "#f66"; + currentMessage = "OK..."; + currentMessage2 = "ONE MORE TIME !"; + } + else { + if (lastStatement && t - lastStatementTime > 3000) { // lastStatementTime is not used here + currentMessage = lastStatement; + } + else { + currentMessage = ["!!!", "GREAT!", "COOL!", "OMG!", "AHAH!", "RUDE!", "EPIC!", "WICKED!", "SHAME!", "HEHEHE!", "BWAHAHA!"]; + lastStatement = currentMessage = currentMessage[Math.floor(Math.random() * currentMessage.length)]; + lastStatementTime = 0; + } + } + } + else { + if (playingSince<0) { + currentMessage = "INCOMING NEW PLAYER..."; + currentMessage2 = "25Ā¢ 25Ā¢ 25Ā¢ 25Ā¢ 25Ā¢"; + } + else if (playingSince<6000 && lifes==4) { + announcePlayer(player); + } + else { + currentMessageClr2 = "#f66"; + if (lastStatement && t - lastStatementTime < 3000) { + currentMessage2 = lastStatement; + } + else { + if (neverPlayed) { + if (helpVisible()) { + currentMessageClr = currentMessageClr2 = "#f7c"; + currentMessage = MOBILE ? "TAP ON ASTEROIDS" : "PRESS ASTEROIDS LETTER"; + currentMessage2 = "TO SEND THEM TO THE GAME"; + } + } + else if (lifes > 4 && t - lastExtraLife > 5000) { + currentMessageClr = currentMessageClr2 = "#f66"; + currentMessage = "DON'T LET PLAYER"; + currentMessage2 = "REACH ᐃᐃᐃᐃᐃ !!!"; + } + else if (score > 10000 && t - lastExtraLife < 4500) { + currentMessageClr = currentMessageClr2 = "#f66"; + currentMessage = "OH NO! PLAYER JUST"; + currentMessage2 = "WON AN EXTRA LIFE!"; + } + else if (player==2 && 5000 5) { + lastStatement = 0; + if (Math.random() < 0.0001 * dt && t - lastStatementTime > 8000) { + currentMessage2 = [ + "COME ON! KILL IT!", + "JUST DO IT!", + "I WANT ¢¢¢", + "GIVE ME SOME ¢¢¢", + "DO IT!", + "DESTROY IT!" + ]; + lastStatement = currentMessage2 = currentMessage2[Math.floor(Math.random() * currentMessage2.length)]; + lastStatementTime = t; + } + } + } + } + } + + if (currentMessage2 && lastMessage2 !== currentMessage2 && + (currentMessageClr2 == "#f66" || currentMessageClr2 == "#f7c")) { + play(Amsg); + } + + ctx.save(); + ctx.translate(GAME_MARGIN, MOBILE ? 40 : 2); + ctx.lineWidth = (t%600>300) ? 2 : 1; + ctx.save(); + ctx.strokeStyle = currentMessageClr; + font(currentMessage, MOBILE ? 1.5 : 2, 1); + ctx.restore(); + ctx.save(); + ctx.strokeStyle = currentMessageClr2; + ctx.translate(0, MOBILE ? 30 : 40); + font(lastMessage2 = currentMessage2, MOBILE ? 1.5 : 2, 1); + ctx.restore(); + ctx.restore(); + + if (gameOver) return; + + ctx.save(); + ctx.translate(FW - GAME_MARGIN, 2); + ctx.lineWidth = 2; + ctx.strokeStyle = "#7cf"; + font(((playingSince>0&&awaitingContinue||player)*25)+"Ā¢", 2, -1); + ctx.restore(); + + + ctx.save(); + ctx.globalAlpha = musicTick ? 1 : 0.6; + ctx.strokeStyle = "#7cf"; + ctx.translate(FW - GAME_MARGIN, FH - 30); + if (combos) font(combos+"x", 1.5, -1); + ctx.restore(); + + /* + if (combos && combosTarget-combos < 9) { + ctx.save(); + ctx.strokeStyle = "#7cf"; + ctx.globalAlpha = musicTick ? 1 : 0.5; + ctx.translate(FW - GAME_MARGIN, FH - 50); + font((1+combosTarget-combos)+" ", 1, -1); + ctx.translate(0, 0); + path(UFO); + ctx.stroke(); + ctx.restore(); + } + */ + + if (achievements) { + ctx.save(); + ctx.translate(GAME_MARGIN + 50, FH - 20); + ctx.strokeStyle = "#fc7"; + drawAchievements(1); + ctx.restore(); + } +} + +function drawAchievements (fontSize) { + for (var j = 0; j < 3; j++) { + var badge = achievements[j]; + if (badge) { + ctx.save(); + ctx.translate(100 * j, 0); + path(badgesIcons[j]); + ctx.stroke(); + ctx.translate(0, -20 - 10 * fontSize); + font(""+badge, fontSize); + ctx.restore(); + } + } +} +/* global +g +gl +textureGame +smoothstep +glSetTexture +glBindFBO +glGetFBOTexture +glUniformLocation +glBindTexture +laserFbo +playerFbo +glareFbo +fbo1 fbo2 +persistenceFbo +copyShader +glBindShader +laserShader +playerShader +blur1dShader +gameShader +glareShader +persistenceShader +t +excitementSmoothed +gameOver +player +playingSince +lifes +lastLoseShot +shaking +jumping +dying +*/ + +function drawPostProcessing () { + glSetTexture(textureGame, g); + + // Laser + glBindFBO(laserFbo); + glBindShader(laserShader); + gl.uniform1i(glUniformLocation(laserShader, "t"), glBindTexture(textureGame, 0)); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + // Player / env + glBindFBO(playerFbo); + glBindShader(playerShader); + gl.uniform1f(glUniformLocation(playerShader, "pt"), playingSince / 1000); + gl.uniform1f(glUniformLocation(playerShader, "pl"), player); + gl.uniform1f(glUniformLocation(playerShader, "ex"), gameOver || excitementSmoothed); + gl.uniform1f(glUniformLocation(playerShader, "J"), jumping); + gl.uniform1f(glUniformLocation(playerShader, "P"), playingSince<0 || gameOver || dying ? 0 : 1); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(fbo1); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(playerFbo), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 2, 2 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(fbo2); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(fbo1), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), -2, 2 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(fbo1); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(fbo2), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 6, 0 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(playerFbo); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(fbo1), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 0, 2 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + // Glare + glBindFBO(glareFbo); + glBindShader(glareShader); + gl.uniform1i(glUniformLocation(glareShader, "t"), glBindTexture(glGetFBOTexture(laserFbo), 0)); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(fbo1); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(glareFbo), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 2, -4 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(glareFbo); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(fbo1), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 4, -8 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + // Blur + glBindFBO(fbo1); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(laserFbo), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 0.5, 0.5 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(fbo2); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(fbo1), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), -0.5, 0.5 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(fbo1); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(fbo2), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 1, 0 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(fbo2); + glBindShader(blur1dShader); + gl.uniform1i(glUniformLocation(blur1dShader, "t"), glBindTexture(glGetFBOTexture(fbo1), 0)); + gl.uniform2f(glUniformLocation(blur1dShader, "dir"), 0, 1 ); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + // Persistence + glBindFBO(fbo1); + glBindShader(persistenceShader); + gl.uniform1i(glUniformLocation(persistenceShader, "t"), glBindTexture(glGetFBOTexture(fbo2), 0)); + gl.uniform1i(glUniformLocation(persistenceShader, "r"), glBindTexture(glGetFBOTexture(persistenceFbo), 1)); + gl.drawArrays(gl.TRIANGLES, 0, 6); + glBindFBO(persistenceFbo); + glBindShader(copyShader); + gl.uniform1i(glUniformLocation(copyShader, "t"), glBindTexture(glGetFBOTexture(fbo1), 0)); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + // Final draw + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + glBindShader(gameShader); + gl.uniform1i(glUniformLocation(gameShader, "G"), glBindTexture(glGetFBOTexture(laserFbo), 0)); + gl.uniform1i(glUniformLocation(gameShader, "R"), glBindTexture(glGetFBOTexture(persistenceFbo), 1)); + gl.uniform1i(glUniformLocation(gameShader, "B"), glBindTexture(glGetFBOTexture(fbo2), 2)); + gl.uniform1i(glUniformLocation(gameShader, "L"), glBindTexture(glGetFBOTexture(glareFbo), 3)); + gl.uniform1i(glUniformLocation(gameShader, "E"), glBindTexture(glGetFBOTexture(playerFbo), 4)); + gl.uniform1f(glUniformLocation(gameShader, "s"), + !player ? smoothstep(-4000, -3000, playingSince) : 1); + gl.uniform1f(glUniformLocation(gameShader, "F"), + smoothstep(300, 0, t-lastLoseShot) + + !gameOver && lifes>4 ? 0.5 * smoothstep(-1, 1, Math.cos(0.01*t)) : 0); + gl.uniform2f(glUniformLocation(gameShader, "k"), shaking[0], shaking[1]); + gl.drawArrays(gl.TRIANGLES, 0, 6); +} +/* eslint-disable no-undef */ +/* eslint-enable no-unused-vars */ + +randomAsteroids(); +raf=requestAnimationFrame(render); + +if (DEBUG) { + /* eslint-disable */ +/* + // DEBUG the game over screen + setTimeout(function () { + playingSince = -1; + awaitingContinue = 0; + player = 42; + achievements = [123, 45, 6]; + gameOver = 1; + }, 1000); +*/ + // Debug the levels + addEventListener("resize", function () { + playingSince = -1; + awaitingContinue = 0; + player ++; + incomingObjects = []; + console.log("player=", player); +/* + ufos.push([ + 0, 0, 0, 0, 0 + ]); + */ + + }); + +/* + setTimeout(function () { + killSmoothed ++; + }, 100); + setTimeout(function () { + killSmoothed ++; + }, 2000); + */ + + // Debug the incomingObjects + +/* + setInterval(function () { + createInc(); + if (incomingObjects[0]) sendAsteroid(incomingObjects[0]); + incomingObjects.splice(0, 1); + }, 800); +*/ + + /* eslint-enable */ +} + +window._behindAsteroids_send = function () { + createInc(); + if (incomingObjects[0]) sendAsteroid(incomingObjects[0]); + incomingObjects.splice(0, 1); +}; + +// Game Render Loop + +var _lastT, _lastCheckSize = -9999; +function render (_t) { + raf=requestAnimationFrame(render); + if (!_lastT) _lastT = _t; + dt = Math.min(100, _t-_lastT); + _lastT = _t; + + if (t-_lastCheckSize>200) checkSize(); + + t += dt; // accumulate the game time (that is not the same as _t) + + // UPDATE game + update(); + + // RENDER game + + // Game rendering + + ctx = gameCtx; + + ctx.save(); + + drawGame(); + + ctx.restore(); + + RENDER_CB(); +} + +// Game Update Loop + +function update () { + playingSince += dt; + + if (t-ufoMusicTime>1200) { + ufoMusicTime = t; + if (ufos[0]) + play(Aufo); + } + + if(!gameOver && !awaitingContinue) { + + if (playingSince > 0 && !achievements) { + achievements = [0,0,0]; + } + + var i; + var nbSpaceshipBullets = 0; + + if (!dying && playingSince>0 && t-musicPaused>5000 && player > 2 && !ufos.length) { + + combosTarget = Math.floor(30 - 25 * Math.exp(-(player-3)/15)); + var musicFreq = 3*combos/combosTarget; + if (combos > combosTarget) { + musicPaused = t; + neverUFOs = combos = 0; + ufos.push([ + W * Math.random(), + H * Math.random(), + 0, + 0, + 0 + ]); + achievements[2] ++; + } + + musicPhase += musicFreq*2*Math.PI*dt/1000; + if ((Math.sin(musicPhase) > 0) !== musicTick) { + musicTick = !musicTick; + play(musicTick ? Amusic1 : Amusic2); + } + } + + // randomly send some asteroids + /* + if (Math.random() < 0.001 * dt) + randomInGameAsteroid(); + */ + + // player lifecycle + + if (lifes == 0 && playingSince > 0) { + // player enter + resurrectionTime = t; + lifes = 4; + player++; + score = 0; + scoreForLife = 10000; + jumpingAmp = 0; + jumpingFreq = 0; + asteroids = []; + ufos = []; + play(Acoin); + if (player > 1) { + //localStorage.ba_pl = player; + //localStorage.ba_ach = achievements; + } + } + + // inc lifecycle + + if (playingSince > 1000 && !dying) { + for (i = 0; i < incomingObjects.length; i++) { + var o = incomingObjects[i]; + if (!o[10]) { + var p = incPosition(o); + var matchingTap = tap && circleCollides(tap, p, (MOBILE ? 60 : 20) + 10 * o[6]); + if (keys[o[7]] || matchingTap) { + // send an asteroid + neverPlayed = tap = keys[o[7]] = 0; + if (sendAsteroid(o)) { + achievements[0] ++; + if (player > 3) combos ++; + incomingObjects.splice(i--, 1); + } + else { + // failed to aim (red aiming) + score += 5000; + combos = 0; + lastLoseShot = o[10] = t; + } + } + } + else { + if (t-o[10] > 1000) + incomingObjects.splice(i--, 1); + } + } + tap = 0; + + while(maybeCreateInc()); + } + + // spaceship lifecycle + + if (dying && t-dying > 2000 + (lifes>1 ? 0 : 2000)) { + dying = 0; + spaceship = [ W/2, H/2, 0, 0, 0 ]; + if (--lifes) { + resurrectionTime = t; + } + else { + // Player lost. game over + playingSince = -5000; + randomAsteroids(); + ufos = []; + setTimeout(function(){ play(Aleave); }, 1000); + } + } + + // score lifecycle + + if (score >= scoreForLife) { + lastExtraLife = t; + lifes ++; + scoreForLife += 10000; + play(Alife); + if (lifes > 5) { + gameOver = 1; + incomingObjects = []; + ufos = []; + randomAsteroids(); + //localStorage.ba_pl=0; + } + } + + if (!dying && playingSince>0 && t - lastScoreIncrement > 100) { + score += 10; + lastScoreIncrement = t; + } + best = Math.max(best, score); + + // collision + + bullets.forEach(function (bull, i) { + if (!bull[5]) nbSpaceshipBullets ++; + var j; + + if (bull[4]<900) { + // bullet-spaceship collision + if (!dying && circleCollides(bull, spaceship, 20)) { + explose(bull); + bullets.splice(i, 1); + spaceshipDie(); + return; + } + + // bullet-ufo collision + for (j = 0; j < ufos.length; ++j) { + var ufo = ufos[j]; + if (circleCollides(bull, ufo, 20)) { + explose(bull); + bullets.splice(i, 1); + ufos.splice(j, 1); + return; + } + } + } + + for (j = 0; j < asteroids.length; ++j) { + var aster = asteroids[j]; + var lvl = aster[5]; + // bullet-asteroid collision + if (circleCollides(bull, aster, 10 * lvl)) { + explose(bull); + bullets.splice(i, 1); + explodeAsteroid(j); + score += 50 * Math.floor(0.4 * (6 - lvl) * (6 - lvl)); + return; + } + } + }); + + if (!dying && playingSince > 0) asteroids.forEach(function (aster, j) { + // asteroid-spaceship collision + if (circleCollides(aster, spaceship, 10 + 10 * aster[5])) { + if (t - resurrectionTime < 200) { + // if spaceship just resurect, will explode the asteroid + explodeAsteroid(j); + } + else { + // otherwise, player die + explose(spaceship); + spaceshipDie(); + } + } + }); + + // run spaceship AI + AIexcitement = 0; + if (!dying && playingSince > 0) { + var ax = Math.cos(spaceship[4]); + var ay = Math.sin(spaceship[4]); + + // ai logic (determine the 3 inputs) + aiLogic(1-Math.exp(-(player-0.8)/14)); + + // apply ai inputs with game logic + + var rotSpeed = 0.004 + 0.003 * (1-Math.exp(-player/40)); + var accSpeed = 0.0003 - 0.0002 * Math.exp(-(player-1)/5) + 0.00001 * player; + var shotRate = 100 + 1000 * Math.exp(-(player-1)/8) + 300 * Math.exp(-player/20); + + spaceship[2] += AIboost * dt * accSpeed * ax; + spaceship[3] += AIboost * dt * accSpeed * ay; + spaceship[4] = normAngle(spaceship[4] + AIrotate * dt * rotSpeed); + if (nbSpaceshipBullets < 3) { + if (AIshoot && t-lastBulletShoot > shotRate) { + lastBulletShoot = t; + play(Ashot); + shoot(spaceship, 0.3, spaceship[4]); + } + } + } + } + + euclidPhysics(spaceship); + asteroids.forEach(polarPhysics); + ufos.forEach(euclidPhysics); + bullets.forEach(euclidPhysics); + particles.forEach(polarPhysics); + + ufos.forEach(applyUFOlogic); + incomingObjects.forEach(applyIncLogic); + + particles.forEach(applyLife); + loopOutOfBox(spaceship); + asteroids.forEach(playingSince > 0 && !awaitingContinue && !gameOver ? destroyOutOfBox : loopOutOfBox); + ufos.forEach(loopOutOfBox); + bullets.forEach(applyLife); + bullets.forEach(loopOutOfBox); + + excitementSmoothed += 0.04 * (AIexcitement - excitementSmoothed); + AIboostSmoothed += 0.04 * (AIboost - AIboostSmoothed); + + // handling jumping / shaking + killSmoothed -= dt * 0.0003 * killSmoothed; + jumpingAmpSmoothed += 0.04 * (jumpingAmp - jumpingAmpSmoothed); + jumpingFreqSmoothed += 0.04 * (jumpingFreq - jumpingFreqSmoothed); + if (killSmoothed > 1.3) { + if (jumpingAmp < 0.5) { + jumpingFreq = 1 + Math.random(); + jumpingAmp ++; + } + } + if (killSmoothed < 0.8) { + jumpingAmp = 0; + } + var prevPhase = jumpingPhase; + jumpingPhase += jumpingFreq *2*Math.PI*dt/1000; + jumping = jumpingAmpSmoothed * Math.pow(Math.cos(jumpingPhase), 2.0); + if (Math.cos(prevPhase) < 0 && 0 < Math.cos(jumpingPhase)) { + jumpingFreq = 1 + 3 * Math.random() * Math.random(); + } + if (jumpingAmp < 0.5) { + jumpingAmpSmoothed += 0.04 * (jumpingAmp - jumpingAmpSmoothed); + } + + var shake = jumpingAmp * Math.pow(smoothstep(0.2, 0.0, jumping), 0.5); + if (shake > 0.5 && t-lastJump>100) { + play(Ajump); + lastJump = t; + } + shaking = [ + 30 * shake * (Math.random()-0.5) / FW, + 30 * shake * (Math.random()-0.5) / FH + ]; +} + + +// Game DRAWING + +function drawGame () { + ctx.save(); + ctx.fillStyle = "#000"; + ctx.fillRect(0, 0, W, H); + ctx.restore(); + + renderCollection(asteroids, drawAsteroid); + renderCollection(ufos, drawUFO); + renderCollection(bullets, drawBullet); + renderCollection(particles, drawParticle); + + if (playingSince > 0 && !awaitingContinue && !gameOver) { + ctx.save(); + translateTo(spaceship); + drawSpaceship(spaceship); + ctx.restore(); + } + + drawGameUI(); + + drawGlitch(); +} + + +function translateTo (p) { + ctx.translate(p[0], p[1]); +} + +function renderCollection (coll, draw) { + for (var i=0; i4 ? 0.5 * smoothstep(-1, 1, Math.cos(0.01*t)) : 0; + var k = [ shaking[0], shaking[1] ]; + return { + pt: pt, + pl: pl, + ex: ex, + J: J, + P: P, + s: s, + F: F, + k: k, + W: W, + H: H, + S: SEED + }; + } +}; + +}); diff --git a/cookbook/src/examples/behindasteroids/index.js b/cookbook/src/examples/behindasteroids/index.js new file mode 100755 index 0000000..41e306e --- /dev/null +++ b/cookbook/src/examples/behindasteroids/index.js @@ -0,0 +1,339 @@ +//@flow +import React, { Component } from "react"; +import { Shaders, Node, GLSL, Bus, Backbuffer } from "gl-react"; +import { Surface } from "gl-react-dom"; +import gameBuild from "./build"; + +/** + * This example reproduce the after effects made in a js13k game: + * https://github.com/gre/behind-asteroids + * see also https://github.com/gre/behind-asteroids/blob/master/src/effects.js + */ + +const shaders = Shaders.create({ + blur1d: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D t; +uniform vec2 dim; +uniform vec2 dir; +void main() { + vec4 color = vec4(0.0); + vec2 off1 = vec2(1.3846153846) * dir; + vec2 off2 = vec2(3.2307692308) * dir; + color += texture2D(t, uv) * 0.2270270270; + color += texture2D(t, uv + (off1 / dim)) * 0.3162162162; + color += texture2D(t, uv - (off1 / dim)) * 0.3162162162; + color += texture2D(t, uv + (off2 / dim)) * 0.0702702703; + color += texture2D(t, uv - (off2 / dim)) * 0.0702702703; + gl_FragColor = color; +} + ` + }, + game: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D G; +uniform sampler2D R; +uniform sampler2D B; +uniform sampler2D L; +uniform sampler2D E; +uniform float s; +uniform float F; +uniform vec2 k; +float squircleDist (vec2 a, vec2 b) { + float p = 10.0; + vec2 c = a-b; + return pow(abs(pow(abs(c.x), p)+pow(abs(c.y), p)), 1.0/p); +} +void main() { + vec2 UV = uv + k; + vec2 pos = (UV/0.98)-0.01; + float d = squircleDist(UV, vec2(0.5)); + float dd = smoothstep(0.45, 0.51, d); + pos = mix(pos, vec2(0.5), 0.2 * (0.6 - d) - 0.02 * d); + vec3 gc = texture2D(G, pos).rgb; + gl_FragColor = + step(0.0, UV.x) * + step(UV.x, 1.0) * + step(0.0, UV.y) * + step(UV.y, 1.0) * + vec4(( + vec3(0.03 + 0.1 * F, 0.04, 0.05) + + mix(vec3(0.05, 0.1, 0.15) - gc, 2.0 * gc, s) + + s * ( + texture2D(L, pos).rgb + + vec3(0.3 + F, 0.6, 1.0) * ( + texture2D(R, pos).rgb + + 3.0 * texture2D(B, pos).rgb + ) + + 0.5 * texture2D(E, pos).rgb + ) + ) + * mix(1.0, smoothstep(1.0, 0.0, dd), 0.6), 1.0); +} + ` + }, + glare: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D t; +void main() { + gl_FragColor = vec4(step(0.9, texture2D(t, uv).r)); +} + ` + }, + laser: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D t; +void main() { + vec3 c = texture2D(t, uv).rgb; + vec2 off = 0.003 * vec2( + cos(47.0 * uv.y), + sin(67.0 * uv.x) + ); + gl_FragColor = vec4( + c.r + c.g + c.b + texture2D(t, uv+off).b + ); +} + + ` + }, + persistence: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D t; +uniform sampler2D r; +void main() { + vec3 b = texture2D(r, uv).rgb; + gl_FragColor = vec4( + b * (0.82 - 0.3 * b.r * b.r) + + texture2D(t, uv).rgb, + 1.0); +} +` + + }, + player: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform float pt; +uniform float pl; +uniform float S; +uniform float ex; +uniform float J; +uniform float P; +float disc (vec2 c, vec2 r) { + return step(length((uv - c) / r), 1.0); +} +float squircle (vec2 c, vec2 r, float p) { + vec2 v = (uv - c) / r; + return step(pow(abs(v.x), p) + pow(abs(v.y), p), 1.0); +} +vec3 env () { + return 0.1 + + 0.3 * vec3(1.0, 0.9, 0.7) * + smoothstep(0.4, 0.1, distance(uv, vec2(0.2, 1.2))) + + 0.4 * vec3(0.8, 0.6, 1.0) * + smoothstep(0.5, 0.2, distance(uv, vec2(1.3, 0.7))); +} +vec4 player (float p, float dx) { + vec4 c = vec4(0.0); + vec2 e = vec2( + min(ex, 1.0), + mix(min(ex, 1.0), min(ex-1.0, 1.0), 0.5)); + vec4 skin = 0.2 + 0.4 * pow(abs(cos(4.*p+S)), 2.0) * + vec4(1.0, 0.7, 0.3, 1.0); + vec4 hair = vec4(0.5, 0.3, 0.3, 1.0); + vec4 sweater = vec4( + 0.3 * (1.0 + cos(3.*p + 6.*S)), + 0.2 * (1.0 + cos(7.*p + 7.*S)), + 0.1+0.2 * (1.0 + sin(7.*p + 8.*S)), + 1.0); + float feminity = step(sin(9.0*p+S), 0.0); + float hairSize = 0.02 + 0.02 * feminity * cos(p+S); + float walk = step(dx, -0.01) + step(0.01, dx); + float play = (1.0 - walk) * step(0.0, pt); + vec2 pos = vec2(0.5) + + J * vec2(0.0, 0.2) + + walk * vec2( + 0.03 * cos(4.0*pt + sin(pt)), + 0.05 * abs(sin(3.0*pt))) + + e * play * (1.0 - P) * vec2( + 0.05 * cos(pt * (1.0 + 0.1 * sin(pt))), + 0.05 * abs(sin(pt))); + vec2 pos2 = mix(pos, vec2(0.5), 0.5); + pos.x += dx; + pos2.x += dx; + c += skin * disc(pos, vec2(0.06, 0.1)); + c *= 1.0 - (0.5 + 0.5 * feminity) * + disc(pos - vec2(0.0, 0.04), vec2(0.03, 0.01)); + c *= 1.0 - disc(pos + vec2(0.03, 0.03), vec2(0.02, 0.01)); + c *= 1.0 - disc(pos + vec2(-0.03, 0.03), vec2(0.02, 0.01)); + c *= 1.0 - 0.6 * disc(pos, vec2(0.01, 0.02)); + c += hair * disc(pos + vec2(0.0, hairSize), vec2(0.07, 0.1 + hairSize)); + c += play * (hair + skin) * disc(pos2 - vec2( + -0.2 + 0.01 * cos(5.0*pt), + 0.45 - 0.1 * e.y * step(0.0, pt) * P * pow(abs(sin(8.0 * pt * + (1.0 + 0.2 * cos(pt)))), 4.0) + ), vec2(0.055, 0.05)); + c += play * (hair + skin) * disc(pos2 - vec2( + 0.2 + 0.01 * cos(5.0*pt), + 0.45 - 0.1 * e.x * step(2.0, pt) * P * pow(abs(cos(7.0 * pt)), 4.0) + ), vec2(0.055, 0.05)); + c += step(c.a, 0.0) * (hair + skin) * + squircle(pos - vec2(0.0, 0.10 + 0.02 * feminity), + vec2(0.05 - 0.01 * feminity, 0.03), 4.0); + vec2 sr = vec2( + 0.16 + 0.04 * sin(9.*p), + 0.27 + 0.02 * cos(9.*p)); + c += step(c.r+c.g+c.b, 0.0) * sweater * step(1.0, + squircle(pos - vec2(0.0, 0.35), sr * (1.0 - 0.1 * feminity), 4.0) + + disc(pos - vec2(0.0, 0.35), sr)); + return c; +} +void main() { + float light = 0.6 + 0.4 * smoothstep(2.0, 0.0, distance(pt, -2.0)); + vec4 c = vec4(0.0); + c += (1.0 - smoothstep(-0.0, -5.0, pt)) * + player(pl+step(pt, 0.0), -0.6 * smoothstep(-1., -5., pt)); + c += step (1.0, pl) * + player(pl+step(pt, 0.0)-1.0, 2.0 *smoothstep(-4., -1., pt)); + c *= 1.0 - 1.3 * distance(uv, vec2(0.5)); + gl_FragColor = vec4(light * mix(env(), c.rgb, clamp(c.a, 0.0, 1.0)), 1.0); +} +` + } +}); + +const Blur1D = + ({ dim, dir, children: t }) => + ; + +export default class Example extends Component { + render () { + const {showCanvas} = this.props; + const {pt,pl,ex,J,P,s,F,k,S,W,H} = + // HACK to just render the game + this._ ? this._.getWebGLParams() : +({ pt: 0, pl: 0, ex: 0, J: 0, P: 0, s: 0, F: 0, k: [0,0], W: 2, H: 2, S: 0 }); + const dim = [ W, H ]; + + return ( +
+ + + + + this.refs.gameCanvas }} + /> + + + + + + + + + + + + + + + + + this.refs.laser }} + /> + + + + + + {() => this.refs.glare} + + + + + + + + + {() => this.refs.laser + //FIXME this should be glare instead. + //but i think there is a bug in gl-react! + } + + + + + + + + + + + this.refs.laser, + R: () => this.refs.persistence, + B: () => this.refs.glareBlurred, + L: () => this.refs.glareCursor, + E: () => this.refs.player, + s, + F, + k + }} /> + + +
+ ); + } + + _: any; + componentDidMount () { + this._ = gameBuild( + this.refs.container, + this.refs.gameCanvas, + () => this.forceUpdate() + ); + } + componentWillUnmount() { + this._.dispose(); + } + + sendAsteroid = () => window._behindAsteroids_send(); + + static defaultProps = { + showCanvas: false, + }; +} diff --git a/cookbook/src/examples/behindasteroids/meta.js b/cookbook/src/examples/behindasteroids/meta.js new file mode 100755 index 0000000..91a15bc --- /dev/null +++ b/cookbook/src/examples/behindasteroids/meta.js @@ -0,0 +1,18 @@ +import React from "react"; + +export const title = "Behind Asteroids (js13k 2015 – greweb)"; + +export const toolbox = [ + { prop: "showCanvas", + title: "Under the hood", + Editor: + ({ value, onChange }) => +
+ +

The rest of the rendering is done in WebGL (ported in gl-react).

+
+ }, +]; diff --git a/cookbook/src/examples/blurimgtitle/index.js b/cookbook/src/examples/blurimgtitle/index.js new file mode 100755 index 0000000..15177ae --- /dev/null +++ b/cookbook/src/examples/blurimgtitle/index.js @@ -0,0 +1,165 @@ +//@flow +import React, { PureComponent, Component, PropTypes } from "react"; +import { Shaders, Node, GLSL, Bus, LinearCopy } from "gl-react"; +import { Surface } from "gl-react-dom"; +import JSON2D from "react-json2d"; +import {Blur1D} from "../blurxy"; +import {Blur} from "../blurmulti"; +import {BlurV} from "../blurmap"; + +const shaders = Shaders.create({ + ImageTitle: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D img, imgBlurred, imgTone, title, blurMap; +uniform float colorThreshold; +float monochrome (vec3 c) { + return 0.2125 * c.r + 0.7154 * c.g + 0.0721 * c.b; +} +void main() { + float blurFactor = texture2D(blurMap, uv).r; + vec4 bgColor = mix( + texture2D(img, uv), + texture2D(imgBlurred, uv), + step(0.01, blurFactor) + ); + vec4 textColor = vec4(vec3( + step(monochrome(texture2D(imgTone, uv).rgb), colorThreshold) + ), 1.0); + float isText = 1.0 - texture2D(title, uv).r; + gl_FragColor = mix(bgColor, textColor, isText); +}` + }, + TitleBlurMap: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D t; +uniform float threshold; +void main() { +gl_FragColor = vec4( + vec3(smoothstep(1.0, threshold, texture2D(t, uv).r)), + 1.0); +}` + }, +}); + +const AveragePixels = ({ children, quality }) => + + + {children} + + ; + +const TitleBlurMap = ({ children: title, threshold }) => + + {title} + + }} + width={64} + height={64} + />; + +class Title extends PureComponent { + render () { + const { children, width, height } = this.props; + return {{ + size: [ width, height ], + background: "#fff", + draws: [ + { + "font": "bold 78px Didot,Georgia,serif", + "fillStyle": "#000", + "textBaseline": "top", + "textAlign": "center" + }, + [ "fillText", children, width/2, 10, 84 ], + ] + }}; + } +} + +class ImageTitle extends Component { + static contextTypes = { + width: PropTypes.number.isRequired, + height: PropTypes.number.isRequired, + }; + render() { + const { children: img, text, colorThreshold } = this.props; + const { width, height } = this.context; + return ( + this.refs.imgBlurred, + title:() => this.refs.title, + imgTone:() => this.refs.imgTone, + blurMap:() => this.refs.blurMap, + }}> + + + + {text} + + + + + + {() => this.refs.title} + + + + + + {img} + + + + + this.refs.blurMap} + factor={4} + passes={4}> + {img} + + + + + ); + } +} + +export default class Example extends Component { + render() { + const { title, colorThreshold, image } = this.props; + return ( + + + {image} + + + ); + } + + static defaultProps = { + title: "Hello\nSan Francisco\n☻", + colorThreshold: 0.6, + image: require("./sf-6.jpg"), + }; +} diff --git a/cookbook/src/examples/blurimgtitle/meta.js b/cookbook/src/examples/blurimgtitle/meta.js new file mode 100755 index 0000000..c48e0ed --- /dev/null +++ b/cookbook/src/examples/blurimgtitle/meta.js @@ -0,0 +1,36 @@ +import ImagesPicker from "../../toolbox/ImagesPicker"; +import makeTextArea from "../../toolbox/makeTextArea"; +import makeFloatSlider from "../../toolbox/makeFloatSlider"; + +export const title = "Dynamic Blur Image Title"; + +export const toolbox = [ + { prop: "title", + title: "Title", + Editor: makeTextArea({ + height: 140, + padding: 6, + fontFamily: "Didot,Georgia,serif", + fontSize: "36px", + lineHeight: "42px", + fontWeight: "bold", + textAlign: "center", + }) }, + { prop: "colorThreshold", + title: "Color Threshold", + Editor: makeFloatSlider(0, 1, 0.01) }, // FIXME black <-> white + { prop: "image", + title: "Image", + Editor: ImagesPicker, + style: { width: 400 }, + imageStyle: { maxWidth: 56, maxHeight: 56, marginBottom: 16, }, + images: [ + require("./sf-1.jpg"), + require("./sf-2.jpg"), + require("./sf-3.jpg"), + require("./sf-4.jpg"), + require("./sf-5.jpg"), + require("./sf-6.jpg"), + require("./sf-7.jpg"), + ] }, +]; diff --git a/cookbook/src/examples/blurimgtitle/sf-1.jpg b/cookbook/src/examples/blurimgtitle/sf-1.jpg new file mode 100755 index 0000000..ecf1d69 Binary files /dev/null and b/cookbook/src/examples/blurimgtitle/sf-1.jpg differ diff --git a/cookbook/src/examples/blurimgtitle/sf-2.jpg b/cookbook/src/examples/blurimgtitle/sf-2.jpg new file mode 100755 index 0000000..bf98e68 Binary files /dev/null and b/cookbook/src/examples/blurimgtitle/sf-2.jpg differ diff --git a/cookbook/src/examples/blurimgtitle/sf-3.jpg b/cookbook/src/examples/blurimgtitle/sf-3.jpg new file mode 100755 index 0000000..a27d486 Binary files /dev/null and b/cookbook/src/examples/blurimgtitle/sf-3.jpg differ diff --git a/cookbook/src/examples/blurimgtitle/sf-4.jpg b/cookbook/src/examples/blurimgtitle/sf-4.jpg new file mode 100755 index 0000000..2f75b7d Binary files /dev/null and b/cookbook/src/examples/blurimgtitle/sf-4.jpg differ diff --git a/cookbook/src/examples/blurimgtitle/sf-5.jpg b/cookbook/src/examples/blurimgtitle/sf-5.jpg new file mode 100755 index 0000000..c81c6de Binary files /dev/null and b/cookbook/src/examples/blurimgtitle/sf-5.jpg differ diff --git a/cookbook/src/examples/blurimgtitle/sf-6.jpg b/cookbook/src/examples/blurimgtitle/sf-6.jpg new file mode 100755 index 0000000..b73a869 Binary files /dev/null and b/cookbook/src/examples/blurimgtitle/sf-6.jpg differ diff --git a/cookbook/src/examples/blurimgtitle/sf-7.jpg b/cookbook/src/examples/blurimgtitle/sf-7.jpg new file mode 100755 index 0000000..710bec7 Binary files /dev/null and b/cookbook/src/examples/blurimgtitle/sf-7.jpg differ diff --git a/cookbook/src/examples/blurmap/index.js b/cookbook/src/examples/blurmap/index.js new file mode 100755 index 0000000..e98e520 --- /dev/null +++ b/cookbook/src/examples/blurmap/index.js @@ -0,0 +1,68 @@ +//@flow +import React, { Component } from "react"; +import { Shaders, Node, GLSL, connectSize } from "gl-react"; +import { Surface } from "gl-react-dom"; + +const shaders = Shaders.create({ + blurV1D: { + frag: GLSL`precision highp float; +varying vec2 uv; +uniform sampler2D t, map; +uniform vec2 direction, resolution; +vec4 blur9(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { + vec4 color = vec4(0.0); + vec2 off1 = vec2(1.3846153846) * direction; + vec2 off2 = vec2(3.2307692308) * direction; + color += texture2D(image, uv) * 0.2270270270; + color += texture2D(image, uv + (off1 / resolution)) * 0.3162162162; + color += texture2D(image, uv - (off1 / resolution)) * 0.3162162162; + color += texture2D(image, uv + (off2 / resolution)) * 0.0702702703; + color += texture2D(image, uv - (off2 / resolution)) * 0.0702702703; + return color; +} +void main() { + gl_FragColor = blur9(t, uv, resolution, direction * texture2D(map, uv).rg); +}` + } +}); + +// Same concept than Blur1D except it takes one more prop: +// a map texture that tells the blur intensity for a given position. +export const BlurV1D = + connectSize(({ children: t, direction, map, width, height }) => + ); + +// And its N-pass version +import {directionForPass} from "../blurmulti"; +export const BlurV = + connectSize(({ children, factor, map, passes }) => { + const rec = pass => + pass <= 0 + ? children + : + {rec(pass-1)} + ; + return rec(passes); + }); + +export default class Example extends Component { + render() { + const { factor, passes, map } = this.props; + return ( + + + https://i.imgur.com/NjbLHx2.jpg + + + ); + } + static defaultProps = { + factor: 2, + passes: 4, + map: StaticBlurMap.images[0], + }; +}; +import StaticBlurMap from "../../toolbox/StaticBlurMap"; diff --git a/cookbook/src/examples/blurmap/meta.js b/cookbook/src/examples/blurmap/meta.js new file mode 100755 index 0000000..576da6f --- /dev/null +++ b/cookbook/src/examples/blurmap/meta.js @@ -0,0 +1,21 @@ +import markdown from "../../markdown"; +import StaticBlurMap from "../../toolbox/StaticBlurMap"; +import makeFloatSlider from "../../toolbox/makeFloatSlider"; +export const title = "Blur with intensity map & multi-pass"; +export const desc = markdown` +common use-case: vary Blur based on the position +`; +export const descAfter = markdown` +We use a texture to map the depth of the Blur. +`; +export const toolbox = [ + { prop: "factor", + title: "Blur", + Editor: makeFloatSlider(0, 8, 0.2) }, + { prop: "passes", + title: value => `Blur Passes (${value})`, + Editor: makeFloatSlider(1, 8, 1) }, + { prop: "map", + title: "Blur Texture Map", + Editor: StaticBlurMap }, +]; diff --git a/cookbook/src/examples/blurmapdyn/index.js b/cookbook/src/examples/blurmapdyn/index.js new file mode 100755 index 0000000..d823999 --- /dev/null +++ b/cookbook/src/examples/blurmapdyn/index.js @@ -0,0 +1,46 @@ +//@flow +import React, { Component } from "react"; +import { Shaders, Node, Bus, GLSL } from "gl-react"; +import { Surface } from "gl-react-dom"; +import {BlurV} from "../blurmap"; +import timeLoop from "../../HOC/timeLoop"; + +const shaders = Shaders.create({ + ConicalGradiant: { + frag: GLSL`precision highp float; +varying vec2 uv; +uniform float phase; +const float PI = 3.14159; +void main () { + gl_FragColor = vec4(vec3( + mod(phase + atan(uv.x-0.5, uv.y-0.5)/(2.0*PI), 1.0) + ), 1.0); +}` } +}); + +const ConicalGradiantLoop = timeLoop(({ time }) => + ); + +export default class Example extends Component { + render() { + const { factor, passes } = this.props; + // also needs to be computed once. + return ( + + + + + this.refs.blurMapBus} passes={passes} factor={factor}> + https://i.imgur.com/NjbLHx2.jpg + + + ); + } + static defaultProps = { + factor: 6, + passes: 4, + }; +}; diff --git a/cookbook/src/examples/blurmapdyn/meta.js b/cookbook/src/examples/blurmapdyn/meta.js new file mode 100755 index 0000000..6436dfa --- /dev/null +++ b/cookbook/src/examples/blurmapdyn/meta.js @@ -0,0 +1,14 @@ +import markdown from "../../markdown"; +import makeFloatSlider from "../../toolbox/makeFloatSlider"; +export const title = "Blur with intensity map & multi-pass"; +export const desc = markdown` +Any arbitrary shader can be used as a blur map! +`; +export const toolbox = [ + { prop: "factor", + title: "Blur", + Editor: makeFloatSlider(0, 8, 0.2) }, + { prop: "passes", + title: value => `Blur Passes (${value})`, + Editor: makeFloatSlider(1, 8, 1) }, +]; diff --git a/cookbook/src/examples/blurmapmouse/index.js b/cookbook/src/examples/blurmapmouse/index.js new file mode 100755 index 0000000..e018429 --- /dev/null +++ b/cookbook/src/examples/blurmapmouse/index.js @@ -0,0 +1,62 @@ +//@flow +import React, { Component } from "react"; +import { Shaders, Node, Bus, GLSL } from "gl-react"; +import { Surface } from "gl-react-dom"; +import {BlurV} from "../blurmap"; + +const shaders = Shaders.create({ + Offset: { + frag: GLSL`precision highp float; +varying vec2 uv; +uniform sampler2D t; +uniform vec2 offset; +void main () { + gl_FragColor = texture2D(t, uv + offset); +}` + } +}); + +const Offset = ({ t, offset }) => + ; + +export default class Example extends Component { + state = { + offset: [0,0], + }; + render() { + const { map } = this.props; + const { offset } = this.state; +// Sharing computation of a GL Node. +// should not be passed straight to BlurV's map because +// it would duplicates it in the tree ([passes] times) +// Instead, we need to express a graph and share the +// computation with a Bus ref. +// We pass to BlurV's map prop a function that resolve that ref. + return ( + + + + + this.refs.blurMapBus} passes={6} factor={6}> + https://i.imgur.com/NjbLHx2.jpg + + + ); + } + onMouseMove = (e: *) => { + const rect = e.target.getBoundingClientRect(); + this.setState({ offset: [ + -(e.clientX - rect.left - rect.width/2) / rect.width, + (e.clientY - rect.top - rect.height/2) / rect.height + ] }); + }; + onMouseLeave = () => this.setState({ offset: [0,0] }); + static defaultProps = { + map: StaticBlurMap.images[0], + }; +}; +import StaticBlurMap from "../../toolbox/StaticBlurMap"; diff --git a/cookbook/src/examples/blurmapmouse/meta.js b/cookbook/src/examples/blurmapmouse/meta.js new file mode 100755 index 0000000..4bbdad8 --- /dev/null +++ b/cookbook/src/examples/blurmapmouse/meta.js @@ -0,0 +1,16 @@ +import markdown from "../../markdown"; +import StaticBlurMap from "../../toolbox/StaticBlurMap"; +export const title = "Blur map and Mouse position"; +export const desc = markdown` +Dynamically change Blur Map with mouse move +`; +export const descAfter = markdown` +This example is the first to show the need of Sharing Computation: +We want the "offset" framebuffer to be computed once, +we use gl-react [\`\`](/api#bus) concept for this. +`; +export const toolbox = [ + { prop: "map", + title: "Blur Texture Map", + Editor: StaticBlurMap }, +]; diff --git a/cookbook/src/examples/blurmulti/index.js b/cookbook/src/examples/blurmulti/index.js new file mode 100755 index 0000000..f494edb --- /dev/null +++ b/cookbook/src/examples/blurmulti/index.js @@ -0,0 +1,45 @@ +//@flow +import React, { Component } from "react"; +import { connectSize } from "gl-react"; +import { Surface } from "gl-react-dom"; +import { Blur1D } from "../blurxy"; + +// empirical strategy to chose a 2d vector for a blur pass +const NORM = Math.sqrt(2)/2; +export const directionForPass = (p: number, factor: number, total: number) => { + const f = factor * 2 * Math.ceil(p / 2) / total; + switch ((p-1) % 4) { // alternate horizontal, vertical and 2 diagonals + case 0: return [f,0]; + case 1: return [0,f]; + case 2: return [f*NORM,f*NORM]; + default: return [f*NORM,-f*NORM]; + } +} + +// recursively apply Blur1D to make a multi pass Blur component +export const Blur = connectSize(({ children, factor, passes }) => { + const rec = pass => + pass <= 0 + ? children + : + {rec(pass-1)} + ; + return rec(passes); +}); + +export default class Example extends Component { + render() { + const { factor, passes } = this.props; + return ( + + + https://i.imgur.com/iPKTONG.jpg + + + ); + } + static defaultProps = { + factor: 2, + passes: 4, + }; +} diff --git a/cookbook/src/examples/blurmulti/meta.js b/cookbook/src/examples/blurmulti/meta.js new file mode 100755 index 0000000..81837f2 --- /dev/null +++ b/cookbook/src/examples/blurmulti/meta.js @@ -0,0 +1,22 @@ +import markdown from "../../markdown"; +import makeFloatSlider from "../../toolbox/makeFloatSlider"; +export const title = "multi-pass Blur"; +export const desc = markdown` +For a better Blur quality, we need more than 2 passes. +We generalize the concept to make a N-passes component. +4-passes Blur is good enough, even without downscaling. +`; +export const descAfter = markdown` +We apply blur on various direction, not just X and Y axis. +We also vary the intensity for each pass. +\`directionForPass\` implements this empirical approach. +`; + +export const toolbox = [ + { prop: "factor", + title: "Blur", + Editor: makeFloatSlider(0, 8, 0.2) }, + { prop: "passes", + title: passes => `Blur Passes (${passes})`, + Editor: makeFloatSlider(0, 8, 1) }, +]; diff --git a/cookbook/src/examples/blurvideo/index.js b/cookbook/src/examples/blurvideo/index.js new file mode 100755 index 0000000..d53b1fb --- /dev/null +++ b/cookbook/src/examples/blurvideo/index.js @@ -0,0 +1,45 @@ +//@flow +import React, { Component } from "react"; +import { Bus } from "gl-react"; +import { Surface } from "gl-react-dom"; +import {BlurV} from "../blurmap"; +import {Saturate} from "../saturation"; +import {Video, videoMP4} from "../video"; + +// We must use a if we don't want the