/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include "MeshUtil.h" #include "../../../CryCommon/VertexFormats.h" #include "../../../CryCommon/MTPseudoRandom.h" using namespace stable_rand; CMTRand_int32 randGen; void stable_rand::setSeed(uint32 seed) {randGen.seed(seed); } float stable_rand::randUnit() { return randGen.GenerateFloat() * 2.f - 1.f; } float stable_rand::randPositive() { return randGen.GenerateFloat(); } float stable_rand::randBias(float noise) { return 1.f + randUnit() * noise; } float computeFade(float current, float start, float end, float fadingLength) { if (current < start - fadingLength) { return 0; } else if (current < start) { return 1 - (start - current) / fadingLength; } else if (current > end + fadingLength) { return 0; } else if (current > end) { return 1 - (current - end) / fadingLength; } else { return 1; } } Vec2 rotate(float x, float y, float rad) { Vec2 ret; float cosRad = cos(rad); float sinRad = sin(rad); ret.x = x * cosRad - y * sinRad; ret.y = y * cosRad + x * sinRad; return ret; } void MeshUtil::GenDisk(float radius, int polySides, int ringCount, bool capInnerHole, const ColorF& clr, float* ringPosArray, std::vector& vertOut, std::vector& idxOut) { bool gCollectPosArray = false; if (ringPosArray == NULL) { // create a uniformly spaced setting ringPosArray = new float[ringCount]; float ringCountf = (float)ringCount; for (int i = 0; i < ringCount; i++) { ringPosArray[i] = (i + 1) / ringCountf; } gCollectPosArray = true; } // Current strategy: whether or not capping, center pivot is always generated only not indexed in later case. // UV mapping follows polar representation: u for thou, v for theta DWORD dclr = clr.pack_argb8888(); vertOut.resize(polySides * ringCount + 1); // rings and the center SVF_P3F_C4B_T2F& centerV = vertOut[0]; centerV.xyz = Vec3(0, 0, 0); centerV.st = Vec2(0, 0); centerV.color.dcolor = dclr; // Generate all vertices starting from inner rings: for (int i = 0; i < polySides; i++) { float theta = i / (float)polySides; float angle = 2 * PI * theta; float x = cos(angle); float y = sin(angle); //float noise = randUnit() * noiseStrength; for (int r = 0; r < ringCount; r++) { float curRingRadius = ringPosArray[r]; float thou = (r + 1) / (float)(ringCount); //curRingRadius += noise; // apply translation noise SVF_P3F_C4B_T2F& vert = vertOut[i * ringCount + r + 1]; vert.xyz.Set(x * curRingRadius, y * curRingRadius, 0); vert.st.set(thou, theta); vert.color.dcolor = dclr; } } // Rings' Indices; int holeCapperIdxCount = 0; if (capInnerHole) { holeCapperIdxCount = polySides * 3; } idxOut.resize(polySides * (ringCount - 1) * 6 + holeCapperIdxCount); for (int i = 0; i < polySides; i++) { int baseVertIdx0 = i * ringCount; for (int r = 0; r < ringCount - 1; r++) { // counter clock wise: int a = baseVertIdx0 + r; int b = a + 1; int c = (a + ringCount + 1) % vertOut.size(); int d = (c - 1) % vertOut.size(); int baseIdx = (i * (ringCount - 1) + r) * 6 + holeCapperIdxCount; idxOut[baseIdx] = b; idxOut[baseIdx + 1] = c; idxOut[baseIdx + 2] = a; idxOut[baseIdx + 3] = a; idxOut[baseIdx + 4] = c; idxOut[baseIdx + 5] = d; } } // indices for the capper: if (capInnerHole) { // inner circle: // vert: [nPolySide][nRing] // index: [nPolySide][3] for (int i = 0; i < polySides; i++) { int baseIdx = i * 3; idxOut[baseIdx] = 0; idxOut[baseIdx + 1] = 1 + i * ringCount; idxOut[baseIdx + 2] = (i != (polySides - 1)) ? (2 + i * ringCount) : 1; } } if (gCollectPosArray) { delete [] ringPosArray; } } //Generate a hoop which consists of specified number of inscribe circles void MeshUtil::GenHoop(float radius, int polySides, float thickness, int ringCount, const ColorF& clr, float noiseStrength, int noiseSeed, float startAngle, float endAngle, float fadeAngle, std::vector& vertOut, std::vector& idxOut) { // use inscribe circles. Tangent point is at the origin: setSeed(noiseSeed); polySides *= 2; if (ringCount < 2) { // Log("Warning: Illegal ring count"); ringCount = 2; } if (endAngle < startAngle) { float tmp = endAngle; endAngle = startAngle; startAngle = tmp; } float startAngleRad = startAngle / 180 * PI; float endAngleRad = endAngle / 180 * PI; float fadeAngleRad = fadeAngle / 180 * PI; // Verts: vertOut.resize(polySides * ringCount); float innerRadius = radius - thickness; float deltaRadius = thickness / (float)(ringCount - 1); float deltaTheta = 1 / (float)polySides; for (int i = 0; i < polySides; i++) { float spikeThetaWidth = 0.18f * deltaTheta; float thetaLinear = i / (float)polySides + randUnit() * 0.5f * deltaTheta - spikeThetaWidth; // old linear mapping float k = 2.6f * thetaLinear - 1.3f; float theta = 0.5f + 0.5f * tanf(k) / tanf(1.3f); float angle = 2 * PI * theta; float x = cos(angle); float y = sin(angle); float fade = computeFade(angle, startAngleRad, endAngleRad, fadeAngleRad); float noise = randUnit() * noiseStrength; for (int r = 0; r < ringCount; r++) { float curRingRadius = innerRadius + r * deltaRadius; float thou = r / (float)(ringCount - 1); curRingRadius += noise; // apply translation noise SVF_P3F_C4B_T2F& vert = vertOut[i * ringCount + r]; vert.xyz.Set((x - 1) * curRingRadius, y * curRingRadius, 0); vert.st.set(thou, theta); ColorF c(clr.r, clr.g, clr.b, clr.a * fade); vert.color.dcolor = c.pack_argb8888(); } // generate spikes i++; theta += 2 * spikeThetaWidth; angle = 2 * PI * theta; x = cos(angle); y = sin(angle); for (int r = 0; r < ringCount; r++) { float curRingRadius = innerRadius + r * deltaRadius; float thou = r / (float)(ringCount - 1); curRingRadius += noise; // apply translation noise SVF_P3F_C4B_T2F& vert = vertOut[i * ringCount + r]; vert.xyz.Set((x - 1) * curRingRadius, y * curRingRadius, 0); vert.st.set(thou, theta); ColorF c(clr.r, clr.g, clr.b, clr.a * fade); vert.color.dcolor = c.pack_argb8888(); } } // Indices; idxOut.resize(polySides * (ringCount - 1) * 6); for (int i = 0; i < polySides; i++) { int baseVertIdx0 = i * ringCount; for (int r = 0; r < ringCount - 1; r++) { // counter clock wise: int a = baseVertIdx0 + r; int b = a + 1; int c = (a + ringCount + 1) % vertOut.size(); int d = (c - 1) % vertOut.size(); int baseIdx = (i * (ringCount - 1) + r) * 6; idxOut[baseIdx] = b; idxOut[baseIdx + 1] = c; idxOut[baseIdx + 2] = a; idxOut[baseIdx + 3] = a; idxOut[baseIdx + 4] = c; idxOut[baseIdx + 5] = d; } } } void MeshUtil::GenTrapezoidFan(int numSideVert, float radius, float startAngleDegree, float endAngleDegree, float centerThickness, const ColorF& clr, std::vector& vertOut, std::vector& idxOut) { if (endAngleDegree < startAngleDegree) { float tmp = endAngleDegree; endAngleDegree = startAngleDegree; startAngleDegree = tmp; } float startAngle = startAngleDegree / 180 * PI; float endAngle = endAngleDegree / 180 * PI; float midAngle = 0.5f * (startAngle + endAngle); float halfAngleRange = 0.5f * (endAngle - startAngle); float angleDelta = (endAngle - startAngle) / (numSideVert - 1); vertOut.resize(numSideVert * 2); float dirX = cos(midAngle); float dirY = sin(midAngle); DWORD dclr = clr.pack_argb8888(); for (int i = 0; i < numSideVert; i++) { float relativeAngle = i * angleDelta; float theta = startAngle + relativeAngle; float x = cos(theta); float y = sin(theta); float angleRatio = (theta - midAngle) / halfAngleRange; float u = i / (float)(numSideVert - 1); SVF_P3F_C4B_T2F& vert = vertOut[i * 2]; float yRela = angleRatio; vert.xyz.Set((-yRela * dirY) * centerThickness, (yRela * dirX) * centerThickness, 0); // swap x,y and negate and translate vert.st.set(u, 0); vert.color.dcolor = dclr; // top: SVF_P3F_C4B_T2F& vert2 = vertOut[i * 2 + 1]; vert2.xyz.Set(x * radius, y * radius, 0); vert2.st.set(u, 1); vert2.color.dcolor = dclr; } idxOut.resize((numSideVert - 1) * 6); for (int i = 0; i < numSideVert - 1; i++) { int baseVertIdx = i; int a = baseVertIdx * 2; int b = a + 2; int c = b + 1; int d = a + 1; int baseIdx = i * 6; idxOut[baseIdx] = b; idxOut[baseIdx + 1] = c; idxOut[baseIdx + 2] = a; idxOut[baseIdx + 3] = a; idxOut[baseIdx + 4] = c; idxOut[baseIdx + 5] = d; } } void MeshUtil::GenFan(int numSideVert, float radius, float startAngleDegree, float endAngleDegree, const ColorF& clr, std::vector& vertOut, std::vector& idxOut) { if (endAngleDegree < startAngleDegree) { float tmp = endAngleDegree; endAngleDegree = startAngleDegree; startAngleDegree = tmp; } float startAngle = startAngleDegree / 180 * PI; float endAngle = endAngleDegree / 180 * PI; float angleDelta = (endAngle - startAngle) / (numSideVert - 1); vertOut.resize(numSideVert + 1); DWORD dclr = clr.pack_argb8888(); // center SVF_P3F_C4B_T2F& centerVert = vertOut[0]; centerVert.xyz.Set(0, 0, 0); centerVert.st.set(0, 0); centerVert.color.dcolor = dclr; for (int i = 0; i < numSideVert; i++) { float relativeAngle = i * angleDelta; float theta = startAngle + relativeAngle; float x = cos(theta); float y = sin(theta); float xLocal = cos(relativeAngle); float yLocal = sin(relativeAngle); // top: SVF_P3F_C4B_T2F& vert2 = vertOut[i + 1]; vert2.xyz.Set(x * radius, y * radius, 0); vert2.st.set(xLocal, yLocal); vert2.color.dcolor = dclr; } idxOut.resize((numSideVert - 1) * 3); for (int i = 0; i < numSideVert - 1; i++) { int baseVertIdx = i + 1; int a = 0; int b = baseVertIdx; int c = baseVertIdx + 1; int baseIdx = i * 3; idxOut[baseIdx] = a; idxOut[baseIdx + 1] = c; idxOut[baseIdx + 2] = b; } } // A shaft/falloff-fan is a simple coarse fan-shaped mesh with odd number of side-vertices. // It's UV mapping spans a strict rectangular shape (x:[0,1,0] y:[0,1]). // On top of the fan shape, there's a concentric beam which simulates the spike effect. void MeshUtil::GenShaft(float radius, float centerThickness, int complexity, float startAngleDegree, float endAngleDegree, const ColorF& clr, std::vector& vertOut, std::vector& idxOut) { if (complexity <= 1) { return; } int numSideVert = complexity; GenTrapezoidFan(numSideVert, radius, startAngleDegree, endAngleDegree, centerThickness, clr, vertOut, idxOut); float midAngleDegree = 0.5f * (startAngleDegree + endAngleDegree); float halfAngleRangeDegree = 0.5f * (endAngleDegree - startAngleDegree); const float beamWidthFactor = 0.1f; float beamHalfAngle = halfAngleRangeDegree * beamWidthFactor; std::vector beamVertOut; std::vector beamIdxOut; ColorF clr2 = clr * 1.1f; GenTrapezoidFan(2, radius, midAngleDegree - beamHalfAngle, midAngleDegree + beamHalfAngle, centerThickness, clr2, beamVertOut, beamIdxOut); } void MeshUtil::GenStreak(float dir, float radius, float thickness, const ColorF& clr, std::vector& vertOut, std::vector& idxOut) { static const int polySides = 24; Matrix33 rotMx; rotMx.SetRotationAA(dir * PI, Vec3(0, 0, 1)); DWORD dclr = clr.pack_argb8888(); vertOut.resize(polySides + 1); SVF_P3F_C4B_T2F& centerV = vertOut[0]; centerV.xyz = Vec3(0, 0, 0); centerV.st = Vec2(0, 0); centerV.color.dcolor = dclr; // Generate all vertices for (int i = 0; i < polySides; i++) { float theta = i / (float)polySides; float angle = 2 * PI * theta; float x = cos(angle); float y = sin(angle) * thickness; Vec2 scale(x, y); scale = scale * rotMx; x = scale.x; y = scale.y; SVF_P3F_C4B_T2F& vert = vertOut[i + 1]; vert.xyz.Set(x * radius, y * radius, 0); vert.st.set(1.0f, theta); vert.color.dcolor = dclr; } // indices; int centerIdxCount = polySides * 3; idxOut.resize(centerIdxCount); for (int i = 0; i < polySides; i++) { int baseIdx = i * 3; idxOut[baseIdx] = 0; idxOut[baseIdx + 1] = 1 + i; idxOut[baseIdx + 2] = (i != (polySides - 1)) ? (2 + i) : 1; } } void MeshUtil::GenSprites(std::vector& spriteList, float aspectRatio, bool packPivotPos, std::vector& vertOut, std::vector& idxOut) { vertOut.resize(spriteList.size() * 4); for (unsigned int i = 0; i < spriteList.size(); i++) { SpritePoint& sprite = spriteList[i]; Vec2& pivot = sprite.pos; float size = sprite.size; float rot = sprite.rotation; ColorF clr = sprite.color; float radius = size; DWORD dclr = clr.pack_argb8888(); int vertIdx = i * 4; SVF_P3F_C4B_T2F& vert0 = vertOut[vertIdx]; Vec2 p = rotate(-radius, -radius, rot); float zComp; if (packPivotPos) { zComp = (float)(((int)floor((pivot.x * 0.5f + 0.5f) * 4095) << 12) | ((int)floor((pivot.y * 0.5f + 0.5f) * 4095))) / (4096 * 4096); } else { zComp = rot; } vert0.xyz.Set(p.x / aspectRatio + pivot.x, p.y + pivot.y, zComp); vert0.color.dcolor = dclr; vert0.st.set(0, 0); SVF_P3F_C4B_T2F& vert1 = vertOut[vertIdx + 1]; p = rotate(radius, -radius, rot); vert1.xyz.Set(p.x / aspectRatio + pivot.x, p.y + pivot.y, zComp); vert1.color.dcolor = dclr; vert1.st.set(1, 0); SVF_P3F_C4B_T2F& vert2 = vertOut[vertIdx + 2]; p = rotate(radius, radius, rot); vert2.xyz.Set(p.x / aspectRatio + pivot.x, p.y + pivot.y, zComp); vert2.color.dcolor = dclr; vert2.st.set(1, 1); SVF_P3F_C4B_T2F& vert3 = vertOut[vertIdx + 3]; p = rotate(-radius, radius, rot); vert3.xyz.Set(p.x / aspectRatio + pivot.x, p.y + pivot.y, zComp); vert3.color.dcolor = dclr; vert3.st.set(0, 1); } } void MeshUtil::TrianglizeQuadIndices(int quadCount, std::vector& idxOut) { idxOut.resize(quadCount * 6); for (int i = 0; i < quadCount; i++) { int baseVertIdx = i * 4; int a = baseVertIdx; int b = a + 1; int c = b + 1; int d = c + 1; int baseIdx = i * 6; idxOut[baseIdx] = b; idxOut[baseIdx + 1] = c; idxOut[baseIdx + 2] = a; idxOut[baseIdx + 3] = c; idxOut[baseIdx + 4] = a; idxOut[baseIdx + 5] = d; } } void MeshUtil::GenScreenTile(float x0, float y0, float x1, float y1, ColorF clr, int rowCount, int columnCount, std::vector& vertOut, std::vector& idxOut) { int yCount = rowCount + 1; int xCount = columnCount + 1; vertOut.resize(xCount * yCount); DWORD dclr = clr.pack_argb8888(); float xSpan = x1 - x0; float ySpan = y1 - y0; float xDelta = xSpan / columnCount; float yDelta = ySpan / rowCount; float uDelta = 1.f / columnCount; float vDelta = 1.f / rowCount; for (int j = 0; j < yCount; j++) { for (int i = 0; i < xCount; i++) { SVF_P3F_C4B_T2F& vert = vertOut[ i + j * xCount ]; vert.xyz.Set(x0 + xDelta * i, y0 + yDelta * j, 0); vert.st.set(uDelta * i, vDelta * j); vert.color.dcolor = dclr; } } idxOut.resize(rowCount * columnCount * 6); for (int j = 0; j < rowCount; j++) { for (int i = 0; i < columnCount; i++) { int a = i + j * xCount; int b = a + 1; int c = (i + 1) + (j + 1) * xCount; int d = c - 1; int baseIdx = (i + j * columnCount) * 6; idxOut[ baseIdx + 0 ] = b; idxOut[ baseIdx + 1 ] = c; idxOut[ baseIdx + 2 ] = a; idxOut[ baseIdx + 3 ] = a; idxOut[ baseIdx + 4 ] = c; idxOut[ baseIdx + 5 ] = d; } } }