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

[v9] Fix SimpleMeshLayer #8201

Merged
merged 4 commits into from Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
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 instanceModelMatrix_c0;
Pessimistress marked this conversation as resolved.
Show resolved Hide resolved
in vec3 instanceModelMatrix_c1;
in vec3 instanceModelMatrix_c2;
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(instanceModelMatrix_c0, instanceModelMatrix_c1, instanceModelMatrix_c2);
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: {
instanceModelMatrix_c0: {
size: 3,
elementOffset: 0
},
instanceModelMatrix__LOCATION_1: {
instanceModelMatrix_c1: {
size: 3,
elementOffset: 3
},
instanceModelMatrix__LOCATION_2: {
instanceModelMatrix_c2: {
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