awayjs-display
Version:
AwayJS displaylist classes
667 lines (548 loc) • 16.7 kB
text/typescript
import IAsset from "awayjs-core/lib/library/IAsset";
import ElementsType from "../graphics/ElementsType";
import LineElements from "../graphics/LineElements";
import ElementsBase from "../graphics/ElementsBase";
import TriangleElements from "../graphics/TriangleElements";
import MaterialBase from "../materials/MaterialBase";
import PrimitivePrefabBase from "../prefabs/PrimitivePrefabBase";
/**
* A Cylinder primitive sprite.
*/
class PrimitiveCylinderPrefab extends PrimitivePrefabBase
{
public _pBottomRadius:number;
public _pSegmentsW:number;
public _pSegmentsH:number;
private _topRadius:number;
private _height:number;
private _topClosed:boolean;
private _bottomClosed:boolean;
private _surfaceClosed:boolean;
private _yUp:boolean;
private _numVertices:number = 0;
/**
* The radius of the top end of the cylinder.
*/
public get topRadius():number
{
return this._topRadius;
}
public set topRadius(value:number)
{
this._topRadius = value;
this._pInvalidatePrimitive();
}
/**
* The radius of the bottom end of the cylinder.
*/
public get bottomRadius():number
{
return this._pBottomRadius;
}
public set bottomRadius(value:number)
{
this._pBottomRadius = value;
this._pInvalidatePrimitive();
}
/**
* The radius of the top end of the cylinder.
*/
public get height():number
{
return this._height;
}
public set height(value:number)
{
this._height = value;
this._pInvalidatePrimitive();
}
/**
* Defines the number of horizontal segments that make up the cylinder. Defaults to 16.
*/
public get segmentsW():number
{
return this._pSegmentsW;
}
public set segmentsW(value:number)
{
this.setSegmentsW(value);
}
public setSegmentsW(value:number)
{
this._pSegmentsW = value;
this._pInvalidatePrimitive();
this._pInvalidateUVs();
}
/**
* Defines the number of vertical segments that make up the cylinder. Defaults to 1.
*/
public get segmentsH():number
{
return this._pSegmentsH;
}
public set segmentsH(value:number)
{
this.setSegmentsH(value)
}
public setSegmentsH(value:number)
{
this._pSegmentsH = value;
this._pInvalidatePrimitive();
this._pInvalidateUVs();
}
/**
* Defines whether the top end of the cylinder is closed (true) or open.
*/
public get topClosed():boolean
{
return this._topClosed;
}
public set topClosed(value:boolean)
{
this._topClosed = value;
this._pInvalidatePrimitive();
}
/**
* Defines whether the bottom end of the cylinder is closed (true) or open.
*/
public get bottomClosed():boolean
{
return this._bottomClosed;
}
public set bottomClosed(value:boolean)
{
this._bottomClosed = value;
this._pInvalidatePrimitive();
}
/**
* Defines whether the cylinder poles should lay on the Y-axis (true) or on the Z-axis (false).
*/
public get yUp():boolean
{
return this._yUp;
}
public set yUp(value:boolean)
{
this._yUp = value;
this._pInvalidatePrimitive();
}
/**
* Creates a new Cylinder object.
* @param topRadius The radius of the top end of the cylinder.
* @param bottomRadius The radius of the bottom end of the cylinder
* @param height The radius of the bottom end of the cylinder
* @param segmentsW Defines the number of horizontal segments that make up the cylinder. Defaults to 16.
* @param segmentsH Defines the number of vertical segments that make up the cylinder. Defaults to 1.
* @param topClosed Defines whether the top end of the cylinder is closed (true) or open.
* @param bottomClosed Defines whether the bottom end of the cylinder is closed (true) or open.
* @param yUp Defines whether the cone poles should lay on the Y-axis (true) or on the Z-axis (false).
*/
constructor(material:MaterialBase = null, elementsType:string = "triangle", topRadius:number = 50, bottomRadius:number = 50, height:number = 100, segmentsW:number = 16, segmentsH:number = 1, topClosed:boolean = true, bottomClosed:boolean = true, surfaceClosed:boolean = true, yUp:boolean = true)
{
super(material, elementsType);
this._topRadius = topRadius;
this._pBottomRadius = bottomRadius;
this._height = height;
this._pSegmentsW = segmentsW;
this._pSegmentsH = segmentsH;
this._topClosed = topClosed;
this._bottomClosed = bottomClosed;
this._surfaceClosed = surfaceClosed;
this._yUp = yUp;
}
/**
* @inheritDoc
*/
public _pBuildGraphics(target:ElementsBase, elementsType:string)
{
var indices:Uint16Array;
var positions:ArrayBufferView;
var normals:Float32Array;
var tangents:Float32Array;
var i:number;
var j:number;
var x:number;
var y:number;
var z:number;
var vidx:number;
var fidx:number;
var radius:number;
var revolutionAngle:number;
var dr:number;
var latNormElev:number;
var latNormBase:number;
var numIndices:number = 0;
var comp1:number;
var comp2:number;
var startIndex:number = 0;
var nextVertexIndex:number = 0;
var centerVertexIndex:number = 0;
var t1:number;
var t2:number;
// reset utility variables
this._numVertices = 0;
// evaluate revolution steps
var revolutionAngleDelta:number = 2*Math.PI/this._pSegmentsW;
if (elementsType == ElementsType.TRIANGLE) {
var triangleGraphics:TriangleElements = <TriangleElements> target;
// evaluate target number of vertices, triangles and indices
if (this._surfaceClosed) {
this._numVertices += (this._pSegmentsH + 1)*(this._pSegmentsW + 1); // segmentsH + 1 because of closure, segmentsW + 1 because of UV unwrapping
numIndices += this._pSegmentsH*this._pSegmentsW*6; // each level has segmentW quads, each of 2 triangles
}
if (this._topClosed) {
this._numVertices += 2*(this._pSegmentsW + 1); // segmentsW + 1 because of unwrapping
numIndices += this._pSegmentsW*3; // one triangle for each segment
}
if (this._bottomClosed) {
this._numVertices += 2*(this._pSegmentsW + 1);
numIndices += this._pSegmentsW*3;
}
// need to initialize raw arrays or can be reused?
if (this._numVertices == triangleGraphics.numVertices) {
indices = triangleGraphics.indices.get(triangleGraphics.numElements);
positions = triangleGraphics.positions.get(this._numVertices);
normals = triangleGraphics.normals.get(this._numVertices);
tangents = triangleGraphics.tangents.get(this._numVertices);
} else {
indices = new Uint16Array(numIndices);
positions = new Float32Array(this._numVertices*3);
normals = new Float32Array(this._numVertices*3);
tangents = new Float32Array(this._numVertices*3);
this._pInvalidateUVs();
}
vidx = 0;
fidx = 0;
// top
if (this._topClosed && this._topRadius > 0) {
z = -0.5*this._height;
// central vertex
if (this._yUp) {
t1 = 1;
t2 = 0;
comp1 = -z;
comp2 = 0;
} else {
t1 = 0;
t2 = -1;
comp1 = 0;
comp2 = z;
}
positions[vidx] = 0;
positions[vidx + 1] = comp1;
positions[vidx + 2] = comp2;
normals[vidx] = 0;
normals[vidx + 1] = t1;
normals[vidx + 2] = t2;
tangents[vidx] = 1;
tangents[vidx + 1] = 0;
tangents[vidx + 2] = 0;
vidx += 3;
nextVertexIndex += 1;
for (i = 0; i <= this._pSegmentsW; ++i) {
// revolution vertex
revolutionAngle = i*revolutionAngleDelta;
x = this._topRadius*Math.cos(revolutionAngle);
y = this._topRadius*Math.sin(revolutionAngle);
if (this._yUp) {
comp1 = -z;
comp2 = y;
} else {
comp1 = y;
comp2 = z;
}
if (i == this._pSegmentsW) {
positions[vidx] = positions[startIndex + 3];
positions[vidx + 1] = positions[startIndex + 4];
positions[vidx + 2] = positions[startIndex + 5];
} else {
positions[vidx] = x;
positions[vidx + 1] = comp1;
positions[vidx + 2] = comp2;
}
normals[vidx] = 0;
normals[vidx + 1] = t1;
normals[vidx + 2] = t2;
tangents[vidx] = 1;
tangents[vidx + 1] = 0;
tangents[vidx + 2] = 0;
vidx += 3;
if (i > 0) {
// add triangle
indices[fidx++] = nextVertexIndex - 1;
indices[fidx++] = centerVertexIndex;
indices[fidx++] = nextVertexIndex;
}
nextVertexIndex += 1;
}
}
// bottom
if (this._bottomClosed && this._pBottomRadius > 0) {
z = 0.5*this._height;
startIndex = nextVertexIndex*3;
centerVertexIndex = nextVertexIndex;
// central vertex
if (this._yUp) {
t1 = -1;
t2 = 0;
comp1 = -z;
comp2 = 0;
} else {
t1 = 0;
t2 = 1;
comp1 = 0;
comp2 = z;
}
if (i > 0) {
positions[vidx] = 0;
positions[vidx + 1] = comp1;
positions[vidx + 2] = comp2;
normals[vidx] = 0;
normals[vidx + 1] = t1;
normals[vidx + 2] = t2;
tangents[vidx] = 1;
tangents[vidx + 1] = 0;
tangents[vidx + 2] = 0;
vidx += 3;
}
nextVertexIndex += 1;
for (i = 0; i <= this._pSegmentsW; ++i) {
// revolution vertex
revolutionAngle = i*revolutionAngleDelta;
x = this._pBottomRadius*Math.cos(revolutionAngle);
y = this._pBottomRadius*Math.sin(revolutionAngle);
if (this._yUp) {
comp1 = -z;
comp2 = y;
} else {
comp1 = y;
comp2 = z;
}
if (i == this._pSegmentsW) {
positions[vidx] = positions[startIndex + 3];
positions[vidx + 1] = positions[startIndex + 4];
positions[vidx + 2] = positions[startIndex + 5];
} else {
positions[vidx] = x;
positions[vidx + 1] = comp1;
positions[vidx + 2] = comp2;
}
normals[vidx] = 0;
normals[vidx + 1] = t1;
normals[vidx + 2] = t2;
tangents[vidx] = 1;
tangents[vidx + 1] = 0;
tangents[vidx + 2] = 0;
vidx += 3;
if (i > 0) {
// add triangle
indices[fidx++] = nextVertexIndex - 1;
indices[fidx++] = nextVertexIndex;
indices[fidx++] = centerVertexIndex;
}
nextVertexIndex += 1;
}
}
// The normals on the lateral surface all have the same incline, i.e.
// the "elevation" component (Y or Z depending on yUp) is constant.
// Same principle goes for the "base" of these vectors, which will be
// calculated such that a vector [base,elev] will be a unit vector.
dr = (this._pBottomRadius - this._topRadius);
latNormElev = dr/this._height;
latNormBase = (latNormElev == 0)? 1 : this._height/dr;
// lateral surface
if (this._surfaceClosed) {
var a:number;
var b:number;
var c:number;
var d:number;
var na0:number, na1:number, naComp1:number, naComp2:number;
for (j = 0; j <= this._pSegmentsH; ++j) {
radius = this._topRadius - ((j/this._pSegmentsH)*(this._topRadius - this._pBottomRadius));
z = -(this._height/2) + (j/this._pSegmentsH*this._height);
startIndex = nextVertexIndex*3;
for (i = 0; i <= this._pSegmentsW; ++i) {
// revolution vertex
revolutionAngle = i*revolutionAngleDelta;
x = radius*Math.cos(revolutionAngle);
y = radius*Math.sin(revolutionAngle);
na0 = latNormBase*Math.cos(revolutionAngle);
na1 = latNormBase*Math.sin(revolutionAngle);
if (this._yUp) {
t1 = 0;
t2 = -na0;
comp1 = -z;
comp2 = y;
naComp1 = latNormElev;
naComp2 = na1;
} else {
t1 = -na0;
t2 = 0;
comp1 = y;
comp2 = z;
naComp1 = na1;
naComp2 = latNormElev;
}
if (i == this._pSegmentsW) {
positions[vidx] = positions[startIndex];
positions[vidx + 1] = positions[startIndex + 1];
positions[vidx + 2] = positions[startIndex + 2];
normals[vidx] = na0;
normals[vidx + 1] = latNormElev;
normals[vidx + 2] = na1;
tangents[vidx] = na1;
tangents[vidx + 1] = t1;
tangents[vidx + 2] = t2;
} else {
positions[vidx] = x;
positions[vidx + 1] = comp1;
positions[vidx + 2] = comp2;
normals[vidx] = na0;
normals[vidx + 1] = naComp1;
normals[vidx + 2] = naComp2;
tangents[vidx] = -na1;
tangents[vidx + 1] = t1;
tangents[vidx + 2] = t2;
}
vidx += 3;
// close triangle
if (i > 0 && j > 0) {
a = nextVertexIndex; // current
b = nextVertexIndex - 1; // previous
c = b - this._pSegmentsW - 1; // previous of last level
d = a - this._pSegmentsW - 1; // current of last level
indices[fidx++] = a;
indices[fidx++] = b;
indices[fidx++] = c;
indices[fidx++] = a;
indices[fidx++] = c;
indices[fidx++] = d;
}
nextVertexIndex++;
}
}
}
// build real data from raw data
triangleGraphics.setIndices(indices);
triangleGraphics.setPositions(positions);
triangleGraphics.setNormals(normals);
triangleGraphics.setTangents(tangents);
} else if (elementsType == ElementsType.LINE) {
var lineGraphics:LineElements = <LineElements> target;
var numSegments:number = this._pSegmentsH*this._pSegmentsW*2 + this._pSegmentsW;
positions = new Float32Array(numSegments*6);
var thickness:Float32Array = new Float32Array(numSegments);
vidx = 0;
fidx = 0;
var _radius = 50;
for (j = 0; j <= this._pSegmentsH; ++j) {
radius = this._topRadius - ((j/this._pSegmentsH)*(this._topRadius - this._pBottomRadius));
z = -(this._height/2) + (j/this._pSegmentsH*this._height);
for (i = 0; i <= this._pSegmentsW; ++i) {
// revolution vertex
revolutionAngle = i*revolutionAngleDelta;
x = radius*Math.cos(revolutionAngle);
y = radius*Math.sin(revolutionAngle);
if (this._yUp) {
comp1 = -z;
comp2 = y;
} else {
comp1 = y;
comp2 = z;
}
if (i > 0) {
//horizonal lines
positions[vidx++] = x;
positions[vidx++] = comp1;
positions[vidx++] = comp2;
thickness[fidx++] = 1;
//vertical lines
if (j > 0) {
var addx:number = (j == 1)? 3 - (6*(this._pSegmentsW-i) + 12*i) : 3 - this._pSegmentsW*12;
positions[vidx] = positions[vidx++ + addx];
positions[vidx] = positions[vidx++ + addx];
positions[vidx] = positions[vidx++ + addx];
positions[vidx++] = x;
positions[vidx++] = comp1;
positions[vidx++] = comp2;
thickness[fidx++] = 1;
}
}
//horizonal lines
if (i < this._pSegmentsW) {
positions[vidx++] = x;
positions[vidx++] = comp1;
positions[vidx++] = comp2;
}
}
}
// build real data from raw data
lineGraphics.setPositions(positions);
lineGraphics.setThickness(thickness);
}
}
/**
* @inheritDoc
*/
public _pBuildUVs(target:ElementsBase, elementsType:string)
{
var i:number;
var j:number;
var x:number;
var y:number;
var revolutionAngle:number;
var uvs:ArrayBufferView;
if (elementsType == ElementsType.TRIANGLE) {
var triangleGraphics:TriangleElements = <TriangleElements> target;
// need to initialize raw array or can be reused?
if (triangleGraphics.uvs && this._numVertices == triangleGraphics.numVertices) {
uvs = triangleGraphics.uvs.get(this._numVertices);
} else {
uvs = new Float32Array(this._numVertices*2);
}
// evaluate revolution steps
var revolutionAngleDelta:number = 2*Math.PI/this._pSegmentsW;
// current uv component index
var index:number = 0;
// top
if (this._topClosed) {
uvs[index++] = 0.5*this._scaleU; // central vertex
uvs[index++] = 0.5*this._scaleV;
for (i = 0; i <= this._pSegmentsW; ++i) {
revolutionAngle = i*revolutionAngleDelta;
x = 0.5 + 0.5* -Math.cos(revolutionAngle);
y = 0.5 + 0.5*Math.sin(revolutionAngle);
uvs[index++] = x*this._scaleU; // revolution vertex
uvs[index++] = y*this._scaleV;
}
}
// bottom
if (this._bottomClosed) {
uvs[index++] = 0.5*this._scaleU; // central vertex
uvs[index++] = 0.5*this._scaleV;
for (i = 0; i <= this._pSegmentsW; ++i) {
revolutionAngle = i*revolutionAngleDelta;
x = 0.5 + 0.5*Math.cos(revolutionAngle);
y = 0.5 + 0.5*Math.sin(revolutionAngle);
uvs[index++] = x*this._scaleU; // revolution vertex
uvs[index++] = y*this._scaleV;
}
}
// lateral surface
if (this._surfaceClosed) {
for (j = 0; j <= this._pSegmentsH; ++j) {
for (i = 0; i <= this._pSegmentsW; ++i) {
// revolution vertex
uvs[index++] = ( i/this._pSegmentsW )*this._scaleU;
uvs[index++] = ( j/this._pSegmentsH )*this._scaleV;
}
}
}
// build real data from raw data
triangleGraphics.setUVs(uvs);
} else if (elementsType == ElementsType.LINE) {
//nothing to do here
}
}
}
export default PrimitiveCylinderPrefab;