mirror of
https://github.com/ZDoom/Raze.git
synced 2024-12-25 11:40:54 +00:00
409 lines
9.5 KiB
C
409 lines
9.5 KiB
C
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <math.h>
|
||
|
#include <GLFW/glfw3.h>
|
||
|
#include "nanosvg.h"
|
||
|
#include "tesselator.h"
|
||
|
|
||
|
|
||
|
void* stdAlloc(void* userData, unsigned int size)
|
||
|
{
|
||
|
int* allocated = ( int*)userData;
|
||
|
TESS_NOTUSED(userData);
|
||
|
*allocated += (int)size;
|
||
|
return malloc(size);
|
||
|
}
|
||
|
|
||
|
void stdFree(void* userData, void* ptr)
|
||
|
{
|
||
|
TESS_NOTUSED(userData);
|
||
|
free(ptr);
|
||
|
}
|
||
|
|
||
|
struct MemPool
|
||
|
{
|
||
|
unsigned char* buf;
|
||
|
unsigned int cap;
|
||
|
unsigned int size;
|
||
|
};
|
||
|
|
||
|
void* poolAlloc( void* userData, unsigned int size )
|
||
|
{
|
||
|
struct MemPool* pool = (struct MemPool*)userData;
|
||
|
size = (size+0x7) & ~0x7;
|
||
|
if (pool->size + size < pool->cap)
|
||
|
{
|
||
|
unsigned char* ptr = pool->buf + pool->size;
|
||
|
pool->size += size;
|
||
|
return ptr;
|
||
|
}
|
||
|
printf("out of mem: %d < %d!\n", pool->size + size, pool->cap);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void poolFree( void* userData, void* ptr )
|
||
|
{
|
||
|
// empty
|
||
|
TESS_NOTUSED(userData);
|
||
|
TESS_NOTUSED(ptr);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Undefine this to see non-interactive heap allocator version.
|
||
|
#define USE_POOL 1
|
||
|
|
||
|
|
||
|
int run = 1;
|
||
|
int cdt = 0;
|
||
|
|
||
|
static void key(GLFWwindow* window, int key, int scancode, int action, int mods)
|
||
|
{
|
||
|
TESS_NOTUSED(scancode);
|
||
|
TESS_NOTUSED(mods);
|
||
|
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
|
||
|
glfwSetWindowShouldClose(window, GL_TRUE);
|
||
|
if (key == GLFW_KEY_SPACE && action == GLFW_PRESS)
|
||
|
run = !run;
|
||
|
if (key == GLFW_KEY_C && action == GLFW_PRESS)
|
||
|
cdt = !cdt;
|
||
|
}
|
||
|
|
||
|
int main(int argc, char *argv[])
|
||
|
{
|
||
|
GLFWwindow* window;
|
||
|
const GLFWvidmode* mode;
|
||
|
int width,height,i,j;
|
||
|
struct SVGPath* bg;
|
||
|
struct SVGPath* fg;
|
||
|
struct SVGPath* it;
|
||
|
float bounds[4],view[4],cx,cy,w,offx,offy;
|
||
|
float t = 0.0f, pt = 0.0f;
|
||
|
TESSalloc ma;
|
||
|
TESStesselator* tess = 0;
|
||
|
const int nvp = 3;
|
||
|
unsigned char* vflags = 0;
|
||
|
#ifdef USE_POOL
|
||
|
struct MemPool pool;
|
||
|
unsigned char mem[1024*1024];
|
||
|
int nvflags = 0;
|
||
|
#else
|
||
|
int allocated = 0;
|
||
|
double t0 = 0, t1 = 0;
|
||
|
#endif
|
||
|
TESS_NOTUSED(argc);
|
||
|
TESS_NOTUSED(argv);
|
||
|
|
||
|
if (!glfwInit()) {
|
||
|
printf("Failed to init GLFW.");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
printf("loading...\n");
|
||
|
// Load assets
|
||
|
bg = svgParseFromFile("../Bin/bg.svg");
|
||
|
if (!bg) return -1;
|
||
|
fg = svgParseFromFile("../Bin/fg.svg");
|
||
|
if (!fg) return -1;
|
||
|
|
||
|
printf("go...\n");
|
||
|
|
||
|
// Flip y
|
||
|
for (it = bg; it != NULL; it = it->next)
|
||
|
for (i = 0; i < it->npts; ++i)
|
||
|
it->pts[i*2+1] = -it->pts[i*2+1];
|
||
|
for (it = fg; it != NULL; it = it->next)
|
||
|
for (i = 0; i < it->npts; ++i)
|
||
|
it->pts[i*2+1] = -it->pts[i*2+1];
|
||
|
|
||
|
// Find FG bounds and center.
|
||
|
bounds[0] = bounds[2] = fg->pts[0];
|
||
|
bounds[1] = bounds[3] = fg->pts[1];
|
||
|
for (it = fg; it != NULL; it = it->next)
|
||
|
{
|
||
|
for (i = 0; i < it->npts; ++i)
|
||
|
{
|
||
|
const float x = it->pts[i*2];
|
||
|
const float y = it->pts[i*2+1];
|
||
|
if (x < bounds[0]) bounds[0] = x;
|
||
|
if (y < bounds[1]) bounds[1] = y;
|
||
|
if (x > bounds[2]) bounds[2] = x;
|
||
|
if (y > bounds[3]) bounds[3] = y;
|
||
|
}
|
||
|
}
|
||
|
cx = (bounds[0]+bounds[2])/2;
|
||
|
cy = (bounds[1]+bounds[3])/2;
|
||
|
for (it = fg; it != NULL; it = it->next)
|
||
|
{
|
||
|
for (i = 0; i < it->npts; ++i)
|
||
|
{
|
||
|
it->pts[i*2] -= cx;
|
||
|
it->pts[i*2+1] -= cy;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Find BG bounds.
|
||
|
bounds[0] = bounds[2] = bg->pts[0];
|
||
|
bounds[1] = bounds[3] = bg->pts[1];
|
||
|
for (it = bg; it != NULL; it = it->next)
|
||
|
{
|
||
|
for (i = 0; i < it->npts; ++i)
|
||
|
{
|
||
|
const float x = it->pts[i*2];
|
||
|
const float y = it->pts[i*2+1];
|
||
|
if (x < bounds[0]) bounds[0] = x;
|
||
|
if (y < bounds[1]) bounds[1] = y;
|
||
|
if (x > bounds[2]) bounds[2] = x;
|
||
|
if (y > bounds[3]) bounds[3] = y;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef USE_POOL
|
||
|
|
||
|
pool.size = 0;
|
||
|
pool.cap = sizeof(mem);
|
||
|
pool.buf = mem;
|
||
|
memset(&ma, 0, sizeof(ma));
|
||
|
ma.memalloc = poolAlloc;
|
||
|
ma.memfree = poolFree;
|
||
|
ma.userData = (void*)&pool;
|
||
|
ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices.
|
||
|
|
||
|
#else
|
||
|
|
||
|
t0 = glfwGetTime();
|
||
|
|
||
|
memset(&ma, 0, sizeof(ma));
|
||
|
ma.memalloc = stdAlloc;
|
||
|
ma.memfree = stdFree;
|
||
|
ma.userData = (void*)&allocated;
|
||
|
ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices.
|
||
|
|
||
|
tess = tessNewTess(&ma);
|
||
|
if (!tess)
|
||
|
return -1;
|
||
|
|
||
|
tessSetOption(tess, TESS_CONSTRAINED_DELAUNAY_TRIANGULATION, 1);
|
||
|
|
||
|
// Offset the foreground shape to center of the bg.
|
||
|
offx = (bounds[2]+bounds[0])/2;
|
||
|
offy = (bounds[3]+bounds[1])/2;
|
||
|
for (it = fg; it != NULL; it = it->next)
|
||
|
{
|
||
|
for (i = 0; i < it->npts; ++i)
|
||
|
{
|
||
|
it->pts[i*2] += offx;
|
||
|
it->pts[i*2+1] += offy;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Add contours.
|
||
|
for (it = bg; it != NULL; it = it->next)
|
||
|
tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts);
|
||
|
for (it = fg; it != NULL; it = it->next)
|
||
|
tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts);
|
||
|
if (!tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, nvp, 2, 0))
|
||
|
return -1;
|
||
|
|
||
|
t1 = glfwGetTime();
|
||
|
|
||
|
printf("Time: %.3f ms\n", (t1 - t0) * 1000.0f);
|
||
|
printf("Memory used: %.1f kB\n", allocated/1024.0f);
|
||
|
|
||
|
#endif
|
||
|
|
||
|
mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||
|
width = mode->width - 40;
|
||
|
height = mode->height - 80;
|
||
|
window = glfwCreateWindow(width, height, "Libtess2 Demo", NULL, NULL);
|
||
|
if (!window) {
|
||
|
glfwTerminate();
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
glfwSetKeyCallback(window, key);
|
||
|
glfwMakeContextCurrent(window);
|
||
|
|
||
|
// Adjust bounds so that we get nice view of the bg.
|
||
|
cx = (bounds[0]+bounds[2])/2;
|
||
|
cy = (bounds[3]+bounds[1])/2;
|
||
|
w = (bounds[2]-bounds[0])/2;
|
||
|
view[0] = cx - w*1.2f;
|
||
|
view[2] = cx + w*1.2f;
|
||
|
view[1] = cy - w*1.2f*(float)height/(float)width;
|
||
|
view[3] = cy + w*1.2f*(float)height/(float)width;
|
||
|
|
||
|
glfwSetTime(0);
|
||
|
|
||
|
while (!glfwWindowShouldClose(window))
|
||
|
{
|
||
|
int winWidth, winHeight;
|
||
|
int fbWidth, fbHeight;
|
||
|
float pxr, ct;
|
||
|
|
||
|
glfwGetWindowSize(window, &winWidth, &winHeight);
|
||
|
glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
|
||
|
|
||
|
// Calculate pixel ration for hi-dpi devices.
|
||
|
pxr = (float)fbWidth / (float)winWidth;
|
||
|
|
||
|
ct = (float)glfwGetTime();
|
||
|
if (run) t += ct - pt;
|
||
|
pt = ct;
|
||
|
|
||
|
// Update and render
|
||
|
glViewport(0, 0, fbWidth, fbHeight);
|
||
|
glClearColor(0.3f, 0.3f, 0.32f, 1.0f);
|
||
|
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
|
||
|
glEnable(GL_BLEND);
|
||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||
|
glDisable(GL_TEXTURE_2D);
|
||
|
glMatrixMode(GL_PROJECTION);
|
||
|
glLoadIdentity();
|
||
|
glOrtho(view[0],view[2],view[1],view[3],-1,1);
|
||
|
glMatrixMode(GL_MODELVIEW);
|
||
|
glLoadIdentity();
|
||
|
glDisable(GL_DEPTH_TEST);
|
||
|
glEnable(GL_BLEND);
|
||
|
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||
|
|
||
|
#ifdef USE_POOL
|
||
|
pool.size = 0; // reset pool
|
||
|
tess = tessNewTess(&ma);
|
||
|
if (tess)
|
||
|
{
|
||
|
tessSetOption(tess, TESS_CONSTRAINED_DELAUNAY_TRIANGULATION, cdt);
|
||
|
|
||
|
offx = (view[2]+view[0])/2 + sinf(t) * (view[2]-view[0])/2;
|
||
|
offy = (view[3]+view[1])/2 + cosf(t*3.13f) * (view[3]-view[1])/6;
|
||
|
|
||
|
for (it = fg; it != NULL; it = it->next)
|
||
|
{
|
||
|
for (i = 0; i < it->npts; ++i)
|
||
|
{
|
||
|
it->pts[i*2] += offx;
|
||
|
it->pts[i*2+1] += offy;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (it = bg; it != NULL; it = it->next)
|
||
|
tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts);
|
||
|
for (it = fg; it != NULL; it = it->next)
|
||
|
tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts);
|
||
|
|
||
|
for (it = fg; it != NULL; it = it->next)
|
||
|
{
|
||
|
for (i = 0; i < it->npts; ++i)
|
||
|
{
|
||
|
it->pts[i*2] -= offx;
|
||
|
it->pts[i*2+1] -= offy;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// First combine contours and then triangulate, this removes unnecessary inner vertices.
|
||
|
if (tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_BOUNDARY_CONTOURS, 0, 0, 0))
|
||
|
{
|
||
|
const float* verts = tessGetVertices(tess);
|
||
|
const int* vinds = tessGetVertexIndices(tess);
|
||
|
const int nverts = tessGetVertexCount(tess);
|
||
|
const int* elems = tessGetElements(tess);
|
||
|
const int nelems = tessGetElementCount(tess);
|
||
|
|
||
|
if (nverts > nvflags)
|
||
|
{
|
||
|
if (vflags)
|
||
|
free(vflags);
|
||
|
nvflags = nverts;
|
||
|
vflags = (unsigned char*)malloc(sizeof(unsigned char)*nvflags);
|
||
|
}
|
||
|
|
||
|
if (vflags)
|
||
|
{
|
||
|
// Vertex indices describe the order the indices were added and can be used
|
||
|
// to map the tesselator output to input. Vertices marked as TESS_UNDEF
|
||
|
// are the ones that were created at the intersection of segments.
|
||
|
// That is, if vflags is set it means that the vertex comes from intersegment.
|
||
|
for (i = 0; i < nverts; ++i)
|
||
|
vflags[i] = vinds[i] == TESS_UNDEF ? 1 : 0;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < nelems; ++i)
|
||
|
{
|
||
|
int b = elems[i*2];
|
||
|
int n = elems[i*2+1];
|
||
|
tessAddContour(tess, 2, &verts[b*2], sizeof(float)*2, n);
|
||
|
}
|
||
|
if (!tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, nvp, 2, 0))
|
||
|
tess = 0;
|
||
|
}
|
||
|
else
|
||
|
tess = 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Draw tesselated pieces.
|
||
|
if (tess)
|
||
|
{
|
||
|
const float* verts = tessGetVertices(tess);
|
||
|
const int* vinds = tessGetVertexIndices(tess);
|
||
|
const int* elems = tessGetElements(tess);
|
||
|
const int nverts = tessGetVertexCount(tess);
|
||
|
const int nelems = tessGetElementCount(tess);
|
||
|
|
||
|
// Draw polygons.
|
||
|
glColor4ub(255,255,255,128);
|
||
|
for (i = 0; i < nelems; ++i)
|
||
|
{
|
||
|
const int* p = &elems[i*nvp];
|
||
|
glBegin(GL_TRIANGLE_FAN);
|
||
|
for (j = 0; j < nvp && p[j] != TESS_UNDEF; ++j)
|
||
|
glVertex2f(verts[p[j]*2], verts[p[j]*2+1]);
|
||
|
glEnd();
|
||
|
}
|
||
|
|
||
|
glLineWidth(1.0f * pxr);
|
||
|
glPointSize(3.0f * pxr);
|
||
|
|
||
|
glColor4ub(0,0,0,16);
|
||
|
for (i = 0; i < nelems; ++i)
|
||
|
{
|
||
|
const int* p = &elems[i*nvp];
|
||
|
glBegin(GL_LINE_LOOP);
|
||
|
for (j = 0; j < nvp && p[j] != TESS_UNDEF; ++j)
|
||
|
glVertex2f(verts[p[j]*2], verts[p[j]*2+1]);
|
||
|
glEnd();
|
||
|
}
|
||
|
|
||
|
glColor4ub(0,0,0,128);
|
||
|
glBegin(GL_POINTS);
|
||
|
for (i = 0; i < nverts; ++i)
|
||
|
{
|
||
|
if (vflags && vflags[vinds[i]])
|
||
|
glColor4ub(255,0,0,192);
|
||
|
else
|
||
|
glColor4ub(0,0,0,128);
|
||
|
glVertex2f(verts[i*2], verts[i*2+1]);
|
||
|
}
|
||
|
glEnd();
|
||
|
|
||
|
glPointSize(1.0f);
|
||
|
}
|
||
|
|
||
|
glEnable(GL_DEPTH_TEST);
|
||
|
glfwSwapBuffers(window);
|
||
|
glfwPollEvents();
|
||
|
}
|
||
|
|
||
|
if (tess) tessDeleteTess(tess);
|
||
|
|
||
|
if (vflags)
|
||
|
free(vflags);
|
||
|
|
||
|
svgDelete(bg);
|
||
|
svgDelete(fg);
|
||
|
|
||
|
glfwTerminate();
|
||
|
return 0;
|
||
|
}
|