Skip to content

Commit

Permalink
Merge 9494dd5 into fc5c605
Browse files Browse the repository at this point in the history
  • Loading branch information
ibgreen committed Mar 20, 2019
2 parents fc5c605 + 9494dd5 commit 4b6ed79
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 126 deletions.
37 changes: 23 additions & 14 deletions docs/api-reference/core/attribute.md
@@ -1,7 +1,13 @@
# Attribute (Experimental)

Wrapper class to be used with the `Model` class' `render` and `setAttributes` methods.
Helper class used with the `Model` class' `render` and `setAttributes` methods.

Manages one attribute, allowing it to be set to one of:
* An externally created `Buffer`.
* A JavaScript array, automatically creating/managing a `Buffer`.
* A constant value

Also holds an accessor object.

## Usage

Expand All @@ -15,7 +21,7 @@ import {_Attribute as Attribute} from '@luma.gl/core';
// construct the model.
const positions = new Attribute({
id: 'vertexPositions',
size: 3,
accessor: {size: 3},
value: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0])
});

Expand All @@ -30,31 +36,34 @@ model.draw();

The constructor for the Attribute class. Use this to create a new Attribute.

`new Attribute(gl, options);`
`new Attribute(gl : WebGLRenderingContext, props : Object)`

* `gl` - WebGL context.
* `size` (*number*) - The number of components in each element the buffer (1-4).
* `id` (*string*, optional) - Identifier of the attribute. Cannot be updated.
* `type` (*GLenum*, optional) - Type of the attribute. If not supplied will be inferred from `value`. Cannot be updated.
* `isIndexed` (*bool*, optional) - If the attribute is element index. Default `false`. Cannot be updated.
* `constant` (*bool*, optional) - If the attribute is a constant. Default `false`.
* `isInstanced` (*bool*, optional) - Whether buffer contains instance data. Default `false`.
* `normalized` (*boolean*, optional) - Default `false`
* `integer` (*boolean*, optional) - Default `false`
* `offset` (*number*, optional) - where the data starts in the buffer. Default `0`.
* `stride` (*number*, optional) - an additional offset between each element in the buffer. Default `0`.
* `value` (*TypedArray*) - value of the attribute.
- If `constant` is `true`, the length of `value` should match `size`
- If `constant` is `false`, the length of `value` should be `size` multiplies the number of vertices.
* `buffer` (*Buffer*) - an external buffer for the attribute.
* `accessor` (`Object` | `Accessor`) - accessor object to be used when setting the attribute.

Deprecated props (these are now stored on the accessor):
* `size` (*number*) - The number of components in each element the buffer (1-4).
* `type` (*GLenum*, optional) - Type of the attribute. If not supplied will be inferred from `value`. Cannot be updated.
* `isIndexed` (*bool*, optional) - If the attribute is element index. Default `false`. Cannot be updated.
* `isInstanced` (*bool*, optional) - Whether buffer contains instance data. Default `false`.
* `normalized` (*boolean*, optional) - Default `false`.
* `integer` (*boolean*, optional) - Default `false`.
* `offset` (*number*, optional) - where the data starts in the buffer. Default `0`.
* `stride` (*number*, optional) - an additional offset between each element in the buffer. Default `0`.


### delete
### delete() : Attribute

Free WebGL resources associated with this attribute.


### update
### update(props : Object) : Attribute

