Build the ribbon ourselves for great justice

This commit is contained in:
Tony Garnock-Jones 2023-02-14 13:40:55 +01:00
parent ce9c15836b
commit 6fd5e65376
2 changed files with 102 additions and 19 deletions

View File

@ -120,16 +120,18 @@
slen F slen F
SetPen SetPen
Home Home
"double" SetSideOrientation
#t SetSmooth
PenDown PenDown
[6 CW dlen F] 240 times [6 CW dlen F] 240 times
PenUp PenUp
]>>>> ]>>>>
<sprite "tt" [] <sprite "tt" []
<move <v 0.0 0.01 0.75> <move <v 1.0 0.01 0.75>
<rotate <v -0.125 0.0 0.0> <rotate <v -0.125 0.0 0.0>
<texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg" <texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg"
<v 3.0 3.0> <v 2.0 1.0>
<v 0.0 0.0> <v 0.0 0.0>
1.0] 1.0]
<turtle [ <turtle [

View File

@ -6,9 +6,10 @@ import {
Ray, Ray,
Scene, Scene,
Vector3, Vector3,
VertexData,
} from '@babylonjs/core/Legacy/legacy'; } from '@babylonjs/core/Legacy/legacy';
import { VM as BaseVM, Input, Primitives, Environment } from './cat.js'; import * as Cat from './cat.js';
export class PenState { export class PenState {
templatePath: Vector3[] = [new Vector3()]; templatePath: Vector3[] = [new Vector3()];
@ -47,17 +48,18 @@ export class PenState {
const D2R = Math.PI / 180; const D2R = Math.PI / 180;
export class TurtleVM extends BaseVM<TurtleVM> { export class TurtleVM extends Cat.VM<TurtleVM> {
container: Mesh; container: Mesh;
meshes: Mesh[] = []; meshes: Mesh[] = [];
counter = 0; counter = 0;
sideOrientation = Mesh.BACKSIDE; // TODO: see SideOrientation primitive below sideOrientation = Mesh.DEFAULTSIDE;
pen = new PenState(); pen = new PenState();
pos = new Vector3(); pos = new Vector3();
q = new Quaternion(); q = new Quaternion();
prevQ = this.q; prevQ = this.q;
smooth = false;
get euler(): Vector3 { get euler(): Vector3 {
return this.q.toEulerAngles(); return this.q.toEulerAngles();
@ -66,7 +68,7 @@ export class TurtleVM extends BaseVM<TurtleVM> {
constructor( constructor(
name: string, name: string,
scene: Scene | null, scene: Scene | null,
program: Input, program: Cat.Input,
) { ) {
super(program, TurtlePrimitives); super(program, TurtlePrimitives);
this.container = new Mesh(name, scene); this.container = new Mesh(name, scene);
@ -109,16 +111,96 @@ export class TurtleVM extends BaseVM<TurtleVM> {
throw new Error('todo'); throw new Error('todo');
} }
const m = MeshBuilder.CreateRibbon(this.container.name + this.counter++, { const m = new Mesh(this.container.name + this.counter++);
pathArray: this.pen.paths!, // TODO: this.sideOrientation
sideOrientation: this.sideOrientation,
}); const vertexData = new VertexData();
const positions: number[] = [];
const indices: number[] = [];
const normals: number[] = [];
const uvs: number[] = [];
const paths = this.pen.paths!;
const pathCount = paths.length;
const stepCount = paths[0].length;
const us: number[][] = [];
const vs: number[][] = [];
const uTotal: number[] = [];
const vTotal: number[] = [];
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
uTotal.push(0);
us.push([]);
let prev = paths[pathIndex][0];
for (let stepIndex = 0; stepIndex < stepCount; stepIndex++) {
const curr = paths[pathIndex][stepIndex];
uTotal[pathIndex] += curr.subtract(prev).length();
us[pathIndex][stepIndex] = uTotal[pathIndex];
prev = curr;
}
}
for (let stepIndex = 0; stepIndex < stepCount; stepIndex++) {
vTotal.push(0);
vs.push([]);
let prev = paths[0][stepIndex];
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
const curr = paths[pathIndex][stepIndex];
vTotal[stepIndex] += curr.subtract(prev).length();
vs[stepIndex][pathIndex] = vTotal[stepIndex];
prev = curr;
}
}
function pushPoint(pathIndex: number, stepIndex: number): number {
const pointIndex = positions.length / 3;
positions.push(... paths[pathIndex][stepIndex].asArray());
uvs.push(us[pathIndex][stepIndex] / uTotal[pathIndex],
vs[stepIndex][pathIndex] / vTotal[stepIndex]);
return pointIndex;
}
const cachedIndices: { [key: string]: number } = {};
function cachedPoint(pathIndex: number, stepIndex: number): number {
const v = paths[pathIndex][stepIndex].asArray().map(n => n.toFixed(3)).toString();
if (!(v in cachedIndices)) cachedIndices[v] = pushPoint(pathIndex, stepIndex);
return cachedIndices[v];
}
const computePointIndex = this.smooth ? cachedPoint : pushPoint;
for (let pathIndex = 1; pathIndex < pathCount; pathIndex++) {
for (let stepIndex = 1; stepIndex < stepCount; stepIndex++) {
indices.push(computePointIndex(pathIndex - 1, stepIndex - 1),
computePointIndex(pathIndex - 1, stepIndex),
computePointIndex(pathIndex, stepIndex - 1));
indices.push(computePointIndex(pathIndex - 1, stepIndex),
computePointIndex(pathIndex, stepIndex),
computePointIndex(pathIndex, stepIndex - 1));
}
}
VertexData.ComputeNormals(positions, indices, normals);
VertexData._ComputeSides(this.sideOrientation, positions, indices, normals, uvs);
vertexData.positions = new Float32Array(positions);
vertexData.indices = new Int32Array(indices);
vertexData.normals = new Float32Array(normals);
vertexData.uvs = new Float32Array(uvs);
vertexData.applyToMesh(m);
// const m = MeshBuilder.CreateRibbon(this.container.name + this.counter++, {
// pathArray: this.pen.paths!,
// sideOrientation: this.sideOrientation,
// });
m.parent = this.container; m.parent = this.container;
this.meshes.push(m); this.meshes.push(m);
} }
} }
export const TurtlePrimitives: Environment<TurtleVM> = Object.assign({}, Primitives, { export const TurtlePrimitives: Cat.Environment<TurtleVM> = Object.assign({}, Cat.Primitives, {
'Home'() { this.pos = new Vector3(); this.resetQ(new Quaternion()); return []; }, 'Home'() { this.pos = new Vector3(); this.resetQ(new Quaternion()); return []; },
'GetPos'() { return [this.pos.asArray()]; }, 'GetPos'() { return [this.pos.asArray()]; },
@ -166,19 +248,18 @@ export const TurtlePrimitives: Environment<TurtleVM> = Object.assign({}, Primiti
'PenUp'() { this.penUp(false); return []; }, 'PenUp'() { this.penUp(false); return []; },
'Close'() { this.penUp(true); return []; }, 'Close'() { this.penUp(true); return []; },
'SideOrientation'(s) { 'SetSmooth'(b) { this.smooth = b as boolean; return []; },
// TODO: why is this back to front?? argh 'SetSideOrientation'(s) {
switch (s) { switch (s) {
case "default": this.sideOrientation = Mesh.BACKSIDE; break; case "default": this.sideOrientation = Mesh.DEFAULTSIDE; break;
case "front": this.sideOrientation = Mesh.BACKSIDE; break; case "front": this.sideOrientation = Mesh.FRONTSIDE; break;
case "back": this.sideOrientation = Mesh.FRONTSIDE; break; case "back": this.sideOrientation = Mesh.BACKSIDE; break;
case "double": this.sideOrientation = Mesh.DOUBLESIDE; break; case "double": this.sideOrientation = Mesh.DOUBLESIDE; break;
default: default: throw new TypeError("Invalid SideOrientation: " + s);
break;
} }
return []; return [];
}, },
} satisfies Environment<TurtleVM>); } satisfies Cat.Environment<TurtleVM>);
function mitredExtrude( function mitredExtrude(
name: string, name: string,