| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501 |
- #include "primitives.h"
- #include <QVector3D>
- #include <cmath>
- #include <memory>
- #include <vector>
- namespace Render::GL {
- namespace {
- Mesh *createUnitCylinderMesh(int radialSegments) {
- const float radius = 1.0f;
- const float halfH = 0.5f;
- std::vector<Vertex> v;
- std::vector<unsigned int> idx;
- for (int y = 0; y <= 1; ++y) {
- float py = y ? halfH : -halfH;
- float vCoord = float(y);
- for (int i = 0; i <= radialSegments; ++i) {
- float u = float(i) / float(radialSegments);
- float ang = u * 6.28318530718f;
- float px = radius * std::cos(ang);
- float pz = radius * std::sin(ang);
- QVector3D n(px, 0.0f, pz);
- n.normalize();
- v.push_back({{px, py, pz}, {n.x(), n.y(), n.z()}, {u, vCoord}});
- }
- }
- int row = radialSegments + 1;
- for (int i = 0; i < radialSegments; ++i) {
- int a = 0 * row + i;
- int b = 0 * row + i + 1;
- int c = 1 * row + i + 1;
- int d = 1 * row + i;
- idx.push_back(a);
- idx.push_back(b);
- idx.push_back(c);
- idx.push_back(c);
- idx.push_back(d);
- idx.push_back(a);
- }
- int baseTop = (int)v.size();
- v.push_back({{0.0f, halfH, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.5f, 0.5f}});
- for (int i = 0; i <= radialSegments; ++i) {
- float u = float(i) / float(radialSegments);
- float ang = u * 6.28318530718f;
- float px = radius * std::cos(ang);
- float pz = radius * std::sin(ang);
- v.push_back({{px, halfH, pz},
- {0.0f, 1.0f, 0.0f},
- {0.5f + 0.5f * std::cos(ang), 0.5f + 0.5f * std::sin(ang)}});
- }
- for (int i = 1; i <= radialSegments; ++i) {
- idx.push_back(baseTop);
- idx.push_back(baseTop + i);
- idx.push_back(baseTop + i + 1);
- }
- int baseBot = (int)v.size();
- v.push_back({{0.0f, -halfH, 0.0f}, {0.0f, -1.0f, 0.0f}, {0.5f, 0.5f}});
- for (int i = 0; i <= radialSegments; ++i) {
- float u = float(i) / float(radialSegments);
- float ang = u * 6.28318530718f;
- float px = radius * std::cos(ang);
- float pz = radius * std::sin(ang);
- v.push_back({{px, -halfH, pz},
- {0.0f, -1.0f, 0.0f},
- {0.5f + 0.5f * std::cos(ang), 0.5f + 0.5f * std::sin(ang)}});
- }
- for (int i = 1; i <= radialSegments; ++i) {
- idx.push_back(baseBot);
- idx.push_back(baseBot + i + 1);
- idx.push_back(baseBot + i);
- }
- return new Mesh(v, idx);
- }
- Mesh *createUnitSphereMesh(int latSegments, int lonSegments) {
- const float r = 1.0f;
- std::vector<Vertex> v;
- std::vector<unsigned int> idx;
- for (int y = 0; y <= latSegments; ++y) {
- float vy = float(y) / float(latSegments);
- float phi = vy * 3.14159265358979323846f;
- float py = r * std::cos(phi);
- float pr = r * std::sin(phi);
- for (int x = 0; x <= lonSegments; ++x) {
- float vx = float(x) / float(lonSegments);
- float theta = vx * 6.28318530717958647692f;
- float px = pr * std::cos(theta);
- float pz = pr * std::sin(theta);
- QVector3D n(px, py, pz);
- n.normalize();
- v.push_back({{px, py, pz}, {n.x(), n.y(), n.z()}, {vx, vy}});
- }
- }
- int row = lonSegments + 1;
- for (int y = 0; y < latSegments; ++y) {
- for (int x = 0; x < lonSegments; ++x) {
- int a = y * row + x;
- int b = a + 1;
- int c = (y + 1) * row + x + 1;
- int d = (y + 1) * row + x;
- idx.push_back(a);
- idx.push_back(b);
- idx.push_back(c);
- idx.push_back(c);
- idx.push_back(d);
- idx.push_back(a);
- }
- }
- return new Mesh(v, idx);
- }
- Mesh *createUnitConeMesh(int radialSegments) {
- const float baseR = 1.0f;
- const float halfH = 0.5f;
- std::vector<Vertex> v;
- std::vector<unsigned int> idx;
- int apexIdx = 0;
- v.push_back({{0.0f, +halfH, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.5f, 1.0f}});
- for (int i = 0; i <= radialSegments; ++i) {
- float u = float(i) / float(radialSegments);
- float ang = u * 6.28318530718f;
- float px = baseR * std::cos(ang);
- float pz = baseR * std::sin(ang);
- QVector3D n(px, baseR, pz);
- n.normalize();
- v.push_back({{px, -halfH, pz}, {n.x(), n.y(), n.z()}, {u, 0.0f}});
- }
- for (int i = 1; i <= radialSegments; ++i) {
- idx.push_back(apexIdx);
- idx.push_back(i);
- idx.push_back(i + 1);
- }
- int baseCenter = (int)v.size();
- v.push_back({{0.0f, -halfH, 0.0f}, {0.0f, -1.0f, 0.0f}, {0.5f, 0.5f}});
- int baseStart = (int)v.size();
- for (int i = 0; i <= radialSegments; ++i) {
- float u = float(i) / float(radialSegments);
- float ang = u * 6.28318530718f;
- float px = baseR * std::cos(ang);
- float pz = baseR * std::sin(ang);
- v.push_back({{px, -halfH, pz},
- {0.0f, -1.0f, 0.0f},
- {0.5f + 0.5f * std::cos(ang), 0.5f + 0.5f * std::sin(ang)}});
- }
- for (int i = 0; i < radialSegments; ++i) {
- idx.push_back(baseCenter);
- idx.push_back(baseStart + i + 1);
- idx.push_back(baseStart + i);
- }
- return new Mesh(v, idx);
- }
- Mesh *createCapsuleMesh(int radialSegments, int heightSegments) {
- const float radius = 0.25f;
- const float halfH = 0.5f;
- std::vector<Vertex> verts;
- std::vector<unsigned int> idx;
- for (int y = 0; y <= heightSegments; ++y) {
- float v = float(y) / float(heightSegments);
- float py = -halfH + v * (2.0f * halfH);
- for (int i = 0; i <= radialSegments; ++i) {
- float u = float(i) / float(radialSegments);
- float ang = u * 6.2831853f;
- float px = radius * std::cos(ang);
- float pz = radius * std::sin(ang);
- QVector3D n(px, 0.0f, pz);
- n.normalize();
- verts.push_back({{px, py, pz}, {n.x(), n.y(), n.z()}, {u, v}});
- }
- }
- int row = radialSegments + 1;
- for (int y = 0; y < heightSegments; ++y) {
- for (int i = 0; i < radialSegments; ++i) {
- int a = y * row + i;
- int b = y * row + i + 1;
- int c = (y + 1) * row + i + 1;
- int d = (y + 1) * row + i;
- idx.push_back(a);
- idx.push_back(b);
- idx.push_back(c);
- idx.push_back(c);
- idx.push_back(d);
- idx.push_back(a);
- }
- }
- int baseTop = (int)verts.size();
- verts.push_back({{0.0f, halfH, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.5f, 0.5f}});
- for (int i = 0; i <= radialSegments; ++i) {
- float u = float(i) / float(radialSegments);
- float ang = u * 6.2831853f;
- float px = radius * std::cos(ang);
- float pz = radius * std::sin(ang);
- verts.push_back(
- {{px, halfH, pz},
- {0.0f, 1.0f, 0.0f},
- {0.5f + 0.5f * std::cos(ang), 0.5f + 0.5f * std::sin(ang)}});
- }
- for (int i = 1; i <= radialSegments; ++i) {
- idx.push_back(baseTop);
- idx.push_back(baseTop + i);
- idx.push_back(baseTop + i + 1);
- }
- int baseBot = (int)verts.size();
- verts.push_back({{0.0f, -halfH, 0.0f}, {0.0f, -1.0f, 0.0f}, {0.5f, 0.5f}});
- for (int i = 0; i <= radialSegments; ++i) {
- float u = float(i) / float(radialSegments);
- float ang = u * 6.2831853f;
- float px = radius * std::cos(ang);
- float pz = radius * std::sin(ang);
- verts.push_back(
- {{px, -halfH, pz},
- {0.0f, -1.0f, 0.0f},
- {0.5f + 0.5f * std::cos(ang), 0.5f + 0.5f * std::sin(ang)}});
- }
- for (int i = 1; i <= radialSegments; ++i) {
- idx.push_back(baseBot);
- idx.push_back(baseBot + i + 1);
- idx.push_back(baseBot + i);
- }
- return new Mesh(verts, idx);
- }
- float simpleHash(float seed) {
- float x = std::sin(seed * 12.9898f) * 43758.5453f;
- return x - std::floor(x);
- }
- Mesh *createUnitTorsoMesh(int radialSegments, int heightSegments) {
- const float halfH = 0.5f;
- const float TWO_PI = 6.28318530718f;
- const bool invertProfile = true;
- auto clampf = [](float x, float a, float b) {
- return x < a ? a : (x > b ? b : x);
- };
- auto smoothstep01 = [&](float x) {
- x = clampf(x, 0.0f, 1.0f);
- return x * x * (3.0f - 2.0f * x);
- };
- auto smoothBand = [&](float t, float a, float b) {
- float enter = smoothstep01((t - a) / (b - a + 1e-6f));
- float exit = smoothstep01((t - b) / (a - b - 1e-6f));
- float v = enter < exit ? enter : exit;
- return clampf(v, 0.0f, 1.0f);
- };
- struct Axes {
- float ax;
- float az;
- };
- struct Key {
- float t;
- Axes A;
- };
- const Key keys[] = {
- {0.10f, {0.98f, 0.92f}}, {0.20f, {1.02f, 0.96f}}, {0.45f, {0.82f, 0.78f}},
- {0.65f, {1.20f, 1.04f}}, {0.85f, {1.42f, 1.18f}}, {1.02f, {1.60f, 1.06f}},
- {1.10f, {1.20f, 0.96f}},
- };
- constexpr int KEY_COUNT = sizeof(keys) / sizeof(keys[0]);
- auto catRom = [](float p0, float p1, float p2, float p3, float u) {
- return 0.5f * ((2.0f * p1) + (-p0 + p2) * u +
- (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * u * u +
- (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * u * u * u);
- };
- auto sampleAxes = [&](float t) -> Axes {
- t = clampf(t, 0.0f, 1.0f);
- int i = 0;
- while (i + 1 < KEY_COUNT && t > keys[i + 1].t) {
- ++i;
- }
- int i0 = i > 0 ? i - 1 : 0;
- int i1 = i;
- int i2 = (i + 1 < KEY_COUNT) ? i + 1 : KEY_COUNT - 1;
- int i3 = (i + 2 < KEY_COUNT) ? i + 2 : KEY_COUNT - 1;
- float denom = (keys[i2].t - keys[i1].t);
- float u = denom > 1e-6f ? (t - keys[i1].t) / denom : 0.0f;
- u = clampf(u, 0.0f, 1.0f);
- float ax =
- catRom(keys[i0].A.ax, keys[i1].A.ax, keys[i2].A.ax, keys[i3].A.ax, u);
- float az =
- catRom(keys[i0].A.az, keys[i1].A.az, keys[i2].A.az, keys[i3].A.az, u);
- return {ax, az};
- };
- auto ellipseRadius = [](float a, float b, float ang) {
- float c = std::cos(ang), s = std::sin(ang);
- float denom = std::sqrt((b * b * c * c) + (a * a * s * s));
- return (a * b) / (denom + 1e-8f);
- };
- auto xOffsetAt = [&](float t) {
- return 0.02f * smoothBand(t, 0.6f, 0.95f) -
- 0.01f * smoothBand(t, 0.0f, 0.2f);
- };
- auto zOffsetAt = [&](float t) {
- float lordosis = -0.03f * smoothBand(t, 0.15f, 0.40f);
- float chestFwd = 0.035f * smoothBand(t, 0.65f, 0.85f);
- float neckBack = -0.015f * smoothBand(t, 0.90f, 1.00f);
- return lordosis + chestFwd + neckBack;
- };
- auto twistAt = [&](float t) { return 0.10f * smoothBand(t, 0.55f, 0.95f); };
- auto thetaScale = [&](float t, float ang) {
- float s = 0.0f;
- float sinA = std::sin(ang), cosA = std::cos(ang), cos2 = cosA * cosA;
- s += 0.07f * smoothBand(t, 0.68f, 0.88f) * std::max(0.0f, sinA);
- s += -0.03f * smoothBand(t, 0.65f, 0.90f) * std::max(0.0f, -sinA);
- s += 0.06f * smoothBand(t, 0.55f, 0.75f) * cos2;
- s += -0.02f * smoothBand(t, 0.40f, 0.55f) * cos2;
- s += 0.015f * smoothBand(t, 0.70f, 0.95f) * cosA;
- return 1.0f + s;
- };
- auto micro = [](float s) {
- float f = std::sin(s * 12.9898f) * 43758.5453f;
- return f - std::floor(f);
- };
- auto samplePos = [&](float t, float ang) -> QVector3D {
- float ts = invertProfile ? (1.0f - t) : t;
- Axes A = sampleAxes(ts);
- float twist = twistAt(ts);
- float th = ang + twist;
- float R = ellipseRadius(A.ax, A.az, th);
- float S = thetaScale(ts, th);
- float r = R * S;
- float px = r * std::cos(th);
- float pz = r * std::sin(th);
- px += xOffsetAt(ts);
- pz += zOffsetAt(ts);
- float py = -halfH + t * (2.0f * halfH);
- float s = (t * 37.0f) + (ang * 3.0f);
- px += (micro(s) - 0.5f) * 0.004f;
- pz += (micro(s + 1.23f) - 0.5f) * 0.004f;
- return QVector3D(px, py, pz);
- };
- std::vector<Vertex> v;
- std::vector<unsigned int> idx;
- v.reserve((radialSegments + 1) * (heightSegments + 1) +
- (radialSegments + 1) * 2 + 2);
- idx.reserve(radialSegments * heightSegments * 6 + radialSegments * 6);
- for (int y = 0; y <= heightSegments; ++y) {
- float t = float(y) / float(heightSegments);
- float dt = 1.0f / float(heightSegments);
- float vCoord = t;
- for (int i = 0; i <= radialSegments; ++i) {
- float u = float(i) / float(radialSegments);
- float ang = u * TWO_PI;
- float da = TWO_PI / float(radialSegments);
- QVector3D p = samplePos(t, ang);
- QVector3D pu = samplePos(t, ang + da);
- QVector3D pv = samplePos(clampf(t + dt, 0.0f, 1.0f), ang);
- QVector3D du = pu - p;
- QVector3D dv = pv - p;
- QVector3D n = QVector3D::crossProduct(du, dv);
- if (n.lengthSquared() > 0.0f) {
- n.normalize();
- }
- v.push_back({{p.x(), p.y(), p.z()}, {n.x(), n.y(), n.z()}, {u, vCoord}});
- }
- }
- int row = radialSegments + 1;
- for (int y = 0; y < heightSegments; ++y) {
- for (int i = 0; i < radialSegments; ++i) {
- int a = y * row + i;
- int b = y * row + i + 1;
- int c = (y + 1) * row + i + 1;
- int d = (y + 1) * row + i;
- idx.push_back(a);
- idx.push_back(b);
- idx.push_back(c);
- idx.push_back(c);
- idx.push_back(d);
- idx.push_back(a);
- }
- }
- {
- int baseTop = (int)v.size();
- float tTop = 1.0f;
- float tTopS = invertProfile ? (1.0f - tTop) : tTop;
- QVector3D cTop(xOffsetAt(tTopS), halfH, zOffsetAt(tTopS));
- v.push_back({{cTop.x(), cTop.y(), cTop.z()}, {0, 1, 0}, {0.5f, 0.5f}});
- for (int i = 0; i <= radialSegments; ++i) {
- float u = float(i) / float(radialSegments);
- float ang = u * TWO_PI;
- QVector3D p = samplePos(tTop, ang);
- v.push_back({{p.x(), p.y(), p.z()},
- {0, 1, 0},
- {0.5f + 0.5f * std::cos(ang), 0.5f + 0.5f * std::sin(ang)}});
- }
- for (int i = 1; i <= radialSegments; ++i) {
- idx.push_back(baseTop);
- idx.push_back(baseTop + i);
- idx.push_back(baseTop + i + 1);
- }
- }
- {
- int baseBot = (int)v.size();
- float tBot = 0.0f;
- float tBotS = invertProfile ? (1.0f - tBot) : tBot;
- QVector3D cBot(xOffsetAt(tBotS), -halfH, zOffsetAt(tBotS));
- v.push_back({{cBot.x(), cBot.y(), cBot.z()}, {0, -1, 0}, {0.5f, 0.5f}});
- for (int i = 0; i <= radialSegments; ++i) {
- float u = float(i) / float(radialSegments);
- float ang = u * TWO_PI;
- QVector3D p = samplePos(tBot, ang);
- v.push_back({{p.x(), p.y(), p.z()},
- {0, -1, 0},
- {0.5f + 0.5f * std::cos(ang), 0.5f + 0.5f * std::sin(ang)}});
- }
- for (int i = 1; i <= radialSegments; ++i) {
- idx.push_back(baseBot);
- idx.push_back(baseBot + i + 1);
- idx.push_back(baseBot + i);
- }
- }
- return new Mesh(v, idx);
- }
- } // namespace
- Mesh *getUnitCylinder(int radialSegments) {
- static std::unique_ptr<Mesh> s_mesh(createUnitCylinderMesh(radialSegments));
- return s_mesh.get();
- }
- Mesh *getUnitSphere(int latSegments, int lonSegments) {
- static std::unique_ptr<Mesh> s_mesh(
- createUnitSphereMesh(latSegments, lonSegments));
- return s_mesh.get();
- }
- Mesh *getUnitCone(int radialSegments) {
- static std::unique_ptr<Mesh> s_mesh(createUnitConeMesh(radialSegments));
- return s_mesh.get();
- }
- Mesh *getUnitCapsule(int radialSegments, int heightSegments) {
- static std::unique_ptr<Mesh> s_mesh(
- createCapsuleMesh(radialSegments, heightSegments));
- return s_mesh.get();
- }
- Mesh *getUnitTorso(int radialSegments, int heightSegments) {
- static std::unique_ptr<Mesh> s_mesh(
- createUnitTorsoMesh(radialSegments, heightSegments));
- return s_mesh.get();
- }
- } // namespace Render::GL
|