├── .gitignore
├── example
└── hello
│ ├── flint.json
│ ├── shaders
│ ├── cursor.frag
│ └── cursor.vert
│ ├── README.md
│ ├── webpack.config.js
│ ├── package.json
│ └── hello.js
├── package.json
├── README.md
├── lib
├── ReactFlintIDOperations.js
├── ReactFlintInjection.js
├── ReactFlintReconcileTransaction.js
├── render.js
└── ReactFlintComponent.js
└── LICENSE.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | npm-debug.log
--------------------------------------------------------------------------------
/example/hello/flint.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Hello",
3 | "files": [
4 | "build/hello.js"
5 | ],
6 | "entrypoint": "build/hello.js"
7 | }
--------------------------------------------------------------------------------
/example/hello/shaders/cursor.frag:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | in lowp vec4 fragmentColor;
4 | out lowp vec4 outColor;
5 |
6 | void main()
7 | {
8 | outColor = vec4(0, 0, 0, 1);
9 | }
--------------------------------------------------------------------------------
/example/hello/shaders/cursor.vert:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | in vec3 Position;
4 | in vec4 VertexColor;
5 | uniform mat4 Modelm;
6 | uniform mat4 Viewm;
7 | uniform mat4 Projectionm;
8 | out vec4 fragmentColor;
9 |
10 | void main()
11 | {
12 | gl_Position = Projectionm * (Viewm * (Modelm * vec4(Position, 1.0)));
13 | fragmentColor = VertexColor;
14 | }
--------------------------------------------------------------------------------
/example/hello/README.md:
--------------------------------------------------------------------------------
1 | FlintVR React Hello Example
2 | ---------------------------
3 |
4 | This is a simple app for Flint VR that puts a model on the screen, and turns
5 | it into a cursor that follows where you look.
6 |
7 |
8 | Getting Started
9 | ===============
10 |
11 | * npm install
12 | * npm run build
13 |
14 |
15 | Running
16 | =======
17 |
18 | * npm run serve
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flint-react",
3 | "version": "0.0.1",
4 | "description": "React bindings for FlintVR",
5 | "main": "./lib/render.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "directories": {
10 | "lib": "./lib"
11 | },
12 | "dependencies": {
13 | "react": "~0.14.7"
14 | },
15 | "author": "Eric Florenzano",
16 | "license": "BSD-3-Clause"
17 | }
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | flint-react
2 | -----------
3 |
4 | This is a small shim library to wrap FlintVR with React.js bindings.
5 |
6 |
7 | How to Use
8 | ----------
9 |
10 | Please see the example project. Basically this is just regular react
11 | but you can use 'scene' and 'model' components from FlintVR.
12 |
13 | ```javascript
14 | import { render } from 'flint-react';
15 |
16 | global.vrmain = function(env) {
17 | render(
18 |
19 |
20 |
21 | );
22 | }
23 | ```
--------------------------------------------------------------------------------
/example/hello/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | context: __dirname,
3 | entry: './hello.js',
4 | output: {
5 | path: __dirname + '/build',
6 | filename: 'hello.js'
7 | },
8 | module: {
9 | loaders: [
10 | {
11 | test: /\.jsx?$/,
12 | exclude: /(node_modules|bower_components)/,
13 | loader: 'babel',
14 | query: {
15 | presets: ['es2015', 'react']
16 | }
17 | },
18 | {
19 | test: /\.(vert|frag)$/,
20 | loader: 'raw'
21 | }
22 | ]
23 | }
24 | };
--------------------------------------------------------------------------------
/lib/ReactFlintIDOperations.js:
--------------------------------------------------------------------------------
1 | const blessedNodes = {};
2 |
3 | class ReactBlessedIDOperations {
4 | add(ID, node) {
5 | blessedNodes[ID] = node;
6 | return this;
7 | }
8 |
9 | get(ID) {
10 | const numPeriods = ID.match(/\./g).length;
11 |
12 | // If the node is root, we return the scene
13 | if (numPeriods === 0) {
14 | return Flint.scene;
15 | }
16 |
17 | return blessedNodes[ID];
18 | }
19 |
20 | getParent(ID) {
21 | const numPeriods = ID.match(/\./g).length;
22 |
23 | // If the node is root, we return null
24 | if (numPeriods === 1) {
25 | return Flint.scene;
26 | }
27 |
28 | const parentID = ID.split('.').slice(0, -1).join('.');
29 | return this.get(parentID);
30 | }
31 |
32 | drop(ID) {
33 | delete blessedNodes[ID];
34 | return this;
35 | }
36 | }
37 |
38 | export default new ReactBlessedIDOperations();
--------------------------------------------------------------------------------
/lib/ReactFlintInjection.js:
--------------------------------------------------------------------------------
1 | import ReactInjection from 'react/lib/ReactInjection';
2 | import ReactComponentEnvironment from 'react/lib/ReactComponentEnvironment';
3 | import ReactFlintReconcileTransaction from './ReactFlintReconcileTransaction';
4 | import ReactFlintComponent from './ReactFlintComponent';
5 |
6 | export default function inject() {
7 |
8 | ReactInjection.NativeComponent.injectGenericComponentClass(
9 | ReactFlintComponent
10 | );
11 |
12 | ReactInjection.Updates.injectReconcileTransaction(
13 | ReactFlintReconcileTransaction
14 | );
15 |
16 | ReactInjection.EmptyComponent.injectEmptyComponent('element');
17 |
18 | // NOTE: we're monkeypatching ReactComponentEnvironment because
19 | // ReactInjection.Component.injectEnvironment() currently throws,
20 | // as it's already injected by ReactDOM for backward compat in 0.14 betas.
21 | // Read more: https://github.com/Yomguithereal/react-blessed/issues/5
22 | ReactComponentEnvironment.processChildrenUpdates = function () {};
23 | ReactComponentEnvironment.replaceNodeWithMarkupByID = function () {};
24 | }
--------------------------------------------------------------------------------
/example/hello/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flint-react-example-hello",
3 | "version": "0.0.0",
4 | "description": "Hello world for Flint VR",
5 | "main": "build/hello.js",
6 | "scripts": {
7 | "build": "webpack --config webpack.config.js",
8 | "serve": "python -m SimpleHTTPServer 8000"
9 | },
10 | "author": "Eric Florenzano",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "babel-cli": "~6.1.4",
14 | "babel-preset-es2015": "~6.1.4",
15 | "babel-preset-react": "~6.1.4",
16 | "babel-preset-stage-0": "~6.1.2",
17 | "babel-preset-stage-2": "~6.1.2",
18 | "babel-preset-stage-3": "~6.1.2",
19 | "babel-preset-stage-1": "~6.1.2",
20 | "babel-core": "~6.1.4",
21 | "babel-loader": "~6.1.0",
22 | "webpack": "~1.12.4",
23 | "raw-loader": "~0.5.1",
24 | "react": "~0.14.7",
25 | "invariant": "~2.2.0",
26 | "lodash": "~3.10.1"
27 | },
28 | "babel": {
29 | "presets": [
30 | "es2015",
31 | "react",
32 | "stage-0",
33 | "stage-1",
34 | "stage-2",
35 | "stage-3"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/ReactFlintReconcileTransaction.js:
--------------------------------------------------------------------------------
1 | import CallbackQueue from 'react/lib/CallbackQueue';
2 | import PooledClass from 'react/lib/PooledClass';
3 | import Transaction from 'react/lib/Transaction';
4 | import { extend } from 'lodash';
5 |
6 | const ON_FLINT_READY_QUEUEING = {
7 | initialize: function () {
8 | this.reactMountReady.reset();
9 | },
10 | close: function () {
11 | this.reactMountReady.notifyAll();
12 | }
13 | };
14 |
15 | function ReactFlintReconcileTransaction() {
16 | this.reinitializeTransaction();
17 | this.reactMountReady = CallbackQueue.getPooled(null);
18 | }
19 |
20 | const Mixin = {
21 | getTransactionWrappers: function() {
22 | return [ON_FLINT_READY_QUEUEING];
23 | },
24 | getReactMountReady: function() {
25 | return this.reactMountReady;
26 | },
27 | destructor: function() {
28 | CallbackQueue.release(this.reactMountReady);
29 | this.reactMountReady = null;
30 | }
31 | };
32 |
33 | extend(
34 | ReactFlintReconcileTransaction.prototype,
35 | Transaction.Mixin,
36 | Mixin
37 | );
38 |
39 | PooledClass.addPoolingTo(ReactFlintReconcileTransaction);
40 |
41 | export default ReactFlintReconcileTransaction;
--------------------------------------------------------------------------------
/lib/render.js:
--------------------------------------------------------------------------------
1 | import ReactInstanceHandles from 'react/lib/ReactInstanceHandles';
2 | import ReactElement from 'react/lib/ReactElement';
3 | import ReactUpdates from 'react/lib/ReactUpdates';
4 | import ReactFlintIDOperations from './ReactFlintIDOperations';
5 | import invariant from 'invariant';
6 | import instantiateReactComponent from 'react/lib/instantiateReactComponent';
7 | import inject from './ReactFlintInjection';
8 |
9 | inject();
10 |
11 | function render(element) {
12 |
13 | // Is the given element valid?
14 | invariant(
15 | ReactElement.isValidElement(element),
16 | 'render(): You must pass a valid ReactElement.'
17 | );
18 |
19 | // Creating a root id & creating the screen
20 | const id = ReactInstanceHandles.createReactRootID();
21 |
22 | // Mounting the app
23 | const component = instantiateReactComponent(element);
24 |
25 | // The initial render is synchronous but any updates that happen during
26 | // rendering, in componentWillMount or componentDidMount, will be batched
27 | // according to the current batching strategy.
28 | ReactUpdates.batchedUpdates(() => {
29 | // Batched mount component
30 | const transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
31 | transaction.perform(() => {
32 | component.mountComponent(id, transaction, {});
33 | });
34 | ReactUpdates.ReactReconcileTransaction.release(transaction);
35 | });
36 |
37 | // Returning the scene so the user can attach listeners etc.
38 | return component._instance;
39 | }
40 |
41 | export { render };
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016, Eric Florenzano
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are
6 | met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above
11 | copyright notice, this list of conditions and the following
12 | disclaimer in the documentation and/or other materials provided
13 | with the distribution.
14 | * Neither the name of the author nor the names of other
15 | contributors may be used to endorse or promote products derived
16 | from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/example/hello/hello.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '../../lib/render';
3 | import cursorVertexShader from './shaders/cursor.vert'
4 | import cursorFragmentShader from './shaders/cursor.frag'
5 |
6 | const { VERTEX_POSITION, VERTEX_COLOR, Geometry, Program, Vector3f, Vector4f } = Flint.Core;
7 |
8 | function getCubeGeometry() {
9 | const cubeVertices = [
10 | VERTEX_POSITION, VERTEX_COLOR,
11 | Vector3f(-1, 1, -1), Vector4f(1, 0, 1, 1), // top
12 | Vector3f( 1, 1, -1), Vector4f(0, 1, 0, 1),
13 | Vector3f( 1, 1, 1), Vector4f(0, 0, 1, 1),
14 | Vector3f(-1, 1, 1), Vector4f(1, 0, 0, 1),
15 | Vector3f(-1, -1, -1), Vector4f(0, 0, 1, 1), // bottom
16 | Vector3f(-1, -1, 1), Vector4f(0, 1, 0, 1),
17 | Vector3f( 1, -1, 1), Vector4f(1, 0, 1, 1),
18 | Vector3f( 1, -1, -1), Vector4f(1, 0, 0, 1)
19 | ];
20 | const cubeIndices = [
21 | 0, 1, 2, 2, 3, 0, // top
22 | 4, 5, 6, 6, 7, 4, // bottom
23 | 2, 6, 7, 7, 1, 2, // right
24 | 0, 4, 5, 5, 3, 0, // left
25 | 3, 5, 6, 6, 2, 3, // front
26 | 0, 1, 7, 7, 4, 0 // back
27 | ];
28 | return Geometry({
29 | vertices: cubeVertices,
30 | indices: cubeIndices
31 | });
32 | }
33 |
34 | const Cursor = React.createClass({
35 | getDefaultProps: function() {
36 | return {
37 | geometry: getCubeGeometry(),
38 | program: Program(cursorVertexShader, cursorFragmentShader)
39 | };
40 | },
41 |
42 | handleFrame: function(ev) {
43 | const pos = ev.viewPos.add(ev.viewFwd.multiply(5));
44 | this.refs.model.position.x = pos.x;
45 | this.refs.model.position.y = pos.y;
46 | this.refs.model.position.z = pos.z;
47 | },
48 |
49 | render: function() {
50 | return (
51 |
56 |
57 | );
58 | }
59 | });
60 |
61 | global.vrmain = function(env) {
62 | render(
63 |
64 |
65 |
66 | );
67 | }
--------------------------------------------------------------------------------
/lib/ReactFlintComponent.js:
--------------------------------------------------------------------------------
1 | import ReactMultiChild from 'react/lib/ReactMultiChild';
2 | import ReactFlintIDOperations from './ReactFlintIDOperations';
3 | import invariant from 'invariant';
4 | import {extend, groupBy, startCase, filter, each} from 'lodash';
5 |
6 | const FLINT_TYPES = {
7 | model: true,
8 | scene: true
9 | };
10 |
11 | export default class ReactFlintComponent {
12 | constructor(tag) {
13 | this._tag = tag.toLowerCase();
14 | this._renderedChildren = null;
15 | this._previousStyle = null;
16 | this._previousStyleCopy = null;
17 | this._rootNodeID = null;
18 | this._wrapperState = null;
19 | this._topLevelWrapper = null;
20 | this._nodeWithLegacyProperties = null;
21 | }
22 |
23 | construct(element) {
24 | this._currentElement = element;
25 | }
26 |
27 | mountComponent(rootID, transaction, context) {
28 | this._rootNodeID = rootID;
29 |
30 | const node = this.mountNode(
31 | ReactFlintIDOperations.getParent(rootID),
32 | this._currentElement
33 | );
34 |
35 | ReactFlintIDOperations.add(rootID, node);
36 |
37 | // Get the children
38 | let childrenToUse = this._currentElement.props.children;
39 | // Make sure it's an array
40 | childrenToUse = childrenToUse === null ? [] : [].concat(childrenToUse);
41 | /*
42 | // Filter out non-Flint types for now
43 | childrenToUse = filter(childrenToUse, function(child) {
44 | print('asdf ' + child + ' ' + (typeof child));
45 | return FLINT_TYPES[typeof child];
46 | });
47 | */
48 |
49 | if (childrenToUse.length > 0) {
50 | this.mountChildren(childrenToUse, transaction, context);
51 | }
52 | }
53 |
54 | mountNode(parent, element) {
55 | const {props, type} = element,
56 | {children, ...options} = props,
57 | flintElementExists = FLINT_TYPES[type.toLowerCase()];
58 |
59 | invariant(
60 | flintElementExists,
61 | `Invalid Flint element "${type}".`
62 | );
63 |
64 | let node = null;
65 | if (type.toLowerCase() === 'scene') {
66 | print('scene: ' + parent);
67 | node = Flint.scene;
68 | each(options, function(value, key) {
69 | node[key] = value;
70 | });
71 | } else {
72 | print('parent: ' + parent);
73 | node = Flint.Core[startCase(type)](options);
74 | parent.add(node);
75 | }
76 |
77 | return node;
78 | }
79 |
80 | receiveComponent(nextElement, transaction, context) {
81 | const {props: {children, ...options}} = nextElement,
82 | node = ReactFlintIDOperations.get(this._rootNodeID);
83 |
84 | // Update the properties on the node
85 | this._updating = true;
86 | each(options, function(value, key) {
87 | // TODO: Bind 'this' to the component such that FlintVR won't override it
88 | node[key] = value;
89 | });
90 | this._updating = false;
91 |
92 | // Updating children
93 | let childrenToUse = children === null ? [] : [].concat(children);
94 | /*
95 | // Filter out non-Flint types for now
96 | childrenToUse = filter(childrenToUse, function(child) {
97 | return FLINT_TYPES[typeof child] || false;
98 | });
99 | */
100 | this.updateChildren(childrenToUse, transaction, context);
101 | }
102 |
103 | unmountComponent() {
104 | this.unmountChildren();
105 |
106 | const node = ReactFlintIDOperations.get(this._rootNodeID);
107 | const parent = ReactFlintIDOperations.getParent(this._rootNodeID);
108 | parent.remove(node);
109 |
110 | ReactFlintIDOperations.drop(this._rootNodeID);
111 |
112 | this._rootNodeID = null;
113 | }
114 |
115 | getPublicInstance() {
116 | return ReactFlintIDOperations.get(this._rootNodeID);
117 | }
118 | }
119 |
120 | extend(
121 | ReactFlintComponent.prototype,
122 | ReactMultiChild.Mixin
123 | );
--------------------------------------------------------------------------------