Files
Justin Marshall 8c4a087aa9 Filesystem update.
2026-05-09 07:54:14 -07:00

248 lines
5.8 KiB
C++

/*
===========================================================================
IceTech GPL Source Code
Copyright (C) 2026 Justin Marshall
This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
IceTech Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
IceTech Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with IceTech Source Code. If not, see <http://www.gnu.org/licenses/>.
If you have questions concerning this license or the applicable additional terms, you may contact in writing Justin Marshall, justinmarshall20@gmail.com
===========================================================================
*/
#include <vector>
#include <cstring>
#include "tess/tess.h"
struct GLVertex
{
float px, py, pz;
float nx, ny, nz;
float u0, v0;
float u1, v1;
float r, g, b, a;
};
struct TessVertex
{
GLdouble coords[3];
GLVertex vtx;
};
struct TessContext
{
std::vector<GLVertex>* out = nullptr;
std::vector<TessVertex*>* allocated = nullptr;
GLenum currentPrim = 0;
std::vector<GLVertex> primVerts;
bool failed = false;
};
static void TessBeginCB(GLenum type, void* userData)
{
TessContext* ctx = reinterpret_cast<TessContext*>(userData);
ctx->currentPrim = type;
ctx->primVerts.clear();
}
static void TessEndCB(void* userData)
{
TessContext* ctx = reinterpret_cast<TessContext*>(userData);
if (ctx->failed)
return;
switch (ctx->currentPrim)
{
case GL_TRIANGLES:
if ((ctx->primVerts.size() % 3) != 0)
{
ctx->failed = true;
return;
}
for (size_t i = 0; i < ctx->primVerts.size(); i += 3)
{
ctx->out->push_back(ctx->primVerts[i + 0]);
ctx->out->push_back(ctx->primVerts[i + 1]);
ctx->out->push_back(ctx->primVerts[i + 2]);
}
break;
case GL_TRIANGLE_FAN:
if (ctx->primVerts.size() < 3)
break;
for (size_t i = 1; i + 1 < ctx->primVerts.size(); ++i)
{
ctx->out->push_back(ctx->primVerts[0]);
ctx->out->push_back(ctx->primVerts[i]);
ctx->out->push_back(ctx->primVerts[i + 1]);
}
break;
case GL_TRIANGLE_STRIP:
if (ctx->primVerts.size() < 3)
break;
for (size_t i = 0; i + 2 < ctx->primVerts.size(); ++i)
{
if ((i & 1) == 0)
{
ctx->out->push_back(ctx->primVerts[i + 0]);
ctx->out->push_back(ctx->primVerts[i + 1]);
ctx->out->push_back(ctx->primVerts[i + 2]);
}
else
{
ctx->out->push_back(ctx->primVerts[i + 1]);
ctx->out->push_back(ctx->primVerts[i + 0]);
ctx->out->push_back(ctx->primVerts[i + 2]);
}
}
break;
default:
ctx->failed = true;
break;
}
ctx->primVerts.clear();
ctx->currentPrim = 0;
}
static void TessErrorCB(GLenum errorCode, void* userData)
{
(void)errorCode;
TessContext* ctx = reinterpret_cast<TessContext*>(userData);
ctx->failed = true;
}
static void TessVertexCB(void* vertexData, void* userData)
{
TessContext* ctx = reinterpret_cast<TessContext*>(userData);
TessVertex* tv = reinterpret_cast<TessVertex*>(vertexData);
if (!tv)
{
ctx->failed = true;
return;
}
ctx->primVerts.push_back(tv->vtx);
}
static void TessCombineCB(GLdouble newVertex[3],
void* neighborData[4],
GLfloat neighborWeight[4],
void** outData,
void* userData)
{
TessContext* ctx = reinterpret_cast<TessContext*>(userData);
TessVertex* nv = new TessVertex{};
nv->coords[0] = newVertex[0];
nv->coords[1] = newVertex[1];
nv->coords[2] = newVertex[2];
std::memset(&nv->vtx, 0, sizeof(nv->vtx));
nv->vtx.px = static_cast<float>(newVertex[0]);
nv->vtx.py = static_cast<float>(newVertex[1]);
nv->vtx.pz = static_cast<float>(newVertex[2]);
for (int i = 0; i < 4; ++i)
{
if (!neighborData[i])
continue;
TessVertex* src = reinterpret_cast<TessVertex*>(neighborData[i]);
const float w = neighborWeight[i];
nv->vtx.nx += src->vtx.nx * w;
nv->vtx.ny += src->vtx.ny * w;
nv->vtx.nz += src->vtx.nz * w;
nv->vtx.u0 += src->vtx.u0 * w;
nv->vtx.v0 += src->vtx.v0 * w;
nv->vtx.u1 += src->vtx.u1 * w;
nv->vtx.v1 += src->vtx.v1 * w;
nv->vtx.r += src->vtx.r * w;
nv->vtx.g += src->vtx.g * w;
nv->vtx.b += src->vtx.b * w;
nv->vtx.a += src->vtx.a * w;
}
ctx->allocated->push_back(nv);
*outData = nv;
}
void TessellatePolygon(const std::vector<GLVertex>& src, std::vector<GLVertex>& out)
{
out.clear();
if (src.size() < 3)
return;
GLUtesselator* tess = gluNewTess();
if (!tess)
return;
std::vector<TessVertex*> allocated;
allocated.reserve(src.size() + 8);
TessContext ctx;
ctx.out = &out;
ctx.allocated = &allocated;
gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
gluTessProperty(tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE);
gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (_GLUfuncptr)&TessBeginCB);
gluTessCallback(tess, GLU_TESS_END_DATA, (_GLUfuncptr)&TessEndCB);
gluTessCallback(tess, GLU_TESS_ERROR_DATA, (_GLUfuncptr)&TessErrorCB);
gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (_GLUfuncptr)&TessVertexCB);
gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (_GLUfuncptr)&TessCombineCB);
gluTessBeginPolygon(tess, &ctx);
gluTessBeginContour(tess);
for (const GLVertex& v : src)
{
TessVertex* tv = new TessVertex{};
tv->coords[0] = static_cast<GLdouble>(v.px);
tv->coords[1] = static_cast<GLdouble>(v.py);
tv->coords[2] = static_cast<GLdouble>(v.pz);
tv->vtx = v;
allocated.push_back(tv);
gluTessVertex(tess, tv->coords, tv);
}
gluTessEndContour(tess);
gluTessEndPolygon(tess);
gluDeleteTess(tess);
for (TessVertex* p : allocated)
delete p;
if (ctx.failed)
out.clear();
}