Skip to content

Commit

Permalink
[v9] Fix SimpleMeshLayer (#8201)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pessimistress committed Oct 15, 2023
1 parent 5c40367 commit eaa106b
Show file tree
Hide file tree
Showing 14 changed files with 163 additions and 144 deletions.
8 changes: 0 additions & 8 deletions docs/api-reference/mesh-layers/simple-mesh-layer.md
Expand Up @@ -156,14 +156,6 @@ Whether to render the mesh in wireframe mode.
This is an object that contains material props for [lighting effect](../core/lighting-effect.md) applied on extruded polygons.
Check [the lighting guide](../../developer-guide/using-lighting.md#constructing-a-material-instance) for configurable settings.

##### `_useMeshColors` (Boolean, optional) {#_usemeshcolors}

- Default: `false`

Whether to color pixels using vertex colors supplied in the mesh (the `COLOR_0` or `colors` attribute). If set to `true`, vertex colors will be used. If set to `false` (default) vertex colors will be ignored.

Remarks:
- In the next major release of deck.gl, vertex colors are expected to always be used when supplied with a mesh. This property will then likely be removed and effectively default to true.

### Data Accessors

Expand Down
7 changes: 5 additions & 2 deletions modules/core/src/lib/attribute/attribute.ts
Expand Up @@ -349,14 +349,17 @@ export default class Attribute extends DataColumn<AttributeOptions, AttributeInt
return vertexIndex * this.size;
}

getValue(): Record<string, Buffer | NumericArray | null> {
getValue(): Record<string, Buffer | TypedArray | null> {
const shaderAttributeDefs = this.settings.shaderAttributes;
const result = super.getValue();
if (!shaderAttributeDefs) {
return result;
}
for (const shaderAttributeName in shaderAttributeDefs) {
Object.assign(result, super.getValue(shaderAttributeName));
Object.assign(
result,
super.getValue(shaderAttributeName, shaderAttributeDefs[shaderAttributeName])
);
}
return result;
}
Expand Down
17 changes: 14 additions & 3 deletions modules/core/src/lib/attribute/data-column.ts
Expand Up @@ -245,10 +245,21 @@ export default class DataColumn<Options, State> {
return this.state.externalBuffer || this._buffer;
}

getValue(attributeName: string = this.id): Record<string, Buffer | NumericArray | null> {
const result: Record<string, Buffer | NumericArray | null> = {};
getValue(
attributeName: string = this.id,
options: Partial<ShaderAttributeOptions> | null = null
): Record<string, Buffer | TypedArray | null> {
const result: Record<string, Buffer | TypedArray | null> = {};
if (this.state.constant) {
result[attributeName] = this.value;
const value = this.value as TypedArray;
if (options) {
const shaderAttributeDef = resolveShaderAttribute(this.getAccessor(), options);
const offset = shaderAttributeDef.offset / value.BYTES_PER_ELEMENT;
const size = shaderAttributeDef.size || this.size;
result[attributeName] = value.subarray(offset, offset + size);
} else {
result[attributeName] = value;
}
} else {
result[attributeName] = this.getBuffer();
}
Expand Down
4 changes: 2 additions & 2 deletions modules/core/src/lib/layer.ts
Expand Up @@ -783,8 +783,8 @@ export default abstract class Layer<PropsT extends {} = {}> extends Component<
} else {
attributeBuffers[attributeName] = value;
}
} else {
constantAttributes[attributeName] = value as TypedArray;
} else if (value) {
constantAttributes[attributeName] = value;
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions modules/geo-layers/src/index.ts
Expand Up @@ -29,7 +29,7 @@ export {default as H3ClusterLayer} from './h3-layers/h3-cluster-layer';
export {default as H3HexagonLayer} from './h3-layers/h3-hexagon-layer';
// TODO v9 layers that depend on luma glTF code
// export {default as Tile3DLayer} from './tile-3d-layer/tile-3d-layer';
// export {default as TerrainLayer} from './terrain-layer/terrain-layer';
export {default as TerrainLayer} from './terrain-layer/terrain-layer';
export {default as MVTLayer} from './mvt-layer/mvt-layer';
export {default as GeohashLayer} from './geohash-layer/geohash-layer';

Expand All @@ -45,7 +45,7 @@ export type {TileLayerProps} from './tile-layer/tile-layer';
export type {TripsLayerProps} from './trips-layer/trips-layer';
export type {QuadkeyLayerProps} from './quadkey-layer/quadkey-layer';
// TODO v9 layers that depend on luma glTF code
// export type {TerrainLayerProps} from './terrain-layer/terrain-layer';
export type {TerrainLayerProps} from './terrain-layer/terrain-layer';
// export type {Tile3DLayerProps} from './tile-3d-layer/tile-3d-layer';
export type {MVTLayerProps} from './mvt-layer/mvt-layer';
export type {GeoCellLayerProps as _GeoCellLayerProps} from './geo-cell-layer/GeoCellLayer';
Expand Down
5 changes: 3 additions & 2 deletions modules/main/src/index.ts
Expand Up @@ -134,13 +134,14 @@ export {
TripsLayer,
// TODO v9 glTF dependent layers
// Tile3DLayer,
// TerrainLayer,
TerrainLayer,
MVTLayer,
GeohashLayer
} from '@deck.gl/geo-layers';

// TODO v9 glTF dependent layers
// export {SimpleMeshLayer, ScenegraphLayer} from '@deck.gl/mesh-layers';
// ScenegraphLayer
export {SimpleMeshLayer} from '@deck.gl/mesh-layers';

//
// REACT BINDINGS PACKAGE
Expand Down
4 changes: 2 additions & 2 deletions modules/mesh-layers/src/index.ts
Expand Up @@ -21,8 +21,8 @@

// TODO - v9 - restore when luma.gl/gltf module is available again

// export {default as SimpleMeshLayer} from './simple-mesh-layer/simple-mesh-layer';
// export type {SimpleMeshLayerProps} from './simple-mesh-layer/simple-mesh-layer';
export {default as SimpleMeshLayer} from './simple-mesh-layer/simple-mesh-layer';
export type {SimpleMeshLayerProps} from './simple-mesh-layer/simple-mesh-layer';

// export type {ScenegraphLayerProps} from './scenegraph-layer/scenegraph-layer';
// export {default as ScenegraphLayer} from './scenegraph-layer/scenegraph-layer';
Expand Up @@ -16,7 +16,9 @@ in vec3 instancePositions;
in vec3 instancePositions64Low;
in vec4 instanceColors;
in vec3 instancePickingColors;
in mat3 instanceModelMatrix;
in vec3 instanceModelMatrixCol0;
in vec3 instanceModelMatrixCol1;
in vec3 instanceModelMatrixCol2;
in vec3 instanceTranslation;
// Outputs to fragment shader
Expand All @@ -35,6 +37,7 @@ void main(void) {
cameraPosition = project_uCameraPosition;
vColor = vec4(colors * instanceColors.rgb, instanceColors.a);
mat3 instanceModelMatrix = mat3(instanceModelMatrixCol0, instanceModelMatrixCol1, instanceModelMatrixCol2);
vec3 pos = (instanceModelMatrix * positions) * sizeScale + instanceTranslation;
if (composeModelMatrix) {
Expand Down
Expand Up @@ -34,7 +34,7 @@ import {
} from '@deck.gl/core';
import {Texture} from '@luma.gl/core';
import {Model, Geometry} from '@luma.gl/engine';
import {PBRMaterialParser} from '@luma.gl/gltf';
// import {PBRMaterialParser} from '@luma.gl/gltf';
import {GL} from '@luma.gl/constants';

import {MATRIX_ATTRIBUTES, shouldComposeModelMatrix} from '../utils/matrix';
Expand All @@ -55,38 +55,53 @@ import type {MeshAttribute, MeshAttributes} from '@loaders.gl/schema';
import type {Geometry as GeometryType} from '@luma.gl/engine';
import {getMeshBoundingBox} from '@loaders.gl/schema';

function validateGeometryAttributes(attributes: Record<string, any>, useMeshColors: boolean): void {
const hasColorAttribute = attributes.COLOR_0 || attributes.colors;
const useColorAttribute = hasColorAttribute && useMeshColors;
if (!useColorAttribute) {
attributes.colors = {constant: true, value: new Float32Array([1, 1, 1])};
function normalizeGeometryAttributes(attributes: MeshAttributes): MeshAttributes {
const positionAttribute = attributes.positions || attributes.POSITION;
log.assert(positionAttribute, 'no "postions" or "POSITION" attribute in mesh');

const vertexCount = positionAttribute.value.length / positionAttribute.size;
let colorAttribute = attributes.COLOR_0 || attributes.colors;
if (!colorAttribute) {
colorAttribute = {size: 3, value: new Float32Array(vertexCount * 3).fill(1)};
}
let normalAttribute = attributes.NORMAL || attributes.normals;
if (!normalAttribute) {
normalAttribute = {size: 3, value: new Float32Array(vertexCount * 3).fill(0)};
}
let texCoordAttribute = attributes.TEXCOORD_0 || attributes.texCoords;
if (!texCoordAttribute) {
texCoordAttribute = {size: 2, value: new Float32Array(vertexCount * 2).fill(0)};
}
log.assert(
attributes.positions || attributes.POSITION,
'no "postions" or "POSITION" attribute in mesh'
);

return {
positions: positionAttribute,
colors: colorAttribute,
normals: normalAttribute,
texCoords: texCoordAttribute
};
}

/*
* Convert mesh data into geometry
* @returns {Geometry} geometry
*/
function getGeometry(data: Mesh, useMeshColors: boolean): Geometry {
if ((data as any).attributes) {
validateGeometryAttributes((data as any).attributes, useMeshColors);
if (data instanceof Geometry) {
return data;
} else {
return new Geometry(data);
}
} else if ((data as MeshAttributes).positions || (data as MeshAttributes).POSITION) {
validateGeometryAttributes(data, useMeshColors);
function getGeometry(data: Mesh): Geometry {
if (data instanceof Geometry) {
// @ts-expect-error data.attributes is readonly
data.attributes = normalizeGeometryAttributes(data.attributes);
return data;
} else if ((data as any).attributes) {
return new Geometry({
...data,
topology: 'triangle-list',
attributes: normalizeGeometryAttributes((data as any).attributes)
});
} else {
return new Geometry({
// @ts-expect-error Type incompatiblity between luma.gl and loaders.gl
attributes: data
topology: 'triangle-list',
attributes: normalizeGeometryAttributes(data as MeshAttributes)
});
}
throw Error('Invalid mesh');
}

const DEFAULT_COLOR: [number, number, number, number] = [0, 0, 0, 255];
Expand Down Expand Up @@ -141,13 +156,6 @@ type _SimpleMeshLayerProps<DataT> = {
* @default 1
*/
sizeScale?: number;
/**
* @deprecated Whether to color pixels using vertex colors supplied in the mesh (the `COLOR_0` or `colors` attribute).
* If set to `false` vertex colors will be ignored.
* This prop will be removed and set to always true in the next major release.
* @default false
*/
_useMeshColors?: boolean;

/**
* (Experimental) If rendering only one instance of the mesh, set this to false to treat mesh positions
Expand Down Expand Up @@ -177,9 +185,6 @@ const defaultProps: DefaultProps<SimpleMeshLayerProps> = {
mesh: {type: 'object', value: null, async: true},
texture: {type: 'image', value: null, async: true},
sizeScale: {type: 'number', value: 1, min: 0},
// Whether the color attribute in a mesh will be used
// This prop will be removed and set to true in next major release
_useMeshColors: {type: 'boolean', value: false},

// _instanced is a hack to use world position instead of meter offsets in mesh
// TODO - formalize API
Expand All @@ -190,7 +195,7 @@ const defaultProps: DefaultProps<SimpleMeshLayerProps> = {
wireframe: false,
// Optional material for 'lighting' shader module
material: true,
getPosition: {type: 'accessor', value: x => x.position},
getPosition: {type: 'accessor', value: (x: any) => x.position},
getColor: {type: 'accessor', value: DEFAULT_COLOR},

// yaw, pitch and roll are in degrees
Expand All @@ -213,7 +218,7 @@ export default class SimpleMeshLayer<DataT = any, ExtraPropsT extends {} = {}> e
static layerName = 'SimpleMeshLayer';

state!: {
materialParser?: PBRMaterialParser;
// materialParser?: PBRMaterialParser;
model?: Model;
emptyTexture: Texture;
hasNormals?: boolean;
Expand Down Expand Up @@ -255,7 +260,7 @@ export default class SimpleMeshLayer<DataT = any, ExtraPropsT extends {} = {}> e

if (!result) {
// Otherwise, calculate bounding box from positions
const {attributes} = getGeometry(mesh as Mesh, this.props._useMeshColors);
const {attributes} = getGeometry(mesh as Mesh);
attributes.POSITION = attributes.POSITION || attributes.positions;

//@ts-expect-error
Expand Down Expand Up @@ -291,7 +296,7 @@ export default class SimpleMeshLayer<DataT = any, ExtraPropsT extends {} = {}> e
this.setState({
// Avoid luma.gl's missing uniform warning
// TODO - add feature to luma.gl to specify ignored uniforms?
emptyTexture: this.context.device.createTexture({
emptyTexture: this.context.device.createTexture({
data: new Uint8Array(4),
width: 1,
height: 1
Expand Down Expand Up @@ -322,9 +327,9 @@ export default class SimpleMeshLayer<DataT = any, ExtraPropsT extends {} = {}> e
this.setTexture(props.texture);
}

if (this.state.model) {
this.state.model.setDrawMode(this.props.wireframe ? GL.LINE_STRIP : GL.TRIANGLES);
}
// if (this.state.model) {
// this.state.model.setDrawMode(this.props.wireframe ? GL.LINE_STRIP : GL.TRIANGLES);
// }
}

finalizeState(context: LayerContext) {
Expand All @@ -334,35 +339,38 @@ export default class SimpleMeshLayer<DataT = any, ExtraPropsT extends {} = {}> e
}

draw({uniforms}) {
if (!this.state.model) {
const {model} = this.state;
if (!model) {
return;
}

const {viewport, renderPass} = this.context;
const {sizeScale, coordinateSystem, _instanced} = this.props;

this.state.model
.setUniforms(uniforms)
.setUniforms({
sizeScale,
composeModelMatrix: !_instanced || shouldComposeModelMatrix(viewport, coordinateSystem),
flatShading: !this.state.hasNormals
})
.draw(renderPass);
model.setUniforms(uniforms);
model.setUniforms({
sizeScale,
composeModelMatrix: !_instanced || shouldComposeModelMatrix(viewport, coordinateSystem),
flatShading: !this.state.hasNormals
});
model.draw(renderPass);
}

protected getModel(mesh: Mesh): Model {
const model = new Model(this.context.device, {
...this.getShaders(),
id: this.props.id,
geometry: getGeometry(mesh, this.props._useMeshColors),
bufferLayout: this.getAttributeManager()!.getBufferLayouts(),
geometry: getGeometry(mesh),
isInstanced: true
});

const {texture} = this.props;
const {emptyTexture} = this.state;
model.setBindings({
sampler: (texture as Texture) || emptyTexture
});
model.setUniforms({
sampler: texture || emptyTexture,
hasTexture: Boolean(texture)
});

Expand All @@ -375,8 +383,10 @@ export default class SimpleMeshLayer<DataT = any, ExtraPropsT extends {} = {}> e
// props.mesh may not be ready at this time.
// The sampler will be set when `getModel` is called
if (model) {
model.setBindings({
sampler: texture || emptyTexture
});
model.setUniforms({
sampler: texture || emptyTexture,
hasTexture: Boolean(texture)
});
}
Expand Down
6 changes: 3 additions & 3 deletions modules/mesh-layers/src/utils/matrix.ts
Expand Up @@ -54,15 +54,15 @@ export const MATRIX_ATTRIBUTES = {
size: 12,
accessor: ['getOrientation', 'getScale', 'getTranslation', 'getTransformMatrix'],
shaderAttributes: {
instanceModelMatrix__LOCATION_0: {
instanceModelMatrixCol0: {
size: 3,
elementOffset: 0
},
instanceModelMatrix__LOCATION_1: {
instanceModelMatrixCol1: {
size: 3,
elementOffset: 3
},
instanceModelMatrix__LOCATION_2: {
instanceModelMatrixCol2: {
size: 3,
elementOffset: 6
},
Expand Down
1 change: 0 additions & 1 deletion test/render/index.js
Expand Up @@ -24,7 +24,6 @@ import {SnapshotTestRunner} from '@deck.gl/test-utils';

import './jupyter-widget';

// luma.gl v9 disable - likely a canvas sizing type issue?
test('Render Test', t => {
// tape's default timeout is 500ms
t.timeoutAfter(TEST_CASES.length * 10000 + 10000);
Expand Down

0 comments on commit eaa106b

Please sign in to comment.