Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add createCapsule in PrimitiveMesh #515

Merged
merged 3 commits into from
Oct 11, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions packages/core/src/mesh/PrimitiveMesh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,127 @@ export class PrimitiveMesh {
return mesh;
}

/**
* Create a capsule mesh.
* @param engine - Engine
* @param radius - The radius of the two hemispherical ends
* @param height - The height of the cylindrical part, measured between the centers of the hemispherical ends
* @param radialSegments - Hemispherical end radial segments
* @param heightSegments - Cylindrical part height segments
* @param noLongerAccessible - No longer access the vertices of the mesh after creation
* @returns Capsule model mesh
*/
static createCapsule(
engine: Engine,
radius: number = 0.5,
height: number = 2,
radialSegments: number = 6,
heightSegments: number = 1,
noLongerAccessible: boolean = true
): ModelMesh {
const mesh = new ModelMesh(engine);

radialSegments = Math.max(2, Math.floor(radialSegments));
heightSegments = Math.floor(heightSegments);

JujieX marked this conversation as resolved.
Show resolved Hide resolved
const radialCount = radialSegments + 1;
const verticalCount = heightSegments + 1;
const halfHeight = height * 0.5;
const unitHeight = height / heightSegments;
const torsoVertexCount = radialCount * verticalCount;
const torsoRectangleCount = radialSegments * heightSegments;

const capVertexCount = radialCount * radialCount;
const capRectangleCount = radialSegments * radialSegments;

const totalVertexCount = torsoVertexCount + 2 * capVertexCount;
const indices = PrimitiveMesh._generateIndices(
engine,
totalVertexCount,
(torsoRectangleCount + 2 * capRectangleCount) * 6
);

const radialCountReciprocal = 1.0 / radialCount;
const radialSegmentsReciprocal = 1.0 / radialSegments;
const heightSegmentsReciprocal = 1.0 / heightSegments;

const halfPI = Math.PI / 2;
const doublePI = Math.PI * 2;

const positions = new Array<Vector3>(totalVertexCount);
const normals = new Array<Vector3>(totalVertexCount);
const uvs = new Array<Vector2>(totalVertexCount);

let indicesOffset = 0;

// create torso
for (let i = 0; i < torsoVertexCount; ++i) {
const x = i % radialCount;
const y = (i * radialCountReciprocal) | 0;
const u = x * radialSegmentsReciprocal;
const v = y * heightSegmentsReciprocal;
const theta = -halfPI + u * doublePI;
const sinTheta = Math.sin(theta);
const cosTheta = Math.cos(theta);

positions[i] = new Vector3(radius * sinTheta, y * unitHeight - halfHeight, radius * cosTheta);
normals[i] = new Vector3(sinTheta, 0, cosTheta);
uvs[i] = new Vector2(u, 1 - v);
GuoLei1990 marked this conversation as resolved.
Show resolved Hide resolved
}

for (let i = 0; i < torsoRectangleCount; ++i) {
const x = i % radialSegments;
const y = (i * radialSegmentsReciprocal) | 0;

const a = y * radialCount + x;
const b = a + 1;
const c = a + radialCount;
const d = c + 1;

indices[indicesOffset++] = b;
indices[indicesOffset++] = c;
indices[indicesOffset++] = a;
indices[indicesOffset++] = b;
indices[indicesOffset++] = d;
indices[indicesOffset++] = c;
}

PrimitiveMesh._createCapsuleCap(
radius,
height,
radialSegments,
doublePI,
torsoVertexCount,
1,
positions,
normals,
uvs,
indices,
indicesOffset
);

PrimitiveMesh._createCapsuleCap(
radius,
height,
radialSegments,
-doublePI,
torsoVertexCount + capVertexCount,
-1,
positions,
normals,
uvs,
indices,
indicesOffset + 6 * capRectangleCount
);

const { bounds } = mesh;
bounds.min.setValue(-radius, -radius - halfHeight, -radius);
bounds.max.setValue(radius, radius + halfHeight, radius);

PrimitiveMesh._initialize(mesh, positions, normals, uvs, indices, noLongerAccessible);
return mesh;
}

private static _initialize(
mesh: ModelMesh,
positions: Vector3[],
Expand Down Expand Up @@ -700,4 +821,61 @@ export class PrimitiveMesh {
}
return indices;
}

private static _createCapsuleCap(
radius: number,
height: number,
radialSegments: number,
capAlphaRange: number,
offset: number,
posIndex: number,
positions: Vector3[],
normals: Vector3[],
uvs: Vector2[],
indices: Uint16Array | Uint32Array,
indicesOffset: number
) {
const radialCount = radialSegments + 1;
const halfHeight = height * 0.5;
const capVertexCount = radialCount * radialCount;
const capRectangleCount = radialSegments * radialSegments;
const radialCountReciprocal = 1.0 / radialCount;
const radialSegmentsReciprocal = 1.0 / radialSegments;

for (let i = 0; i < capVertexCount; ++i) {
const x = i % radialCount;
const y = (i * radialCountReciprocal) | 0;
const u = x * radialSegmentsReciprocal;
const v = y * radialSegmentsReciprocal;
const alphaDelta = u * capAlphaRange;
const thetaDelta = (v * Math.PI) / 2;
const sinTheta = Math.sin(thetaDelta);

const posX = -radius * Math.cos(alphaDelta) * sinTheta;
const posY = (radius * Math.cos(thetaDelta) + halfHeight) * posIndex;
const posZ = radius * Math.sin(alphaDelta) * sinTheta;

const index = i + offset;
positions[index] = new Vector3(posX, posY, posZ);
normals[index] = new Vector3(posX, posY, posZ);
uvs[index] = new Vector2(u, v);
}

for (let i = 0; i < capRectangleCount; ++i) {
const x = i % radialSegments;
const y = (i * radialSegmentsReciprocal) | 0;

const a = y * radialCount + x + offset;
const b = a + 1;
const c = a + radialCount;
const d = c + 1;

indices[indicesOffset++] = b;
indices[indicesOffset++] = a;
indices[indicesOffset++] = d;
indices[indicesOffset++] = a;
indices[indicesOffset++] = c;
indices[indicesOffset++] = d;
}
}
}