Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add gltf-model component and system. #2333

Merged
merged 18 commits into from Feb 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
81 changes: 81 additions & 0 deletions docs/components/gltf-model.md
@@ -0,0 +1,81 @@
---
title: gltf-model
type: components
layout: docs
parent_section: components
---

[about-gltf]: https://www.khronos.org/gltf

[glTF][about-gltf] (GL Transmission Format) is an open project by Khronos providing a common, extensible format for 3D assets that is both efficient and highly interoperable with modern web technologies.

The `gltf-model` component loads a 3D model using a glTF (.gltf or .glb) file.

## Why use glTF?

[obj-model]: ./obj-model.md
[collada-model]: ./collada-model.md

In comparison to the older [OBJ][obj-model] format, which supports only vertices, normals, texture coords, and basic materials, glTF provides a more powerful set of features. In addition to all of the above, glTF offers:

- Hierarchical objects
- Scene information (light sources, cameras)
- Skeletal structure and animation
- More robust materials and shaders

For simple models with no animation, OBJ is nevertheless a common and reliable choice.

In comparison to [COLLADA][collada-model], the supported features are very similar. However, because glTF focuses on providing a "transmission format" rather than an editor format, it is more interoperable with web technologies. By analogy, the .PSD (Adobe Photoshop) format is helpful for editing 2D images, but images are converted to .JPG for use on the web. In the same way, glTF is a simpler way of transmitting 3D assets while rendering the same result.

In short, expect glTF models to work with A-Frame more reliably than COLLADA models.

## Example

Load a glTF model by pointing to an asset that specifies the `src` for a glTF file.

```html
<a-scene>
<a-assets>
<a-asset-item id="tree" src="/path/to/tree.gltf"></a-asset-item>
</a-assets>

<a-entity gltf-model="#tree"></a-entity>
</a-scene>
```

## Values

| Type | Description |
|----------|--------------------------------------|
| selector | Selector to an `<a-asset-item>` |
| string | `url()`-enclosed path to a glTF file |

## Events

| Event Name | Description |
|--------------|--------------------------------------------|
| model-loaded | glTF model has been loaded into the scene. |

## Loading Inline

Alternatively, load a glTF model by specifying the path directly within `url()`. This is less performant than using the asset management system.

```html
<a-entity gltf-model="url(/path/to/tree.gltf)"></a-entity>
```

## More Resources
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add sections on playing animations until we get an API?

Copy link
Member Author

@donmccurdy donmccurdy Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but the answer is "write a component that looks very much like animation-mixer", so IMO it would be better to leave that for another PR.


The glTF format is fairly new, and few editors will export a `.gltf` file directly. Instead, various converters are available or in progress:

[fbx-converter]: http://gltf.autodesk.io/
[collada-converter]: http://cesiumjs.org/convertmodel.html
[obj-converter]: https://github.com/AnalyticalGraphicsInc/obj2gltf

- [FBX → glTF][fbx-converter] - coming soon.
- [COLLADA → glTF][collada-converter].
- [OBJ → glTF][obj-converter].

[spec]: https://github.com/KhronosGroup/glTF

See the [official glTF specification][spec] for available features, community resources, and ways to contribute.
33 changes: 33 additions & 0 deletions docs/primitives/a-gltf-model.md
@@ -0,0 +1,33 @@
---
title: <a-gltf-model>
type: primitives
layout: docs
parent_section: primitives
---

The glTF model primitive displays a 3D glTF model created from a 3D
modeling program or downloaded from the web.

## Example

```html
<a-scene>
<a-assets>
<a-asset-item id="tree" src="tree.gltf">
</a-assets>

<!-- Using the asset management system. -->
<a-gltf-model src="#tree"></a-gltf-model>

<!-- Defining the URL inline. Not recommended but more comfortable for web developers. -->
<a-gltf-model src="tree.gltf"></a-gltf-model>
</a-scene>
```

## Attribute

[gltf]: ../components/gltf-model.md

