Making Kinemage ribbons have the same normal for every pair of triangles

This commit is contained in:
Russ Taylor
2026-02-23 13:11:28 -05:00
parent 6b88acd2bc
commit e1d5d369f1
3 changed files with 20 additions and 6 deletions

View File

@@ -254,7 +254,8 @@ function convertKinTriangleArrays (ribbonObject: RibbonObject) {
labelArray: convertedLabels,
positionArray: convertedPositions,
breakArray: convertedBreaks,
colorArray: convertedColors
colorArray: convertedColors,
pairTriangleNormals: ribbonObject.pairTriangleNormals
}
}
@@ -308,7 +309,8 @@ function removePointBreaksTriangleArrays (convertedRibbonObject: RibbonObject) {
labelArray: editedLabels,
positionArray: editedPositions,
breakArray: editedBreaks,
colorArray: editedColors
colorArray: editedColors,
pairTriangleNormals: convertedRibbonObject.pairTriangleNormals
}
}
@@ -371,6 +373,7 @@ class KinParser {
let ballLabel: string[], ballPosition: number[], ballColor: number[]
let isRibbonList = false
let ribbonIsTriangles = false
let prevRibbonPointLabel = ''
let ribbonListDefaultColor: number[] = localColorDict['white']
@@ -533,6 +536,7 @@ class KinParser {
})
}
isRibbonList = true
ribbonIsTriangles = line.startsWith('@triangle') /* triangle or trianglelist */
prevRibbonPointLabel = ''
ribbonPointLabelArray = []
ribbonPointPositionArray = []
@@ -553,7 +557,8 @@ class KinParser {
labelArray: ribbonPointLabelArray,
positionArray: ribbonPointPositionArray,
breakArray: ribbonPointBreakArray,
colorArray: ribbonPointColorArray
colorArray: ribbonPointColorArray,
pairTriangleNormals: !ribbonIsTriangles
})
} else if (line.startsWith('@text')) {
isText = true

View File

@@ -47,7 +47,8 @@ export interface RibbonObject {
labelArray: string[], ///< Array of labels per element
positionArray: number[], ///< Catenation of x, y, z for each element, 9x as many as triangles (3 vertices per triangle)
colorArray: number[], ///< Catenation of r, g, b for each element, 9x as many as triangles (3 colors per triangle)
breakArray: boolean[] ///< A single boolean per element indicating if there is a break there
breakArray: boolean[], ///< A single boolean per element indicating if there is a break there
pairTriangleNormals: boolean ///< Whether to pair every other triangle normal for lighting (true for ribbons, false for triangles)
}
export interface VectorList {

View File

@@ -175,6 +175,7 @@ async function getMesh(ctx: RuntimeContext, ribbonObjects: RibbonObject[]) {
// There are three vertices per triangle.
/// @todo Ribbon lighting is to be set up to make each pair of triangles look like a quad with the same normal.
const numTriangles = coords.length / 9;
let prevTriangleNormal: Vec3 | undefined = undefined;
for (let i = 0; i < numTriangles; i++) {
const vertexList: Vec3[] = [];
@@ -215,14 +216,21 @@ async function getMesh(ctx: RuntimeContext, ribbonObjects: RibbonObject[]) {
b = c;
c = temp;
}
const n = Vec3.zero();
// Put both orientations of the triangle. Add a small amount along the normal to make them
// not be exactly on top of each other so that we only see the front face of each.
let n = Vec3.zero();
Vec3.triangleNormal(n, a, b, c);
if (i % 2 === 1) {
// For ribbons, every other triangle is meant to be paired with the previous one to make a quad with the same normal.
// So use the same normal for every other triangle.
n = prevTriangleNormal || n;
}
prevTriangleNormal = n;
addOffsetTriangle(builderState, a, b, c, n, 0.01);
Vec3.triangleNormal(n, a, c, b);
// Invert the normal for the back face.
Vec3.negate(n, n);
addOffsetTriangle(builderState, a, c, b, n, 0.01);
}
}