//---------------------------------------------------------------------------- TriMesh* StandardMesh::Sphere (int zSamples, int radialSamples, float radius) { int zsm1 = zSamples-1, zsm2 = zSamples-2, zsm3 = zSamples-3; int rsp1 = radialSamples+1; int numVertices = zsm2*rsp1 + 2; int numTriangles = 2*zsm2*radialSamples; int numIndices = 3*numTriangles; int stride = mVFormat->GetStride(); // Create a vertex buffer. VertexBuffer* vbuffer = new0 VertexBuffer(numVertices, stride, mUsage); VertexBufferAccessor vba(mVFormat, vbuffer); // Generate geometry. float invRS = 1.0f/(float)radialSamples; float zFactor = 2.0f/(float)zsm1; int r, z, zStart, i, unit; Float2 tcoord; // Generate points on the unit circle to be used in computing the mesh // points on a cylinder slice. float* sn = new1<float>(rsp1); float* cs = new1<float>(rsp1); for (r = 0; r < radialSamples; ++r) { float angle = Mathf::TWO_PI*invRS*r; cs[r] = Mathf::Cos(angle); sn[r] = Mathf::Sin(angle); } sn[radialSamples] = sn[0]; cs[radialSamples] = cs[0]; // Generate the cylinder itself. for (z = 1, i = 0; z < zsm1; ++z) { float zFraction = -1.0f + zFactor*z; // in (-1,1) float zValue = radius*zFraction; // Compute center of slice. APoint sliceCenter(0.0f, 0.0f, zValue); // Compute radius of slice. float sliceRadius = Mathf::Sqrt(Mathf::FAbs( radius*radius - zValue*zValue)); // Compute slice vertices with duplication at endpoint. AVector normal; int save = i; for (r = 0; r < radialSamples; ++r) { float radialFraction = r*invRS; // in [0,1) AVector radial(cs[r], sn[r], 0.0f); vba.Position<Float3>(i) = sliceCenter + sliceRadius*radial; if (mHasNormals) { normal = vba.Position<Float3>(i); normal.Normalize(); if (mInside) { vba.Normal<Float3>(i) = -normal; } else { vba.Normal<Float3>(i) = normal; } } tcoord[0] = radialFraction; tcoord[1] = 0.5f*(zFraction + 1.0f); for (unit = 0; unit < MAX_UNITS; ++unit) { if (mHasTCoords[unit]) { vba.TCoord<Float2>(unit, i) = tcoord; } } ++i; } vba.Position<Float3>(i) = vba.Position<Float3>(save); if (mHasNormals) { vba.Normal<Float3>(i) = vba.Normal<Float3>(save); } tcoord[0] = 1.0f; tcoord[1] = 0.5f*(zFraction + 1.0f); for (unit = 0; unit < MAX_UNITS; ++unit) { if (mHasTCoords[unit]) { vba.TCoord<Float2>(unit, i) = tcoord; } } ++i; } // south pole vba.Position<Float3>(i) = Float3(0.0f, 0.0f, -radius); if (mHasNormals) { if (mInside) { vba.Normal<Float3>(i) = Float3(0.0f, 0.0f, 1.0f); } else { vba.Normal<Float3>(i) = Float3(0.0f, 0.0f, -1.0f); } } tcoord = Float2(0.5f, 0.5f); for (unit = 0; unit < MAX_UNITS; ++unit) { if (mHasTCoords[unit]) { vba.TCoord<Float2>(unit, i) = tcoord; } } ++i; // north pole vba.Position<Float3>(i) = Float3(0.0f, 0.0f, radius); if (mHasNormals) { if (mInside) { vba.Normal<Float3>(i) = Float3(0.0f, 0.0f, -1.0f); } else { vba.Normal<Float3>(i) = Float3(0.0f, 0.0f, 1.0f); } } tcoord = Float2(0.5f, 1.0f); for (unit = 0; unit < MAX_UNITS; ++unit) { if (mHasTCoords[unit]) { vba.TCoord<Float2>(unit, i) = tcoord; } } ++i; TransformData(vba); // Generate indices. IndexBuffer* ibuffer = new0 IndexBuffer(numIndices, 4, mUsage); int* indices = (int*)ibuffer->GetData(); for (z = 0, zStart = 0; z < zsm3; ++z) { int i0 = zStart; int i1 = i0 + 1; zStart += rsp1; int i2 = zStart; int i3 = i2 + 1; for (i = 0; i < radialSamples; ++i, indices += 6) { if (mInside) { indices[0] = i0++; indices[1] = i2; indices[2] = i1; indices[3] = i1++; indices[4] = i2++; indices[5] = i3++; } else // inside view { indices[0] = i0++; indices[1] = i1; indices[2] = i2; indices[3] = i1++; indices[4] = i3++; indices[5] = i2++; } } } // south pole triangles int numVerticesM2 = numVertices - 2; for (i = 0; i < radialSamples; ++i, indices += 3) { if (mInside) { indices[0] = i; indices[1] = i + 1; indices[2] = numVerticesM2; } else { indices[0] = i; indices[1] = numVerticesM2; indices[2] = i + 1; } } // north pole triangles int numVerticesM1 = numVertices-1, offset = zsm3*rsp1; for (i = 0; i < radialSamples; ++i, indices += 3) { if (mInside) { indices[0] = i + offset; indices[1] = numVerticesM1; indices[2] = i + 1 + offset; } else { indices[0] = i + offset; indices[1] = i + 1 + offset; indices[2] = numVerticesM1; } } delete1(cs); delete1(sn); // The duplication of vertices at the seam cause the automatically // generated bounding volume to be slightly off center. Reset the bound // to use the true information. TriMesh* mesh = new0 TriMesh(mVFormat, vbuffer, ibuffer); mesh->GetModelBound().SetCenter(APoint::ORIGIN); mesh->GetModelBound().SetRadius(radius); return mesh; }
//---------------------------------------------------------------------------- TriMesh* StandardMesh::Torus (int circleSamples, int radialSamples, float outerRadius, float innerRadius) { int numVertices = (circleSamples+1)*(radialSamples+1); int numTriangles = 2*circleSamples*radialSamples; int numIndices = 3*numTriangles; int stride = mVFormat->GetStride(); // Create a vertex buffer. VertexBuffer* vbuffer = new0 VertexBuffer(numVertices, stride, mUsage); VertexBufferAccessor vba(mVFormat, vbuffer); // Generate geometry. float invCS = 1.0f/(float)circleSamples; float invRS = 1.0f/(float)radialSamples; int c, r, i, unit; Float2 tcoord; // Generate the cylinder itself. for (c = 0, i = 0; c < circleSamples; ++c) { // Compute center point on torus circle at specified angle. float circleFraction = c*invCS; // in [0,1) float theta = Mathf::TWO_PI*circleFraction; float cosTheta = Mathf::Cos(theta); float sinTheta = Mathf::Sin(theta); AVector radial(cosTheta, sinTheta, 0.0f); AVector torusMiddle = outerRadius*radial; // Compute slice vertices with duplication at endpoint. int save = i; for (r = 0; r < radialSamples; ++r) { float radialFraction = r*invRS; // in [0,1) float phi = Mathf::TWO_PI*radialFraction; float cosPhi = Mathf::Cos(phi); float sinPhi = Mathf::Sin(phi); AVector normal = cosPhi*radial + sinPhi*AVector::UNIT_Z; vba.Position<Float3>(i) = torusMiddle + innerRadius*normal; if (mHasNormals) { if (mInside) { vba.Normal<Float3>(i) = -normal; } else { vba.Normal<Float3>(i) = normal; } } tcoord = Float2(radialFraction, circleFraction); for (unit = 0; unit < MAX_UNITS; ++unit) { if (mHasTCoords[unit]) { vba.TCoord<Float2>(unit, i) = tcoord; } } ++i; } vba.Position<Float3>(i) = vba.Position<Float3>(save); if (mHasNormals) { vba.Normal<Float3>(i) = vba.Normal<Float3>(save); } tcoord = Float2(1.0f, circleFraction); for (unit = 0; unit < MAX_UNITS; ++unit) { if (mHasTCoords[unit]) { vba.TCoord<Float2>(unit, i) = tcoord; } } ++i; } // Duplicate the cylinder ends to form a torus. for (r = 0; r <= radialSamples; ++r, ++i) { vba.Position<Float3>(i) = vba.Position<Float3>(r); if (mHasNormals) { vba.Normal<Float3>(i) = vba.Normal<Float3>(r); } for (unit = 0; unit < MAX_UNITS; ++unit) { if (mHasTCoords[unit]) { vba.TCoord<Float2>(unit, i) = Float2(vba.TCoord<Float2>(unit, r)[0], 1.0f); } } } TransformData(vba); // Generate indices. IndexBuffer* ibuffer = new0 IndexBuffer(numIndices, 4, mUsage); int* indices = (int*)ibuffer->GetData(); int cStart = 0; for (c = 0; c < circleSamples; ++c) { int i0 = cStart; int i1 = i0 + 1; cStart += radialSamples + 1; int i2 = cStart; int i3 = i2 + 1; for (i = 0; i < radialSamples; ++i, indices += 6) { if (mInside) { indices[0] = i0++; indices[1] = i1; indices[2] = i2; indices[3] = i1++; indices[4] = i3++; indices[5] = i2++; } else // inside view { indices[0] = i0++; indices[1] = i2; indices[2] = i1; indices[3] = i1++; indices[4] = i2++; indices[5] = i3++; } } } // The duplication of vertices at the seam cause the automatically // generated bounding volume to be slightly off center. Reset the bound // to use the true information. TriMesh* mesh = new0 TriMesh(mVFormat, vbuffer, ibuffer); mesh->GetModelBound().SetCenter(APoint::ORIGIN); mesh->GetModelBound().SetRadius(outerRadius); return mesh; }
//---------------------------------------------------------------------------- TriMesh* StandardMesh::Cylinder (int axisSamples, int radialSamples, float radius, float height, bool open) { TriMesh* mesh; int unit; Float2 tcoord; if (open) { int numVertices = axisSamples*(radialSamples+1); int numTriangles = 2*(axisSamples-1)*radialSamples; int numIndices = 3*numTriangles; int stride = mVFormat->GetStride(); // Create a vertex buffer. VertexBuffer* vbuffer = new0 VertexBuffer(numVertices, stride, mUsage); VertexBufferAccessor vba(mVFormat, vbuffer); // Generate geometry. float invRS = 1.0f/(float)radialSamples; float invASm1 = 1.0f/(float)(axisSamples-1); float halfHeight = 0.5f*height; int r, a, aStart, i; // Generate points on the unit circle to be used in computing the // mesh points on a cylinder slice. float* cs = new1<float>(radialSamples + 1); float* sn = new1<float>(radialSamples + 1); for (r = 0; r < radialSamples; ++r) { float angle = Mathf::TWO_PI*invRS*r; cs[r] = Mathf::Cos(angle); sn[r] = Mathf::Sin(angle); } cs[radialSamples] = cs[0]; sn[radialSamples] = sn[0]; // Generate the cylinder itself. for (a = 0, i = 0; a < axisSamples; ++a) { float axisFraction = a*invASm1; // in [0,1] float z = -halfHeight + height*axisFraction; // Compute center of slice. APoint sliceCenter(0.0f, 0.0f, z); // Compute slice vertices with duplication at endpoint. int save = i; for (r = 0; r < radialSamples; ++r) { float radialFraction = r*invRS; // in [0,1) AVector normal(cs[r], sn[r], 0.0f); vba.Position<Float3>(i) = sliceCenter + radius*normal; if (mHasNormals) { if (mInside) { vba.Normal<Float3>(i) = -normal; } else { vba.Normal<Float3>(i) = normal; } } tcoord = Float2(radialFraction, axisFraction); for (unit = 0; unit < MAX_UNITS; ++unit) { if (mHasTCoords[unit]) { vba.TCoord<Float2>(unit, i) = tcoord; } } ++i; } vba.Position<Float3>(i) = vba.Position<Float3>(save); if (mHasNormals) { vba.Normal<Float3>(i) = vba.Normal<Float3>(save); } tcoord = Float2(1.0f, axisFraction); for (unit = 0; unit < MAX_UNITS; ++unit) { if (mHasTCoords[unit]) { vba.TCoord<Float2>(0, i) = tcoord; } } ++i; } TransformData(vba); // Generate indices. IndexBuffer* ibuffer = new0 IndexBuffer(numIndices, 4, mUsage); int* indices = (int*)ibuffer->GetData(); for (a = 0, aStart = 0; a < axisSamples-1; ++a) { int i0 = aStart; int i1 = i0 + 1; aStart += radialSamples + 1; int i2 = aStart; int i3 = i2 + 1; for (i = 0; i < radialSamples; ++i, indices += 6) { if (mInside) { indices[0] = i0++; indices[1] = i2; indices[2] = i1; indices[3] = i1++; indices[4] = i2++; indices[5] = i3++; } else // outside view { indices[0] = i0++; indices[1] = i1; indices[2] = i2; indices[3] = i1++; indices[4] = i3++; indices[5] = i2++; } } } delete1(cs); delete1(sn); mesh = new0 TriMesh(mVFormat, vbuffer, ibuffer); } else { mesh = Sphere(axisSamples, radialSamples, radius); VertexBuffer* vbuffer = mesh->GetVertexBuffer(); int numVertices = vbuffer->GetNumElements(); VertexBufferAccessor vba(mVFormat, vbuffer); // Flatten sphere at poles. float hDiv2 = 0.5f*height; vba.Position<Float3>(numVertices-2)[2] = -hDiv2; // south pole vba.Position<Float3>(numVertices-1)[2] = +hDiv2; // north pole // Remap z-values to [-h/2,h/2]. float zFactor = 2.0f/(axisSamples-1); float tmp0 = radius*(-1.0f + zFactor); float tmp1 = 1.0f/(radius*(+1.0f - zFactor)); for (int i = 0; i < numVertices-2; ++i) { Float3& pos = vba.Position<Float3>(i); pos[2] = hDiv2*(-1.0f + tmp1*(pos[2] - tmp0)); float adjust = radius*Mathf::InvSqrt(pos[0]*pos[0] + pos[1]*pos[1]); pos[0] *= adjust; pos[1] *= adjust; } TransformData(vba); if (mHasNormals) { mesh->UpdateModelSpace(Visual::GU_NORMALS); } } // The duplication of vertices at the seam causes the automatically // generated bounding volume to be slightly off center. Reset the bound // to use the true information. float maxDist = Mathf::Sqrt(radius*radius + height*height); mesh->GetModelBound().SetCenter(APoint::ORIGIN); mesh->GetModelBound().SetRadius(maxDist); return mesh; }