| Attribute | Component Mapping | Default Value |
|-----------|------------------------|---------------|
| src | [gltf-model][gltf].src | null |
12 changes: 10 additions & 2 deletions examples/test/model/index.html
Expand Up @@ -2,24 +2,32 @@
<html>
<head>
<meta charset="utf-8">
<title>Model</title>
<meta name="description" content="Model - A-Frame">
<title>Models (glTF, OBJ, COLLADA)</title>
<meta name="description" content="Models (glTF, OBJ, COLLADA) - A-Frame">
<script src="../../../dist/aframe-master.js"></script>
</head>
<body>
<a-scene stats>
<a-assets>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be cool to add text labels under each model for the model format. And the scene could use a better background <a-sky color="#FAFAFA">

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could also add the different format types in the page title & meta description Models (glTF, COLLADA, OBJ)

Copy link
Member Author

@donmccurdy donmccurdy Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scope creep alert 😉

Added the meta title/description, but I'm getting errors trying to do text.

<a-text value="foo"></a-text>
THREE.WebGLShader: gl.getShaderInfoLog() fragment WARNING: 0:102: '
' : extension directive should occur before any non-preprocessor tokens 
� 1: precision highp float;
2: precision highp int;
3: #define SHADER_NAME ShaderMaterial
4: #define GAMMA_FACTOR 2
5: #define NUM_CLIPPING_PLANES 0
6: #define UNION_CLIPPING_PLANES 0
7: uniform mat4 viewMatrix;
...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that on latest? We pushed some text patches switching to raw shader material.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the latest and greatest master, that warning has been eliminated by using RawShaderMaterial for the sdf and msdf text shaders. so... maybe needs rebase?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebase did the trick. ✅

<a-asset-item id="crate-obj" src="../../assets/models/crate/crate.obj"></a-asset-item>
<a-asset-item id="crate-mtl" src="../../assets/models/crate/crate.mtl"></a-asset-item>
<a-asset-item id="brainstem" src="https://cdn.aframe.io/test-models/models/brainstem/BrainStem.gltf"></a-asset-item>
<a-asset-item id="monster" src="monster/monster.dae"></a-asset-item>
</a-assets>

<a-entity position="0 0 -12">
<a-entity collada-model="#monster" material="color: green" scale="0.25 0.25 0.25"
position="-10 0 1"></a-entity>
<a-text value="COLLADA" color="#000000" position="-2 -2 6" scale="1.5 1.5 1.5"></a-text>

<a-entity gltf-model="#brainstem" position="10 -1 5" scale="3 3 3"></a-entity>
<a-text value="glTF" color="#000000" position="9.75 -2 6" scale="1.5 1.5 1.5"></a-text>

<a-entity obj-model="obj: #crate-obj; mtl: #crate-mtl" position="5 0 5"></a-entity>
<a-entity obj-model="obj: #crate-obj; mtl: #crate-mtl" position="5 2 5"></a-entity>
<a-text value="OBJ" color="#000000" position="4.75 -2 6" scale="1.5 1.5 1.5"></a-text>

<a-sky color="#FAFAFA"></a-sky>
</a-entity>

</a-scene>
Expand Down
37 changes: 37 additions & 0 deletions src/components/gltf-model.js
@@ -0,0 +1,37 @@
var registerComponent = require('../core/component').registerComponent;
var THREE = require('../lib/three');

