@awayjs/graphics
Version:
AwayJS graphics classes
423 lines (422 loc) • 14.8 kB
JavaScript
import { Rectangle, Vector3D, } from '@awayjs/core';
var Vertice3DView = /** @class */ (function () {
function Vertice3DView() {
this._data = [];
}
Object.defineProperty(Vertice3DView.prototype, "length", {
get: function () {
return this._data.length;
},
enumerable: false,
configurable: true
});
Vertice3DView.prototype.toVec = function (vect, index) {
if (vect === void 0) { vect = new Vector3D(); }
if (index === void 0) { index = 0; }
var s = this._data[index];
var l = s.length;
for (var i = 0; i < 4; i++) {
vect._rawData[i] = i < l ? s[i] : 0;
}
return vect;
};
Vertice3DView.prototype.fromVec = function (vect, index) {
if (index === void 0) { index = 0; }
var s = this._data[index];
var l = s.length;
for (var i = 0; i < l; i++) {
s[i] = vect._rawData[i];
}
return this;
};
Vertice3DView.prototype.setData = function (data, index, clone) {
if (clone === void 0) { clone = false; }
this._data[index] = clone ? data.slice() : data;
return this;
};
Vertice3DView.prototype.getData = function (index) {
return this._data[index];
};
Vertice3DView.prototype.lerpTo = function (target, alpha) {
if (alpha === 0) {
return this;
}
if (alpha === 1) {
return this.copyFrom(target);
}
// interpolate all values
for (var i = 0; i < this._data.length; i++) {
var to = target._data[i];
var from = this._data[i];
for (var k = 0; k < this._data[i].length; k++) {
from[k] = (1 - alpha) * from[k] + alpha * to[k];
}
}
return this;
};
Vertice3DView.prototype.add = function (source, scale) {
if (scale === void 0) { scale = 1; }
// interpolate all values
for (var i = 0; i < this._data.length; i++) {
var from = source._data[i];
var to = this._data[i];
for (var k = 0; k < this._data[i].length; k++) {
to[k] += from[k] * scale;
}
}
return this;
};
Vertice3DView.prototype.scale = function (s) {
// interpolate all values
for (var i = 0; i < this._data.length; i++) {
var to = this._data[i];
for (var k = 0; k < this._data[i].length; k++) {
to[k] *= s;
}
}
return this;
};
Vertice3DView.prototype.copyFrom = function (from) {
var to = this._data;
for (var i = 0; i < from.length; i++) {
if (to[i]) {
to[i].set(from._data[i]);
}
else {
to[i] = from._data[i].slice();
}
}
return this;
};
Vertice3DView.prototype.clone = function () {
return new Vertice3DView().copyFrom(this);
};
return Vertice3DView;
}());
export { Vertice3DView };
var PolygonView = /** @class */ (function () {
function PolygonView(points, userData) {
this.vertices = [];
if (points) {
this.vertices = points.map(function (e) { return e.clone(); });
}
if (userData) {
this.userData = userData;
}
}
Object.defineProperty(PolygonView.prototype, "middle", {
get: function () {
if (this._middle)
return this._middle;
this._middle = this.vertices[0].clone();
for (var i = 1; i < this.vertices.length; i++) {
this._middle.add(this.vertices[i]);
}
this._middle.scale(1 / this.vertices.length);
return this._middle;
},
enumerable: false,
configurable: true
});
PolygonView.prototype.clone = function () {
var p = new PolygonView();
var l = this.vertices.length;
for (var i = 0; i < l; i++) {
p.vertices[i] = this.vertices[i].clone();
}
if (this.userData) {
p.userData = this.userData.clone
? this.userData.clone()
: Object.assign({}, this.userData);
}
return p;
};
PolygonView.prototype.add = function (view, clone) {
if (clone === void 0) { clone = false; }
this.vertices.push(clone ? view.clone() : view);
return this;
};
Object.defineProperty(PolygonView.prototype, "length", {
get: function () {
return this.vertices.length;
},
enumerable: false,
configurable: true
});
return PolygonView;
}());
export { PolygonView };
var MeshView = /** @class */ (function () {
function MeshView() {
this.poly = [];
this.rect = new Rectangle();
this.polySize = 3;
this.hasNGones = false;
}
MeshView.prototype.push = function (p) {
this.poly.push(p);
return this;
};
/**
* Unroll all n-gone convex polys to 3-gone polygons
*/
MeshView.prototype.normalise = function () {
if (!this.hasNGones) {
return;
}
var normalised = [];
for (var _i = 0, _a = this.poly; _i < _a.length; _i++) {
var p = _a[_i];
// invalid vertices
if (p.length < this.polySize) {
continue;
}
if (p.length === this.polySize) {
normalised.push(p);
continue;
}
for (var i = 1; i < p.length - 1; i++) {
var n = new PolygonView();
// shared user data;
n.userData = p.userData;
// construct poly in direct order with shared 1 vert (CCW or CW? i don't know ;)
// fan triangulation
// we sure that this a convex polygon
n.add(p.vertices[0].clone());
n.add(p.vertices[(i) % p.length].clone());
n.add(p.vertices[(i + 1) % p.length].clone());
normalised.push(n);
}
}
this.poly = normalised;
return this;
};
/**
* Construct buffer from poly data
* @param index Index of vertices attribute 0 - pos, 1 - uv etc
*/
MeshView.prototype.toFloatArray = function (index) {
if (index === void 0) { index = 0; }
var count = this.poly.length;
if (this.poly.length === 0)
return null;
// check a dimension for calc a output size
var test = this.poly[0].vertices[0].getData(index);
var dim = test.length;
// polygon should has 3 vertices
var data = new Float32Array(count * dim * 3);
for (var i = 0; i < count * 3; i++) {
var poly = this.poly[i / 3 | 0];
var vert = poly.vertices[i % 3];
data.set(vert.getData(index), i * dim);
}
return data;
};
MeshView.fromAttributes = function (attrs, length, polySize, userDataCtor) {
if (polySize === void 0) { polySize = 0; }
var mesh = new MeshView();
mesh.polySize = polySize;
var poly;
var views = attrs.map(function (e) { return e.get(length, 0); });
for (var i = 0; i < length; i++) {
if (i % polySize === 0) {
poly = new PolygonView();
poly.userData = userDataCtor ? new userDataCtor() : null;
}
mesh.poly.push(poly);
var v = new Vertice3DView();
poly.add(v);
for (var k = 0; k < attrs.length; k++) {
var o = attrs[k].offset;
var s = attrs[k].stride;
var d = attrs[k].dimensions;
v.setData(views[k].slice(o + s * i, o + s * i + d), k);
}
}
return mesh;
};
return MeshView;
}());
export { MeshView };
var GeneratorUtils = /** @class */ (function () {
function GeneratorUtils() {
}
GeneratorUtils.InFront = function (n, d, p) {
return n.dotProduct(p) - d > this.EPS;
};
GeneratorUtils.Behind = function (n, d, p) {
return n.dotProduct(p) - d < -this.EPS;
};
GeneratorUtils.OnPlane = function (n, d, p) {
return !this.InFront(n, d, p) && !this.Behind(n, d, p);
};
GeneratorUtils.Intersect = function (a, b, d1, d2) {
var alpha = d1 / (d1 - d2);
return new Vertice3DView().copyFrom(a).lerpTo(b, alpha);
};
GeneratorUtils.SliceNaive = function (out, a, b, c, d1, d2, d3) {
// Calculate the intersection point from a to b
var ab = this.Intersect(a, b, d1, d2);
if (d1 < 0.0) {
// b to c crosses the clipping plane
if (d3 < 0.0) {
var bc = this.Intersect(b, c, d2, d3);
out
.push(new PolygonView([b, bc, ab]))
.push(new PolygonView([bc, c, a]))
.push(new PolygonView([ab, bc, a]));
// c to a crosses the clipping plane
}
else {
var ac = this.Intersect(a, c, d1, d3);
out
.push(new PolygonView([a, ab, ac]))
.push(new PolygonView([ab, b, c]))
.push(new PolygonView([ac, ab, c]));
}
}
else {
// c to a crosses the clipping plane
if (d3 < 0.0) {
var ac = this.Intersect(a, c, d1, d3);
out
.push(new PolygonView([a, ab, ac]))
.push(new PolygonView([ac, ab, b]))
.push(new PolygonView([b, c, ac]));
// b to c crosses the clipping plane
}
else {
var bc = this.Intersect(b, c, d2, d3);
out
.push(new PolygonView([b, bc, ab]))
.push(new PolygonView([a, ab, bc]))
.push(new PolygonView([c, a, bc]));
}
}
return out;
};
// Slices all triangles given a vector of triangles.
// A new output triangle list is generated. The old
// list of triangles is discarded.
// n - The normal of the clipping plane
// d - Distance of clipping plane from the origin
// Reference: Exact Bouyancy for Polyhedra by
// Erin Catto in Game Programming Gems 6
GeneratorUtils.SliceAllNaive = function (mesh, n, d) {
var out = new MeshView();
out.polySize = mesh.polySize;
var tmp = new Vector3D();
for (var i = 0; i < mesh.poly.length; ++i) {
// Grab a triangle from the global triangle list
var tri = mesh.poly[i];
var a = tri.vertices[0];
var b = tri.vertices[1];
var c = tri.vertices[2];
a.toVec(tmp, 0);
// Compute distance of each triangle vertex to the clipping plane
var d1 = a.toVec(tmp).dotProduct(n) - d;
var d2 = b.toVec(tmp).dotProduct(n) - d;
var d3 = c.toVec(tmp).dotProduct(n) - d;
// a to b crosses the clipping plane
if (d1 * d2 < 0.0) {
this.SliceNaive(out, a, b, c, d1, d2, d3);
// a to c crosses the clipping plane
}
else if (d1 * d3 < 0.0) {
this.SliceNaive(out, c, a, b, d3, d1, d2);
// b to c crosses the clipping plane
}
else if (d2 * d3 < 0.0) {
this.SliceNaive(out, b, c, a, d2, d3, d1);
}
else {
out.push(tri);
}
}
return out;
};
/**
* It gen corrupted results, need improve, eXponenta
*
* Splits a polygon in half along a splitting plane using a clipping algorithm
* call Sutherland-Hodgman clipping
* Resource: Page 367 of Ericson (Real-Time Collision Detection)
*/
GeneratorUtils.SutherlandHodgman = function (poly, normal, d, out, callback) {
var frontPoly = new PolygonView();
var backPoly = new PolygonView();
var s = poly.length;
var verts = poly.vertices;
var tmpA = new Vector3D();
var tmpB = new Vector3D();
if (d < 0) {
d = -d;
normal.scaleBy(-1);
}
var vA = verts[s - 1];
var vecA = vA.toVec(tmpA, 0);
var da = normal.dotProduct(vecA) - d;
for (var i = 0; i < s; ++i) {
var vB = verts[i];
var vecB = vB.toVec(tmpB, 0);
var db = normal.dotProduct(vecB) - d;
if (this.InFront(normal, d, vecB)) {
if (this.Behind(normal, d, vecA)) {
var itr = this.Intersect(vB, vA, db, da);
frontPoly.add(itr);
backPoly.add(itr, true);
}
frontPoly.add(vB, true);
}
else if (this.Behind(normal, d, vecB)) {
if (this.InFront(normal, d, vecA)) {
var itr = this.Intersect(vA, vB, da, db);
frontPoly.add(itr, false);
backPoly.add(itr, true);
}
else if (this.OnPlane(normal, d, vecA)) {
backPoly.add(vA, true);
}
backPoly.add(vB, true);
}
else {
frontPoly.add(vB, true);
if (this.OnPlane(normal, d, vecA)) {
backPoly.add(vB, true);
}
}
vA = vB.clone();
vecA = vA.toVec(vecA, 0);
da = db;
}
if (frontPoly.length) {
//if (frontPoly.length < 3) debugger;
callback && callback(poly, frontPoly, false);
if (out.polySize !== frontPoly.length) {
out.hasNGones = true;
}
out.poly.push(frontPoly);
}
if (backPoly.length) {
//if (backPoly.length < 3) debugger;
callback && callback(poly, backPoly, true);
if (out.polySize !== backPoly.length) {
out.hasNGones = true;
}
out.poly.push(backPoly);
}
return out;
};
GeneratorUtils.SliceHodgman = function (mesh, n, d, callback) {
var out = new MeshView();
out.polySize = mesh.polySize;
for (var _i = 0, _a = mesh.poly; _i < _a.length; _i++) {
var p = _a[_i];
this.SutherlandHodgman(p, n, d, out, callback);
}
return out;
};
GeneratorUtils.EPS = 0.0001;
return GeneratorUtils;
}());
export { GeneratorUtils };