You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/RenderDll/Common/RendElements/MeshUtil.cpp

579 lines
18 KiB
C++

/*
* 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 "RenderDll_precompiled.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([[maybe_unused]] float radius, int polySides, int ringCount, bool capInnerHole, const ColorF& clr, float* ringPosArray, std::vector<SVF_P3F_C4B_T2F>& vertOut, std::vector<uint16>& 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<SVF_P3F_C4B_T2F>& vertOut, std::vector<uint16>& 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<SVF_P3F_C4B_T2F>& vertOut, std::vector<uint16>& 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<SVF_P3F_C4B_T2F>& vertOut, std::vector<uint16>& 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<SVF_P3F_C4B_T2F>& vertOut, std::vector<uint16>& 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<SVF_P3F_C4B_T2F> beamVertOut;
std::vector<uint16> 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<SVF_P3F_C4B_T2F>& vertOut, std::vector<uint16>& 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<SpritePoint>& spriteList, float aspectRatio, bool packPivotPos, std::vector<SVF_P3F_C4B_T2F>& vertOut, [[maybe_unused]] std::vector<uint16>& 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<uint16>& 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<SVF_P3F_C4B_T2F>& vertOut, std::vector<uint16>& 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;
}
}
}