1339 lines
40 KiB
Haxe
1339 lines
40 KiB
Haxe
|
|
package iron.object;
|
||
|
|
|
||
|
|
import iron.Scene;
|
||
|
|
import iron.data.SceneFormat;
|
||
|
|
import iron.math.Vec4;
|
||
|
|
import iron.math.Quat;
|
||
|
|
import iron.math.Mat4;
|
||
|
|
|
||
|
|
import iron.data.Data;
|
||
|
|
import iron.data.MeshData;
|
||
|
|
import iron.data.MaterialData;
|
||
|
|
import haxe.ds.Vector;
|
||
|
|
import kha.arrays.Float32Array;
|
||
|
|
import kha.arrays.Uint32Array;
|
||
|
|
import kha.arrays.Int16Array;
|
||
|
|
import kha.Color;
|
||
|
|
import leenkx.trait.internal.RenderDraw;
|
||
|
|
|
||
|
|
class CurveObject extends Object {
|
||
|
|
|
||
|
|
public var data: TCurveData;
|
||
|
|
public var splinesLength: Int;
|
||
|
|
public var equidistantSamples: Int = 0;
|
||
|
|
public var curveMesh: MeshObject = null;
|
||
|
|
|
||
|
|
static var _p0 = new Vec4();
|
||
|
|
static var _p1 = new Vec4();
|
||
|
|
static var _p2 = new Vec4();
|
||
|
|
static var _p3 = new Vec4();
|
||
|
|
|
||
|
|
static var _v1 = new Vec4();
|
||
|
|
static var _v2 = new Vec4();
|
||
|
|
|
||
|
|
public function new(data: TCurveData) {
|
||
|
|
super();
|
||
|
|
this.data = copyCurveData(data);
|
||
|
|
splinesLength = this.data.splines.length;
|
||
|
|
|
||
|
|
if (this.data.shape_keys != null && this.data.shape_keys.length > 0)
|
||
|
|
applyShapeKeys();
|
||
|
|
|
||
|
|
if (this.data.material_refs != null && this.data.material_refs.length > 0)
|
||
|
|
addMeshObject();
|
||
|
|
else
|
||
|
|
draw(this.data.strength, Color.fromFloats(this.data.color[0], this.data.color[1], this.data.color[2], this.data.color[3]));
|
||
|
|
}
|
||
|
|
|
||
|
|
public function addMeshObject(){
|
||
|
|
Data.getMesh("mesh_" + data.name, data.name, function(meshData: MeshData) {
|
||
|
|
var materials = new Vector<MaterialData>(data.material_refs.length);
|
||
|
|
var materialsLoaded = 0;
|
||
|
|
|
||
|
|
for (i in 0...data.material_refs.length) {
|
||
|
|
var ref = data.material_refs[i];
|
||
|
|
|
||
|
|
Data.getMaterial(Scene.active.raw.name, ref, function(mat: MaterialData) {
|
||
|
|
materials[i] = mat;
|
||
|
|
materialsLoaded++;
|
||
|
|
|
||
|
|
if (materialsLoaded == data.material_refs.length) {
|
||
|
|
curveMesh = new MeshObject(meshData, materials);
|
||
|
|
curveMesh.name = this.data.object + "_mesh";
|
||
|
|
curveMesh.raw = cast {
|
||
|
|
name: this.data.object + "_mesh",
|
||
|
|
type: "mesh_object",
|
||
|
|
};
|
||
|
|
curveMesh.setParent(this);
|
||
|
|
curveMesh.addTrait(new leenkx.trait.internal.UniformsManager());
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function copyCurveData(data: TCurveData): TCurveData {
|
||
|
|
var newData: TCurveData = {
|
||
|
|
name: data.name,
|
||
|
|
object: data.object,
|
||
|
|
splines: [],
|
||
|
|
strength: data.strength,
|
||
|
|
color: copyFloat32Array(data.color),
|
||
|
|
material_refs: data.material_refs,
|
||
|
|
shape_keys: data.shape_keys
|
||
|
|
};
|
||
|
|
|
||
|
|
for (spline in data.splines) {
|
||
|
|
var newSpline = {
|
||
|
|
points: [],
|
||
|
|
closed: spline.closed,
|
||
|
|
resolution: spline.resolution,
|
||
|
|
material_index: spline.material_index
|
||
|
|
};
|
||
|
|
|
||
|
|
for (p in spline.points) {
|
||
|
|
newSpline.points.push({
|
||
|
|
co: copyFloat32Array(p.co),
|
||
|
|
handle_left: copyFloat32Array(p.handle_left),
|
||
|
|
handle_right: copyFloat32Array(p.handle_right)
|
||
|
|
});
|
||
|
|
}
|
||
|
|
newData.splines.push(newSpline);
|
||
|
|
}
|
||
|
|
return newData;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function copyFloat32Array(arr: Float32Array): Float32Array {
|
||
|
|
var newArr = new Float32Array(arr.length);
|
||
|
|
for (i in 0...arr.length) newArr[i] = arr[i];
|
||
|
|
return newArr;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getPoint(t: Float, splineIndex: Int = 0): Vec4 {
|
||
|
|
if (data.splines == null || splinesLength <= splineIndex) return new Vec4();
|
||
|
|
|
||
|
|
if (equidistantSamples <= 0) {
|
||
|
|
return getBezierPoint(t, splineIndex);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
return getPointEquidistant(t, splineIndex, equidistantSamples);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getBezierPoint(t: Float, splineIndex: Int = 0): Vec4 {
|
||
|
|
if (data.splines == null || splinesLength <= splineIndex) return new Vec4();
|
||
|
|
|
||
|
|
var spline = data.splines[splineIndex];
|
||
|
|
var points = spline.points;
|
||
|
|
if (points.length < 2) return new Vec4(points[0].co[0], points[0].co[1], points[0].co[2]);
|
||
|
|
|
||
|
|
t = Math.max(0.0, Math.min(1.0, t));
|
||
|
|
|
||
|
|
if (t >= 1.0) {
|
||
|
|
var p = spline.closed ? points[0] : points[points.length - 1];
|
||
|
|
return new Vec4(p.co[0], p.co[1], p.co[2]);
|
||
|
|
}
|
||
|
|
|
||
|
|
var numSegments = spline.closed ? points.length : points.length - 1;
|
||
|
|
var progress = t * numSegments;
|
||
|
|
var index = Std.int(progress);
|
||
|
|
var localT = progress - index;
|
||
|
|
|
||
|
|
var pK = points[index];
|
||
|
|
var pK1 = (spline.closed && index == points.length - 1) ? points[0] : points[index + 1];
|
||
|
|
|
||
|
|
_p0.set(pK.co[0], pK.co[1], pK.co[2]);
|
||
|
|
_p1.set(pK.handle_right[0], pK.handle_right[1], pK.handle_right[2]);
|
||
|
|
_p2.set(pK1.handle_left[0], pK1.handle_left[1], pK1.handle_left[2]);
|
||
|
|
_p3.set(pK1.co[0], pK1.co[1], pK1.co[2]);
|
||
|
|
|
||
|
|
if (spline.resolution == 12) {
|
||
|
|
return interpBezier(localT, _p0, _p1, _p2, _p3);
|
||
|
|
} else {
|
||
|
|
var stepProgress = localT * spline.resolution;
|
||
|
|
var stepIndex = Std.int(stepProgress);
|
||
|
|
var stepT = stepProgress - stepIndex;
|
||
|
|
|
||
|
|
var tA = stepIndex / spline.resolution;
|
||
|
|
var tB = (stepIndex + 1) / spline.resolution;
|
||
|
|
|
||
|
|
var pA = interpBezier(tA, _p0, _p1, _p2, _p3);
|
||
|
|
var pB = interpBezier(tB, _p0, _p1, _p2, _p3);
|
||
|
|
|
||
|
|
return new Vec4(
|
||
|
|
pA.x + (pB.x - pA.x) * stepT,
|
||
|
|
pA.y + (pB.y - pA.y) * stepT,
|
||
|
|
pA.z + (pB.z - pA.z) * stepT
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getPointEquidistant(t: Float, splineIndex: Int = 0, samples: Int = 100): Vec4 {
|
||
|
|
if (data.splines == null || splinesLength <= splineIndex) return new Vec4();
|
||
|
|
|
||
|
|
var spline = data.splines[splineIndex];
|
||
|
|
|
||
|
|
var totalSteps = samples;
|
||
|
|
if (spline.resolution != 12) {
|
||
|
|
var numSegments = spline.closed ? spline.points.length : spline.points.length - 1;
|
||
|
|
totalSteps = spline.resolution * numSegments;
|
||
|
|
if (totalSteps < 1) totalSteps = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
var table = [0.0];
|
||
|
|
var totalLength = 0.0;
|
||
|
|
var lastP = getBezierPoint(0, splineIndex);
|
||
|
|
|
||
|
|
for (i in 1...totalSteps + 1) {
|
||
|
|
var p = getBezierPoint(i / totalSteps, splineIndex);
|
||
|
|
totalLength += lastP.distanceTo(p);
|
||
|
|
table.push(totalLength);
|
||
|
|
lastP = p;
|
||
|
|
}
|
||
|
|
|
||
|
|
var targetDistance = Math.max(0.0, Math.min(1.0, t)) * totalLength;
|
||
|
|
var low = 0;
|
||
|
|
var high = table.length - 1;
|
||
|
|
|
||
|
|
while (low < high - 1) {
|
||
|
|
var mid = Std.int((low + high) / 2);
|
||
|
|
if (table[mid] < targetDistance) low = mid;
|
||
|
|
else high = mid;
|
||
|
|
}
|
||
|
|
|
||
|
|
var distStart = table[low];
|
||
|
|
var distEnd = table[high];
|
||
|
|
var segmentLength = distEnd - distStart;
|
||
|
|
var localT = (segmentLength <= 0) ? 0 : (targetDistance - distStart) / segmentLength;
|
||
|
|
var correctedT = (low + localT) / (table.length - 1);
|
||
|
|
|
||
|
|
return getBezierPoint(correctedT, splineIndex);
|
||
|
|
}
|
||
|
|
|
||
|
|
function interpBezier(t: Float, p0: Vec4, p1: Vec4, p2: Vec4, p3: Vec4): Vec4 {
|
||
|
|
var it = 1.0 - t;
|
||
|
|
var b0 = it * it * it;
|
||
|
|
var b1 = 3.0 * t * it * it;
|
||
|
|
var b2 = 3.0 * t * t * it;
|
||
|
|
var b3 = t * t * t;
|
||
|
|
return new Vec4(
|
||
|
|
p0.x * b0 + p1.x * b1 + p2.x * b2 + p3.x * b3,
|
||
|
|
p0.y * b0 + p1.y * b1 + p2.y * b2 + p3.y * b3,
|
||
|
|
p0.z * b0 + p1.z * b1 + p2.z * b2 + p3.z * b3
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getTangent(t: Float, splineIndex: Int = 0): Vec4 {
|
||
|
|
var delta = 0.001;
|
||
|
|
var t1 = Math.max(0.0, t - delta);
|
||
|
|
var t2 = Math.min(1.0, t + delta);
|
||
|
|
var p1 = getPoint(t1, splineIndex);
|
||
|
|
var p2 = getPoint(t2, splineIndex);
|
||
|
|
var tangent = new Vec4(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z);
|
||
|
|
tangent.normalize();
|
||
|
|
return tangent;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getClosestPoint(target: Vec4, splineIndex: Int = 0): Float {
|
||
|
|
var minD = 1e10;
|
||
|
|
var bestT = 0.0;
|
||
|
|
var spline = data.splines[splineIndex];
|
||
|
|
var segments = spline.closed ? spline.points.length : spline.points.length - 1;
|
||
|
|
var steps = equidistantSamples > 0 ? equidistantSamples : Std.int(spline.resolution * segments);
|
||
|
|
if (steps < 1) steps = 1;
|
||
|
|
for (i in 0...steps + 1) {
|
||
|
|
var t = i / steps;
|
||
|
|
var p = getPoint(t, splineIndex);
|
||
|
|
var d = p.distanceTo(target);
|
||
|
|
if (d < minD) {
|
||
|
|
minD = d;
|
||
|
|
bestT = t;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return bestT;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getLength(splineIndex: Int = 0): Float {
|
||
|
|
var length = 0.0;
|
||
|
|
var lastP = getPoint(0, splineIndex);
|
||
|
|
var spline = data.splines[splineIndex];
|
||
|
|
var segments = spline.closed ? spline.points.length : spline.points.length - 1;
|
||
|
|
var steps = equidistantSamples > 0 ? equidistantSamples : Std.int(spline.resolution * segments);
|
||
|
|
if (steps < 1) steps = 1;
|
||
|
|
for (i in 1...steps + 1) {
|
||
|
|
var p = getPoint(i / steps, splineIndex);
|
||
|
|
length += lastP.distanceTo(p);
|
||
|
|
lastP = p;
|
||
|
|
}
|
||
|
|
return length;
|
||
|
|
}
|
||
|
|
|
||
|
|
var renderCb: kha.graphics4.Graphics->Int->Int->Void = null;
|
||
|
|
|
||
|
|
public function draw(strength: Float = 0.005, color: kha.Color = Color.Black) {
|
||
|
|
if (renderCb != null) RenderDraw.removeOnRender(renderCb);
|
||
|
|
renderCb = RenderDraw.notifyOnRender(function(draw: RenderDraw) {
|
||
|
|
if (!visible) return;
|
||
|
|
draw.color = color;
|
||
|
|
draw.strength = strength;
|
||
|
|
|
||
|
|
var worldMat = this.transform.world;
|
||
|
|
|
||
|
|
for (s in 0...splinesLength) {
|
||
|
|
var spline = data.splines[s];
|
||
|
|
var segments = spline.closed ? spline.points.length : spline.points.length - 1;
|
||
|
|
var totalSteps = spline.resolution * segments;
|
||
|
|
var step = 1.0 / (totalSteps);
|
||
|
|
for (i in 0...totalSteps) {
|
||
|
|
var t1 = i * step;
|
||
|
|
var t2 = (i + 1) * step;
|
||
|
|
|
||
|
|
var p1 = getPoint(t1, s);
|
||
|
|
var p2 = getPoint(t2, s);
|
||
|
|
|
||
|
|
p1.applymat4(worldMat);
|
||
|
|
p2.applymat4(worldMat);
|
||
|
|
|
||
|
|
draw.linev(p1, p2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
var _q = new Quat();
|
||
|
|
var _m = Mat4.identity();
|
||
|
|
var _f = new Vec4();
|
||
|
|
var _r = new Vec4();
|
||
|
|
var _u = new Vec4();
|
||
|
|
var _z = new Vec4();
|
||
|
|
var _vx = new Vec4();
|
||
|
|
var _vy = new Vec4();
|
||
|
|
var _vz = new Vec4();
|
||
|
|
|
||
|
|
public function follow(obj: Object, t: Float, splineIndex: Int = 0, forwardAxis: String = "X") {
|
||
|
|
var pos = getPoint(t, splineIndex);
|
||
|
|
pos.applymat4(this.transform.world);
|
||
|
|
obj.transform.loc.setFrom(pos);
|
||
|
|
|
||
|
|
_f.setFrom(getTangent(t, splineIndex));
|
||
|
|
_f.applyQuat(this.transform.rot);
|
||
|
|
_f.normalize();
|
||
|
|
|
||
|
|
_z.set(0, 0, 1);
|
||
|
|
if (Math.abs(_f.dot(_z)) > 0.9999) {
|
||
|
|
_z.set(0, 1, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
_r.crossvecs(_f, _z).normalize();
|
||
|
|
_u.crossvecs(_r, _f).normalize();
|
||
|
|
|
||
|
|
switch (forwardAxis) {
|
||
|
|
case "X":
|
||
|
|
_vx.setFrom(_f);
|
||
|
|
_vy.set(-_r.x, -_r.y, -_r.z);
|
||
|
|
_vz.setFrom(_u);
|
||
|
|
case "-X":
|
||
|
|
_vx.set(-_f.x, -_f.y, -_f.z);
|
||
|
|
_vy.setFrom(_r);
|
||
|
|
_vz.setFrom(_u);
|
||
|
|
case "Y":
|
||
|
|
_vx.setFrom(_r);
|
||
|
|
_vy.setFrom(_f);
|
||
|
|
_vz.setFrom(_u);
|
||
|
|
case "-Y":
|
||
|
|
_vx.set(-_r.x, -_r.y, -_r.z);
|
||
|
|
_vy.set(-_f.x, -_f.y, -_f.z);
|
||
|
|
_vz.setFrom(_u);
|
||
|
|
case "Z":
|
||
|
|
_vx.setFrom(_r);
|
||
|
|
_vy.set(-_u.x, -_u.y, -_u.z);
|
||
|
|
_vz.setFrom(_f);
|
||
|
|
case "-Z":
|
||
|
|
_vx.setFrom(_r);
|
||
|
|
_vy.setFrom(_u);
|
||
|
|
_vz.set(-_f.x, -_f.y, -_f.z);
|
||
|
|
}
|
||
|
|
|
||
|
|
_m._00 = _vx.x; _m._01 = _vx.y; _m._02 = _vx.z;
|
||
|
|
_m._10 = _vy.x; _m._11 = _vy.y; _m._12 = _vy.z;
|
||
|
|
_m._20 = _vz.x; _m._21 = _vz.y; _m._22 = _vz.z;
|
||
|
|
|
||
|
|
_q.fromRotationMat(_m);
|
||
|
|
obj.transform.rot.setFrom(_q);
|
||
|
|
|
||
|
|
obj.transform.buildMatrix();
|
||
|
|
}
|
||
|
|
|
||
|
|
override public function remove() {
|
||
|
|
visible = false;
|
||
|
|
if (renderCb != null) {
|
||
|
|
RenderDraw.removeOnRender(renderCb);
|
||
|
|
renderCb = null;
|
||
|
|
}
|
||
|
|
super.remove();
|
||
|
|
}
|
||
|
|
|
||
|
|
public function setShapeKey(name: String, value: Float) {
|
||
|
|
if (data.shape_keys == null) return;
|
||
|
|
for (key in data.shape_keys) {
|
||
|
|
if (key.name == name) {
|
||
|
|
key.value = value;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
applyShapeKeys();
|
||
|
|
}
|
||
|
|
|
||
|
|
public function applyShapeKeys() {
|
||
|
|
var basis = data.shape_keys[0];
|
||
|
|
|
||
|
|
for (s in 0...data.splines.length) {
|
||
|
|
var spline = data.splines[s];
|
||
|
|
for (i in 0...spline.points.length) {
|
||
|
|
var pt = spline.points[i];
|
||
|
|
var basisPt = basis.points[i];
|
||
|
|
|
||
|
|
pt.co[0] = basisPt.co[0];
|
||
|
|
pt.co[1] = basisPt.co[1];
|
||
|
|
pt.co[2] = basisPt.co[2];
|
||
|
|
|
||
|
|
pt.handle_left[0] = basisPt.handle_left[0];
|
||
|
|
pt.handle_left[1] = basisPt.handle_left[1];
|
||
|
|
pt.handle_left[2] = basisPt.handle_left[2];
|
||
|
|
|
||
|
|
pt.handle_right[0] = basisPt.handle_right[0];
|
||
|
|
pt.handle_right[1] = basisPt.handle_right[1];
|
||
|
|
pt.handle_right[2] = basisPt.handle_right[2];
|
||
|
|
|
||
|
|
for (k in 1...data.shape_keys.length) {
|
||
|
|
var key = data.shape_keys[k];
|
||
|
|
var val = key.value;
|
||
|
|
if (val == 0) continue;
|
||
|
|
|
||
|
|
var keyPt = key.points[i];
|
||
|
|
|
||
|
|
pt.co[0] += (keyPt.co[0] - basisPt.co[0]) * val;
|
||
|
|
pt.co[1] += (keyPt.co[1] - basisPt.co[1]) * val;
|
||
|
|
pt.co[2] += (keyPt.co[2] - basisPt.co[2]) * val;
|
||
|
|
|
||
|
|
pt.handle_left[0] += (keyPt.handle_left[0] - basisPt.handle_left[0]) * val;
|
||
|
|
pt.handle_left[1] += (keyPt.handle_left[1] - basisPt.handle_left[1]) * val;
|
||
|
|
pt.handle_left[2] += (keyPt.handle_left[2] - basisPt.handle_left[2]) * val;
|
||
|
|
|
||
|
|
pt.handle_right[0] += (keyPt.handle_right[0] - basisPt.handle_right[0]) * val;
|
||
|
|
pt.handle_right[1] += (keyPt.handle_right[1] - basisPt.handle_right[1]) * val;
|
||
|
|
pt.handle_right[2] += (keyPt.handle_right[2] - basisPt.handle_right[2]) * val;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public function generateBevelMesh(bevelDepth: Float = 0.05, bevelResolution: Int = 8, factorStart: Float = 0.0, factorEnd: Float = 1.0, fillCaps: Bool = false): MeshData {
|
||
|
|
if (data.splines == null || splinesLength == 0)
|
||
|
|
return null;
|
||
|
|
|
||
|
|
var numSides = bevelResolution <= 0 ? 4 : (bevelResolution * 2 + 4);
|
||
|
|
|
||
|
|
var rawPositions: Array<Vec4> = [];
|
||
|
|
var rawNormals: Array<Vec4> = [];
|
||
|
|
var rawUVs: Array<Vec4> = [];
|
||
|
|
var indicesByMaterial = new Map<Int, Array<Int>>();
|
||
|
|
|
||
|
|
for (splineIndex in 0...splinesLength) {
|
||
|
|
var spline = data.splines[splineIndex];
|
||
|
|
var matIdx = spline.material_index != null ? spline.material_index : 0;
|
||
|
|
if (!indicesByMaterial.exists(matIdx)) indicesByMaterial.set(matIdx, []);
|
||
|
|
var indices = indicesByMaterial.get(matIdx);
|
||
|
|
|
||
|
|
var segments = spline.closed ? spline.points.length : spline.points.length - 1;
|
||
|
|
var totalSteps = equidistantSamples > 0 ? equidistantSamples : Std.int(spline.resolution * segments);
|
||
|
|
if (totalSteps < 1) totalSteps = 1;
|
||
|
|
|
||
|
|
var startStep = Std.int(totalSteps * Math.max(0.0, Math.min(1.0, factorStart)));
|
||
|
|
var endStep = Std.int(totalSteps * Math.max(0.0, Math.min(1.0, factorEnd)));
|
||
|
|
var isClosedLoop = spline.closed && factorStart == 0.0 && factorEnd == 1.0;
|
||
|
|
var numRings = endStep - startStep + 1;
|
||
|
|
if (numRings < 2) continue;
|
||
|
|
|
||
|
|
var baseIndex = rawPositions.length;
|
||
|
|
|
||
|
|
var tangent = getTangent(factorStart, splineIndex);
|
||
|
|
if (spline.closed && (factorStart == 0.0 || factorStart == 1.0)) {
|
||
|
|
var tIn = getTangent(1.0, splineIndex);
|
||
|
|
var tOut = getTangent(0.0, splineIndex);
|
||
|
|
tangent.set(tIn.x + tOut.x, tIn.y + tOut.y, tIn.z + tOut.z);
|
||
|
|
tangent.normalize();
|
||
|
|
}
|
||
|
|
var normal = new Vec4(0, 1, 0);
|
||
|
|
if (Math.abs(tangent.dot(normal)) > 0.9) normal.set(1, 0, 0);
|
||
|
|
var binormal = new Vec4();
|
||
|
|
binormal.crossvecs(tangent, normal);
|
||
|
|
binormal.normalize();
|
||
|
|
normal.crossvecs(binormal, tangent);
|
||
|
|
normal.normalize();
|
||
|
|
|
||
|
|
var lastTangent = tangent.clone();
|
||
|
|
|
||
|
|
for (s in 0...numRings) {
|
||
|
|
var stepIdx = startStep + s;
|
||
|
|
var t = stepIdx / totalSteps;
|
||
|
|
var p = getPoint(t, splineIndex);
|
||
|
|
var currTangent = getTangent(t, splineIndex);
|
||
|
|
if (spline.closed && (t == 0.0 || t == 1.0)) {
|
||
|
|
var tIn = getTangent(1.0, splineIndex);
|
||
|
|
var tOut = getTangent(0.0, splineIndex);
|
||
|
|
currTangent.set(tIn.x + tOut.x, tIn.y + tOut.y, tIn.z + tOut.z);
|
||
|
|
currTangent.normalize();
|
||
|
|
}
|
||
|
|
|
||
|
|
var axis = new Vec4();
|
||
|
|
axis.crossvecs(lastTangent, currTangent);
|
||
|
|
var dot = Math.max(-1.0, Math.min(1.0, lastTangent.dot(currTangent)));
|
||
|
|
if (axis.length() > 0.00001) {
|
||
|
|
axis.normalize();
|
||
|
|
var angle = Math.acos(dot);
|
||
|
|
var q = new Quat();
|
||
|
|
q.fromAxisAngle(axis, angle);
|
||
|
|
normal.applyQuat(q);
|
||
|
|
binormal.applyQuat(q);
|
||
|
|
}
|
||
|
|
lastTangent = currTangent;
|
||
|
|
|
||
|
|
for (r in 0...(numSides + 1)) {
|
||
|
|
var idx = r % numSides;
|
||
|
|
var angle = (idx / numSides) * Math.PI * 2.0;
|
||
|
|
var cosA = Math.cos(angle);
|
||
|
|
var sinA = Math.sin(angle);
|
||
|
|
|
||
|
|
var dir = new Vec4(
|
||
|
|
normal.x * cosA + binormal.x * sinA,
|
||
|
|
normal.y * cosA + binormal.y * sinA,
|
||
|
|
normal.z * cosA + binormal.z * sinA
|
||
|
|
);
|
||
|
|
dir.normalize();
|
||
|
|
|
||
|
|
var vPos = new Vec4(
|
||
|
|
p.x + dir.x * bevelDepth,
|
||
|
|
p.y + dir.y * bevelDepth,
|
||
|
|
p.z + dir.z * bevelDepth
|
||
|
|
);
|
||
|
|
|
||
|
|
if (isClosedLoop && s == numRings - 1) {
|
||
|
|
vPos = rawPositions[baseIndex + r].clone();
|
||
|
|
dir = rawNormals[baseIndex + r].clone();
|
||
|
|
}
|
||
|
|
|
||
|
|
rawPositions.push(vPos);
|
||
|
|
rawNormals.push(dir);
|
||
|
|
rawUVs.push(new Vec4(t, r / numSides, 0));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var stride = numSides + 1;
|
||
|
|
for (s in 0...numRings) {
|
||
|
|
var nextS = s + 1;
|
||
|
|
if (s == numRings - 1) continue;
|
||
|
|
|
||
|
|
for (r in 0...numSides) {
|
||
|
|
var v0 = baseIndex + s * stride + r;
|
||
|
|
var v1 = baseIndex + s * stride + r + 1;
|
||
|
|
var v2 = baseIndex + nextS * stride + r;
|
||
|
|
var v3 = baseIndex + nextS * stride + r + 1;
|
||
|
|
|
||
|
|
indices.push(v0);
|
||
|
|
indices.push(v2);
|
||
|
|
indices.push(v1);
|
||
|
|
|
||
|
|
indices.push(v1);
|
||
|
|
indices.push(v2);
|
||
|
|
indices.push(v3);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var isPartial = factorStart > 0.0 || factorEnd < 1.0;
|
||
|
|
if (fillCaps && (!spline.closed || isPartial)) {
|
||
|
|
var tStart = factorStart;
|
||
|
|
var startTangent = getTangent(tStart, splineIndex);
|
||
|
|
var capStartNormal = new Vec4(-startTangent.x, -startTangent.y, -startTangent.z);
|
||
|
|
|
||
|
|
var pStart = new Vec4(0, 0, 0);
|
||
|
|
for (r in 0...numSides) {
|
||
|
|
var pos = rawPositions[baseIndex + r];
|
||
|
|
pStart.add(pos);
|
||
|
|
}
|
||
|
|
pStart.mult(1.0 / numSides);
|
||
|
|
|
||
|
|
var capStartCenterIdx = rawPositions.length;
|
||
|
|
rawPositions.push(pStart);
|
||
|
|
rawNormals.push(capStartNormal);
|
||
|
|
rawUVs.push(new Vec4(0.5, 0.5, 0));
|
||
|
|
|
||
|
|
var capStartRingBase = rawPositions.length;
|
||
|
|
for (r in 0...numSides) {
|
||
|
|
var origIdx = baseIndex + r;
|
||
|
|
var angle = (r / numSides) * Math.PI * 2.0;
|
||
|
|
var u = 0.5 + 0.5 * Math.cos(angle);
|
||
|
|
var v = 0.5 + 0.5 * Math.sin(angle);
|
||
|
|
|
||
|
|
rawPositions.push(rawPositions[origIdx].clone());
|
||
|
|
rawNormals.push(capStartNormal);
|
||
|
|
rawUVs.push(new Vec4(u, v, 0));
|
||
|
|
}
|
||
|
|
|
||
|
|
for (r in 0...numSides) {
|
||
|
|
var nextR = (r + 1) % numSides;
|
||
|
|
var v0 = capStartRingBase + r;
|
||
|
|
var v1 = capStartRingBase + nextR;
|
||
|
|
indices.push(capStartCenterIdx);
|
||
|
|
indices.push(v1);
|
||
|
|
indices.push(v0);
|
||
|
|
}
|
||
|
|
|
||
|
|
var tEnd = factorEnd;
|
||
|
|
var endTangent = getTangent(tEnd, splineIndex);
|
||
|
|
var capEndNormal = endTangent.clone();
|
||
|
|
|
||
|
|
var lastRingBase = baseIndex + (numRings - 1) * stride;
|
||
|
|
var pEnd = new Vec4(0, 0, 0);
|
||
|
|
for (r in 0...numSides) {
|
||
|
|
var pos = rawPositions[lastRingBase + r];
|
||
|
|
pEnd.add(pos);
|
||
|
|
}
|
||
|
|
pEnd.mult(1.0 / numSides);
|
||
|
|
|
||
|
|
var capEndCenterIdx = rawPositions.length;
|
||
|
|
rawPositions.push(pEnd);
|
||
|
|
rawNormals.push(capEndNormal);
|
||
|
|
rawUVs.push(new Vec4(0.5, 0.5, 0));
|
||
|
|
|
||
|
|
var capEndRingBase = rawPositions.length;
|
||
|
|
for (r in 0...numSides) {
|
||
|
|
var origIdx = lastRingBase + r;
|
||
|
|
var angle = (r / numSides) * Math.PI * 2.0;
|
||
|
|
var u = 0.5 + 0.5 * Math.cos(angle);
|
||
|
|
var v = 0.5 + 0.5 * Math.sin(angle);
|
||
|
|
|
||
|
|
rawPositions.push(rawPositions[origIdx].clone());
|
||
|
|
rawNormals.push(capEndNormal);
|
||
|
|
rawUVs.push(new Vec4(u, v, 0));
|
||
|
|
}
|
||
|
|
|
||
|
|
for (r in 0...numSides) {
|
||
|
|
var nextR = (r + 1) % numSides;
|
||
|
|
var v0 = capEndRingBase + r;
|
||
|
|
var v1 = capEndRingBase + nextR;
|
||
|
|
indices.push(capEndCenterIdx);
|
||
|
|
indices.push(v0);
|
||
|
|
indices.push(v1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (rawPositions.length == 0) return null;
|
||
|
|
|
||
|
|
var maxdim = 0.0;
|
||
|
|
for (p in rawPositions) {
|
||
|
|
var ax = Math.abs(p.x);
|
||
|
|
var ay = Math.abs(p.y);
|
||
|
|
var az = Math.abs(p.z);
|
||
|
|
if (ax > maxdim) maxdim = ax;
|
||
|
|
if (ay > maxdim) maxdim = ay;
|
||
|
|
if (az > maxdim) maxdim = az;
|
||
|
|
}
|
||
|
|
if (maxdim == 0) maxdim = 1.0;
|
||
|
|
maxdim *= 2;
|
||
|
|
var invdim = 1 / maxdim;
|
||
|
|
|
||
|
|
var numVerts = rawPositions.length;
|
||
|
|
var paa = new Int16Array(numVerts * 4);
|
||
|
|
var naa = new Int16Array(numVerts * 2);
|
||
|
|
var texa = new Int16Array(numVerts * 2);
|
||
|
|
|
||
|
|
for (i in 0...numVerts) {
|
||
|
|
var p = rawPositions[i];
|
||
|
|
var n = rawNormals[i];
|
||
|
|
var uv = rawUVs[i];
|
||
|
|
|
||
|
|
paa.set(i * 4, Std.int(p.x * 32767 * invdim));
|
||
|
|
paa.set(i * 4 + 1, Std.int(p.y * 32767 * invdim));
|
||
|
|
paa.set(i * 4 + 2, Std.int(p.z * 32767 * invdim));
|
||
|
|
naa.set(i * 2, Std.int(n.x * 32767));
|
||
|
|
naa.set(i * 2 + 1, Std.int(n.y * 32767));
|
||
|
|
paa.set(i * 4 + 3, Std.int(n.z * 32767));
|
||
|
|
|
||
|
|
texa.set(i * 2, Std.int(uv.x * 32767));
|
||
|
|
texa.set(i * 2 + 1, Std.int(uv.y * 32767));
|
||
|
|
}
|
||
|
|
|
||
|
|
var indexArrays: Array<TIndexArray> = [];
|
||
|
|
for (matIdx in indicesByMaterial.keys()) {
|
||
|
|
var matIndices = indicesByMaterial.get(matIdx);
|
||
|
|
var inda = new Uint32Array(matIndices.length);
|
||
|
|
for (i in 0...matIndices.length) inda.set(i, matIndices[i]);
|
||
|
|
indexArrays.push({ material: matIdx, values: inda });
|
||
|
|
}
|
||
|
|
|
||
|
|
var pos: TVertexArray = { attrib: "pos", values: paa, data: "short4norm" };
|
||
|
|
var nor: TVertexArray = { attrib: "nor", values: naa, data: "short2norm" };
|
||
|
|
var tex: TVertexArray = { attrib: "tex", values: texa, data: "short2norm" };
|
||
|
|
|
||
|
|
var rawmesh: TMeshData = {
|
||
|
|
name: data.name + "_mesh",
|
||
|
|
sorting_index: 0,
|
||
|
|
vertex_arrays: [pos, nor, tex],
|
||
|
|
index_arrays: indexArrays,
|
||
|
|
scale_pos: maxdim
|
||
|
|
};
|
||
|
|
|
||
|
|
var md = new MeshData(rawmesh, function(d: MeshData) {});
|
||
|
|
md.geom.calculateAABB();
|
||
|
|
return md;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function generateExtrudeMesh(width: Float = 0.1, thickness: Float = 0.1, factorStart: Float = 0.0, factorEnd: Float = 1.0, fillCaps: Bool = false): MeshData {
|
||
|
|
if (data.splines == null || splinesLength == 0)
|
||
|
|
return null;
|
||
|
|
|
||
|
|
var rawPositions: Array<Vec4> = [];
|
||
|
|
var rawNormals: Array<Vec4> = [];
|
||
|
|
var rawUVs: Array<Vec4> = [];
|
||
|
|
var indicesByMaterial = new Map<Int, Array<Int>>();
|
||
|
|
|
||
|
|
var halfW = width * 0.5;
|
||
|
|
var halfH = thickness * 0.5;
|
||
|
|
|
||
|
|
for (splineIndex in 0...splinesLength) {
|
||
|
|
var spline = data.splines[splineIndex];
|
||
|
|
var matIdx = spline.material_index != null ? spline.material_index : 0;
|
||
|
|
if (!indicesByMaterial.exists(matIdx)) indicesByMaterial.set(matIdx, []);
|
||
|
|
var indices = indicesByMaterial.get(matIdx);
|
||
|
|
|
||
|
|
var segments = spline.closed ? spline.points.length : spline.points.length - 1;
|
||
|
|
var totalSteps = equidistantSamples > 0 ? equidistantSamples : Std.int(spline.resolution * segments);
|
||
|
|
if (totalSteps < 1) totalSteps = 1;
|
||
|
|
|
||
|
|
var startStep = Std.int(totalSteps * Math.max(0.0, Math.min(1.0, factorStart)));
|
||
|
|
var endStep = Std.int(totalSteps * Math.max(0.0, Math.min(1.0, factorEnd)));
|
||
|
|
var isClosedLoop = spline.closed && factorStart == 0.0 && factorEnd == 1.0;
|
||
|
|
var numRings = endStep - startStep + 1;
|
||
|
|
if (numRings < 2) continue;
|
||
|
|
|
||
|
|
var baseIndex = rawPositions.length;
|
||
|
|
|
||
|
|
var tangent = getTangent(factorStart, splineIndex);
|
||
|
|
if (spline.closed && (factorStart == 0.0 || factorStart == 1.0)) {
|
||
|
|
var tIn = getTangent(1.0, splineIndex);
|
||
|
|
var tOut = getTangent(0.0, splineIndex);
|
||
|
|
tangent.set(tIn.x + tOut.x, tIn.y + tOut.y, tIn.z + tOut.z);
|
||
|
|
tangent.normalize();
|
||
|
|
}
|
||
|
|
var normal = new Vec4(0, 1, 0);
|
||
|
|
if (Math.abs(tangent.dot(normal)) > 0.9) normal.set(1, 0, 0);
|
||
|
|
var binormal = new Vec4();
|
||
|
|
binormal.crossvecs(tangent, normal);
|
||
|
|
binormal.normalize();
|
||
|
|
normal.crossvecs(binormal, tangent);
|
||
|
|
normal.normalize();
|
||
|
|
|
||
|
|
var lastTangent = tangent.clone();
|
||
|
|
|
||
|
|
if (thickness == 0) {
|
||
|
|
for (s in 0...numRings) {
|
||
|
|
var stepIdx = startStep + s;
|
||
|
|
var t = stepIdx / totalSteps;
|
||
|
|
var p = getPoint(t, splineIndex);
|
||
|
|
var currTangent = getTangent(t, splineIndex);
|
||
|
|
if (spline.closed && (t == 0.0 || t == 1.0)) {
|
||
|
|
var tIn = getTangent(1.0, splineIndex);
|
||
|
|
var tOut = getTangent(0.0, splineIndex);
|
||
|
|
currTangent.set(tIn.x + tOut.x, tIn.y + tOut.y, tIn.z + tOut.z);
|
||
|
|
currTangent.normalize();
|
||
|
|
}
|
||
|
|
|
||
|
|
var axisVec = new Vec4();
|
||
|
|
axisVec.crossvecs(lastTangent, currTangent);
|
||
|
|
var dot = Math.max(-1.0, Math.min(1.0, lastTangent.dot(currTangent)));
|
||
|
|
if (axisVec.length() > 0.00001) {
|
||
|
|
axisVec.normalize();
|
||
|
|
var angle = Math.acos(dot);
|
||
|
|
var q = new Quat();
|
||
|
|
q.fromAxisAngle(axisVec, angle);
|
||
|
|
normal.applyQuat(q);
|
||
|
|
binormal.applyQuat(q);
|
||
|
|
}
|
||
|
|
lastTangent = currTangent;
|
||
|
|
|
||
|
|
var dirW = normal;
|
||
|
|
var dirH = binormal;
|
||
|
|
|
||
|
|
var vPos0 = new Vec4(p.x - dirW.x * halfW, p.y - dirW.y * halfW, p.z - dirW.z * halfW);
|
||
|
|
var vPos1 = new Vec4(p.x + dirW.x * halfW, p.y + dirW.y * halfW, p.z + dirW.z * halfW);
|
||
|
|
|
||
|
|
if (isClosedLoop && s == numRings - 1) {
|
||
|
|
vPos0 = rawPositions[baseIndex].clone();
|
||
|
|
vPos1 = rawPositions[baseIndex + 1].clone();
|
||
|
|
}
|
||
|
|
|
||
|
|
rawPositions.push(vPos0);
|
||
|
|
rawNormals.push(dirH);
|
||
|
|
rawUVs.push(new Vec4(t, 0.0, 0));
|
||
|
|
|
||
|
|
rawPositions.push(vPos1);
|
||
|
|
rawNormals.push(dirH);
|
||
|
|
rawUVs.push(new Vec4(t, 1.0, 0));
|
||
|
|
}
|
||
|
|
|
||
|
|
for (s in 0...numRings - 1) {
|
||
|
|
var v0 = baseIndex + s * 2;
|
||
|
|
var v1 = baseIndex + s * 2 + 1;
|
||
|
|
var v2 = baseIndex + (s + 1) * 2;
|
||
|
|
var v3 = baseIndex + (s + 1) * 2 + 1;
|
||
|
|
|
||
|
|
indices.push(v0);
|
||
|
|
indices.push(v2);
|
||
|
|
indices.push(v1);
|
||
|
|
|
||
|
|
indices.push(v1);
|
||
|
|
indices.push(v2);
|
||
|
|
indices.push(v3);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
for (s in 0...numRings) {
|
||
|
|
var stepIdx = startStep + s;
|
||
|
|
var t = stepIdx / totalSteps;
|
||
|
|
var p = getPoint(t, splineIndex);
|
||
|
|
var currTangent = getTangent(t, splineIndex);
|
||
|
|
if (spline.closed && (t == 0.0 || t == 1.0)) {
|
||
|
|
var tIn = getTangent(1.0, splineIndex);
|
||
|
|
var tOut = getTangent(0.0, splineIndex);
|
||
|
|
currTangent.set(tIn.x + tOut.x, tIn.y + tOut.y, tIn.z + tOut.z);
|
||
|
|
currTangent.normalize();
|
||
|
|
}
|
||
|
|
|
||
|
|
var axisVec = new Vec4();
|
||
|
|
axisVec.crossvecs(lastTangent, currTangent);
|
||
|
|
var dot = Math.max(-1.0, Math.min(1.0, lastTangent.dot(currTangent)));
|
||
|
|
if (axisVec.length() > 0.00001) {
|
||
|
|
axisVec.normalize();
|
||
|
|
var angle = Math.acos(dot);
|
||
|
|
var q = new Quat();
|
||
|
|
q.fromAxisAngle(axisVec, angle);
|
||
|
|
normal.applyQuat(q);
|
||
|
|
binormal.applyQuat(q);
|
||
|
|
}
|
||
|
|
lastTangent = currTangent;
|
||
|
|
|
||
|
|
var dirW = normal;
|
||
|
|
var dirH = binormal;
|
||
|
|
|
||
|
|
var offsets = [
|
||
|
|
new Vec4(-dirW.x * halfW - dirH.x * halfH, -dirW.y * halfW - dirH.y * halfH, -dirW.z * halfW - dirH.z * halfH),
|
||
|
|
new Vec4( dirW.x * halfW - dirH.x * halfH, dirW.y * halfW - dirH.y * halfH, dirW.z * halfW - dirH.z * halfH),
|
||
|
|
new Vec4( dirW.x * halfW + dirH.x * halfH, dirW.y * halfW + dirH.y * halfH, dirW.z * halfW + dirH.z * halfH),
|
||
|
|
new Vec4(-dirW.x * halfW + dirH.x * halfH, -dirW.y * halfW + dirH.y * halfH, -dirW.z * halfW + dirH.z * halfH)
|
||
|
|
];
|
||
|
|
|
||
|
|
var normDirs = [
|
||
|
|
new Vec4(-dirW.x, -dirW.y, -dirW.z),
|
||
|
|
new Vec4( dirW.x, dirW.y, dirW.z),
|
||
|
|
new Vec4( dirH.x, dirH.y, dirH.z),
|
||
|
|
new Vec4(-dirH.x, -dirH.y, -dirH.z)
|
||
|
|
];
|
||
|
|
|
||
|
|
for (i in 0...5) {
|
||
|
|
var idx = i % 4;
|
||
|
|
rawPositions.push(new Vec4(p.x + offsets[idx].x, p.y + offsets[idx].y, p.z + offsets[idx].z));
|
||
|
|
rawNormals.push(normDirs[idx]);
|
||
|
|
rawUVs.push(new Vec4(t, i / 4.0, 0));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (s in 0...numRings) {
|
||
|
|
var nextS = s + 1;
|
||
|
|
if (s == numRings - 1) continue;
|
||
|
|
|
||
|
|
for (i in 0...4) {
|
||
|
|
var v0 = baseIndex + s * 5 + i;
|
||
|
|
var v1 = baseIndex + s * 5 + i + 1;
|
||
|
|
var v2 = baseIndex + nextS * 5 + i;
|
||
|
|
var v3 = baseIndex + nextS * 5 + i + 1;
|
||
|
|
|
||
|
|
indices.push(v0);
|
||
|
|
indices.push(v2);
|
||
|
|
indices.push(v1);
|
||
|
|
|
||
|
|
indices.push(v1);
|
||
|
|
indices.push(v2);
|
||
|
|
indices.push(v3);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var isPartial = factorStart > 0.0 || factorEnd < 1.0;
|
||
|
|
if (fillCaps && (!spline.closed || isPartial)) {
|
||
|
|
var tStart = factorStart;
|
||
|
|
var startTangent = getTangent(tStart, splineIndex);
|
||
|
|
var capStartNormal = new Vec4(-startTangent.x, -startTangent.y, -startTangent.z);
|
||
|
|
|
||
|
|
var pStart = new Vec4(0, 0, 0);
|
||
|
|
for (r in 0...4) {
|
||
|
|
var pos = rawPositions[baseIndex + r];
|
||
|
|
pStart.add(pos);
|
||
|
|
}
|
||
|
|
pStart.mult(0.25);
|
||
|
|
|
||
|
|
var capStartCenterIdx = rawPositions.length;
|
||
|
|
rawPositions.push(pStart);
|
||
|
|
rawNormals.push(capStartNormal);
|
||
|
|
rawUVs.push(new Vec4(0.5, 0.5, 0));
|
||
|
|
|
||
|
|
var capStartRingBase = rawPositions.length;
|
||
|
|
for (r in 0...4) {
|
||
|
|
var origIdx = baseIndex + r;
|
||
|
|
var angle = (r / 4.0) * Math.PI * 2.0;
|
||
|
|
var u = 0.5 + 0.5 * Math.cos(angle);
|
||
|
|
var v = 0.5 + 0.5 * Math.sin(angle);
|
||
|
|
|
||
|
|
rawPositions.push(rawPositions[origIdx].clone());
|
||
|
|
rawNormals.push(capStartNormal);
|
||
|
|
rawUVs.push(new Vec4(u, v, 0));
|
||
|
|
}
|
||
|
|
|
||
|
|
for (r in 0...4) {
|
||
|
|
var nextR = (r + 1) % 4;
|
||
|
|
var v0 = capStartRingBase + r;
|
||
|
|
var v1 = capStartRingBase + nextR;
|
||
|
|
indices.push(capStartCenterIdx);
|
||
|
|
indices.push(v1);
|
||
|
|
indices.push(v0);
|
||
|
|
}
|
||
|
|
|
||
|
|
var tEnd = factorEnd;
|
||
|
|
var endTangent = getTangent(tEnd, splineIndex);
|
||
|
|
var capEndNormal = endTangent.clone();
|
||
|
|
|
||
|
|
var lastRingBase = baseIndex + (numRings - 1) * 5;
|
||
|
|
var pEnd = new Vec4(0, 0, 0);
|
||
|
|
for (r in 0...4) {
|
||
|
|
var pos = rawPositions[lastRingBase + r];
|
||
|
|
pEnd.add(pos);
|
||
|
|
}
|
||
|
|
pEnd.mult(0.25);
|
||
|
|
|
||
|
|
var capEndCenterIdx = rawPositions.length;
|
||
|
|
rawPositions.push(pEnd);
|
||
|
|
rawNormals.push(capEndNormal);
|
||
|
|
rawUVs.push(new Vec4(0.5, 0.5, 0));
|
||
|
|
|
||
|
|
var capEndRingBase = rawPositions.length;
|
||
|
|
for (r in 0...4) {
|
||
|
|
var origIdx = lastRingBase + r;
|
||
|
|
var angle = (r / 4.0) * Math.PI * 2.0;
|
||
|
|
var u = 0.5 + 0.5 * Math.cos(angle);
|
||
|
|
var v = 0.5 + 0.5 * Math.sin(angle);
|
||
|
|
|
||
|
|
rawPositions.push(rawPositions[origIdx].clone());
|
||
|
|
rawNormals.push(capEndNormal);
|
||
|
|
rawUVs.push(new Vec4(u, v, 0));
|
||
|
|
}
|
||
|
|
|
||
|
|
for (r in 0...4) {
|
||
|
|
var nextR = (r + 1) % 4;
|
||
|
|
var v0 = capEndRingBase + r;
|
||
|
|
var v1 = capEndRingBase + nextR;
|
||
|
|
indices.push(capEndCenterIdx);
|
||
|
|
indices.push(v0);
|
||
|
|
indices.push(v1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (rawPositions.length == 0) return null;
|
||
|
|
|
||
|
|
var maxdim = 0.0;
|
||
|
|
for (p in rawPositions) {
|
||
|
|
var ax = Math.abs(p.x);
|
||
|
|
var ay = Math.abs(p.y);
|
||
|
|
var az = Math.abs(p.z);
|
||
|
|
if (ax > maxdim) maxdim = ax;
|
||
|
|
if (ay > maxdim) maxdim = ay;
|
||
|
|
if (az > maxdim) maxdim = az;
|
||
|
|
}
|
||
|
|
if (maxdim == 0) maxdim = 1.0;
|
||
|
|
maxdim *= 2;
|
||
|
|
var invdim = 1 / maxdim;
|
||
|
|
|
||
|
|
var numVerts = rawPositions.length;
|
||
|
|
var paa = new Int16Array(numVerts * 4);
|
||
|
|
var naa = new Int16Array(numVerts * 2);
|
||
|
|
var texa = new Int16Array(numVerts * 2);
|
||
|
|
|
||
|
|
for (i in 0...numVerts) {
|
||
|
|
var p = rawPositions[i];
|
||
|
|
var n = rawNormals[i];
|
||
|
|
var uv = rawUVs[i];
|
||
|
|
|
||
|
|
paa.set(i * 4, Std.int(p.x * 32767 * invdim));
|
||
|
|
paa.set(i * 4 + 1, Std.int(p.y * 32767 * invdim));
|
||
|
|
paa.set(i * 4 + 2, Std.int(p.z * 32767 * invdim));
|
||
|
|
naa.set(i * 2, Std.int(n.x * 32767));
|
||
|
|
naa.set(i * 2 + 1, Std.int(n.y * 32767));
|
||
|
|
paa.set(i * 4 + 3, Std.int(n.z * 32767));
|
||
|
|
|
||
|
|
texa.set(i * 2, Std.int(uv.x * 32767));
|
||
|
|
texa.set(i * 2 + 1, Std.int(uv.y * 32767));
|
||
|
|
}
|
||
|
|
|
||
|
|
var indexArrays: Array<TIndexArray> = [];
|
||
|
|
for (matIdx in indicesByMaterial.keys()) {
|
||
|
|
var matIndices = indicesByMaterial.get(matIdx);
|
||
|
|
var inda = new Uint32Array(matIndices.length);
|
||
|
|
for (i in 0...matIndices.length) inda.set(i, matIndices[i]);
|
||
|
|
indexArrays.push({ material: matIdx, values: inda });
|
||
|
|
}
|
||
|
|
|
||
|
|
var pos: TVertexArray = { attrib: "pos", values: paa, data: "short4norm" };
|
||
|
|
var nor: TVertexArray = { attrib: "nor", values: naa, data: "short2norm" };
|
||
|
|
var tex: TVertexArray = { attrib: "tex", values: texa, data: "short2norm" };
|
||
|
|
|
||
|
|
var rawmesh: TMeshData = {
|
||
|
|
name: data.name + "_mesh",
|
||
|
|
sorting_index: 0,
|
||
|
|
vertex_arrays: [pos, nor, tex],
|
||
|
|
index_arrays: indexArrays,
|
||
|
|
scale_pos: maxdim
|
||
|
|
};
|
||
|
|
|
||
|
|
var md = new MeshData(rawmesh, function(d: MeshData) {});
|
||
|
|
md.geom.calculateAABB();
|
||
|
|
return md;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function generateDeformedMesh(baseMesh: MeshData, splineIndex = -1, forwardAxis: String = "X", repetitions: Int, factorStart: Float = 0.0, factorEnd: Float = 1.0): MeshData {
|
||
|
|
if (data.splines == null || splinesLength == 0 || baseMesh == null || repetitions <= 0)
|
||
|
|
return null;
|
||
|
|
|
||
|
|
var rawPositions: Array<Vec4> = [];
|
||
|
|
var rawNormals: Array<Vec4> = [];
|
||
|
|
var rawUVs: Array<Vec4> = [];
|
||
|
|
var indicesByMaterial = new Map<Int, Array<Int>>();
|
||
|
|
|
||
|
|
var baseRaw = baseMesh.raw;
|
||
|
|
var basePos: Int16Array = null;
|
||
|
|
var baseNor: Int16Array = null;
|
||
|
|
var baseTex: Int16Array = null;
|
||
|
|
|
||
|
|
for (va in baseRaw.vertex_arrays) {
|
||
|
|
if (va.attrib == "pos") basePos = va.values;
|
||
|
|
else if (va.attrib == "nor") baseNor = va.values;
|
||
|
|
else if (va.attrib == "tex") baseTex = va.values;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (basePos == null) return null;
|
||
|
|
|
||
|
|
var baseVertCount = Std.int(basePos.length / 4);
|
||
|
|
var baseScale = baseRaw.scale_pos;
|
||
|
|
|
||
|
|
var minVal = 1e10;
|
||
|
|
var maxVal = -1e10;
|
||
|
|
var unpackedPos = new Array<Vec4>();
|
||
|
|
var unpackedNor = new Array<Vec4>();
|
||
|
|
var unpackedTex = new Array<Vec4>();
|
||
|
|
|
||
|
|
for (i in 0...baseVertCount) {
|
||
|
|
var px = (basePos.get(i * 4) / 32767) * baseScale;
|
||
|
|
var py = (basePos.get(i * 4 + 1) / 32767) * baseScale;
|
||
|
|
var pz = (basePos.get(i * 4 + 2) / 32767) * baseScale;
|
||
|
|
unpackedPos.push(new Vec4(px, py, pz));
|
||
|
|
|
||
|
|
var fval = 0.0;
|
||
|
|
switch (forwardAxis) {
|
||
|
|
case "X": fval = px;
|
||
|
|
case "-X": fval = -px;
|
||
|
|
case "Y": fval = py;
|
||
|
|
case "-Y": fval = -py;
|
||
|
|
case "Z": fval = pz;
|
||
|
|
case "-Z": fval = -pz;
|
||
|
|
default: fval = px;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (fval < minVal) minVal = fval;
|
||
|
|
if (fval > maxVal) maxVal = fval;
|
||
|
|
|
||
|
|
if (baseNor != null) {
|
||
|
|
var nx = baseNor.get(i * 2) / 32767;
|
||
|
|
var ny = baseNor.get(i * 2 + 1) / 32767;
|
||
|
|
var nz = basePos.get(i * 4 + 3) / 32767;
|
||
|
|
unpackedNor.push(new Vec4(nx, ny, nz));
|
||
|
|
} else {
|
||
|
|
unpackedNor.push(new Vec4(0, 0, 1));
|
||
|
|
}
|
||
|
|
|
||
|
|
if (baseTex != null) {
|
||
|
|
var tu = baseTex.get(i * 2) / 32767;
|
||
|
|
var tv = baseTex.get(i * 2 + 1) / 32767;
|
||
|
|
unpackedTex.push(new Vec4(tu, tv, 0));
|
||
|
|
} else {
|
||
|
|
unpackedTex.push(new Vec4(0, 0, 0));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var sizeVal = maxVal - minVal;
|
||
|
|
if (sizeVal <= 0.00001) sizeVal = 1.0;
|
||
|
|
|
||
|
|
var startIdx = splineIndex == -1 ? 0 : splineIndex;
|
||
|
|
var endIdx = splineIndex == -1 ? splinesLength : splineIndex + 1;
|
||
|
|
|
||
|
|
for (sIdx in startIdx...endIdx) {
|
||
|
|
var spline = data.splines[sIdx];
|
||
|
|
var segments = spline.closed ? spline.points.length : spline.points.length - 1;
|
||
|
|
var frameCount = equidistantSamples > 0 ? equidistantSamples : Std.int(spline.resolution * segments);
|
||
|
|
var framesNor = new Array<Vec4>();
|
||
|
|
var framesBin = new Array<Vec4>();
|
||
|
|
var framesTan = new Array<Vec4>();
|
||
|
|
|
||
|
|
var startTangent = getTangent(0.0, sIdx);
|
||
|
|
if (spline.closed) {
|
||
|
|
var tIn = getTangent(1.0, sIdx);
|
||
|
|
var tOut = getTangent(0.0, sIdx);
|
||
|
|
startTangent.set(tIn.x + tOut.x, tIn.y + tOut.y, tIn.z + tOut.z);
|
||
|
|
startTangent.normalize();
|
||
|
|
}
|
||
|
|
|
||
|
|
var _z = new Vec4(0, 0, 1);
|
||
|
|
if (Math.abs(startTangent.dot(_z)) > 0.9999) {
|
||
|
|
_z.set(0, 1, 0);
|
||
|
|
}
|
||
|
|
var _r = new Vec4();
|
||
|
|
_r.crossvecs(startTangent, _z).normalize();
|
||
|
|
var _u = new Vec4();
|
||
|
|
_u.crossvecs(_r, startTangent).normalize();
|
||
|
|
|
||
|
|
var normal = new Vec4(-_r.x, -_r.y, -_r.z);
|
||
|
|
var binormal = new Vec4(_u.x, _u.y, _u.z);
|
||
|
|
|
||
|
|
var lastTangent = startTangent.clone();
|
||
|
|
|
||
|
|
for (i in 0...frameCount + 1) {
|
||
|
|
var t = i / frameCount;
|
||
|
|
var currTangent = getTangent(t, sIdx);
|
||
|
|
|
||
|
|
if (spline.closed && (t == 0.0 || t == 1.0)) {
|
||
|
|
var tIn = getTangent(1.0, sIdx);
|
||
|
|
var tOut = getTangent(0.0, sIdx);
|
||
|
|
currTangent.set(tIn.x + tOut.x, tIn.y + tOut.y, tIn.z + tOut.z);
|
||
|
|
currTangent.normalize();
|
||
|
|
}
|
||
|
|
|
||
|
|
var axis = new Vec4();
|
||
|
|
axis.crossvecs(lastTangent, currTangent);
|
||
|
|
var dot = Math.max(-1.0, Math.min(1.0, lastTangent.dot(currTangent)));
|
||
|
|
if (axis.length() > 0.00001) {
|
||
|
|
axis.normalize();
|
||
|
|
var angle = Math.acos(dot);
|
||
|
|
var q = new Quat();
|
||
|
|
q.fromAxisAngle(axis, angle);
|
||
|
|
normal.applyQuat(q);
|
||
|
|
binormal.applyQuat(q);
|
||
|
|
}
|
||
|
|
lastTangent = currTangent.clone();
|
||
|
|
|
||
|
|
framesNor.push(normal.clone());
|
||
|
|
framesBin.push(binormal.clone());
|
||
|
|
framesTan.push(currTangent.clone());
|
||
|
|
}
|
||
|
|
|
||
|
|
if (spline.closed) {
|
||
|
|
var endDot = Math.max(-1.0, Math.min(1.0, framesNor[0].dot(framesNor[frameCount])));
|
||
|
|
var endAngle = Math.acos(endDot);
|
||
|
|
|
||
|
|
var crossTest = new Vec4();
|
||
|
|
crossTest.crossvecs(framesNor[0], framesNor[frameCount]);
|
||
|
|
if (framesTan[0].dot(crossTest) < 0) {
|
||
|
|
endAngle = -endAngle;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i in 0...frameCount + 1) {
|
||
|
|
var t = i / frameCount;
|
||
|
|
var qFix = new Quat();
|
||
|
|
qFix.fromAxisAngle(framesTan[i], -endAngle * t);
|
||
|
|
framesNor[i].applyQuat(qFix);
|
||
|
|
framesBin[i].applyQuat(qFix);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (rep in 0...repetitions) {
|
||
|
|
var baseIndex = rawPositions.length;
|
||
|
|
var repTStart = factorStart + (rep / repetitions) * (factorEnd - factorStart);
|
||
|
|
var repTEnd = factorStart + ((rep + 1) / repetitions) * (factorEnd - factorStart);
|
||
|
|
|
||
|
|
for (i in 0...baseVertCount) {
|
||
|
|
var p = unpackedPos[i];
|
||
|
|
var n = unpackedNor[i];
|
||
|
|
var uv = unpackedTex[i];
|
||
|
|
|
||
|
|
var fval = 0.0;
|
||
|
|
var rval = 0.0;
|
||
|
|
var uval = 0.0;
|
||
|
|
var nfval = 0.0;
|
||
|
|
var nrval = 0.0;
|
||
|
|
var nuval = 0.0;
|
||
|
|
|
||
|
|
switch (forwardAxis) {
|
||
|
|
case "X":
|
||
|
|
fval = p.x; rval = p.y; uval = p.z;
|
||
|
|
nfval = n.x; nrval = n.y; nuval = n.z;
|
||
|
|
case "-X":
|
||
|
|
fval = -p.x; rval = -p.y; uval = p.z;
|
||
|
|
nfval = -n.x; nrval = -n.y; nuval = n.z;
|
||
|
|
case "Y":
|
||
|
|
fval = p.y; rval = -p.x; uval = p.z;
|
||
|
|
nfval = n.y; nrval = -n.x; nuval = n.z;
|
||
|
|
case "-Y":
|
||
|
|
fval = -p.y; rval = p.x; uval = p.z;
|
||
|
|
nfval = -n.y; nrval = n.x; nuval = n.z;
|
||
|
|
case "Z":
|
||
|
|
fval = p.z; rval = -p.x; uval = -p.y;
|
||
|
|
nfval = n.z; nrval = -n.x; nuval = -n.y;
|
||
|
|
case "-Z":
|
||
|
|
fval = -p.z; rval = -p.x; uval = p.y;
|
||
|
|
nfval = -n.z; nrval = -n.x; nuval = n.y;
|
||
|
|
default:
|
||
|
|
fval = p.x; rval = p.y; uval = p.z;
|
||
|
|
nfval = n.x; nrval = n.y; nuval = n.z;
|
||
|
|
}
|
||
|
|
|
||
|
|
var localT = (fval - minVal) / sizeVal;
|
||
|
|
var curveT = repTStart + localT * (repTEnd - repTStart);
|
||
|
|
|
||
|
|
if (curveT < 0) curveT = 0;
|
||
|
|
if (curveT > 1) curveT = 1;
|
||
|
|
|
||
|
|
var fIdx = curveT * frameCount;
|
||
|
|
var i0 = Std.int(fIdx);
|
||
|
|
var i1 = i0 + 1;
|
||
|
|
if (i1 > frameCount) i1 = frameCount;
|
||
|
|
var blend = fIdx - i0;
|
||
|
|
|
||
|
|
var n0 = framesNor[i0];
|
||
|
|
var n1 = framesNor[i1];
|
||
|
|
var b0 = framesBin[i0];
|
||
|
|
var b1 = framesBin[i1];
|
||
|
|
var t0 = framesTan[i0];
|
||
|
|
var t1 = framesTan[i1];
|
||
|
|
|
||
|
|
var normalVec = new Vec4(
|
||
|
|
n0.x + (n1.x - n0.x) * blend,
|
||
|
|
n0.y + (n1.y - n0.y) * blend,
|
||
|
|
n0.z + (n1.z - n0.z) * blend
|
||
|
|
);
|
||
|
|
normalVec.normalize();
|
||
|
|
|
||
|
|
var binormalVec = new Vec4(
|
||
|
|
b0.x + (b1.x - b0.x) * blend,
|
||
|
|
b0.y + (b1.y - b0.y) * blend,
|
||
|
|
b0.z + (b1.z - b0.z) * blend
|
||
|
|
);
|
||
|
|
binormalVec.normalize();
|
||
|
|
|
||
|
|
var tangentVec = new Vec4(
|
||
|
|
t0.x + (t1.x - t0.x) * blend,
|
||
|
|
t0.y + (t1.y - t0.y) * blend,
|
||
|
|
t0.z + (t1.z - t0.z) * blend
|
||
|
|
);
|
||
|
|
tangentVec.normalize();
|
||
|
|
|
||
|
|
var curvePos = getPoint(curveT, sIdx);
|
||
|
|
|
||
|
|
var finalPos = new Vec4(
|
||
|
|
curvePos.x + normalVec.x * rval + binormalVec.x * uval,
|
||
|
|
curvePos.y + normalVec.y * rval + binormalVec.y * uval,
|
||
|
|
curvePos.z + normalVec.z * rval + binormalVec.z * uval
|
||
|
|
);
|
||
|
|
|
||
|
|
var finalNor = new Vec4(
|
||
|
|
normalVec.x * nrval + binormalVec.x * nuval + tangentVec.x * nfval,
|
||
|
|
normalVec.y * nrval + binormalVec.y * nuval + tangentVec.y * nfval,
|
||
|
|
normalVec.z * nrval + binormalVec.z * nuval + tangentVec.z * nfval
|
||
|
|
);
|
||
|
|
finalNor.normalize();
|
||
|
|
|
||
|
|
rawPositions.push(finalPos);
|
||
|
|
rawNormals.push(finalNor);
|
||
|
|
rawUVs.push(new Vec4(uv.x, uv.y, 0));
|
||
|
|
}
|
||
|
|
|
||
|
|
for (ia in baseRaw.index_arrays) {
|
||
|
|
var mat = ia.material;
|
||
|
|
if (!indicesByMaterial.exists(mat)) indicesByMaterial.set(mat, []);
|
||
|
|
var destIndices = indicesByMaterial.get(mat);
|
||
|
|
var srcIndices = ia.values;
|
||
|
|
|
||
|
|
for (i in 0...srcIndices.length) {
|
||
|
|
destIndices.push(baseIndex + srcIndices.get(i));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var maxdim = 0.0;
|
||
|
|
for (p in rawPositions) {
|
||
|
|
var ax = Math.abs(p.x);
|
||
|
|
var ay = Math.abs(p.y);
|
||
|
|
var az = Math.abs(p.z);
|
||
|
|
if (ax > maxdim) maxdim = ax;
|
||
|
|
if (ay > maxdim) maxdim = ay;
|
||
|
|
if (az > maxdim) maxdim = az;
|
||
|
|
}
|
||
|
|
if (maxdim == 0) maxdim = 1.0;
|
||
|
|
maxdim *= 2;
|
||
|
|
var invdim = 1 / maxdim;
|
||
|
|
|
||
|
|
var numVerts = rawPositions.length;
|
||
|
|
var paa = new Int16Array(numVerts * 4);
|
||
|
|
var naa = new Int16Array(numVerts * 2);
|
||
|
|
var texa = new Int16Array(numVerts * 2);
|
||
|
|
|
||
|
|
for (i in 0...numVerts) {
|
||
|
|
var p = rawPositions[i];
|
||
|
|
var n = rawNormals[i];
|
||
|
|
var uv = rawUVs[i];
|
||
|
|
|
||
|
|
paa.set(i * 4, Std.int(p.x * 32767 * invdim));
|
||
|
|
paa.set(i * 4 + 1, Std.int(p.y * 32767 * invdim));
|
||
|
|
paa.set(i * 4 + 2, Std.int(p.z * 32767 * invdim));
|
||
|
|
naa.set(i * 2, Std.int(n.x * 32767));
|
||
|
|
naa.set(i * 2 + 1, Std.int(n.y * 32767));
|
||
|
|
paa.set(i * 4 + 3, Std.int(n.z * 32767));
|
||
|
|
|
||
|
|
texa.set(i * 2, Std.int(uv.x * 32767));
|
||
|
|
texa.set(i * 2 + 1, Std.int(uv.y * 32767));
|
||
|
|
}
|
||
|
|
|
||
|
|
var indexArrays: Array<TIndexArray> = [];
|
||
|
|
for (matIdx in indicesByMaterial.keys()) {
|
||
|
|
var matIndices = indicesByMaterial.get(matIdx);
|
||
|
|
var inda = new Uint32Array(matIndices.length);
|
||
|
|
for (i in 0...matIndices.length) inda.set(i, matIndices[i]);
|
||
|
|
indexArrays.push({ material: matIdx, values: inda });
|
||
|
|
}
|
||
|
|
|
||
|
|
var posVA: TVertexArray = { attrib: "pos", values: paa, data: "short4norm" };
|
||
|
|
var norVA: TVertexArray = { attrib: "nor", values: naa, data: "short2norm" };
|
||
|
|
var texVA: TVertexArray = { attrib: "tex", values: texa, data: "short2norm" };
|
||
|
|
|
||
|
|
var rawmesh: TMeshData = {
|
||
|
|
name: data.name + "_mesh",
|
||
|
|
sorting_index: 0,
|
||
|
|
vertex_arrays: [posVA, norVA, texVA],
|
||
|
|
index_arrays: indexArrays,
|
||
|
|
scale_pos: maxdim
|
||
|
|
};
|
||
|
|
|
||
|
|
var md = new MeshData(rawmesh, function(d: MeshData) {});
|
||
|
|
md.geom.calculateAABB();
|
||
|
|
return md;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|