UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

206 lines (160 loc) • 6.56 kB
import { BufferGeometry, MeshBasicMaterial, MeshMatcapMaterial, MeshStandardMaterial } from "three"; import { assert } from "../../../../../../core/assert.js"; import { m3_rm_compose_transform } from "../../../../../../core/geom/mat3/m3_rm_compose_transform.js"; import { GameAssetType } from "../../../../../asset/GameAssetType.js"; import Entity from "../../../../../ecs/Entity.js"; import { Transform } from "../../../../../ecs/transform/Transform.js"; import { InterpolationType } from "../../../../../navigation/ecs/components/InterpolationType.js"; import { ShadedGeometry } from "../../../mesh-v2/ShadedGeometry.js"; import { ShadedGeometryFlags } from "../../../mesh-v2/ShadedGeometryFlags.js"; import { TubeMaterialType } from "../TubeMaterialType.js"; import { build_geometry_catmullrom } from "./build_geometry_catmullrom.js"; import { build_geometry_linear } from "./build_geometry_linear.js"; import { compute_smooth_profile_normals } from "./compute_smooth_profile_normals.js"; import { fix_shape_normal_order } from "./fix_shape_normal_order.js"; /** * * @param {number} segment_start * @param {number} segment_end * @returns {BufferGeometry} * @param {Path} path_component * @param {TubePathStyle} style * @param {number[]|Float32Array} shape_normal * @param {number[]|Float32Array} shape * @param {number[]|Float32Array} shape_transform */ function make_geometry_segment( path_component, style, shape, shape_normal, shape_transform, segment_start, segment_end ) { assert.notNaN(segment_start, 'segment_start'); assert.notNaN(segment_end, 'segment_end'); assert.greaterThanOrEqual(segment_end, segment_start, 'segment_start must be <= segment_end'); const interpolation = path_component.interpolation; if (interpolation === InterpolationType.Linear) { return build_geometry_linear(path_component, style, shape, shape_normal, shape_transform, segment_start, segment_end); } else if (interpolation === InterpolationType.CatmullRom) { return build_geometry_catmullrom(path_component, style, shape, shape_normal, shape_transform, segment_start, segment_end); } else { throw new Error(`Unsupported interpolation type '${interpolation}'`); } } /** * * @param {TubePathStyle} style * @param {AssetManager} am * @returns {Material|MeshStandardMaterial|MeshMatcapMaterial|MeshBasicMaterial} */ function make_material(style, am) { let material_def; const material_type = style.material_type; if (material_type === TubeMaterialType.Basic) { material_def = new MeshBasicMaterial(); } else if (material_type === TubeMaterialType.Standard) { material_def = new MeshStandardMaterial({ metalness: style.material.metalness, roughness: style.material.roughness, }); } else if (material_type === TubeMaterialType.Matcap) { material_def = new MeshMatcapMaterial(); am.get({ path: style.material.texture, type: GameAssetType.Texture, callback: asset => { material_def.matcap = asset.create(); material_def.needsUpdate = true; }, failure: console.error }); } else { throw new Error(`Unsupported material type '${material_type}'`); } return material_def; } export class TubePathBuilder { constructor() { /** * * @type {TubePathStyle|null} */ this.style = null; /** * * @type {Path|null} */ this.path = null; /** * * @type {MaterialManager|null} */ this.materials = null; /** * * @type {AssetManager|null} */ this.assetManager = null; } setPath(path) { this.path = path; } setStyle(style) { this.style = style; } setMaterials(m) { this.materials = m; } setAssetManager(assetManager) { this.assetManager = assetManager; } /** * * @param {Entity[]} destination */ build(destination) { const style = this.style; // generate three.js curve from path const path_component = this.path; const pointCount = path_component.getPointCount(); if (pointCount < 2) { // no curve possible without at least 2 points return; } const material_def = make_material(style, this.assetManager); material_def.color.set(style.color.toUint()); if (style.opacity < 1) { material_def.transparent = true; material_def.opacity = style.opacity; } const segment_count = style.path_mask.length / 2; const shape = fix_shape_normal_order(style.shape); let shape_normals = style.shape_normals; if (shape_normals === undefined || shape_normals === null) { shape_normals = new Float32Array(shape.length); compute_smooth_profile_normals(shape, shape_normals); } const shape_transform = new Float32Array(9); m3_rm_compose_transform(shape_transform, 0, 0, style.width, style.width, 0, 0, 0); for (let i = 0; i < segment_count; i++) { const i2 = i * 2; let segment_start = style.path_mask[i2]; let segment_end = style.path_mask[i2 + 1]; // check for wrong ordering if (segment_end < segment_start) { // violated interval constraint, re-order const t = segment_end; segment_end = segment_start; segment_start = t; } const geometry = make_geometry_segment( path_component, style, shape, shape_normals, shape_transform, segment_start, segment_end ); const entityBuilder = new Entity(); entityBuilder.add(new Transform()); const shaded_geometry = ShadedGeometry.from(geometry, material_def); shaded_geometry.writeFlag(ShadedGeometryFlags.CastShadow, style.cast_shadow); shaded_geometry.writeFlag(ShadedGeometryFlags.ReceiveShadow, style.receive_shadow); entityBuilder.add(shaded_geometry); destination.push(entityBuilder); } } }