That's better
This commit is contained in:
parent
df1f42ea81
commit
c319bb460a
|
@ -51,8 +51,8 @@ if ((navigator as any).oscpu?.startsWith('Linux')) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Interactivity = {
|
export type Interactivity = {
|
||||||
floorMeshes: () => Mesh[],
|
floorMeshes: () => AbstractMesh[],
|
||||||
touchableMeshes: () => Mesh[],
|
touchableMeshes: () => AbstractMesh[],
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EngineOptions = {
|
export type EngineOptions = {
|
||||||
|
|
|
@ -38,11 +38,11 @@ function interpretScene(myId: string, runningEngine: RunningEngine, rootMesh: Me
|
||||||
function spriteMain(spriteName: string, runningEngine: RunningEngine, rootMesh: Mesh, sceneDs: Ref) {
|
function spriteMain(spriteName: string, runningEngine: RunningEngine, rootMesh: Mesh, sceneDs: Ref) {
|
||||||
at sceneDs {
|
at sceneDs {
|
||||||
let currentShape = ShapeTree.empty(spriteName, runningEngine.scene);
|
let currentShape = ShapeTree.empty(spriteName, runningEngine.scene);
|
||||||
currentShape.node.parent = rootMesh;
|
currentShape.rootnode.parent = rootMesh;
|
||||||
on stop currentShape.remove();
|
on stop currentShape.remove();
|
||||||
during Shapes.Sprite({ "name": spriteName, "shape": $shape: Shapes.Shape }) => {
|
during Shapes.Sprite({ "name": spriteName, "shape": $shape: Shapes.Shape }) => {
|
||||||
currentShape = currentShape.reconcile(spriteName, spriteName, shape);
|
currentShape = currentShape.reconcile(spriteName, spriteName, shape);
|
||||||
currentShape.node.parent = rootMesh;
|
currentShape.rootnode.parent = rootMesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
during SceneProtocol.Gravity($direction: Shapes.Vector3) => {
|
during SceneProtocol.Gravity($direction: Shapes.Vector3) => {
|
||||||
|
|
100
src/interiors.ts
100
src/interiors.ts
|
@ -1,100 +0,0 @@
|
||||||
import {
|
|
||||||
Mesh,
|
|
||||||
Nullable,
|
|
||||||
Scene,
|
|
||||||
StandardMaterial,
|
|
||||||
Texture,
|
|
||||||
} from '@babylonjs/core/Legacy/legacy';
|
|
||||||
|
|
||||||
import { adjust, box, subtractMany } from './shapes.js';
|
|
||||||
|
|
||||||
export interface SurfaceOptions {
|
|
||||||
height?: number,
|
|
||||||
thickness?: number,
|
|
||||||
texturePath?: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
export function attachTexture(b: Mesh, scene: Scene, texturePath: string | undefined): Mesh {
|
|
||||||
if (texturePath !== void 0) {
|
|
||||||
const mat = new StandardMaterial(b.name + "_mat", scene);
|
|
||||||
mat.diffuseTexture = new Texture(texturePath, scene);
|
|
||||||
b.material = mat;
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DEFAULT_THICKNESS = 0.125;
|
|
||||||
|
|
||||||
export async function wall(name: string, length: number, scene: Scene, options: SurfaceOptions = {}): Promise<Mesh> {
|
|
||||||
return attachTexture(box(name, length, options.height ?? 2.90, options.thickness ?? DEFAULT_THICKNESS),
|
|
||||||
scene,
|
|
||||||
options.texturePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function floor(name: string, width: number, depth: number, scene: Scene, options: SurfaceOptions = {}): Promise<Mesh> {
|
|
||||||
return attachTexture(box(name, width, options.thickness ?? DEFAULT_THICKNESS, depth),
|
|
||||||
scene,
|
|
||||||
options.texturePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Wall = {
|
|
||||||
texture: string,
|
|
||||||
thickness?: number,
|
|
||||||
holes: Array<Mesh>,
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function room(name: string, width: number, depth: number, scene: Scene,
|
|
||||||
floorTexture: string,
|
|
||||||
ceilingTexture: string,
|
|
||||||
wall0: Nullable<Wall>,
|
|
||||||
wall1: Nullable<Wall>,
|
|
||||||
wall2: Nullable<Wall>,
|
|
||||||
wall3: Nullable<Wall>): Promise<Mesh> {
|
|
||||||
const th = (wallDetails: Wall): number => wallDetails.thickness ?? DEFAULT_THICKNESS;
|
|
||||||
const mkw = async (n: number, width: number, wallDetails: Wall): Promise<Mesh> => {
|
|
||||||
return adjust(subtractMany,
|
|
||||||
await wall(`${name}_${n}`, width, scene, {
|
|
||||||
texturePath: wallDetails.texture,
|
|
||||||
thickness: wallDetails.thickness,
|
|
||||||
}),
|
|
||||||
... wallDetails.holes);
|
|
||||||
};
|
|
||||||
|
|
||||||
const f = await floor(name +"_f", width, depth, scene, { texturePath: floorTexture });
|
|
||||||
f.position.y = -DEFAULT_THICKNESS / 2;
|
|
||||||
|
|
||||||
const c = await floor(name + "_c", width, depth, scene, { texturePath: ceilingTexture });
|
|
||||||
c.position.y = 2.90 + DEFAULT_THICKNESS / 2;
|
|
||||||
c.setParent(f);
|
|
||||||
|
|
||||||
if (wall0 !== null) {
|
|
||||||
const w0 = await mkw(0, width, wall0);
|
|
||||||
w0.position.z = f.scaling.z / 2 + th(wall0) / 2;
|
|
||||||
w0.setParent(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wall1 !== null) {
|
|
||||||
const w1 = await mkw(1, depth, wall1);
|
|
||||||
w1.position.x = f.scaling.x / 2 + th(wall1) / 2;
|
|
||||||
w1.rotation.y = Math.PI / 2;
|
|
||||||
w1.setParent(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wall2 !== null) {
|
|
||||||
const w2 = await mkw(2, width, wall2);
|
|
||||||
w2.position.z = -(f.scaling.z / 2 + th(wall2) / 2);
|
|
||||||
w2.rotation.y = Math.PI;
|
|
||||||
w2.setParent(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wall3 !== null) {
|
|
||||||
const w3 = await mkw(3, depth, wall3);
|
|
||||||
w3.position.x = -(f.scaling.x / 2 + th(wall3) / 2);
|
|
||||||
w3.rotation.y = -Math.PI / 2;
|
|
||||||
w3.setParent(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
f.position.y = DEFAULT_THICKNESS / 2;
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
221
src/shapes.ts
221
src/shapes.ts
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
AbstractMesh,
|
||||||
Color3,
|
Color3,
|
||||||
CubeTexture,
|
CubeTexture,
|
||||||
CSG,
|
CSG,
|
||||||
|
@ -8,7 +9,6 @@ import {
|
||||||
Mesh,
|
Mesh,
|
||||||
MeshBuilder,
|
MeshBuilder,
|
||||||
Node,
|
Node,
|
||||||
PhotoDome,
|
|
||||||
Quaternion,
|
Quaternion,
|
||||||
Scene,
|
Scene,
|
||||||
SceneLoader,
|
SceneLoader,
|
||||||
|
@ -22,64 +22,29 @@ import { KeyedDictionary, Value, is } from "@syndicate-lang/core";
|
||||||
|
|
||||||
import * as Shapes from './gen/shapes.js';
|
import * as Shapes from './gen/shapes.js';
|
||||||
|
|
||||||
export const activeFloorMeshes: Array<Mesh> = [];
|
export const activeFloorMeshes: Array<AbstractMesh> = [];
|
||||||
export const activeTouchableMeshes: Array<Mesh> = [];
|
export const activeTouchableMeshes: Array<AbstractMesh> = [];
|
||||||
|
|
||||||
export function adjust(f: (... csgs: CSG[]) => CSG, ... ms: Mesh[]): Mesh {
|
|
||||||
const csgs = ms.map(m => CSG.FromMesh(m));
|
|
||||||
const c = f(... csgs);
|
|
||||||
if (ms.length > 0) {
|
|
||||||
ms.forEach(m => m.dispose());
|
|
||||||
const scene = ms[0].getScene();
|
|
||||||
const ans = c.toMesh(ms[0].name, null, scene, true);
|
|
||||||
ans.material = ms[0].material;
|
|
||||||
return ans;
|
|
||||||
} else {
|
|
||||||
return c.toMesh("adjusted");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const subtractMany = (a: CSG, ... bs: CSG[]) => bs.reduce((a, b) => a.subtract(b), a);
|
|
||||||
|
|
||||||
export class PhotoSemiDome extends PhotoDome {
|
|
||||||
readonly _size: number;
|
|
||||||
|
|
||||||
constructor(... args: ConstructorParameters<typeof PhotoDome>) {
|
|
||||||
super(... args);
|
|
||||||
this._size = args[2].size!;
|
|
||||||
this._chopMesh();
|
|
||||||
}
|
|
||||||
|
|
||||||
_chopMesh() {
|
|
||||||
const d = this._size;
|
|
||||||
this._mesh.getChildMeshes()[0].dispose(); // remove the covering half-sphere
|
|
||||||
this._mesh = adjust(subtractMany,
|
|
||||||
this._mesh,
|
|
||||||
box(this._mesh.name + "_chop", d, d, d, 0, 0, -d/2));
|
|
||||||
this._mesh.parent = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function box(name: string, width: number, height: number, depth: number, x?: number, y?: number, z?: number): Mesh {
|
|
||||||
const b = MeshBuilder.CreateBox(name, {});
|
|
||||||
b.scaling = new Vector3(width, height, depth);
|
|
||||||
b.position = new Vector3(x ?? 0, y ?? b.scaling.y/2, z ?? 0);
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export class ShapeTree<N extends Node = Node> {
|
export class ShapeTree<N extends Node = Node> {
|
||||||
shapePreserve: Value;
|
shapePreserve: Value;
|
||||||
cleanups: Array<() => void> = [];
|
cleanups: Array<() => void> = [];
|
||||||
|
subnodes: Promise<N[]>;
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public scene: Scene,
|
public scene: Scene,
|
||||||
public shape: Shapes.Shape,
|
public shape: Shapes.Shape,
|
||||||
public node: N,
|
public rootnode: N,
|
||||||
|
subnodes?: Promise<N[]>,
|
||||||
) {
|
) {
|
||||||
this.shapePreserve = Shapes.fromShape(this.shape);
|
this.shapePreserve = Shapes.fromShape(this.shape);
|
||||||
this.node.metadata = {};
|
const metadata = {};
|
||||||
|
this.rootnode.metadata = metadata;
|
||||||
|
this.subnodes = subnodes ?? Promise.resolve([]);
|
||||||
|
this.subnodes.then(ns => ns.forEach(n => n.metadata = metadata));
|
||||||
|
}
|
||||||
|
|
||||||
|
get allnodes(): Promise<N[]> {
|
||||||
|
return this.subnodes.then(ns => [this.rootnode, ... ns]);
|
||||||
}
|
}
|
||||||
|
|
||||||
reconcile(spriteName: string, name: string, shape: Shapes.Shape): ShapeTree {
|
reconcile(spriteName: string, name: string, shape: Shapes.Shape): ShapeTree {
|
||||||
|
@ -88,14 +53,14 @@ export class ShapeTree<N extends Node = Node> {
|
||||||
} else {
|
} else {
|
||||||
this.remove();
|
this.remove();
|
||||||
return build(name, this.scene, shape, {
|
return build(name, this.scene, shape, {
|
||||||
spriteName: m => m.node.metadata.spriteName = spriteName,
|
spriteName: async m => m.rootnode.metadata.spriteName = spriteName,
|
||||||
collisions: m => m.node.checkCollisions = true,
|
collisions: async m => (await m.allnodes).forEach(n => n.checkCollisions = true),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
this.node?.dispose();
|
this.allnodes.then(ns => ns.forEach(n => n.dispose()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static empty(name: string, scene: Scene): ShapeTree {
|
static empty(name: string, scene: Scene): ShapeTree {
|
||||||
|
@ -123,9 +88,9 @@ export function q(q: Shapes.Quaternion): Quaternion {
|
||||||
return new Quaternion(q.a, q.b, q.c, q.d);
|
return new Quaternion(q.a, q.b, q.c, q.d);
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MeshCustomizer = { [key: string]: ((m: ShapeTree<Mesh>) => void) };
|
export type MeshCustomizer = { [key: string]: ((m: ShapeTree<AbstractMesh>) => void) };
|
||||||
|
|
||||||
function applyCustomizer(m: ShapeTree<Mesh>, c: MeshCustomizer) {
|
function applyCustomizer(m: ShapeTree<AbstractMesh>, c: MeshCustomizer) {
|
||||||
Object.values(c).forEach(f => f(m));
|
Object.values(c).forEach(f => f(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,32 +144,38 @@ function releaseTexture(entry: CachedTexture) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type BuiltMesh = {
|
||||||
|
rootnode: AbstractMesh,
|
||||||
|
subnodes?: Promise<AbstractMesh[]>,
|
||||||
|
};
|
||||||
|
|
||||||
export function buildMesh(
|
export function buildMesh(
|
||||||
name: string,
|
name: string,
|
||||||
scene: Scene | null,
|
scene: Scene | null,
|
||||||
meshSpec: Shapes.Mesh,
|
meshSpec: Shapes.Mesh,
|
||||||
): Mesh {
|
): BuiltMesh {
|
||||||
switch (meshSpec._variant) {
|
switch (meshSpec._variant) {
|
||||||
case "Sphere": return MeshBuilder.CreateSphere(name, {}, scene);
|
case "Sphere": return { rootnode: MeshBuilder.CreateSphere(name, {}, scene) };
|
||||||
case "Box": return MeshBuilder.CreateBox(name, {}, scene);
|
case "Box": return { rootnode: MeshBuilder.CreateBox(name, {}, scene) };
|
||||||
case "Ground": {
|
case "Ground": {
|
||||||
const v = v2(meshSpec.value.size);
|
const v = v2(meshSpec.value.size);
|
||||||
return MeshBuilder.CreateGround(name, { width: v.x, height: v.y }, scene ?? void 0);
|
return {
|
||||||
|
rootnode: MeshBuilder.CreateGround(
|
||||||
|
name, { width: v.x, height: v.y }, scene ?? void 0),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
case "Plane": return MeshBuilder.CreatePlane(name, {}, scene);
|
case "Plane": return { rootnode: MeshBuilder.CreatePlane(name, {}, scene) };
|
||||||
case "External": {
|
case "External": {
|
||||||
const primary = new Mesh(name, scene);
|
const rootnode = new Mesh(name, scene);
|
||||||
SceneLoader.ImportMesh(
|
return {
|
||||||
"",
|
rootnode,
|
||||||
meshSpec.value.path,
|
subnodes: new Promise(async resolve => {
|
||||||
void 0,
|
const r =
|
||||||
scene,
|
await SceneLoader.ImportMeshAsync("", meshSpec.value.path, void 0, scene);
|
||||||
meshes => meshes.forEach(m => {
|
r.meshes.forEach(m => m.parent = rootnode);
|
||||||
m.parent = primary;
|
resolve(r.meshes);
|
||||||
console.log('adding submesh');
|
}),
|
||||||
}));
|
};
|
||||||
console.log('returning primary');
|
|
||||||
return primary;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,7 +183,8 @@ export function buildMesh(
|
||||||
export function build(name: string, scene: Scene, shape: Shapes.Shape, customize: MeshCustomizer): ShapeTree {
|
export function build(name: string, scene: Scene, shape: Shapes.Shape, customize: MeshCustomizer): ShapeTree {
|
||||||
switch (shape._variant) {
|
switch (shape._variant) {
|
||||||
case "Mesh": {
|
case "Mesh": {
|
||||||
const t = new ShapeTree<Mesh>(scene, shape, buildMesh(name, scene, shape.value));
|
const m = buildMesh(name, scene, shape.value);
|
||||||
|
const t = new ShapeTree<AbstractMesh>(scene, shape, m.rootnode, m.subnodes);
|
||||||
applyCustomizer(t, customize);
|
applyCustomizer(t, customize);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
@ -225,15 +197,15 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
|
||||||
|
|
||||||
case "Scale": {
|
case "Scale": {
|
||||||
const t = ShapeTree.transform(name, scene, shape);
|
const t = ShapeTree.transform(name, scene, shape);
|
||||||
t.node.scaling = v3(shape.value.v);
|
t.rootnode.scaling = v3(shape.value.v);
|
||||||
build(name + '.inner', scene, shape.value.shape, customize).node.parent = t.node;
|
build(name + '.inner', scene, shape.value.shape, customize).rootnode.parent = t.rootnode;
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "Move": {
|
case "Move": {
|
||||||
const t = ShapeTree.transform(name, scene, shape);
|
const t = ShapeTree.transform(name, scene, shape);
|
||||||
t.node.position = v3(shape.value.v);
|
t.rootnode.position = v3(shape.value.v);
|
||||||
build(name + '.inner', scene, shape.value.shape, customize).node.parent = t.node;
|
build(name + '.inner', scene, shape.value.shape, customize).rootnode.parent = t.rootnode;
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,21 +213,21 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
|
||||||
const t = ShapeTree.transform(name, scene, shape);
|
const t = ShapeTree.transform(name, scene, shape);
|
||||||
switch (shape.value._variant) {
|
switch (shape.value._variant) {
|
||||||
case "euler":
|
case "euler":
|
||||||
t.node.rotation = v3(shape.value.v);
|
t.rootnode.rotation = v3(shape.value.v);
|
||||||
t.node.rotation.scaleInPlace(2 * Math.PI);
|
t.rootnode.rotation.scaleInPlace(2 * Math.PI);
|
||||||
break;
|
break;
|
||||||
case "quaternion":
|
case "quaternion":
|
||||||
t.node.rotationQuaternion = q(shape.value.q);
|
t.rootnode.rotationQuaternion = q(shape.value.q);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
build(name + '.inner', scene, shape.value.shape, customize).node.parent = t.node;
|
build(name + '.inner', scene, shape.value.shape, customize).rootnode.parent = t.rootnode;
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "many": {
|
case "many": {
|
||||||
const t = ShapeTree.transform(name, scene, shape);
|
const t = ShapeTree.transform(name, scene, shape);
|
||||||
shape.value.forEach((s, i) => {
|
shape.value.forEach((s, i) => {
|
||||||
build(name + '[' + i + ']', scene, s, customize).node.parent = t.node;
|
build(name + '[' + i + ']', scene, s, customize).rootnode.parent = t.rootnode;
|
||||||
});
|
});
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
@ -264,7 +236,7 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
|
||||||
const entry = buildTexture(name + '.texture', scene, shape.value.spec);
|
const entry = buildTexture(name + '.texture', scene, shape.value.spec);
|
||||||
const t = build(name + '.inner', scene, shape.value.shape, {
|
const t = build(name + '.inner', scene, shape.value.shape, {
|
||||||
... customize,
|
... customize,
|
||||||
material: m => m.node.material = entry.material,
|
material: async m => (await m.allnodes).forEach(n => n.material = entry.material),
|
||||||
});
|
});
|
||||||
t.cleanups.push(() => releaseTexture(entry));
|
t.cleanups.push(() => releaseTexture(entry));
|
||||||
return t;
|
return t;
|
||||||
|
@ -276,7 +248,7 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
|
||||||
if (shape.value._variant === "transparent") mat.alpha = shape.value.alpha;
|
if (shape.value._variant === "transparent") mat.alpha = shape.value.alpha;
|
||||||
return build(name + '.inner', scene, shape.value.shape, {
|
return build(name + '.inner', scene, shape.value.shape, {
|
||||||
... customize,
|
... customize,
|
||||||
material: m => m.node.material = mat,
|
material: async m => (await m.allnodes).forEach(n => n.material = mat),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,11 +258,12 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
|
||||||
case "Floor":
|
case "Floor":
|
||||||
return build(name, scene, shape.value.shape, {
|
return build(name, scene, shape.value.shape, {
|
||||||
... customize,
|
... customize,
|
||||||
floor: m => {
|
floor: async m => {
|
||||||
activeFloorMeshes.push(m.node);
|
const nodes = await m.allnodes;
|
||||||
|
activeFloorMeshes.push(... nodes);
|
||||||
m.cleanups.push(() => {
|
m.cleanups.push(() => {
|
||||||
const i = activeFloorMeshes.indexOf(m.node);
|
const i = activeFloorMeshes.indexOf(nodes[0]);
|
||||||
if (i !== -1) activeFloorMeshes.splice(i, 1);
|
if (i !== -1) activeFloorMeshes.splice(i, nodes.length);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -298,40 +271,39 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
|
||||||
case "Nonphysical":
|
case "Nonphysical":
|
||||||
return build(name, scene, shape.value.shape, {
|
return build(name, scene, shape.value.shape, {
|
||||||
... customize,
|
... customize,
|
||||||
collisions: m => {
|
collisions: async m => (await m.allnodes).forEach(n => n.checkCollisions = false),
|
||||||
m.node.checkCollisions = false;
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
case "Touchable":
|
case "Touchable":
|
||||||
return build(name, scene, shape.value.shape, {
|
return build(name, scene, shape.value.shape, {
|
||||||
... customize,
|
... customize,
|
||||||
touchable: m => {
|
touchable: async m => {
|
||||||
m.node.metadata.touchable = true;
|
const nodes = await m.allnodes;
|
||||||
activeTouchableMeshes.push(m.node);
|
m.rootnode.metadata.touchable = true;
|
||||||
|
activeTouchableMeshes.push(... nodes);
|
||||||
m.cleanups.push(() => {
|
m.cleanups.push(() => {
|
||||||
const i = activeTouchableMeshes.indexOf(m.node);
|
const i = activeTouchableMeshes.indexOf(nodes[0]);
|
||||||
if (i !== -1) activeTouchableMeshes.splice(i, 1);
|
if (i !== -1) activeTouchableMeshes.splice(i, nodes.length);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
case "CSG": {
|
case "CSG": {
|
||||||
const t = new ShapeTree<Mesh>(
|
const m = buildCSG(name, scene, shape.value.expr);
|
||||||
scene, shape, buildCSG(shape.value.expr).toMesh(name, null, scene));
|
const t = new ShapeTree<AbstractMesh>(scene, shape, m.rootnode, m.subnodes);
|
||||||
applyCustomizer(t, customize);
|
applyCustomizer(t, customize);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "Skybox": {
|
case "Skybox": {
|
||||||
const t = new ShapeTree<Mesh>(scene, shape, MeshBuilder.CreateBox(name, { size: 2000 }, scene));
|
const t = new ShapeTree<AbstractMesh>(scene, shape, MeshBuilder.CreateBox(name, { size: 2000 }, scene));
|
||||||
const mat = new StandardMaterial(name, scene);
|
const mat = new StandardMaterial(name, scene);
|
||||||
mat.backFaceCulling = false;
|
mat.backFaceCulling = false;
|
||||||
mat.reflectionTexture = new CubeTexture(shape.value.path, scene);
|
mat.reflectionTexture = new CubeTexture(shape.value.path, scene);
|
||||||
mat.reflectionTexture.coordinatesMode = Texture.SKYBOX_MODE;
|
mat.reflectionTexture.coordinatesMode = Texture.SKYBOX_MODE;
|
||||||
mat.diffuseColor = new Color3(0, 0, 0);
|
mat.diffuseColor = new Color3(0, 0, 0);
|
||||||
mat.specularColor = new Color3(0, 0, 0);
|
mat.specularColor = new Color3(0, 0, 0);
|
||||||
t.node.material = mat;
|
t.rootnode.material = mat;
|
||||||
applyCustomizer(t, customize);
|
applyCustomizer(t, customize);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
@ -344,15 +316,21 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildCSG(expr: Shapes.CSGExpr): CSG {
|
export function buildCSG(name: string, scene: Scene, expr: Shapes.CSGExpr): BuiltMesh {
|
||||||
function walk(expr: Shapes.CSGExpr, matrix: Matrix): CSG {
|
async function walk(expr: Shapes.CSGExpr, matrix: Matrix): Promise<CSG> {
|
||||||
switch (expr._variant) {
|
switch (expr._variant) {
|
||||||
case "mesh": {
|
case "mesh": {
|
||||||
const mesh = buildMesh("", null, expr.shape);
|
const m = buildMesh("", null, expr.shape);
|
||||||
mesh.freezeWorldMatrix(matrix, true);
|
const nodes: Mesh[] =
|
||||||
mesh.unfreezeWorldMatrix();
|
([m.rootnode, ... (m.subnodes ? await m.subnodes : [])]
|
||||||
const c = CSG.FromMesh(mesh);
|
.filter(n => n instanceof Mesh)) as Mesh[];
|
||||||
mesh.dispose();
|
nodes.forEach(n => {
|
||||||
|
n.freezeWorldMatrix(matrix, true);
|
||||||
|
n.unfreezeWorldMatrix();
|
||||||
|
});
|
||||||
|
const c = CSG.FromMesh(nodes[0]);
|
||||||
|
nodes.slice(1).forEach(n => c.unionInPlace(CSG.FromMesh(n)));
|
||||||
|
nodes.forEach(n => n.dispose());
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
case "scale":
|
case "scale":
|
||||||
|
@ -367,28 +345,43 @@ export function buildCSG(expr: Shapes.CSGExpr): CSG {
|
||||||
expr.v.x * 2 * Math.PI,
|
expr.v.x * 2 * Math.PI,
|
||||||
expr.v.z * 2 * Math.PI)));
|
expr.v.z * 2 * Math.PI)));
|
||||||
case "subtract": {
|
case "subtract": {
|
||||||
let c = walk(expr.base, matrix);
|
const c = await walk(expr.base, matrix);
|
||||||
expr.more.forEach(d => c.subtractInPlace(walk(d, matrix)));
|
for (const d of expr.more) {
|
||||||
|
c.subtractInPlace(await walk(d, matrix));
|
||||||
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
case "union": {
|
case "union": {
|
||||||
let c = walk(expr.base, matrix);
|
const c = await walk(expr.base, matrix);
|
||||||
expr.more.forEach(d => c.unionInPlace(walk(d, matrix)));
|
for (const d of expr.more) {
|
||||||
|
c.unionInPlace(await walk(d, matrix));
|
||||||
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
case "intersect": {
|
case "intersect": {
|
||||||
let c = walk(expr.base, matrix);
|
const c = await walk(expr.base, matrix);
|
||||||
expr.more.forEach(d => c.intersectInPlace(walk(d, matrix)));
|
for (const d of expr.more) {
|
||||||
|
c.intersectInPlace(await walk(d, matrix));
|
||||||
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
case "invert": {
|
case "invert": {
|
||||||
let c = walk(expr.shape, matrix);
|
const c = await walk(expr.shape, matrix);
|
||||||
c.inverseInPlace();
|
c.inverseInPlace();
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return walk(expr, Matrix.Identity());
|
const rootnode = new Mesh(name, scene);
|
||||||
|
return {
|
||||||
|
rootnode,
|
||||||
|
subnodes: new Promise(async resolve => {
|
||||||
|
const c = await walk(expr, Matrix.Identity());
|
||||||
|
const m = c.toMesh(name + ".csg", null, scene);
|
||||||
|
m.parent = rootnode;
|
||||||
|
resolve([m]);
|
||||||
|
}),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const builder: { [key: string]: (... args: any[]) => Shapes.Shape } = {
|
export const builder: { [key: string]: (... args: any[]) => Shapes.Shape } = {
|
||||||
|
|
Loading…
Reference in New Issue