/**
* glTF model loader.
*/
module.exports.Component = registerComponent('gltf-model', {
schema: {type: 'model'},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be multiprop to allow room for future props?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I slightly prefer multiprop, but can't actually think of any others we'd need. Maybe crossorigin. I'm expecting animation to be a separate component, but if not that would definitely need some properties. I've used src: {type: 'asset'} in most of my loaders so far i think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, can just leave it as is


init: function () {
this.model = null;
this.loader = new THREE.GLTFLoader();
},

update: function () {
var self = this;
var el = this.el;
var src = this.data;

if (!src) { return; }

this.remove();

this.loader.load(src, function gltfLoaded (gltfModel) {
self.model = gltfModel.scene;
self.system.registerModel(self.model);
el.setObject3D('mesh', self.model);
el.emit('model-loaded', {format: 'gltf', model: self.model});
});
},

remove: function () {
if (!this.model) { return; }
this.el.removeObject3D('mesh');
this.system.unregisterModel(this.model);
}
});
1 change: 1 addition & 0 deletions src/components/index.js
Expand Up @@ -3,6 +3,7 @@ require('./camera');
require('./collada-model');
require('./cursor');
require('./geometry');
require('./gltf-model');
require('./hand-controls');
require('./light');
require('./look-controls');
Expand Down
1 change: 1 addition & 0 deletions src/extras/primitives/index.js
Expand Up @@ -2,6 +2,7 @@ require('./primitives/a-camera');
require('./primitives/a-collada-model');
require('./primitives/a-cursor');
require('./primitives/a-curvedimage');
require('./primitives/a-gltf-model');
require('./primitives/a-image');
require('./primitives/a-light');
require('./primitives/a-obj-model');
Expand Down
6 changes: 2 additions & 4 deletions src/extras/primitives/primitives/a-collada-model.js
@@ -1,9 +1,7 @@
var getMeshMixin = require('../getMeshMixin');
var registerPrimitive = require('../primitives').registerPrimitive;
var utils = require('../../../utils/');

registerPrimitive('a-collada-model', utils.extendDeep({}, getMeshMixin(), {
registerPrimitive('a-collada-model', {
mappings: {
src: 'collada-model'
}
}));
});
7 changes: 7 additions & 0 deletions src/extras/primitives/primitives/a-gltf-model.js
@@ -0,0 +1,7 @@
var registerPrimitive = require('../primitives').registerPrimitive;

registerPrimitive('a-gltf-model', {
mappings: {
src: 'gltf-model'
}
});
2 changes: 2 additions & 0 deletions src/lib/three.js
Expand Up @@ -19,6 +19,7 @@ if (THREE.Cache) {
}

// TODO: Eventually include these only if they are needed by a component.
require('three/examples/js/loaders/GLTFLoader'); // THREE.GLTFLoader
require('three/examples/js/loaders/OBJLoader'); // THREE.OBJLoader
require('three/examples/js/loaders/MTLLoader'); // THREE.MTLLoader
require('three/examples/js/BlendCharacter'); // THREE.BlendCharacter
Expand All @@ -27,6 +28,7 @@ require('../../vendor/VRControls'); // THREE.VRControls
require('../../vendor/VREffect'); // THREE.VREffect

THREE.ColladaLoader.prototype.crossOrigin = 'anonymous';
THREE.GLTFLoader.prototype.crossOrigin = 'anonymous';
THREE.MTLLoader.prototype.crossOrigin = 'anonymous';
THREE.OBJLoader.prototype.crossOrigin = 'anonymous';

Expand Down
41 changes: 41 additions & 0 deletions src/systems/gltf-model.js
@@ -0,0 +1,41 @@
var registerSystem = require('../core/system').registerSystem;
var THREE = require('../lib/three');

/**
* glTF model system.
*/
module.exports.System = registerSystem('gltf-model', {
init: function () {
this.models = [];
},

/**
* Updates shaders for all glTF models in the system.
*/
tick: function () {
var sceneEl = this.sceneEl;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could test this code too

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

if (sceneEl.hasLoaded && this.models.length) {
THREE.GLTFLoader.Shaders.update(sceneEl.object3D, sceneEl.camera);
}
},

/**
* Registers a glTF asset.
* @param {object} gltf Asset containing a scene and (optional) animations and cameras.
*/
registerModel: function (gltf) {
this.models.push(gltf);
},

/**
* Unregisters a glTF asset.
* @param {object} gltf Asset containing a scene and (optional) animations and cameras.
*/
unregisterModel: function (gltf) {
var models = this.models;
var index = models.indexOf(gltf);
if (index >= 0) {
models.splice(index, 1);
}
}
});
1 change: 1 addition & 0 deletions src/systems/index.js
@@ -1,5 +1,6 @@
require('./camera');
require('./geometry');
require('./gltf-model');
require('./light');
require('./material');
require('./tracked-controls');
Expand Down
Binary file added tests/assets/box/Box.bin
Binary file not shown.