UNPKG

@animech-public/playcanvas

Version:
221 lines (218 loc) 7.71 kB
import { Color } from '../../../core/math/color.js'; import { Vec3 } from '../../../core/math/vec3.js'; import { MeshInstance } from '../../../scene/mesh-instance.js'; import { Entity } from '../../../framework/entity.js'; import { CULLFACE_BACK, SEMANTIC_POSITION, SEMANTIC_COLOR } from '../../../platform/graphics/constants.js'; import { BLEND_NORMAL } from '../../../scene/constants.js'; import { COLOR_GRAY } from '../color.js'; import { Mesh } from '../../../scene/mesh.js'; import { Geometry } from '../../../scene/geometry/geometry.js'; import { BoxGeometry } from '../../../scene/geometry/box-geometry.js'; import { CylinderGeometry } from '../../../scene/geometry/cylinder-geometry.js'; import { ConeGeometry } from '../../../scene/geometry/cone-geometry.js'; import { PlaneGeometry } from '../../../scene/geometry/plane-geometry.js'; import { SphereGeometry } from '../../../scene/geometry/sphere-geometry.js'; import { TorusGeometry } from '../../../scene/geometry/torus-geometry.js'; import { Mat4 } from '../../../core/math/mat4.js'; import { createShaderFromCode } from '../../../scene/shader-lib/utils.js'; import { Material } from '../../../scene/materials/material.js'; const SHADING_DAMP_SCALE = 0.25; const SHADING_DAMP_OFFSET = 0.75; const LIGHT_DIR = new Vec3(1, 2, 3); const GEOMETRIES = { box: BoxGeometry, cone: ConeGeometry, cylinder: CylinderGeometry, plane: PlaneGeometry, sphere: SphereGeometry, torus: TorusGeometry }; const shaderDesc = { uniqueName: 'axis-shape', attributes: { vertex_position: SEMANTIC_POSITION, vertex_color: SEMANTIC_COLOR }, vertexCode: ` attribute vec3 vertex_position; attribute vec4 vertex_color; varying vec4 vColor; uniform mat4 matrix_model; uniform mat4 matrix_viewProjection; void main(void) { gl_Position = matrix_viewProjection * matrix_model * vec4(vertex_position, 1.0); gl_Position.z = clamp(gl_Position.z, -abs(gl_Position.w), abs(gl_Position.w)); vColor = vertex_color; } `, fragmentCode: ` precision highp float; varying vec4 vColor; void main(void) { gl_FragColor = vColor; } ` }; const shadingMeshMap = new Map(); const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); const tmpM1 = new Mat4(); const tmpG = new Geometry(); tmpG.positions = []; tmpG.normals = []; const applyShadowColor = (geom, color, transform) => { if (!geom.normals || !geom.positions) { return []; } let localLightDir; if (transform) { localLightDir = tmpM1.copy(transform).invert().transformVector(tmpV1.copy(LIGHT_DIR), tmpV1).normalize(); } geom.colors = []; const shading = []; const numVertices = geom.positions.length / 3; for (let i = 0; i < numVertices; i++) { let strength = 1; if (localLightDir) { const x = geom.normals[i * 3]; const y = geom.normals[i * 3 + 1]; const z = geom.normals[i * 3 + 2]; const normal = tmpV2.set(x, y, z); const dot = localLightDir.dot(normal); strength = dot * SHADING_DAMP_SCALE + SHADING_DAMP_OFFSET; } shading.push(strength); geom.colors.push(strength * color.r * 0xFF, strength * color.g * 0xFF, strength * color.b * 0xFF, color.a * 0xFF); } return shading; }; const setMeshColor = (mesh, color) => { const shading = shadingMeshMap.get(mesh); const colors = []; for (let i = 0; i < shading.length; i++) { colors.push(shading[i] * color.r * 0xFF, shading[i] * color.g * 0xFF, shading[i] * color.b * 0xFF, color.a * 0xFF); } mesh.setColors32(colors); mesh.update(); }; class Shape { constructor(device, options) { var _options$axis, _options$position, _options$rotation, _options$scale, _options$disabled, _options$layers, _options$shading; this._position = void 0; this._rotation = void 0; this._scale = void 0; this._layers = []; this._shading = true; this._disabled = void 0; this._defaultColor = Color.WHITE; this._hoverColor = Color.BLACK; this._disabledColor = COLOR_GRAY; this._cull = CULLFACE_BACK; this.device = void 0; this.axis = void 0; this.entity = void 0; this.triData = []; this.meshInstances = []; this.device = device; this.axis = (_options$axis = options.axis) != null ? _options$axis : 'x'; this._position = (_options$position = options.position) != null ? _options$position : new Vec3(); this._rotation = (_options$rotation = options.rotation) != null ? _options$rotation : new Vec3(); this._scale = (_options$scale = options.scale) != null ? _options$scale : new Vec3(1, 1, 1); this._disabled = (_options$disabled = options.disabled) != null ? _options$disabled : false; this._layers = (_options$layers = options.layers) != null ? _options$layers : this._layers; this._shading = (_options$shading = options.shading) != null ? _options$shading : this._shading; if (options.defaultColor instanceof Color) { this._defaultColor = options.defaultColor; } if (options.hoverColor instanceof Color) { this._hoverColor = options.hoverColor; } if (options.disabledColor instanceof Color) { this._disabledColor = options.disabledColor; } } set disabled(value) { for (let i = 0; i < this.meshInstances.length; i++) { setMeshColor(this.meshInstances[i].mesh, value ? this._disabledColor : this._defaultColor); } this._disabled = value != null ? value : false; } get disabled() { return this._disabled; } set shading(value) { this._shading = value != null ? value : true; const color = this._disabled ? this._disabledColor : this._defaultColor; for (let i = 0; i < this.meshInstances.length; i++) { const mesh = this.meshInstances[i].mesh; mesh.getPositions(tmpG.positions); mesh.getNormals(tmpG.normals); const shadow = applyShadowColor(tmpG, color, this._shading ? this.entity.getWorldTransform() : undefined); shadingMeshMap.set(mesh, shadow); setMeshColor(mesh, color); } } get shading() { return this._shading; } _createRoot(name) { this.entity = new Entity(`${name}:${this.axis}`); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._scale); } _createMesh(geom, shading = true) { const color = this._disabled ? this._disabledColor : this._defaultColor; const shadow = applyShadowColor(geom, color, shading ? this.entity.getWorldTransform() : undefined); const mesh = Mesh.fromGeometry(this.device, geom); shadingMeshMap.set(mesh, shadow); return mesh; } _createRenderComponent(entity, meshes) { const shader = createShaderFromCode(this.device, shaderDesc.vertexCode, shaderDesc.fragmentCode, shaderDesc.uniqueName, { vertex_position: SEMANTIC_POSITION, vertex_color: SEMANTIC_COLOR }); const material = new Material(); material.shader = shader; material.cull = this._cull; material.blendType = BLEND_NORMAL; if (material.chunks) { material.chunks.debugOutputPS = ''; } material.update(); const meshInstances = []; for (let i = 0; i < meshes.length; i++) { const mi = new MeshInstance(meshes[i], material); mi.cull = false; meshInstances.push(mi); this.meshInstances.push(mi); } entity.addComponent('render', { meshInstances: meshInstances, layers: this._layers, castShadows: false }); } _addRenderMesh(entity, type, shading) { const Geometry = GEOMETRIES[type]; if (!Geometry) { throw new Error('Invalid primitive type.'); } this._createRenderComponent(entity, [this._createMesh(new Geometry(), shading)]); } hover(state) { if (this._disabled) { return; } for (let i = 0; i < this.meshInstances.length; i++) { const color = state ? this._hoverColor : this._defaultColor; const mesh = this.meshInstances[i].mesh; setMeshColor(mesh, color); } } destroy() { this.entity.destroy(); } } export { Shape };