```js
attribute.update({value: newValue});
Expand All @@ -63,6 +72,6 @@ attribute.update({value: newValue});
Update attribute options. See `constructor` for possible options.


### getBuffer
### getBuffer() : Buffer

Returns a `Buffer` object associated with this attribute, if any.
250 changes: 162 additions & 88 deletions modules/core/src/core/attribute.js
@@ -1,39 +1,53 @@
/* eslint-disable complexity */
import GL from '@luma.gl/constants';
import {Buffer} from '../webgl';
import {Accessor, Buffer} from '../webgl';
import {log, uid} from '../utils';
import {hasFeature, FEATURES} from '../webgl/features';

const ACCESSOR_DEFAULTS = {
offset: 0,
stride: 0,
normalized: false,
integer: false,
divisor: 0
};

export default class Attribute {
constructor(gl, opts = {}) {
const {id = uid('attribute'), type, isIndexed = false} = opts;
constructor(gl, props = {}) {
const {id = uid('attribute'), type, isIndexed = false} = props;

// Options that cannot be changed later
this.gl = gl;
this.id = id;

this.isIndexed = isIndexed;
this.target = isIndexed ? GL.ELEMENT_ARRAY_BUFFER : GL.ARRAY_BUFFER;
this.type = type;

if (isIndexed && !type) {
// If the attribute is indices, auto infer the correct type
// WebGL2 and WebGL1 w/ uint32 index extension support accepts Uint32Array, otherwise Uint16Array
this.type =
gl && hasFeature(gl, FEATURES.ELEMENT_INDEX_UINT32) ? GL.UNSIGNED_INT : GL.UNSIGNED_SHORT;
}

// Initialize the attribute descriptor, with WebGL and metadata fields
this.constant = false;
this.value = null;
this.externalBuffer = null;
this.buffer = null;
this.userData = {}; // Reserved for application
this.update(opts);
this.accessor = new Accessor(ACCESSOR_DEFAULTS);

// Sanity - no app fields on our attributes. Use userData instead.
Object.seal(this);
// NOTE: this line will copy inline accessor props / props.accessor to this.accessor
this.update(props);

// Check all fields and generate helpful error messages
this._validateAttributeDefinition();
// Update the attribute accessor
const accessorFields = {type};
// If the attribute contains indices, auto infer the correct type
// WebGL2 and WebGL1 w/ uint32 index extension support accepts Uint32Array, otherwise Uint16Array
if (isIndexed && !type) {
accessorFields.type =
accessorFields.type || (gl && hasFeature(gl, FEATURES.ELEMENT_INDEX_UINT32))
? GL.UNSIGNED_INT
: GL.UNSIGNED_SHORT;
}

this.accessor = new Accessor(this.accessor, accessorFields);

// Sanity - don't allow app fields. Use userData instead.
Object.seal(this);
}

delete() {
Expand All @@ -43,44 +57,29 @@ export default class Attribute {
}
}

update(opts) {
const {value, buffer, constant = this.constant || false} = opts;

this.constant = constant;

if (buffer) {
this.externalBuffer = buffer;
this.constant = false;
update(props) {
// TODO - refactored this code
// const {constant = this.constant || false} = props;
// this.constant = constant;
if ('constant' in props) {
this.constant = Boolean(props.constant);
}

this.type = opts.type || buffer.accessor.type;
if (buffer.accessor.divisor !== undefined) {
this.divisor = buffer.accessor.divisor;
}
if (opts.divisor !== undefined) {
this.divisor = opts.divisor;
}
} else if (value) {
this.externalBuffer = null;
this.value = value;

if (!constant && this.gl) {
// Create buffer if needed
this.buffer =
this.buffer ||
new Buffer(
this.gl,
Object.assign({}, opts, {
id: this.id,
target: this.target,
type: this.type
})
);
this.buffer.setData({data: value});
this.type = this.buffer.accessor.type;
if (props.buffer) {
this._setExternalBuffer(props.buffer);
} else if (props.value) {
if (this.constant) {
this._setConstant(props.value);
} else {
this._setBuffer(props.value, props);
}
}

this._setAccessor(opts);
if (props.accessor) {
this.accessor = new Accessor(props.accessor);
}

this._setAccessorFromInlineProps(props);
}

getBuffer() {
Expand All @@ -96,50 +95,125 @@ export default class Attribute {
}
const buffer = this.externalBuffer || this.buffer;
if (buffer) {
return [buffer, this];
return [buffer, this.accessor];
}
return null;
}

// Sets all accessor props except type
// TODO - store on `this.accessor`
_setAccessor(opts) {
const {
// accessor props
size = this.size,
offset = this.offset || 0,
stride = this.stride || 0,
normalized = this.normalized || false,
integer = this.integer || false,
divisor = this.divisor || 0,
instanced,
isInstanced
} = opts;

this.size = size;
this.offset = offset;
this.stride = stride;
this.normalized = normalized;
this.integer = integer;

this.divisor = divisor;

if (isInstanced !== undefined) {
log.deprecated('Attribute.isInstanced')();
this.divisor = isInstanced ? 1 : 0;
// PRIVATE HELPERS

_setExternalBuffer(buffer) {
this.externalBuffer = buffer;
this.constant = false;

const accessorFields = {};
accessorFields.type = buffer.accessor.type;
if (buffer.accessor.divisor !== undefined) {
accessorFields.divisor = buffer.accessor.divisor;
}
if (instanced !== undefined) {
log.deprecated('Attribute.instanced')();
this.divisor = instanced ? 1 : 0;

this.accessor = new Accessor(this.accessor, accessorFields);
}

_setBuffer(value, props) {
this.externalBuffer = null;
this.value = value;

if (this.gl) {
// Create buffer if needed
if (!this.buffer) {
this.buffer = new Buffer(
this.gl,
Object.assign({}, props, {
id: this.id,
target: this.target,
type: this.accessor.type
})
);
}
this.buffer.setData({data: value});
this.accessor = new Accessor(this.accessor, {type: this.buffer.accessor.type});
}
}

_validateAttributeDefinition() {
// Can be undefined for buffers (auto deduced from shaders)
// or larger than 4 for uniform arrays
// assert(
// this.size >= 1 && this.size <= 4,
// `Attribute definition for ${this.id} invalid size`
// );
_setConstant(value) {
this.externalBuffer = null;
this.value = value;
}

// DEPRECATED: Sets all accessor fields from inline props except type
_setAccessorFromInlineProps(props) {
this.accessor = new Accessor(this.accessor, props);
}

// TEMPORARY - Keep deck.gl from breaking - remove as soon as tested
get type() {
log.deprecated('Attribute.type', 'Attribute.accessor.type')();
return this.accessor.type;
}

set type(value) {
log.deprecated('Attribute.type', 'Attribute.accessor.type')();
this.accessor.type = value;
}

get size() {
log.deprecated('Attribute.size', 'Attribute.accessor.size')();
return this.accessor.size;
}

set size(value) {
log.deprecated('Attribute.size', 'Attribute.accessor.size')();
this.accessor.size = value;
}

get offset() {
log.deprecated('Attribute.offset', 'Attribute.accessor.offset')();
return this.accessor.offset;
}

set offset(value) {
log.deprecated('Attribute.offset', 'Attribute.accessor.offset')();
this.accessor.offset = value;
}

get stride() {
log.deprecated('Attribute.stride', 'Attribute.accessor.stride')();
return this.accessor.stride;
}

set stride(value) {
log.deprecated('Attribute.stride', 'Attribute.accessor.stride')();
this.accessor.stride = value;
}

get normalized() {
log.deprecated('Attribute.normalized', 'Attribute.accessor.normalized')();
return this.accessor.normalized;
}

set normalized(value) {
log.deprecated('Attribute.normalized', 'Attribute.accessor.normalized')();
this.accessor.normalized = value;
}

get integer() {
log.deprecated('Attribute.integer', 'Attribute.accessor.integer')();
return this.accessor.integer;
}

set integer(value) {
log.deprecated('Attribute.integer', 'Attribute.accessor.integer')();
this.accessor.integer = value;
}

get divisor() {
log.deprecated('Attribute.divisor', 'Attribute.accessor.divisor')();
return this.accessor.divisor;
}

set divisor(value) {
log.deprecated('Attribute.divisor', 'Attribute.accessor.divisor')();
this.accessor.divisor = value;
}
}
10 changes: 2 additions & 8 deletions modules/core/src/core/model.js
Expand Up @@ -286,14 +286,8 @@ count: ${this.stats.profileFrameCount}`
if (descriptor instanceof Attribute) {
attribute = descriptor;
} else if (descriptor instanceof Buffer) {
attribute =
attribute ||
new Attribute(
gl,
Object.assign({}, descriptor, descriptor.accessor, {
id: attributeName
})
);
const buffer = descriptor;
attribute = attribute || new Attribute(gl, {id: attributeName, accessor: buffer.accessor});
attribute.update({buffer: descriptor});
} else if (attribute) {
attribute.update(descriptor);
Expand Down

0 comments on commit 4b6ed79

Please sign in to comment.