Build the ribbon ourselves for great justice
This commit is contained in:
parent
ce9c15836b
commit
6fd5e65376
|
@ -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 [
|
||||||
|
|
115
src/turtle.ts
115
src/turtle.ts
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue