mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2024-11-26 06:20:48 +00:00
parent
7c4a06320a
commit
637933a23c
20 changed files with 19422 additions and 2 deletions
89
Makefile
89
Makefile
|
@ -377,12 +377,12 @@ endif
|
|||
# ----------
|
||||
|
||||
# Phony targets
|
||||
.PHONY : all client game icon server ref_gl1 ref_gl3 ref_gles3 ref_soft ref_vk
|
||||
.PHONY : all client game icon server ref_gl1 ref_gl3 ref_gles3 ref_soft ref_vk ref_gl4
|
||||
|
||||
# ----------
|
||||
|
||||
# Builds everything
|
||||
all: config client server game ref_gl1 ref_gl3 ref_gles3 ref_soft ref_vk
|
||||
all: config client server game ref_gl1 ref_gl3 ref_gles3 ref_soft ref_vk ref_gl4
|
||||
|
||||
# ----------
|
||||
|
||||
|
@ -694,6 +694,38 @@ build/ref_gles3/%.o: %.c
|
|||
|
||||
# ----------
|
||||
|
||||
# The OpenGL 4.6 renderer lib
|
||||
|
||||
ifeq ($(YQ2_OSTYPE), Windows)
|
||||
|
||||
ref_gl4:
|
||||
@echo "===> Building ref_gl4.dll"
|
||||
${Q}mkdir -p release
|
||||
$(MAKE) release/ref_gl4.dll
|
||||
|
||||
release/ref_gl4.dll : GLAD_INCLUDE = -Isrc/client/refresh/gl4/glad/include
|
||||
release/ref_gl4.dll : LDFLAGS += -shared
|
||||
|
||||
else # not Windows or Darwin - macOS doesn't support OpenGL 4.6
|
||||
|
||||
ref_gl4:
|
||||
@echo "===> Building ref_gl4.so"
|
||||
${Q}mkdir -p release
|
||||
$(MAKE) release/ref_gl4.so
|
||||
|
||||
release/ref_gl4.so : GLAD_INCLUDE = -Isrc/client/refresh/gl4/glad/include
|
||||
release/ref_gl4.so : CFLAGS += -fPIC
|
||||
release/ref_gl4.so : LDFLAGS += -shared
|
||||
|
||||
endif # OS specific ref_gl4 stuff
|
||||
|
||||
build/ref_gl4/%.o: %.c
|
||||
@echo "===> CC $<"
|
||||
${Q}mkdir -p $(@D)
|
||||
${Q}$(CC) -c $(CFLAGS) $(SDLCFLAGS) $(INCLUDE) $(GLAD_INCLUDE) -o $@ $<
|
||||
|
||||
# ----------
|
||||
|
||||
# The soft renderer lib
|
||||
|
||||
ifeq ($(YQ2_OSTYPE), Windows)
|
||||
|
@ -1024,6 +1056,41 @@ endif
|
|||
|
||||
# ----------
|
||||
|
||||
REFGL4_OBJS_ := \
|
||||
src/client/refresh/gl4/gl4_draw.o \
|
||||
src/client/refresh/gl4/gl4_image.o \
|
||||
src/client/refresh/gl4/gl4_light.o \
|
||||
src/client/refresh/gl4/gl4_lightmap.o \
|
||||
src/client/refresh/gl4/gl4_main.o \
|
||||
src/client/refresh/gl4/gl4_mesh.o \
|
||||
src/client/refresh/gl4/gl4_misc.o \
|
||||
src/client/refresh/gl4/gl4_model.o \
|
||||
src/client/refresh/gl4/gl4_sdl.o \
|
||||
src/client/refresh/gl4/gl4_surf.o \
|
||||
src/client/refresh/gl4/gl4_warp.o \
|
||||
src/client/refresh/gl4/gl4_shaders.o \
|
||||
src/client/refresh/files/surf.o \
|
||||
src/client/refresh/files/models.o \
|
||||
src/client/refresh/files/pcx.o \
|
||||
src/client/refresh/files/stb.o \
|
||||
src/client/refresh/files/wal.o \
|
||||
src/client/refresh/files/pvs.o \
|
||||
src/common/shared/shared.o \
|
||||
src/common/md4.o
|
||||
|
||||
REFGL4_OBJS_GLADE_ := \
|
||||
src/client/refresh/gl4/glad/src/glad.o
|
||||
|
||||
ifeq ($(YQ2_OSTYPE), Windows)
|
||||
REFGL4_OBJS_ += \
|
||||
src/backends/windows/shared/hunk.o
|
||||
else # not Windows
|
||||
REFGL4_OBJS_ += \
|
||||
src/backends/unix/shared/hunk.o
|
||||
endif
|
||||
|
||||
# ----------
|
||||
|
||||
REFSOFT_OBJS_ := \
|
||||
src/client/refresh/soft/sw_aclip.o \
|
||||
src/client/refresh/soft/sw_alias.o \
|
||||
|
@ -1061,6 +1128,7 @@ REFSOFT_OBJS_ += \
|
|||
endif
|
||||
|
||||
# ----------
|
||||
|
||||
REFVK_OBJS_ := \
|
||||
src/client/refresh/vk/vk_buffer.o \
|
||||
src/client/refresh/vk/vk_cmd.o \
|
||||
|
@ -1160,6 +1228,8 @@ REFGL3_OBJS = $(patsubst %,build/ref_gl3/%,$(REFGL3_OBJS_))
|
|||
REFGL3_OBJS += $(patsubst %,build/ref_gl3/%,$(REFGL3_OBJS_GLADE_))
|
||||
REFGLES3_OBJS = $(patsubst %,build/ref_gles3/%,$(REFGL3_OBJS_))
|
||||
REFGLES3_OBJS += $(patsubst %,build/ref_gles3/%,$(REFGL3_OBJS_GLADEES_))
|
||||
REFGL4_OBJS = $(patsubst %,build/ref_gl4/%,$(REFGL4_OBJS_))
|
||||
REFGL4_OBJS += $(patsubst %,build/ref_gl4/%,$(REFGL4_OBJS_GLADE_))
|
||||
REFSOFT_OBJS = $(patsubst %,build/ref_soft/%,$(REFSOFT_OBJS_))
|
||||
REFVK_OBJS = $(patsubst %,build/ref_vk/%,$(REFVK_OBJS_))
|
||||
SERVER_OBJS = $(patsubst %,build/server/%,$(SERVER_OBJS_))
|
||||
|
@ -1173,6 +1243,7 @@ GAME_DEPS= $(GAME_OBJS:.o=.d)
|
|||
REFGL1_DEPS= $(REFGL1_OBJS:.o=.d)
|
||||
REFGL3_DEPS= $(REFGL3_OBJS:.o=.d)
|
||||
REFGLES3_DEPS= $(REFGLES3_OBJS:.o=.d)
|
||||
REFGL4_DEPS= $(REFGL4_OBJS:.o=.d)
|
||||
REFSOFT_DEPS= $(REFSOFT_OBJS:.o=.d)
|
||||
REFVK_DEPS= $(REFVK_OBJS:.o=.d)
|
||||
SERVER_DEPS= $(SERVER_OBJS:.o=.d)
|
||||
|
@ -1183,6 +1254,7 @@ SERVER_DEPS= $(SERVER_OBJS:.o=.d)
|
|||
-include $(REFGL1_DEPS)
|
||||
-include $(REFGL3_DEPS)
|
||||
-include $(REFGLES3_DEPS)
|
||||
-include $(REFGL4_DEPS)
|
||||
-include $(REFVK_DEPS)
|
||||
-include $(SERVER_DEPS)
|
||||
|
||||
|
@ -1263,6 +1335,19 @@ release/ref_gles3.so : $(REFGLES3_OBJS)
|
|||
${Q}$(CC) $(LDFLAGS) $(REFGLES3_OBJS) $(LDLIBS) $(SDLLDFLAGS) -o $@
|
||||
endif
|
||||
|
||||
# release/ref_gl4.so
|
||||
ifeq ($(YQ2_OSTYPE), Windows)
|
||||
release/ref_gl4.dll : $(REFGL4_OBJS)
|
||||
@echo "===> LD $@"
|
||||
${Q}$(CC) $(LDFLAGS) $(REFGL4_OBJS) $(LDLIBS) $(DLL_SDLLDFLAGS) -o $@
|
||||
$(Q)strip $@
|
||||
|
||||
else
|
||||
release/ref_gl4.so : $(REFGL4_OBJS)
|
||||
@echo "===> LD $@"
|
||||
${Q}$(CC) $(LDFLAGS) $(REFGL4_OBJS) $(LDLIBS) $(SDLLDFLAGS) -o $@
|
||||
endif
|
||||
|
||||
# release/ref_soft.so
|
||||
ifeq ($(YQ2_OSTYPE), Windows)
|
||||
release/ref_soft.dll : $(REFSOFT_OBJS)
|
||||
|
|
413
src/client/refresh/gl4/gl4_draw.c
Normal file
413
src/client/refresh/gl4/gl4_draw.c
Normal file
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (C) 2016-2017 Daniel Gibson
|
||||
*
|
||||
* This program 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 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Drawing of all images that are not textures
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#include "header/local.h"
|
||||
|
||||
unsigned d_8to24table[256];
|
||||
|
||||
gl4image_t *draw_chars;
|
||||
|
||||
static GLuint vbo2D = 0, vao2D = 0, vao2Dcolor = 0; // vao2D is for textured rendering, vao2Dcolor for color-only
|
||||
|
||||
void
|
||||
GL4_Draw_InitLocal(void)
|
||||
{
|
||||
/* load console characters */
|
||||
draw_chars = R_FindPic("conchars", (findimage_t)GL4_FindImage);
|
||||
if (!draw_chars)
|
||||
{
|
||||
ri.Sys_Error(ERR_FATAL, "%s: Couldn't load pics/conchars.pcx",
|
||||
__func__);
|
||||
}
|
||||
|
||||
// set up attribute layout for 2D textured rendering
|
||||
glGenVertexArrays(1, &vao2D);
|
||||
glBindVertexArray(vao2D);
|
||||
|
||||
glGenBuffers(1, &vbo2D);
|
||||
GL4_BindVBO(vbo2D);
|
||||
|
||||
GL4_UseProgram(gl4state.si2D.shaderProgram);
|
||||
|
||||
glEnableVertexAttribArray(GL4_ATTRIB_POSITION);
|
||||
// Note: the glVertexAttribPointer() configuration is stored in the VAO, not the shader or sth
|
||||
// (that's why I use one VAO per 2D shader)
|
||||
qglVertexAttribPointer(GL4_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 0);
|
||||
|
||||
glEnableVertexAttribArray(GL4_ATTRIB_TEXCOORD);
|
||||
qglVertexAttribPointer(GL4_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 2*sizeof(float));
|
||||
|
||||
// set up attribute layout for 2D flat color rendering
|
||||
|
||||
glGenVertexArrays(1, &vao2Dcolor);
|
||||
glBindVertexArray(vao2Dcolor);
|
||||
|
||||
GL4_BindVBO(vbo2D); // yes, both VAOs share the same VBO
|
||||
|
||||
GL4_UseProgram(gl4state.si2Dcolor.shaderProgram);
|
||||
|
||||
glEnableVertexAttribArray(GL4_ATTRIB_POSITION);
|
||||
qglVertexAttribPointer(GL4_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), 0);
|
||||
|
||||
GL4_BindVAO(0);
|
||||
}
|
||||
|
||||
void
|
||||
GL4_Draw_ShutdownLocal(void)
|
||||
{
|
||||
glDeleteBuffers(1, &vbo2D);
|
||||
vbo2D = 0;
|
||||
glDeleteVertexArrays(1, &vao2D);
|
||||
vao2D = 0;
|
||||
glDeleteVertexArrays(1, &vao2Dcolor);
|
||||
vao2Dcolor = 0;
|
||||
}
|
||||
|
||||
// bind the texture before calling this
|
||||
static void
|
||||
drawTexturedRectangle(float x, float y, float w, float h,
|
||||
float sl, float tl, float sh, float th)
|
||||
{
|
||||
/*
|
||||
* x,y+h x+w,y+h
|
||||
* sl,th--------sh,th
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* sl,tl--------sh,tl
|
||||
* x,y x+w,y
|
||||
*/
|
||||
|
||||
GLfloat vBuf[16] = {
|
||||
// X, Y, S, T
|
||||
x, y+h, sl, th,
|
||||
x, y, sl, tl,
|
||||
x+w, y+h, sh, th,
|
||||
x+w, y, sh, tl
|
||||
};
|
||||
|
||||
GL4_BindVAO(vao2D);
|
||||
|
||||
// Note: while vao2D "remembers" its vbo for drawing, binding the vao does *not*
|
||||
// implicitly bind the vbo, so I need to explicitly bind it before glBufferData()
|
||||
GL4_BindVBO(vbo2D);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vBuf), vBuf, GL_STREAM_DRAW);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
//glMultiDrawArrays(mode, first, count, drawcount) ??
|
||||
}
|
||||
|
||||
/*
|
||||
* Draws one 8*8 graphics character with 0 being transparent.
|
||||
* It can be clipped to the top of the screen to allow the console to be
|
||||
* smoothly scrolled off.
|
||||
*/
|
||||
void
|
||||
GL4_Draw_CharScaled(int x, int y, int num, float scale)
|
||||
{
|
||||
int row, col;
|
||||
float frow, fcol, size, scaledSize;
|
||||
num &= 255;
|
||||
|
||||
if ((num & 127) == 32)
|
||||
{
|
||||
return; /* space */
|
||||
}
|
||||
|
||||
if (y <= -8)
|
||||
{
|
||||
return; /* totally off screen */
|
||||
}
|
||||
|
||||
row = num >> 4;
|
||||
col = num & 15;
|
||||
|
||||
frow = row * 0.0625;
|
||||
fcol = col * 0.0625;
|
||||
size = 0.0625;
|
||||
|
||||
scaledSize = 8*scale;
|
||||
|
||||
// TODO: batchen?
|
||||
|
||||
GL4_UseProgram(gl4state.si2D.shaderProgram);
|
||||
GL4_Bind(draw_chars->texnum);
|
||||
drawTexturedRectangle(x, y, scaledSize, scaledSize, fcol, frow, fcol+size, frow+size);
|
||||
}
|
||||
|
||||
gl4image_t *
|
||||
GL4_Draw_FindPic(char *name)
|
||||
{
|
||||
return R_FindPic(name, (findimage_t)GL4_FindImage);
|
||||
}
|
||||
|
||||
void
|
||||
GL4_Draw_GetPicSize(int *w, int *h, char *pic)
|
||||
{
|
||||
gl4image_t *gl;
|
||||
|
||||
gl = R_FindPic(pic, (findimage_t)GL4_FindImage);
|
||||
|
||||
if (!gl)
|
||||
{
|
||||
*w = *h = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
*w = gl->width;
|
||||
*h = gl->height;
|
||||
}
|
||||
|
||||
void
|
||||
GL4_Draw_StretchPic(int x, int y, int w, int h, char *pic)
|
||||
{
|
||||
gl4image_t *gl = R_FindPic(pic, (findimage_t)GL4_FindImage);
|
||||
|
||||
if (!gl)
|
||||
{
|
||||
R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic);
|
||||
return;
|
||||
}
|
||||
|
||||
GL4_UseProgram(gl4state.si2D.shaderProgram);
|
||||
GL4_Bind(gl->texnum);
|
||||
|
||||
drawTexturedRectangle(x, y, w, h, gl->sl, gl->tl, gl->sh, gl->th);
|
||||
}
|
||||
|
||||
void
|
||||
GL4_Draw_PicScaled(int x, int y, char *pic, float factor)
|
||||
{
|
||||
gl4image_t *gl = R_FindPic(pic, (findimage_t)GL4_FindImage);
|
||||
if (!gl)
|
||||
{
|
||||
R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic);
|
||||
return;
|
||||
}
|
||||
|
||||
GL4_UseProgram(gl4state.si2D.shaderProgram);
|
||||
GL4_Bind(gl->texnum);
|
||||
|
||||
drawTexturedRectangle(x, y, gl->width*factor, gl->height*factor, gl->sl, gl->tl, gl->sh, gl->th);
|
||||
}
|
||||
|
||||
/*
|
||||
* This repeats a 64*64 tile graphic to fill
|
||||
* the screen around a sized down
|
||||
* refresh window.
|
||||
*/
|
||||
void
|
||||
GL4_Draw_TileClear(int x, int y, int w, int h, char *pic)
|
||||
{
|
||||
gl4image_t *image = R_FindPic(pic, (findimage_t)GL4_FindImage);
|
||||
if (!image)
|
||||
{
|
||||
R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic);
|
||||
return;
|
||||
}
|
||||
|
||||
GL4_UseProgram(gl4state.si2D.shaderProgram);
|
||||
GL4_Bind(image->texnum);
|
||||
|
||||
drawTexturedRectangle(x, y, w, h, x/64.0f, y/64.0f, (x+w)/64.0f, (y+h)/64.0f);
|
||||
}
|
||||
|
||||
void
|
||||
GL4_DrawFrameBufferObject(int x, int y, int w, int h, GLuint fboTexture, const float v_blend[4])
|
||||
{
|
||||
qboolean underwater = (gl4_newrefdef.rdflags & RDF_UNDERWATER) != 0;
|
||||
gl4ShaderInfo_t* shader = underwater ? &gl4state.si2DpostProcessWater
|
||||
: &gl4state.si2DpostProcess;
|
||||
GL4_UseProgram(shader->shaderProgram);
|
||||
GL4_Bind(fboTexture);
|
||||
|
||||
if(underwater && shader->uniLmScalesOrTime != -1)
|
||||
{
|
||||
glUniform1f(shader->uniLmScalesOrTime, gl4_newrefdef.time);
|
||||
}
|
||||
if(shader->uniVblend != -1)
|
||||
{
|
||||
glUniform4fv(shader->uniVblend, 1, v_blend);
|
||||
}
|
||||
|
||||
drawTexturedRectangle(x, y, w, h, 0, 1, 1, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills a box of pixels with a single color
|
||||
*/
|
||||
void
|
||||
GL4_Draw_Fill(int x, int y, int w, int h, int c)
|
||||
{
|
||||
union
|
||||
{
|
||||
unsigned c;
|
||||
byte v[4];
|
||||
} color;
|
||||
int i;
|
||||
|
||||
if ((unsigned)c > 255)
|
||||
{
|
||||
ri.Sys_Error(ERR_FATAL, "Draw_Fill: bad color");
|
||||
}
|
||||
|
||||
color.c = d_8to24table[c];
|
||||
|
||||
GLfloat vBuf[8] = {
|
||||
// X, Y
|
||||
x, y+h,
|
||||
x, y,
|
||||
x+w, y+h,
|
||||
x+w, y
|
||||
};
|
||||
|
||||
for(i=0; i<3; ++i)
|
||||
{
|
||||
gl4state.uniCommonData.color.Elements[i] = color.v[i] * (1.0f/255.0f);
|
||||
}
|
||||
gl4state.uniCommonData.color.A = 1.0f;
|
||||
|
||||
GL4_UpdateUBOCommon();
|
||||
|
||||
GL4_UseProgram(gl4state.si2Dcolor.shaderProgram);
|
||||
GL4_BindVAO(vao2Dcolor);
|
||||
|
||||
GL4_BindVBO(vbo2D);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vBuf), vBuf, GL_STREAM_DRAW);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
// in GL1 this is called R_Flash() (which just calls R_PolyBlend())
|
||||
// now implemented in 2D mode and called after SetGL2D() because
|
||||
// it's pretty similar to GL4_Draw_FadeScreen()
|
||||
void
|
||||
GL4_Draw_Flash(const float color[4], float x, float y, float w, float h)
|
||||
{
|
||||
if (gl_polyblend->value == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int i=0;
|
||||
|
||||
GLfloat vBuf[8] = {
|
||||
// X, Y
|
||||
x, y+h,
|
||||
x, y,
|
||||
x+w, y+h,
|
||||
x+w, y
|
||||
};
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
for(i=0; i<4; ++i) gl4state.uniCommonData.color.Elements[i] = color[i];
|
||||
|
||||
GL4_UpdateUBOCommon();
|
||||
|
||||
GL4_UseProgram(gl4state.si2Dcolor.shaderProgram);
|
||||
|
||||
GL4_BindVAO(vao2Dcolor);
|
||||
|
||||
GL4_BindVBO(vbo2D);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vBuf), vBuf, GL_STREAM_DRAW);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void
|
||||
GL4_Draw_FadeScreen(void)
|
||||
{
|
||||
float color[4] = {0, 0, 0, 0.6f};
|
||||
GL4_Draw_Flash(color, 0, 0, vid.width, vid.height);
|
||||
}
|
||||
|
||||
void
|
||||
GL4_Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, const byte *data, int bits)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
GL4_Bind(0);
|
||||
|
||||
unsigned image32[320*240]; /* was 256 * 256, but we want a bit more space */
|
||||
|
||||
unsigned* img = image32;
|
||||
|
||||
if (bits == 32)
|
||||
{
|
||||
img = (unsigned *)data;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(cols*rows > 320*240)
|
||||
{
|
||||
/* in case there is a bigger video after all,
|
||||
* malloc enough space to hold the frame */
|
||||
img = (unsigned*)malloc(cols*rows*4);
|
||||
}
|
||||
|
||||
for(i=0; i<rows; ++i)
|
||||
{
|
||||
int rowOffset = i*cols;
|
||||
for(j=0; j<cols; ++j)
|
||||
{
|
||||
byte palIdx = data[rowOffset+j];
|
||||
img[rowOffset+j] = gl4_rawpalette[palIdx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GL4_UseProgram(gl4state.si2D.shaderProgram);
|
||||
|
||||
GLuint glTex;
|
||||
glGenTextures(1, &glTex);
|
||||
GL4_SelectTMU(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, glTex);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, gl4_tex_solid_format,
|
||||
cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
|
||||
|
||||
if(img != image32 && img != (unsigned *)data)
|
||||
{
|
||||
free(img);
|
||||
}
|
||||
|
||||
// Note: gl_filter_min could be GL_*_MIPMAP_* so we can't use it for min filter here (=> no mipmaps)
|
||||
// but gl_filter_max (either GL_LINEAR or GL_NEAREST) should do the trick.
|
||||
GLint filter = (r_videos_unfiltered->value == 0) ? gl_filter_max : GL_NEAREST;
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
|
||||
|
||||
drawTexturedRectangle(x, y, w, h, 0.0f, 0.0f, 1.0f, 1.0f);
|
||||
|
||||
glDeleteTextures(1, &glTex);
|
||||
|
||||
GL4_Bind(0);
|
||||
}
|
845
src/client/refresh/gl4/gl4_image.c
Normal file
845
src/client/refresh/gl4/gl4_image.c
Normal file
|
@ -0,0 +1,845 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (C) 2016-2017 Daniel Gibson
|
||||
*
|
||||
* This program 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 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Texture handling for OpenGL4
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#include "header/local.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
int minimize, maximize;
|
||||
} glmode_t;
|
||||
|
||||
glmode_t modes[] = {
|
||||
{"GL_NEAREST", GL_NEAREST, GL_NEAREST},
|
||||
{"GL_LINEAR", GL_LINEAR, GL_LINEAR},
|
||||
{"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
|
||||
{"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
|
||||
{"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
|
||||
{"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
|
||||
};
|
||||
|
||||
int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST;
|
||||
int gl_filter_max = GL_LINEAR;
|
||||
|
||||
gl4image_t gl4textures[MAX_GL4TEXTURES];
|
||||
int numgl4textures = 0;
|
||||
static int image_max = 0;
|
||||
|
||||
void
|
||||
GL4_TextureMode(char *string)
|
||||
{
|
||||
const int num_modes = sizeof(modes)/sizeof(modes[0]);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_modes; i++)
|
||||
{
|
||||
if (!Q_stricmp(modes[i].name, string))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == num_modes)
|
||||
{
|
||||
R_Printf(PRINT_ALL, "bad filter name '%s' (probably from gl_texturemode)\n", string);
|
||||
return;
|
||||
}
|
||||
|
||||
gl_filter_min = modes[i].minimize;
|
||||
gl_filter_max = modes[i].maximize;
|
||||
|
||||
/* clamp selected anisotropy */
|
||||
if (gl4config.anisotropic)
|
||||
{
|
||||
if (gl_anisotropic->value > gl4config.max_anisotropy)
|
||||
{
|
||||
ri.Cvar_SetValue("r_anisotropic", gl4config.max_anisotropy);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ri.Cvar_SetValue("r_anisotropic", 0.0);
|
||||
}
|
||||
|
||||
gl4image_t *glt;
|
||||
|
||||
const char* nolerplist = gl_nolerp_list->string;
|
||||
const char* lerplist = r_lerp_list->string;
|
||||
qboolean unfiltered2D = r_2D_unfiltered->value != 0;
|
||||
|
||||
/* change all the existing texture objects */
|
||||
for (i = 0, glt = gl4textures; i < numgl4textures; i++, glt++)
|
||||
{
|
||||
qboolean nolerp = false;
|
||||
/* r_2D_unfiltered and gl_nolerp_list allow rendering stuff unfiltered even if gl_filter_* is filtered */
|
||||
if (unfiltered2D && glt->type == it_pic)
|
||||
{
|
||||
// exception to that exception: stuff on the r_lerp_list
|
||||
nolerp = (lerplist== NULL) || (strstr(lerplist, glt->name) == NULL);
|
||||
}
|
||||
else if(nolerplist != NULL && strstr(nolerplist, glt->name) != NULL)
|
||||
{
|
||||
nolerp = true;
|
||||
}
|
||||
|
||||
GL4_SelectTMU(GL_TEXTURE0);
|
||||
GL4_Bind(glt->texnum);
|
||||
if ((glt->type != it_pic) && (glt->type != it_sky)) /* mipmapped texture */
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
|
||||
|
||||
/* Set anisotropic filter if supported and enabled */
|
||||
if (gl4config.anisotropic && gl_anisotropic->value)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, max(gl_anisotropic->value, 1.f));
|
||||
}
|
||||
}
|
||||
else /* texture has no mipmaps */
|
||||
{
|
||||
if (nolerp)
|
||||
{
|
||||
// this texture shouldn't be filtered at all (no gl_nolerp_list or r_2D_unfiltered case)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we can't use gl_filter_min which might be GL_*_MIPMAP_*
|
||||
// also, there's no anisotropic filtering for textures w/o mipmaps
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GL4_Bind(GLuint texnum)
|
||||
{
|
||||
extern gl4image_t *draw_chars;
|
||||
|
||||
if (gl_nobind->value && draw_chars) /* performance evaluation option */
|
||||
{
|
||||
texnum = draw_chars->texnum;
|
||||
}
|
||||
|
||||
if (gl4state.currenttexture == texnum)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gl4state.currenttexture = texnum;
|
||||
GL4_SelectTMU(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texnum);
|
||||
}
|
||||
|
||||
void
|
||||
GL4_BindLightmap(int lightmapnum)
|
||||
{
|
||||
int i=0;
|
||||
if(lightmapnum < 0 || lightmapnum >= MAX_LIGHTMAPS)
|
||||
{
|
||||
R_Printf(PRINT_ALL, "WARNING: Invalid lightmapnum %i used!\n", lightmapnum);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gl4state.currentlightmap == lightmapnum)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gl4state.currentlightmap = lightmapnum;
|
||||
for(i=0; i<MAX_LIGHTMAPS_PER_SURFACE; ++i)
|
||||
{
|
||||
// this assumes that GL_TEXTURE<i+1> = GL_TEXTURE<i> + 1
|
||||
// at least for GL_TEXTURE0 .. GL_TEXTURE31 that's true
|
||||
GL4_SelectTMU(GL_TEXTURE1+i);
|
||||
glBindTexture(GL_TEXTURE_2D, gl4state.lightmap_textureIDs[lightmapnum][i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns has_alpha
|
||||
*/
|
||||
qboolean
|
||||
GL4_Upload32(unsigned *data, int width, int height, qboolean mipmap)
|
||||
{
|
||||
qboolean res;
|
||||
|
||||
int i;
|
||||
int c = width * height;
|
||||
byte *scan = ((byte *)data) + 3;
|
||||
int comp = gl4_tex_solid_format;
|
||||
int samples = gl4_solid_format;
|
||||
|
||||
for (i = 0; i < c; i++, scan += 4)
|
||||
{
|
||||
if (*scan != 255)
|
||||
{
|
||||
samples = gl4_alpha_format;
|
||||
comp = gl4_tex_alpha_format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, comp, width, height,
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
|
||||
res = (samples == gl4_alpha_format);
|
||||
|
||||
if (mipmap)
|
||||
{
|
||||
// TODO: some hardware may require mipmapping disabled for NPOT textures!
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
|
||||
}
|
||||
else // if the texture has no mipmaps, we can't use gl_filter_min which might be GL_*_MIPMAP_*
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
|
||||
}
|
||||
|
||||
if (mipmap && gl4config.anisotropic && gl_anisotropic->value)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, max(gl_anisotropic->value, 1.f));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns has_alpha
|
||||
*/
|
||||
qboolean
|
||||
GL4_Upload8(byte *data, int width, int height, qboolean mipmap, qboolean is_sky)
|
||||
{
|
||||
int s = width * height;
|
||||
unsigned *trans = malloc(s * sizeof(unsigned));
|
||||
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
int p = data[i];
|
||||
trans[i] = d_8to24table[p];
|
||||
|
||||
/* transparent, so scan around for
|
||||
another color to avoid alpha fringes */
|
||||
if (p == 255)
|
||||
{
|
||||
if ((i > width) && (data[i - width] != 255))
|
||||
{
|
||||
p = data[i - width];
|
||||
}
|
||||
else if ((i < s - width) && (data[i + width] != 255))
|
||||
{
|
||||
p = data[i + width];
|
||||
}
|
||||
else if ((i > 0) && (data[i - 1] != 255))
|
||||
{
|
||||
p = data[i - 1];
|
||||
}
|
||||
else if ((i < s - 1) && (data[i + 1] != 255))
|
||||
{
|
||||
p = data[i + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
p = 0;
|
||||
}
|
||||
|
||||
/* copy rgb components */
|
||||
((byte *)&trans[i])[0] = ((byte *)&d_8to24table[p])[0];
|
||||
((byte *)&trans[i])[1] = ((byte *)&d_8to24table[p])[1];
|
||||
((byte *)&trans[i])[2] = ((byte *)&d_8to24table[p])[2];
|
||||
}
|
||||
}
|
||||
|
||||
qboolean ret = GL4_Upload32(trans, width, height, mipmap);
|
||||
free(trans);
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
short x, y;
|
||||
} floodfill_t;
|
||||
|
||||
/* must be a power of 2 */
|
||||
#define FLOODFILL_FIFO_SIZE 0x1000
|
||||
#define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1)
|
||||
|
||||
#define FLOODFILL_STEP(off, dx, dy) \
|
||||
{ \
|
||||
if (pos[off] == fillcolor) \
|
||||
{ \
|
||||
pos[off] = 255; \
|
||||
fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \
|
||||
inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \
|
||||
} \
|
||||
else if (pos[off] != 255) \
|
||||
{ \
|
||||
fdc = pos[off]; \
|
||||
} \
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill background pixels so mipmapping doesn't have haloes
|
||||
*/
|
||||
static void
|
||||
FloodFillSkin(byte *skin, int skinwidth, int skinheight)
|
||||
{
|
||||
byte fillcolor = *skin; /* assume this is the pixel to fill */
|
||||
floodfill_t fifo[FLOODFILL_FIFO_SIZE];
|
||||
int inpt = 0, outpt = 0;
|
||||
int filledcolor = 0;
|
||||
int i;
|
||||
|
||||
/* attempt to find opaque black */
|
||||
for (i = 0; i < 256; ++i)
|
||||
{
|
||||
if (LittleLong(d_8to24table[i]) == (255 << 0)) /* alpha 1.0 */
|
||||
{
|
||||
filledcolor = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* can't fill to filled color or to transparent color (used as visited marker) */
|
||||
if ((fillcolor == filledcolor) || (fillcolor == 255))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fifo[inpt].x = 0, fifo[inpt].y = 0;
|
||||
inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
|
||||
|
||||
while (outpt != inpt)
|
||||
{
|
||||
int x = fifo[outpt].x, y = fifo[outpt].y;
|
||||
int fdc = filledcolor;
|
||||
byte *pos = &skin[x + skinwidth * y];
|
||||
|
||||
outpt = (outpt + 1) & FLOODFILL_FIFO_MASK;
|
||||
|
||||
if (x > 0)
|
||||
{
|
||||
FLOODFILL_STEP(-1, -1, 0);
|
||||
}
|
||||
|
||||
if (x < skinwidth - 1)
|
||||
{
|
||||
FLOODFILL_STEP(1, 1, 0);
|
||||
}
|
||||
|
||||
if (y > 0)
|
||||
{
|
||||
FLOODFILL_STEP(-skinwidth, 0, -1);
|
||||
}
|
||||
|
||||
if (y < skinheight - 1)
|
||||
{
|
||||
FLOODFILL_STEP(skinwidth, 0, 1);
|
||||
}
|
||||
|
||||
skin[x + skinwidth * y] = fdc;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is also used as an entry point for the generated r_notexture
|
||||
*/
|
||||
gl4image_t *
|
||||
GL4_LoadPic(char *name, byte *pic, int width, int realwidth,
|
||||
int height, int realheight, size_t data_size,
|
||||
imagetype_t type, int bits)
|
||||
{
|
||||
gl4image_t *image = NULL;
|
||||
GLuint texNum=0;
|
||||
int i;
|
||||
|
||||
qboolean nolerp = false;
|
||||
if (r_2D_unfiltered->value && type == it_pic)
|
||||
{
|
||||
// if r_2D_unfiltered is true(ish), nolerp should usually be true,
|
||||
// *unless* the texture is on the r_lerp_list
|
||||
nolerp = (r_lerp_list->string == NULL) || (strstr(r_lerp_list->string, name) == NULL);
|
||||
}
|
||||
else if (gl_nolerp_list != NULL && gl_nolerp_list->string != NULL)
|
||||
{
|
||||
nolerp = strstr(gl_nolerp_list->string, name) != NULL;
|
||||
}
|
||||
/* find a free gl4image_t */
|
||||
for (i = 0, image = gl4textures; i < numgl4textures; i++, image++)
|
||||
{
|
||||
if (image->texnum == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == numgl4textures)
|
||||
{
|
||||
if (numgl4textures == MAX_GL4TEXTURES)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "MAX_GLTEXTURES");
|
||||
}
|
||||
|
||||
numgl4textures++;
|
||||
}
|
||||
|
||||
image = &gl4textures[i];
|
||||
|
||||
if (strlen(name) >= sizeof(image->name))
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: \"%s\" is too long", __func__, name);
|
||||
}
|
||||
|
||||
strcpy(image->name, name);
|
||||
image->registration_sequence = registration_sequence;
|
||||
|
||||
image->width = width;
|
||||
image->height = height;
|
||||
image->type = type;
|
||||
|
||||
if ((type == it_skin) && (bits == 8))
|
||||
{
|
||||
FloodFillSkin(pic, width, height);
|
||||
}
|
||||
|
||||
image->is_lava = (strstr(name, "lava") != NULL);
|
||||
|
||||
// image->scrap = false; // TODO: reintroduce scrap? would allow optimizations in 2D rendering..
|
||||
|
||||
glGenTextures(1, &texNum);
|
||||
|
||||
image->texnum = texNum;
|
||||
|
||||
GL4_SelectTMU(GL_TEXTURE0);
|
||||
GL4_Bind(texNum);
|
||||
|
||||
if (bits == 8)
|
||||
{
|
||||
// resize 8bit images only when we forced such logic
|
||||
if (r_scale8bittextures->value)
|
||||
{
|
||||
byte *image_converted;
|
||||
int scale = 2;
|
||||
|
||||
// scale 3 times if lerp image
|
||||
if (!nolerp && (vid.height >= 240 * 3))
|
||||
scale = 3;
|
||||
|
||||
image_converted = malloc(width * height * scale * scale);
|
||||
if (!image_converted)
|
||||
return NULL;
|
||||
|
||||
if (scale == 3) {
|
||||
scale3x(pic, image_converted, width, height);
|
||||
} else {
|
||||
scale2x(pic, image_converted, width, height);
|
||||
}
|
||||
|
||||
image->has_alpha = GL4_Upload8(image_converted, width * scale, height * scale,
|
||||
(image->type != it_pic && image->type != it_sky),
|
||||
image->type == it_sky);
|
||||
free(image_converted);
|
||||
}
|
||||
else
|
||||
{
|
||||
image->has_alpha = GL4_Upload8(pic, width, height,
|
||||
(image->type != it_pic && image->type != it_sky),
|
||||
image->type == it_sky);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
image->has_alpha = GL4_Upload32((unsigned *)pic, width, height,
|
||||
(image->type != it_pic && image->type != it_sky));
|
||||
}
|
||||
|
||||
if (realwidth && realheight)
|
||||
{
|
||||
if ((realwidth <= image->width) && (realheight <= image->height))
|
||||
{
|
||||
image->width = realwidth;
|
||||
image->height = realheight;
|
||||
}
|
||||
else
|
||||
{
|
||||
R_Printf(PRINT_DEVELOPER,
|
||||
"Warning, image '%s' has hi-res replacement smaller than the original! (%d x %d) < (%d x %d)\n",
|
||||
name, image->width, image->height, realwidth, realheight);
|
||||
}
|
||||
}
|
||||
|
||||
image->sl = 0;
|
||||
image->sh = 1;
|
||||
image->tl = 0;
|
||||
image->th = 1;
|
||||
|
||||
if (nolerp)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
}
|
||||
#if 0 // TODO: the scrap could allow batch rendering 2D stuff? not sure it's worth the hassle..
|
||||
/* load little pics into the scrap */
|
||||
if (!nolerp && (image->type == it_pic) && (bits == 8) &&
|
||||
(image->width < 64) && (image->height < 64))
|
||||
{
|
||||
int x, y;
|
||||
int i, j, k;
|
||||
int texnum;
|
||||
|
||||
texnum = Scrap_AllocBlock(image->width, image->height, &x, &y);
|
||||
|
||||
if (texnum == -1)
|
||||
{
|
||||
goto nonscrap;
|
||||
}
|
||||
|
||||
scrap_dirty = true;
|
||||
|
||||
/* copy the texels into the scrap block */
|
||||
k = 0;
|
||||
|
||||
for (i = 0; i < image->height; i++)
|
||||
{
|
||||
for (j = 0; j < image->width; j++, k++)
|
||||
{
|
||||
scrap_texels[texnum][(y + i) * BLOCK_WIDTH + x + j] = pic[k];
|
||||
}
|
||||
}
|
||||
|
||||
image->texnum = TEXNUM_SCRAPS + texnum;
|
||||
image->scrap = true;
|
||||
image->has_alpha = true;
|
||||
image->sl = (x + 0.01) / (float)BLOCK_WIDTH;
|
||||
image->sh = (x + image->width - 0.01) / (float)BLOCK_WIDTH;
|
||||
image->tl = (y + 0.01) / (float)BLOCK_WIDTH;
|
||||
image->th = (y + image->height - 0.01) / (float)BLOCK_WIDTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
nonscrap:
|
||||
image->scrap = false;
|
||||
image->texnum = TEXNUM_IMAGES + (image - gltextures);
|
||||
R_Bind(image->texnum);
|
||||
|
||||
if (bits == 8)
|
||||
{
|
||||
image->has_alpha = R_Upload8(pic, width, height,
|
||||
(image->type != it_pic && image->type != it_sky),
|
||||
image->type == it_sky);
|
||||
}
|
||||
else
|
||||
{
|
||||
image->has_alpha = R_Upload32((unsigned *)pic, width, height,
|
||||
(image->type != it_pic && image->type != it_sky));
|
||||
}
|
||||
|
||||
image->upload_width = upload_width; /* after power of 2 and scales */
|
||||
image->upload_height = upload_height;
|
||||
image->paletted = uploaded_paletted;
|
||||
|
||||
if (realwidth && realheight)
|
||||
{
|
||||
if ((realwidth <= image->width) && (realheight <= image->height))
|
||||
{
|
||||
image->width = realwidth;
|
||||
image->height = realheight;
|
||||
}
|
||||
else
|
||||
{
|
||||
R_Printf(PRINT_DEVELOPER,
|
||||
"Warning, image '%s' has hi-res replacement smaller than the original! (%d x %d) < (%d x %d)\n",
|
||||
name, image->width, image->height, realwidth, realheight);
|
||||
}
|
||||
}
|
||||
|
||||
image->sl = 0;
|
||||
image->sh = 1;
|
||||
image->tl = 0;
|
||||
image->th = 1;
|
||||
|
||||
if (nolerp)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
}
|
||||
}
|
||||
#endif // 0
|
||||
return image;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds or loads the given image or NULL
|
||||
*/
|
||||
gl4image_t *
|
||||
GL4_FindImage(const char *name, imagetype_t type)
|
||||
{
|
||||
gl4image_t *image;
|
||||
int i, len;
|
||||
char *ptr;
|
||||
char namewe[256];
|
||||
const char* ext;
|
||||
|
||||
if (!name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ext = COM_FileExtension(name);
|
||||
if(!ext[0])
|
||||
{
|
||||
/* file has no extension */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = strlen(name);
|
||||
|
||||
/* Remove the extension */
|
||||
memset(namewe, 0, 256);
|
||||
memcpy(namewe, name, len - (strlen(ext) + 1));
|
||||
|
||||
if (len < 5)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* fix backslashes */
|
||||
while ((ptr = strchr(name, '\\')))
|
||||
{
|
||||
*ptr = '/';
|
||||
}
|
||||
|
||||
/* look for it */
|
||||
for (i = 0, image = gl4textures; i < numgl4textures; i++, image++)
|
||||
{
|
||||
if (!strcmp(name, image->name))
|
||||
{
|
||||
image->registration_sequence = registration_sequence;
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// load the pic from disk
|
||||
//
|
||||
image = (gl4image_t *)R_LoadImage(name, namewe, ext, type,
|
||||
r_retexturing->value, (loadimage_t)GL4_LoadPic);
|
||||
|
||||
if (!image && r_validation->value)
|
||||
{
|
||||
R_Printf(PRINT_ALL, "%s: can't load %s\n", __func__, name);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
gl4image_t *
|
||||
GL4_RegisterSkin(char *name)
|
||||
{
|
||||
return GL4_FindImage(name, it_skin);
|
||||
}
|
||||
|
||||
/*
|
||||
* Any image that was not touched on
|
||||
* this registration sequence
|
||||
* will be freed.
|
||||
*/
|
||||
void
|
||||
GL4_FreeUnusedImages(void)
|
||||
{
|
||||
int i;
|
||||
gl4image_t *image;
|
||||
|
||||
/* never free r_notexture or particle texture */
|
||||
gl4_notexture->registration_sequence = registration_sequence;
|
||||
gl4_particletexture->registration_sequence = registration_sequence;
|
||||
|
||||
for (i = 0, image = gl4textures; i < numgl4textures; i++, image++)
|
||||
{
|
||||
if (image->registration_sequence == registration_sequence)
|
||||
{
|
||||
continue; /* used this sequence */
|
||||
}
|
||||
|
||||
if (!image->registration_sequence)
|
||||
{
|
||||
continue; /* free image_t slot */
|
||||
}
|
||||
|
||||
if (image->type == it_pic)
|
||||
{
|
||||
continue; /* don't free pics */
|
||||
}
|
||||
|
||||
/* free it */
|
||||
glDeleteTextures(1, &image->texnum);
|
||||
memset(image, 0, sizeof(*image));
|
||||
}
|
||||
}
|
||||
|
||||
qboolean
|
||||
GL4_ImageHasFreeSpace(void)
|
||||
{
|
||||
int i, used;
|
||||
gl4image_t *image;
|
||||
|
||||
used = 0;
|
||||
|
||||
for (i = 0, image = gl4textures; i < numgl4textures; i++, image++)
|
||||
{
|
||||
if (!image->name[0])
|
||||
continue;
|
||||
if (image->registration_sequence == registration_sequence)
|
||||
{
|
||||
used ++;
|
||||
}
|
||||
}
|
||||
|
||||
if (image_max < used)
|
||||
{
|
||||
image_max = used;
|
||||
}
|
||||
|
||||
// should same size of free slots as currently used
|
||||
return (numgl4textures + used) < MAX_GL4TEXTURES;
|
||||
}
|
||||
|
||||
void
|
||||
GL4_ShutdownImages(void)
|
||||
{
|
||||
int i;
|
||||
gl4image_t *image;
|
||||
|
||||
for (i = 0, image = gl4textures; i < numgl4textures; i++, image++)
|
||||
{
|
||||
if (!image->registration_sequence)
|
||||
{
|
||||
continue; /* free image_t slot */
|
||||
}
|
||||
|
||||
/* free it */
|
||||
glDeleteTextures(1, &image->texnum);
|
||||
memset(image, 0, sizeof(*image));
|
||||
}
|
||||
}
|
||||
|
||||
static qboolean IsNPOT(int v)
|
||||
{
|
||||
unsigned int uv = v;
|
||||
// just try all the power of two values between 1 and 1 << 15 (32k)
|
||||
for(unsigned int i=0; i<16; ++i)
|
||||
{
|
||||
unsigned int pot = (1u << i);
|
||||
if(uv & pot)
|
||||
{
|
||||
return uv != pot;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
GL4_ImageList_f(void)
|
||||
{
|
||||
int i, used, texels;
|
||||
qboolean freeup;
|
||||
gl4image_t *image;
|
||||
const char *formatstrings[2] = {
|
||||
"RGB ",
|
||||
"RGBA"
|
||||
};
|
||||
|
||||
const char* potstrings[2] = {
|
||||
" POT", "NPOT"
|
||||
};
|
||||
|
||||
R_Printf(PRINT_ALL, "------------------\n");
|
||||
texels = 0;
|
||||
used = 0;
|
||||
|
||||
for (i = 0, image = gl4textures; i < numgl4textures; i++, image++)
|
||||
{
|
||||
int w, h;
|
||||
char *in_use = "";
|
||||
qboolean isNPOT = false;
|
||||
if (image->texnum == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (image->registration_sequence == registration_sequence)
|
||||
{
|
||||
in_use = "*";
|
||||
used++;
|
||||
}
|
||||
|
||||
w = image->width;
|
||||
h = image->height;
|
||||
|
||||
isNPOT = IsNPOT(w) || IsNPOT(h);
|
||||
|
||||
texels += w*h;
|
||||
|
||||
char imageType = '?';
|
||||
switch (image->type)
|
||||
{
|
||||
case it_skin:
|
||||
imageType = 'M';
|
||||
break;
|
||||
case it_sprite:
|
||||
imageType = 'S';
|
||||
break;
|
||||
case it_wall:
|
||||
imageType = 'W';
|
||||
break;
|
||||
case it_pic:
|
||||
imageType = 'P';
|
||||
break;
|
||||
case it_sky:
|
||||
imageType = 'Y';
|
||||
break;
|
||||
default:
|
||||
imageType = '?';
|
||||
break;
|
||||
}
|
||||
char isLava = image->is_lava ? 'L' : ' ';
|
||||
|
||||
R_Printf(PRINT_ALL, "%c%c %3i %3i %s %s: %s %s\n", imageType, isLava, w, h,
|
||||
formatstrings[image->has_alpha], potstrings[isNPOT], image->name, in_use);
|
||||
}
|
||||
|
||||
R_Printf(PRINT_ALL, "Total texel count (not counting mipmaps): %i\n", texels);
|
||||
freeup = GL4_ImageHasFreeSpace();
|
||||
R_Printf(PRINT_ALL, "Used %d of %d images%s.\n", used, image_max, freeup ? ", has free space" : "");
|
||||
}
|
403
src/client/refresh/gl4/gl4_light.c
Normal file
403
src/client/refresh/gl4/gl4_light.c
Normal file
|
@ -0,0 +1,403 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (C) 2016-2017 Daniel Gibson
|
||||
*
|
||||
* This program 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 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Lightmaps and dynamic lighting
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#include "header/local.h"
|
||||
|
||||
extern gl4lightmapstate_t gl4_lms;
|
||||
|
||||
int r_dlightframecount;
|
||||
static vec3_t pointcolor;
|
||||
static cplane_t *lightplane; /* used as shadow plane */
|
||||
vec3_t lightspot;
|
||||
|
||||
void
|
||||
GL4_MarkSurfaceLights(dlight_t *light, int bit, mnode_t *node, int r_dlightframecount)
|
||||
{
|
||||
msurface_t *surf;
|
||||
int i;
|
||||
|
||||
/* mark the polygons */
|
||||
surf = gl4_worldmodel->surfaces + node->firstsurface;
|
||||
|
||||
for (i = 0; i < node->numsurfaces; i++, surf++)
|
||||
{
|
||||
int sidebit;
|
||||
float dist;
|
||||
|
||||
if (surf->dlightframe != r_dlightframecount)
|
||||
{
|
||||
surf->dlightbits = 0;
|
||||
surf->dlightframe = r_dlightframecount;
|
||||
}
|
||||
|
||||
dist = DotProduct(light->origin, surf->plane->normal) - surf->plane->dist;
|
||||
|
||||
if (dist >= 0)
|
||||
{
|
||||
sidebit = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
sidebit = SURF_PLANEBACK;
|
||||
}
|
||||
|
||||
if ((surf->flags & SURF_PLANEBACK) != sidebit)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
surf->dlightbits |= bit;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GL4_PushDlights(void)
|
||||
{
|
||||
int i;
|
||||
dlight_t *l;
|
||||
|
||||
/* because the count hasn't advanced yet for this frame */
|
||||
r_dlightframecount = gl4_framecount + 1;
|
||||
|
||||
l = gl4_newrefdef.dlights;
|
||||
|
||||
gl4state.uniLightsData.numDynLights = gl4_newrefdef.num_dlights;
|
||||
|
||||
for (i = 0; i < gl4_newrefdef.num_dlights; i++, l++)
|
||||
{
|
||||
gl4UniDynLight* udl = &gl4state.uniLightsData.dynLights[i];
|
||||
R_MarkLights(l, 1 << i, gl4_worldmodel->nodes, r_dlightframecount, GL4_MarkSurfaceLights);
|
||||
|
||||
VectorCopy(l->origin, udl->origin);
|
||||
VectorCopy(l->color, udl->color);
|
||||
udl->intensity = l->intensity;
|
||||
}
|
||||
|
||||
assert(MAX_DLIGHTS == 32 && "If MAX_DLIGHTS changes, remember to adjust the uniform buffer definition in the shader!");
|
||||
|
||||
if(i < MAX_DLIGHTS)
|
||||
{
|
||||
memset(&gl4state.uniLightsData.dynLights[i], 0, (MAX_DLIGHTS-i)*sizeof(gl4state.uniLightsData.dynLights[0]));
|
||||
}
|
||||
|
||||
GL4_UpdateUBOLights();
|
||||
}
|
||||
|
||||
static int
|
||||
RecursiveLightPoint(mnode_t *node, vec3_t start, vec3_t end)
|
||||
{
|
||||
float front, back, frac;
|
||||
int side;
|
||||
cplane_t *plane;
|
||||
vec3_t mid;
|
||||
msurface_t *surf;
|
||||
int s, t, ds, dt;
|
||||
int i;
|
||||
mtexinfo_t *tex;
|
||||
byte *lightmap;
|
||||
int maps;
|
||||
int r;
|
||||
|
||||
if (node->contents != CONTENTS_NODE)
|
||||
{
|
||||
return -1; /* didn't hit anything */
|
||||
}
|
||||
|
||||
/* calculate mid point */
|
||||
plane = node->plane;
|
||||
front = DotProduct(start, plane->normal) - plane->dist;
|
||||
back = DotProduct(end, plane->normal) - plane->dist;
|
||||
side = front < 0;
|
||||
|
||||
if ((back < 0) == side)
|
||||
{
|
||||
return RecursiveLightPoint(node->children[side], start, end);
|
||||
}
|
||||
|
||||
frac = front / (front - back);
|
||||
mid[0] = start[0] + (end[0] - start[0]) * frac;
|
||||
mid[1] = start[1] + (end[1] - start[1]) * frac;
|
||||
mid[2] = start[2] + (end[2] - start[2]) * frac;
|
||||
|
||||
/* go down front side */
|
||||
r = RecursiveLightPoint(node->children[side], start, mid);
|
||||
|
||||
if (r >= 0)
|
||||
{
|
||||
return r; /* hit something */
|
||||
}
|
||||
|
||||
if ((back < 0) == side)
|
||||
{
|
||||
return -1; /* didn't hit anuthing */
|
||||
}
|
||||
|
||||
/* check for impact on this node */
|
||||
VectorCopy(mid, lightspot);
|
||||
lightplane = plane;
|
||||
|
||||
surf = gl4_worldmodel->surfaces + node->firstsurface;
|
||||
|
||||
for (i = 0; i < node->numsurfaces; i++, surf++)
|
||||
{
|
||||
if (surf->flags & (SURF_DRAWTURB | SURF_DRAWSKY))
|
||||
{
|
||||
continue; /* no lightmaps */
|
||||
}
|
||||
|
||||
tex = surf->texinfo;
|
||||
|
||||
s = DotProduct(mid, tex->vecs[0]) + tex->vecs[0][3];
|
||||
t = DotProduct(mid, tex->vecs[1]) + tex->vecs[1][3];
|
||||
|
||||
if ((s < surf->texturemins[0]) ||
|
||||
(t < surf->texturemins[1]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ds = s - surf->texturemins[0];
|
||||
dt = t - surf->texturemins[1];
|
||||
|
||||
if ((ds > surf->extents[0]) || (dt > surf->extents[1]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!surf->samples)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ds >>= 4;
|
||||
dt >>= 4;
|
||||
|
||||
lightmap = surf->samples;
|
||||
VectorCopy(vec3_origin, pointcolor);
|
||||
|
||||
lightmap += 3 * (dt * ((surf->extents[0] >> 4) + 1) + ds);
|
||||
|
||||
for (maps = 0; maps < MAX_LIGHTMAPS_PER_SURFACE && surf->styles[maps] != 255;
|
||||
maps++)
|
||||
{
|
||||
const float *rgb;
|
||||
int j;
|
||||
|
||||
rgb = gl4_newrefdef.lightstyles[surf->styles[maps]].rgb;
|
||||
|
||||
/* Apply light level to models */
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
float scale;
|
||||
|
||||
scale = rgb[j] * r_modulate->value;
|
||||
pointcolor[j] += lightmap[j] * scale * (1.0 / 255);
|
||||
}
|
||||
|
||||
lightmap += 3 * ((surf->extents[0] >> 4) + 1) *
|
||||
((surf->extents[1] >> 4) + 1);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* go down back side */
|
||||
return RecursiveLightPoint(node->children[!side], mid, end);
|
||||
}
|
||||
|
||||
void
|
||||
GL4_LightPoint(entity_t *currententity, vec3_t p, vec3_t color)
|
||||
{
|
||||
vec3_t end;
|
||||
float r;
|
||||
int lnum;
|
||||
dlight_t *dl;
|
||||
vec3_t dist;
|
||||
float add;
|
||||
|
||||
if (!gl4_worldmodel->lightdata || !currententity)
|
||||
{
|
||||
color[0] = color[1] = color[2] = 1.0;
|
||||
return;
|
||||
}
|
||||
|
||||
end[0] = p[0];
|
||||
end[1] = p[1];
|
||||
end[2] = p[2] - 2048;
|
||||
|
||||
// TODO: don't just aggregate the color, but also save position of brightest+nearest light
|
||||
// for shadow position and maybe lighting on model?
|
||||
|
||||
r = RecursiveLightPoint(gl4_worldmodel->nodes, p, end);
|
||||
|
||||
if (r == -1)
|
||||
{
|
||||
VectorCopy(vec3_origin, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy(pointcolor, color);
|
||||
}
|
||||
|
||||
/* add dynamic lights */
|
||||
dl = gl4_newrefdef.dlights;
|
||||
|
||||
for (lnum = 0; lnum < gl4_newrefdef.num_dlights; lnum++, dl++)
|
||||
{
|
||||
VectorSubtract(currententity->origin,
|
||||
dl->origin, dist);
|
||||
add = dl->intensity - VectorLength(dist);
|
||||
add *= (1.0f / 256.0f);
|
||||
|
||||
if (add > 0)
|
||||
{
|
||||
VectorMA(color, add, dl->color, color);
|
||||
}
|
||||
}
|
||||
|
||||
VectorScale(color, r_modulate->value, color);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Combine and scale multiple lightmaps into the floating format in blocklights
|
||||
*/
|
||||
void
|
||||
GL4_BuildLightMap(msurface_t *surf, int offsetInLMbuf, int stride)
|
||||
{
|
||||
int smax, tmax;
|
||||
int r, g, b, a, max;
|
||||
int i, j, size, map, numMaps;
|
||||
byte *lightmap;
|
||||
|
||||
if (surf->texinfo->flags &
|
||||
(SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP))
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "GL4_BuildLightMap called for non-lit surface");
|
||||
}
|
||||
|
||||
smax = (surf->extents[0] >> 4) + 1;
|
||||
tmax = (surf->extents[1] >> 4) + 1;
|
||||
size = smax * tmax;
|
||||
|
||||
stride -= (smax << 2);
|
||||
|
||||
if (size > 34*34*3)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "Bad s_blocklights size");
|
||||
}
|
||||
|
||||
// count number of lightmaps surf actually has
|
||||
for (numMaps = 0; numMaps < MAX_LIGHTMAPS_PER_SURFACE && surf->styles[numMaps] != 255; ++numMaps)
|
||||
{}
|
||||
|
||||
if (!surf->samples)
|
||||
{
|
||||
// no lightmap samples? set at least one lightmap to fullbright, rest to 0 as normal
|
||||
|
||||
if (numMaps == 0) numMaps = 1; // make sure at least one lightmap is set to fullbright
|
||||
|
||||
for (map = 0; map < MAX_LIGHTMAPS_PER_SURFACE; ++map)
|
||||
{
|
||||
// we always create 4 (MAX_LIGHTMAPS_PER_SURFACE) lightmaps.
|
||||
// if surf has less (numMaps < 4), the remaining ones are zeroed out.
|
||||
// this makes sure that all 4 lightmap textures in gl4state.lightmap_textureIDs[i] have the same layout
|
||||
// and the shader can use the same texture coordinates for all of them
|
||||
|
||||
int c = (map < numMaps) ? 255 : 0;
|
||||
byte* dest = gl4_lms.lightmap_buffers[map] + offsetInLMbuf;
|
||||
|
||||
for (i = 0; i < tmax; i++, dest += stride)
|
||||
{
|
||||
memset(dest, c, 4*smax);
|
||||
dest += 4*smax;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* add all the lightmaps */
|
||||
|
||||
// Note: dynamic lights aren't handled here anymore, they're handled in the shader
|
||||
|
||||
// as we don't apply scale here anymore, nor blend the numMaps lightmaps together,
|
||||
// the code has gotten a lot easier and we can copy directly from surf->samples to dest
|
||||
// without converting to float first etc
|
||||
|
||||
lightmap = surf->samples;
|
||||
|
||||
for(map=0; map<numMaps; ++map)
|
||||
{
|
||||
byte* dest = gl4_lms.lightmap_buffers[map] + offsetInLMbuf;
|
||||
int idxInLightmap = 0;
|
||||
for (i = 0; i < tmax; i++, dest += stride)
|
||||
{
|
||||
for (j = 0; j < smax; j++)
|
||||
{
|
||||
r = lightmap[idxInLightmap * 3 + 0];
|
||||
g = lightmap[idxInLightmap * 3 + 1];
|
||||
b = lightmap[idxInLightmap * 3 + 2];
|
||||
|
||||
/* determine the brightest of the three color components */
|
||||
if (r > g) max = r;
|
||||
else max = g;
|
||||
|
||||
if (b > max) max = b;
|
||||
|
||||
/* alpha is ONLY used for the mono lightmap case. For this
|
||||
reason we set it to the brightest of the color components
|
||||
so that things don't get too dim. */
|
||||
a = max;
|
||||
|
||||
dest[0] = r;
|
||||
dest[1] = g;
|
||||
dest[2] = b;
|
||||
dest[3] = a;
|
||||
|
||||
dest += 4;
|
||||
++idxInLightmap;
|
||||
}
|
||||
}
|
||||
|
||||
lightmap += size * 3; /* skip to next lightmap */
|
||||
}
|
||||
|
||||
for ( ; map < MAX_LIGHTMAPS_PER_SURFACE; ++map)
|
||||
{
|
||||
// like above, fill up remaining lightmaps with 0
|
||||
|
||||
byte* dest = gl4_lms.lightmap_buffers[map] + offsetInLMbuf;
|
||||
|
||||
for (i = 0; i < tmax; i++, dest += stride)
|
||||
{
|
||||
memset(dest, 0, 4*smax);
|
||||
dest += 4*smax;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
268
src/client/refresh/gl4/gl4_lightmap.c
Normal file
268
src/client/refresh/gl4/gl4_lightmap.c
Normal file
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (C) 2016-2017 Daniel Gibson
|
||||
*
|
||||
* This program 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 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Lightmap handling
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#include "header/local.h"
|
||||
|
||||
|
||||
#define TEXNUM_LIGHTMAPS 1024
|
||||
|
||||
extern gl4lightmapstate_t gl4_lms;
|
||||
|
||||
void
|
||||
GL4_LM_InitBlock(void)
|
||||
{
|
||||
memset(gl4_lms.allocated, 0, sizeof(gl4_lms.allocated));
|
||||
}
|
||||
|
||||
void
|
||||
GL4_LM_UploadBlock(void)
|
||||
{
|
||||
int map;
|
||||
|
||||
// NOTE: we don't use the dynamic lightmap anymore - all lightmaps are loaded at level load
|
||||
// and not changed after that. they're blended dynamically depending on light styles
|
||||
// though, and dynamic lights are (will be) applied in shader, hopefully per fragment.
|
||||
|
||||
GL4_BindLightmap(gl4_lms.current_lightmap_texture);
|
||||
|
||||
// upload all 4 lightmaps
|
||||
for(map=0; map < MAX_LIGHTMAPS_PER_SURFACE; ++map)
|
||||
{
|
||||
GL4_SelectTMU(GL_TEXTURE1+map); // this relies on GL_TEXTURE2 being GL_TEXTURE1+1 etc
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
gl4_lms.internal_format = GL_LIGHTMAP_FORMAT;
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, gl4_lms.internal_format,
|
||||
BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT,
|
||||
GL_UNSIGNED_BYTE, gl4_lms.lightmap_buffers[map]);
|
||||
}
|
||||
|
||||
if (++gl4_lms.current_lightmap_texture == MAX_LIGHTMAPS)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "LM_UploadBlock() - MAX_LIGHTMAPS exceeded\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* returns a texture number and the position inside it
|
||||
*/
|
||||
qboolean
|
||||
GL4_LM_AllocBlock(int w, int h, int *x, int *y)
|
||||
{
|
||||
int i, j;
|
||||
int best, best2;
|
||||
|
||||
best = BLOCK_HEIGHT;
|
||||
|
||||
for (i = 0; i < BLOCK_WIDTH - w; i++)
|
||||
{
|
||||
best2 = 0;
|
||||
|
||||
for (j = 0; j < w; j++)
|
||||
{
|
||||
if (gl4_lms.allocated[i + j] >= best)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (gl4_lms.allocated[i + j] > best2)
|
||||
{
|
||||
best2 = gl4_lms.allocated[i + j];
|
||||
}
|
||||
}
|
||||
|
||||
if (j == w)
|
||||
{
|
||||
/* this is a valid spot */
|
||||
*x = i;
|
||||
*y = best = best2;
|
||||
}
|
||||
}
|
||||
|
||||
if (best + h > BLOCK_HEIGHT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < w; i++)
|
||||
{
|
||||
gl4_lms.allocated[*x + i] = best + h;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
GL4_LM_BuildPolygonFromSurface(gl4model_t *currentmodel, msurface_t *fa)
|
||||
{
|
||||
int i, lindex, lnumverts;
|
||||
medge_t *pedges, *r_pedge;
|
||||
float *vec;
|
||||
float s, t;
|
||||
glpoly_t *poly;
|
||||
vec3_t total;
|
||||
vec3_t normal;
|
||||
|
||||
/* reconstruct the polygon */
|
||||
pedges = currentmodel->edges;
|
||||
lnumverts = fa->numedges;
|
||||
|
||||
VectorClear(total);
|
||||
|
||||
/* draw texture */
|
||||
poly = Hunk_Alloc(sizeof(glpoly_t) +
|
||||
(lnumverts - 4) * sizeof(gl4_3D_vtx_t));
|
||||
poly->next = fa->polys;
|
||||
poly->flags = fa->flags;
|
||||
fa->polys = poly;
|
||||
poly->numverts = lnumverts;
|
||||
|
||||
VectorCopy(fa->plane->normal, normal);
|
||||
|
||||
if(fa->flags & SURF_PLANEBACK)
|
||||
{
|
||||
// if for some reason the normal sticks to the back of the plane, invert it
|
||||
// so it's usable for the shader
|
||||
for (i=0; i<3; ++i) normal[i] = -normal[i];
|
||||
}
|
||||
|
||||
for (i = 0; i < lnumverts; i++)
|
||||
{
|
||||
gl4_3D_vtx_t* vert = &poly->vertices[i];
|
||||
|
||||
lindex = currentmodel->surfedges[fa->firstedge + i];
|
||||
|
||||
if (lindex > 0)
|
||||
{
|
||||
r_pedge = &pedges[lindex];
|
||||
vec = currentmodel->vertexes[r_pedge->v[0]].position;
|
||||
}
|
||||
else
|
||||
{
|
||||
r_pedge = &pedges[-lindex];
|
||||
vec = currentmodel->vertexes[r_pedge->v[1]].position;
|
||||
}
|
||||
|
||||
s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
|
||||
s /= fa->texinfo->image->width;
|
||||
|
||||
t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
|
||||
t /= fa->texinfo->image->height;
|
||||
|
||||
VectorAdd(total, vec, total);
|
||||
VectorCopy(vec, vert->pos);
|
||||
vert->texCoord[0] = s;
|
||||
vert->texCoord[1] = t;
|
||||
|
||||
/* lightmap texture coordinates */
|
||||
s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
|
||||
s -= fa->texturemins[0];
|
||||
s += fa->light_s * 16;
|
||||
s += 8;
|
||||
s /= BLOCK_WIDTH * 16; /* fa->texinfo->texture->width; */
|
||||
|
||||
t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
|
||||
t -= fa->texturemins[1];
|
||||
t += fa->light_t * 16;
|
||||
t += 8;
|
||||
t /= BLOCK_HEIGHT * 16; /* fa->texinfo->texture->height; */
|
||||
|
||||
vert->lmTexCoord[0] = s;
|
||||
vert->lmTexCoord[1] = t;
|
||||
|
||||
VectorCopy(normal, vert->normal);
|
||||
vert->lightFlags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GL4_LM_CreateSurfaceLightmap(msurface_t *surf)
|
||||
{
|
||||
int smax, tmax;
|
||||
|
||||
if (surf->flags & (SURF_DRAWSKY | SURF_DRAWTURB))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
smax = (surf->extents[0] >> 4) + 1;
|
||||
tmax = (surf->extents[1] >> 4) + 1;
|
||||
|
||||
if (!GL4_LM_AllocBlock(smax, tmax, &surf->light_s, &surf->light_t))
|
||||
{
|
||||
GL4_LM_UploadBlock();
|
||||
GL4_LM_InitBlock();
|
||||
|
||||
if (!GL4_LM_AllocBlock(smax, tmax, &surf->light_s, &surf->light_t))
|
||||
{
|
||||
ri.Sys_Error(ERR_FATAL, "Consecutive calls to LM_AllocBlock(%d,%d) failed\n",
|
||||
smax, tmax);
|
||||
}
|
||||
}
|
||||
|
||||
surf->lightmaptexturenum = gl4_lms.current_lightmap_texture;
|
||||
|
||||
GL4_BuildLightMap(surf, (surf->light_t * BLOCK_WIDTH + surf->light_s) * LIGHTMAP_BYTES, BLOCK_WIDTH * LIGHTMAP_BYTES);
|
||||
}
|
||||
|
||||
void
|
||||
GL4_LM_BeginBuildingLightmaps(gl4model_t *m)
|
||||
{
|
||||
|
||||
static lightstyle_t lightstyles[MAX_LIGHTSTYLES];
|
||||
int i;
|
||||
|
||||
memset(gl4_lms.allocated, 0, sizeof(gl4_lms.allocated));
|
||||
|
||||
gl4_framecount = 1; /* no dlightcache */
|
||||
|
||||
/* setup the base lightstyles so the lightmaps
|
||||
won't have to be regenerated the first time
|
||||
they're seen */
|
||||
for (i = 0; i < MAX_LIGHTSTYLES; i++)
|
||||
{
|
||||
lightstyles[i].rgb[0] = 1;
|
||||
lightstyles[i].rgb[1] = 1;
|
||||
lightstyles[i].rgb[2] = 1;
|
||||
lightstyles[i].white = 3;
|
||||
}
|
||||
|
||||
gl4_newrefdef.lightstyles = lightstyles;
|
||||
|
||||
gl4_lms.current_lightmap_texture = 0;
|
||||
gl4_lms.internal_format = GL_LIGHTMAP_FORMAT;
|
||||
|
||||
// Note: the dynamic lightmap used to be initialized here, we don't use that anymore.
|
||||
}
|
||||
|
||||
void
|
||||
GL4_LM_EndBuildingLightmaps(void)
|
||||
{
|
||||
GL4_LM_UploadBlock();
|
||||
}
|
||||
|
2059
src/client/refresh/gl4/gl4_main.c
Normal file
2059
src/client/refresh/gl4/gl4_main.c
Normal file
File diff suppressed because it is too large
Load diff
1002
src/client/refresh/gl4/gl4_mesh.c
Normal file
1002
src/client/refresh/gl4/gl4_mesh.c
Normal file
File diff suppressed because it is too large
Load diff
171
src/client/refresh/gl4/gl4_misc.c
Normal file
171
src/client/refresh/gl4/gl4_misc.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (C) 2016-2017 Daniel Gibson
|
||||
*
|
||||
* This program 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 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Misc OpenGL4 refresher functions
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#include "header/local.h"
|
||||
|
||||
gl4image_t *gl4_notexture; /* use for bad textures */
|
||||
gl4image_t *gl4_particletexture; /* little dot for particles */
|
||||
|
||||
void
|
||||
GL4_SetDefaultState(void)
|
||||
{
|
||||
glClearColor(1, 0, 0.5, 0.5);
|
||||
#ifndef YQ2_GL3_GLES
|
||||
// in GLES this is only supported with an extension:
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_multisample_compatibility.txt
|
||||
// but apparently it's just enabled by default if set in the context?
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
#endif
|
||||
glCullFace(GL_FRONT);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
#ifndef YQ2_GL3_GLES
|
||||
// in GLES GL_FILL is the only supported mode
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
#endif
|
||||
|
||||
// TODO: gl1_texturealphamode?
|
||||
GL4_TextureMode(gl_texturemode->string);
|
||||
//R_TextureAlphaMode(gl1_texturealphamode->string);
|
||||
//R_TextureSolidMode(gl1_texturesolidmode->string);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
#ifndef YQ2_GL3_GLES // see above
|
||||
if (gl_msaa_samples->value)
|
||||
{
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
// glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST); TODO what is this for?
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static byte dottexture[8][8] = {
|
||||
{0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 1, 1, 0, 0, 0, 0},
|
||||
{0, 1, 1, 1, 1, 0, 0, 0},
|
||||
{0, 1, 1, 1, 1, 0, 0, 0},
|
||||
{0, 0, 1, 1, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0},
|
||||
};
|
||||
|
||||
void
|
||||
GL4_InitParticleTexture(void)
|
||||
{
|
||||
int x, y;
|
||||
byte data[8][8][4];
|
||||
|
||||
/* particle texture */
|
||||
for (x = 0; x < 8; x++)
|
||||
{
|
||||
for (y = 0; y < 8; y++)
|
||||
{
|
||||
data[y][x][0] = 255;
|
||||
data[y][x][1] = 255;
|
||||
data[y][x][2] = 255;
|
||||
data[y][x][3] = dottexture[x][y] * 255;
|
||||
}
|
||||
}
|
||||
|
||||
gl4_particletexture = GL4_LoadPic("***particle***", (byte *)data,
|
||||
8, 0, 8, 0, 8 * 8, it_sprite, 32);
|
||||
|
||||
/* also use this for bad textures, but without alpha */
|
||||
for (x = 0; x < 8; x++)
|
||||
{
|
||||
for (y = 0; y < 8; y++)
|
||||
{
|
||||
data[y][x][0] = dottexture[x & 3][y & 3] * 255;
|
||||
data[y][x][1] = 0;
|
||||
data[y][x][2] = 0;
|
||||
data[y][x][3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
gl4_notexture = GL4_LoadPic("***r_notexture***", (byte *)data,
|
||||
8, 0, 8, 0, 8 * 8, it_wall, 32);
|
||||
}
|
||||
|
||||
void
|
||||
GL4_ScreenShot(void)
|
||||
{
|
||||
int w=vid.width, h=vid.height;
|
||||
|
||||
#ifdef YQ2_GL3_GLES
|
||||
// My RPi4's GLES3 doesn't like GL_RGB, so use GL_RGBA with GLES
|
||||
// TODO: we could convert the screenshot to RGB before writing
|
||||
// so the resulting file is smaller
|
||||
static const int comps = 4;
|
||||
#else // Desktop GL
|
||||
static const int comps = 3;
|
||||
#endif
|
||||
byte *buffer = malloc(w*h*comps);
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
R_Printf(PRINT_ALL, "GL4_ScreenShot: Couldn't malloc %d bytes\n", w*h*3);
|
||||
return;
|
||||
}
|
||||
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glReadPixels(0, 0, w, h, (comps == 4) ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
// the pixels are now row-wise left to right, bottom to top,
|
||||
// but we need them row-wise left to right, top to bottom.
|
||||
// so swap bottom rows with top rows
|
||||
{
|
||||
size_t bytesPerRow = comps*w;
|
||||
YQ2_VLA(byte, rowBuffer, bytesPerRow);
|
||||
byte *curRowL = buffer; // first byte of first row
|
||||
byte *curRowH = buffer + bytesPerRow*(h-1); // first byte of last row
|
||||
while(curRowL < curRowH)
|
||||
{
|
||||
memcpy(rowBuffer, curRowL, bytesPerRow);
|
||||
memcpy(curRowL, curRowH, bytesPerRow);
|
||||
memcpy(curRowH, rowBuffer, bytesPerRow);
|
||||
|
||||
curRowL += bytesPerRow;
|
||||
curRowH -= bytesPerRow;
|
||||
}
|
||||
YQ2_VLAFREE(rowBuffer);
|
||||
}
|
||||
|
||||
ri.Vid_WriteScreenshot(w, h, comps, buffer);
|
||||
|
||||
free(buffer);
|
||||
}
|
834
src/client/refresh/gl4/gl4_model.c
Normal file
834
src/client/refresh/gl4/gl4_model.c
Normal file
|
@ -0,0 +1,834 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (C) 2016-2017 Daniel Gibson
|
||||
*
|
||||
* This program 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 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Model loading and caching for OpenGL4. Includes the .bsp file format
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#include "header/local.h"
|
||||
|
||||
enum { MAX_MOD_KNOWN = 512 };
|
||||
|
||||
YQ2_ALIGNAS_TYPE(int) static byte mod_novis[MAX_MAP_LEAFS / 8];
|
||||
gl4model_t mod_known[MAX_MOD_KNOWN];
|
||||
static int mod_numknown;
|
||||
static int mod_max = 0;
|
||||
int registration_sequence;
|
||||
|
||||
//===============================================================================
|
||||
|
||||
static qboolean
|
||||
Mod_HasFreeSpace(void)
|
||||
{
|
||||
int i, used;
|
||||
gl4model_t *mod;
|
||||
|
||||
used = 0;
|
||||
|
||||
for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++)
|
||||
{
|
||||
if (!mod->name[0])
|
||||
continue;
|
||||
if (mod->registration_sequence == registration_sequence)
|
||||
{
|
||||
used ++;
|
||||
}
|
||||
}
|
||||
|
||||
if (mod_max < used)
|
||||
{
|
||||
mod_max = used;
|
||||
}
|
||||
|
||||
// should same size of free slots as currently used
|
||||
return (mod_numknown + mod_max) < MAX_MOD_KNOWN;
|
||||
}
|
||||
|
||||
const byte*
|
||||
GL4_Mod_ClusterPVS(int cluster, const gl4model_t *model)
|
||||
{
|
||||
if ((cluster == -1) || !model->vis)
|
||||
{
|
||||
return mod_novis;
|
||||
}
|
||||
|
||||
return Mod_DecompressVis((byte *)model->vis +
|
||||
model->vis->bitofs[cluster][DVIS_PVS],
|
||||
(model->vis->numclusters + 7) >> 3);
|
||||
}
|
||||
|
||||
void
|
||||
GL4_Mod_Modellist_f(void)
|
||||
{
|
||||
int i, total, used;
|
||||
gl4model_t *mod;
|
||||
qboolean freeup;
|
||||
|
||||
total = 0;
|
||||
used = 0;
|
||||
R_Printf(PRINT_ALL, "Loaded models:\n");
|
||||
|
||||
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
|
||||
{
|
||||
char *in_use = "";
|
||||
|
||||
if (mod->registration_sequence == registration_sequence)
|
||||
{
|
||||
in_use = "*";
|
||||
used ++;
|
||||
}
|
||||
|
||||
if (!mod->name[0])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
R_Printf(PRINT_ALL, "%8i : %s %s\n",
|
||||
mod->extradatasize, mod->name, in_use);
|
||||
total += mod->extradatasize;
|
||||
}
|
||||
|
||||
R_Printf(PRINT_ALL, "Total resident: %i\n", total);
|
||||
// update statistics
|
||||
freeup = Mod_HasFreeSpace();
|
||||
R_Printf(PRINT_ALL, "Used %d of %d models%s.\n", used, mod_max, freeup ? ", has free space" : "");
|
||||
}
|
||||
|
||||
void
|
||||
GL4_Mod_Init(void)
|
||||
{
|
||||
mod_max = 0;
|
||||
memset(mod_novis, 0xff, sizeof(mod_novis));
|
||||
}
|
||||
|
||||
static void
|
||||
Mod_LoadSubmodels(gl4model_t *loadmodel, byte *mod_base, lump_t *l)
|
||||
{
|
||||
dmodel_t *in;
|
||||
gl4model_t *out;
|
||||
int i, j, count;
|
||||
|
||||
in = (void *)(mod_base + l->fileofs);
|
||||
|
||||
if (l->filelen % sizeof(*in))
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s",
|
||||
__func__, loadmodel->name);
|
||||
}
|
||||
|
||||
count = l->filelen / sizeof(*in);
|
||||
out = Hunk_Alloc(count * sizeof(*out));
|
||||
|
||||
loadmodel->submodels = out;
|
||||
loadmodel->numsubmodels = count;
|
||||
|
||||
for (i = 0; i < count; i++, in++, out++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
// copy parent as template for first model
|
||||
memcpy(out, loadmodel, sizeof(*out));
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy first as template for model
|
||||
memcpy(out, loadmodel->submodels, sizeof(*out));
|
||||
}
|
||||
|
||||
Com_sprintf (out->name, sizeof(out->name), "*%d", i);
|
||||
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
/* spread the mins / maxs by a pixel */
|
||||
out->mins[j] = LittleFloat(in->mins[j]) - 1;
|
||||
out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
|
||||
out->origin[j] = LittleFloat(in->origin[j]);
|
||||
}
|
||||
|
||||
out->radius = Mod_RadiusFromBounds(out->mins, out->maxs);
|
||||
out->firstnode = LittleLong(in->headnode);
|
||||
out->firstmodelsurface = LittleLong(in->firstface);
|
||||
out->nummodelsurfaces = LittleLong(in->numfaces);
|
||||
// visleafs
|
||||
out->numleafs = 0;
|
||||
// check limits
|
||||
if (out->firstnode >= loadmodel->numnodes)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: Inline model %i has bad firstnode",
|
||||
__func__, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills in s->texturemins[] and s->extents[]
|
||||
*/
|
||||
static void
|
||||
Mod_CalcSurfaceExtents(gl4model_t *loadmodel, msurface_t *s)
|
||||
{
|
||||
float mins[2], maxs[2], val;
|
||||
int i, j, e;
|
||||
mvertex_t *v;
|
||||
mtexinfo_t *tex;
|
||||
int bmins[2], bmaxs[2];
|
||||
|
||||
mins[0] = mins[1] = 999999;
|
||||
maxs[0] = maxs[1] = -99999;
|
||||
|
||||
tex = s->texinfo;
|
||||
|
||||
for (i = 0; i < s->numedges; i++)
|
||||
{
|
||||
e = loadmodel->surfedges[s->firstedge + i];
|
||||
|
||||
if (e >= 0)
|
||||
{
|
||||
v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
|
||||
}
|
||||
else
|
||||
{
|
||||
v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
|
||||
}
|
||||
|
||||
for (j = 0; j < 2; j++)
|
||||
{
|
||||
val = v->position[0] * tex->vecs[j][0] +
|
||||
v->position[1] * tex->vecs[j][1] +
|
||||
v->position[2] * tex->vecs[j][2] +
|
||||
tex->vecs[j][3];
|
||||
|
||||
if (val < mins[j])
|
||||
{
|
||||
mins[j] = val;
|
||||
}
|
||||
|
||||
if (val > maxs[j])
|
||||
{
|
||||
maxs[j] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
bmins[i] = floor(mins[i] / 16);
|
||||
bmaxs[i] = ceil(maxs[i] / 16);
|
||||
|
||||
s->texturemins[i] = bmins[i] * 16;
|
||||
s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
|
||||
}
|
||||
}
|
||||
|
||||
extern void
|
||||
GL4_SubdivideSurface(msurface_t *fa, gl4model_t* loadmodel);
|
||||
|
||||
static int calcTexinfoAndFacesSize(byte *mod_base, const lump_t *fl, const lump_t *tl)
|
||||
{
|
||||
dface_t* face_in = (void *)(mod_base + fl->fileofs);
|
||||
texinfo_t* texinfo_in = (void *)(mod_base + tl->fileofs);
|
||||
|
||||
if (fl->filelen % sizeof(*face_in) || tl->filelen % sizeof(*texinfo_in))
|
||||
{
|
||||
// will error out when actually loading it
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
|
||||
int face_count = fl->filelen / sizeof(*face_in);
|
||||
int texinfo_count = tl->filelen / sizeof(*texinfo_in);
|
||||
|
||||
{
|
||||
// out = Hunk_Alloc(count * sizeof(*out));
|
||||
int baseSize = face_count * sizeof(msurface_t);
|
||||
baseSize = (baseSize + 31) & ~31;
|
||||
ret += baseSize;
|
||||
|
||||
int ti_size = texinfo_count * sizeof(mtexinfo_t);
|
||||
ti_size = (ti_size + 31) & ~31;
|
||||
ret += ti_size;
|
||||
}
|
||||
|
||||
int numWarpFaces = 0;
|
||||
|
||||
for (int surfnum = 0; surfnum < face_count; surfnum++, face_in++)
|
||||
{
|
||||
int numverts = LittleShort(face_in->numedges);
|
||||
int ti = LittleShort(face_in->texinfo);
|
||||
if ((ti < 0) || (ti >= texinfo_count))
|
||||
{
|
||||
return 0; // will error out
|
||||
}
|
||||
int texFlags = LittleLong(texinfo_in[ti].flags);
|
||||
|
||||
/* set the drawing flags */
|
||||
if (texFlags & SURF_WARP)
|
||||
{
|
||||
if (numverts > 60)
|
||||
return 0; // will error out in R_SubdividePolygon()
|
||||
|
||||
// GL4_SubdivideSurface(out, loadmodel); /* cut up polygon for warps */
|
||||
// for each (pot. recursive) call to R_SubdividePolygon():
|
||||
// sizeof(glpoly_t) + ((numverts - 4) + 2) * sizeof(gl4_3D_vtx_t)
|
||||
|
||||
// this is tricky, how much is allocated depends on the size of the surface
|
||||
// which we don't know (we'd need the vertices etc to know, but we can't load
|
||||
// those without allocating...)
|
||||
// so we just count warped faces and use a generous estimate below
|
||||
|
||||
++numWarpFaces;
|
||||
}
|
||||
else
|
||||
{
|
||||
// GL4_LM_BuildPolygonFromSurface(out);
|
||||
// => poly = Hunk_Alloc(sizeof(glpoly_t) + (numverts - 4) * sizeof(gl4_3D_vtx_t));
|
||||
int polySize = sizeof(glpoly_t) + (numverts - 4) * sizeof(gl4_3D_vtx_t);
|
||||
polySize = (polySize + 31) & ~31;
|
||||
ret += polySize;
|
||||
}
|
||||
}
|
||||
|
||||
// yeah, this is a bit hacky, but it looks like for each warped face
|
||||
// 256-55000 bytes are allocated (usually on the lower end),
|
||||
// so just assume 48k per face to be safe
|
||||
ret += numWarpFaces * 49152;
|
||||
ret += 5000000; // and 5MB extra just in case
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
Mod_LoadFaces(gl4model_t *loadmodel, byte *mod_base, lump_t *l)
|
||||
{
|
||||
dface_t *in;
|
||||
msurface_t *out;
|
||||
int i, count, surfnum;
|
||||
int planenum, side;
|
||||
int ti;
|
||||
|
||||
in = (void *)(mod_base + l->fileofs);
|
||||
|
||||
if (l->filelen % sizeof(*in))
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s",
|
||||
__func__, loadmodel->name);
|
||||
}
|
||||
|
||||
count = l->filelen / sizeof(*in);
|
||||
out = Hunk_Alloc(count * sizeof(*out));
|
||||
|
||||
loadmodel->surfaces = out;
|
||||
loadmodel->numsurfaces = count;
|
||||
|
||||
GL4_LM_BeginBuildingLightmaps(loadmodel);
|
||||
|
||||
for (surfnum = 0; surfnum < count; surfnum++, in++, out++)
|
||||
{
|
||||
out->firstedge = LittleLong(in->firstedge);
|
||||
out->numedges = LittleShort(in->numedges);
|
||||
out->flags = 0;
|
||||
out->polys = NULL;
|
||||
|
||||
planenum = LittleShort(in->planenum);
|
||||
side = LittleShort(in->side);
|
||||
|
||||
if (side)
|
||||
{
|
||||
out->flags |= SURF_PLANEBACK;
|
||||
}
|
||||
|
||||
if (planenum < 0 || planenum >= loadmodel->numplanes)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: Incorrect %d planenum.",
|
||||
__func__, planenum);
|
||||
}
|
||||
out->plane = loadmodel->planes + planenum;
|
||||
|
||||
ti = LittleShort(in->texinfo);
|
||||
|
||||
if ((ti < 0) || (ti >= loadmodel->numtexinfo))
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: bad texinfo number",
|
||||
__func__);
|
||||
}
|
||||
|
||||
out->texinfo = loadmodel->texinfo + ti;
|
||||
|
||||
Mod_CalcSurfaceExtents(loadmodel, out);
|
||||
|
||||
/* lighting info */
|
||||
for (i = 0; i < MAX_LIGHTMAPS_PER_SURFACE; i++)
|
||||
{
|
||||
out->styles[i] = in->styles[i];
|
||||
}
|
||||
|
||||
i = LittleLong(in->lightofs);
|
||||
|
||||
if (i == -1)
|
||||
{
|
||||
out->samples = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
out->samples = loadmodel->lightdata + i;
|
||||
}
|
||||
|
||||
/* set the drawing flags */
|
||||
if (out->texinfo->flags & SURF_WARP)
|
||||
{
|
||||
out->flags |= SURF_DRAWTURB;
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
out->extents[i] = 16384;
|
||||
out->texturemins[i] = -8192;
|
||||
}
|
||||
|
||||
GL4_SubdivideSurface(out, loadmodel); /* cut up polygon for warps */
|
||||
}
|
||||
|
||||
if (r_fixsurfsky->value)
|
||||
{
|
||||
if (out->texinfo->flags & SURF_SKY)
|
||||
{
|
||||
out->flags |= SURF_DRAWSKY;
|
||||
}
|
||||
}
|
||||
|
||||
/* create lightmaps and polygons */
|
||||
if (!(out->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP)))
|
||||
{
|
||||
GL4_LM_CreateSurfaceLightmap(out);
|
||||
}
|
||||
|
||||
if (!(out->texinfo->flags & SURF_WARP))
|
||||
{
|
||||
GL4_LM_BuildPolygonFromSurface(loadmodel, out);
|
||||
}
|
||||
}
|
||||
|
||||
GL4_LM_EndBuildingLightmaps();
|
||||
}
|
||||
|
||||
static void
|
||||
Mod_LoadLeafs(gl4model_t *loadmodel, byte *mod_base, lump_t *l)
|
||||
{
|
||||
dleaf_t *in;
|
||||
mleaf_t *out;
|
||||
int i, j, count, p;
|
||||
|
||||
in = (void *)(mod_base + l->fileofs);
|
||||
|
||||
if (l->filelen % sizeof(*in))
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s",
|
||||
__func__, loadmodel->name);
|
||||
}
|
||||
|
||||
count = l->filelen / sizeof(*in);
|
||||
out = Hunk_Alloc(count * sizeof(*out));
|
||||
|
||||
loadmodel->leafs = out;
|
||||
loadmodel->numleafs = count;
|
||||
|
||||
for (i = 0; i < count; i++, in++, out++)
|
||||
{
|
||||
unsigned firstleafface;
|
||||
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
out->minmaxs[j] = LittleShort(in->mins[j]);
|
||||
out->minmaxs[3 + j] = LittleShort(in->maxs[j]);
|
||||
}
|
||||
|
||||
p = LittleLong(in->contents);
|
||||
out->contents = p;
|
||||
|
||||
out->cluster = LittleShort(in->cluster);
|
||||
out->area = LittleShort(in->area);
|
||||
|
||||
// make unsigned long from signed short
|
||||
firstleafface = LittleShort(in->firstleafface) & 0xFFFF;
|
||||
out->nummarksurfaces = LittleShort(in->numleaffaces) & 0xFFFF;
|
||||
|
||||
out->firstmarksurface = loadmodel->marksurfaces + firstleafface;
|
||||
if ((firstleafface + out->nummarksurfaces) > loadmodel->nummarksurfaces)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: wrong marksurfaces position in %s",
|
||||
__func__, loadmodel->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
Mod_LoadMarksurfaces(gl4model_t *loadmodel, byte *mod_base, lump_t *l)
|
||||
{
|
||||
int i, j, count;
|
||||
short *in;
|
||||
msurface_t **out;
|
||||
|
||||
in = (void *)(mod_base + l->fileofs);
|
||||
|
||||
if (l->filelen % sizeof(*in))
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s",
|
||||
__func__, loadmodel->name);
|
||||
}
|
||||
|
||||
count = l->filelen / sizeof(*in);
|
||||
out = Hunk_Alloc(count * sizeof(*out));
|
||||
|
||||
loadmodel->marksurfaces = out;
|
||||
loadmodel->nummarksurfaces = count;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
j = LittleShort(in[i]);
|
||||
|
||||
if ((j < 0) || (j >= loadmodel->numsurfaces))
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: bad surface number", __func__);
|
||||
}
|
||||
|
||||
out[i] = loadmodel->surfaces + j;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
Mod_LoadBrushModel(gl4model_t *mod, void *buffer, int modfilelen)
|
||||
{
|
||||
int i;
|
||||
dheader_t *header;
|
||||
byte *mod_base;
|
||||
|
||||
if (mod != mod_known)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "Loaded a brush model after the world");
|
||||
}
|
||||
|
||||
header = (dheader_t *)buffer;
|
||||
|
||||
i = LittleLong(header->version);
|
||||
|
||||
if (i != BSPVERSION)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: %s has wrong version number (%i should be %i)",
|
||||
__func__, mod->name, i, BSPVERSION);
|
||||
}
|
||||
|
||||
/* swap all the lumps */
|
||||
mod_base = (byte *)header;
|
||||
|
||||
for (i = 0; i < sizeof(dheader_t) / 4; i++)
|
||||
{
|
||||
((int *)header)[i] = LittleLong(((int *)header)[i]);
|
||||
}
|
||||
|
||||
// calculate the needed hunksize from the lumps
|
||||
int hunkSize = 0;
|
||||
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_VERTEXES], sizeof(dvertex_t), sizeof(mvertex_t), 0);
|
||||
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_EDGES], sizeof(dedge_t), sizeof(medge_t), 0);
|
||||
hunkSize += sizeof(medge_t) + 31; // for count+1 in Mod_LoadEdges()
|
||||
int surfEdgeCount = (header->lumps[LUMP_SURFEDGES].filelen+sizeof(int)-1)/sizeof(int);
|
||||
if(surfEdgeCount < MAX_MAP_SURFEDGES) // else it errors out later anyway
|
||||
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_SURFEDGES], sizeof(int), sizeof(int), 0);
|
||||
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_LIGHTING], 1, 1, 0);
|
||||
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_PLANES], sizeof(dplane_t), sizeof(cplane_t)*2, 0);
|
||||
hunkSize += calcTexinfoAndFacesSize(mod_base, &header->lumps[LUMP_FACES], &header->lumps[LUMP_TEXINFO]);
|
||||
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_LEAFFACES], sizeof(short), sizeof(msurface_t *), 0); // yes, out is indeed a pointer!
|
||||
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_VISIBILITY], 1, 1, 0);
|
||||
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_LEAFS], sizeof(dleaf_t), sizeof(mleaf_t), 0);
|
||||
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_NODES], sizeof(dnode_t), sizeof(mnode_t), 0);
|
||||
hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_MODELS], sizeof(dmodel_t), sizeof(gl4model_t), 0);
|
||||
|
||||
mod->extradata = Hunk_Begin(hunkSize);
|
||||
mod->type = mod_brush;
|
||||
|
||||
/* load into heap */
|
||||
Mod_LoadVertexes(mod->name, &mod->vertexes, &mod->numvertexes, mod_base,
|
||||
&header->lumps[LUMP_VERTEXES], 0);
|
||||
Mod_LoadEdges(mod->name, &mod->edges, &mod->numedges,
|
||||
mod_base, &header->lumps[LUMP_EDGES], 1);
|
||||
Mod_LoadSurfedges(mod->name, &mod->surfedges, &mod->numsurfedges,
|
||||
mod_base, &header->lumps[LUMP_SURFEDGES], 0);
|
||||
Mod_LoadLighting(&mod->lightdata, mod_base, &header->lumps[LUMP_LIGHTING]);
|
||||
Mod_LoadPlanes (mod->name, &mod->planes, &mod->numplanes,
|
||||
mod_base, &header->lumps[LUMP_PLANES], 0);
|
||||
Mod_LoadTexinfo (mod->name, &mod->texinfo, &mod->numtexinfo,
|
||||
mod_base, &header->lumps[LUMP_TEXINFO], (findimage_t)GL4_FindImage,
|
||||
gl4_notexture, 0);
|
||||
Mod_LoadFaces(mod, mod_base, &header->lumps[LUMP_FACES]);
|
||||
Mod_LoadMarksurfaces(mod, mod_base, &header->lumps[LUMP_LEAFFACES]);
|
||||
Mod_LoadVisibility(&mod->vis, mod_base, &header->lumps[LUMP_VISIBILITY]);
|
||||
Mod_LoadLeafs(mod, mod_base, &header->lumps[LUMP_LEAFS]);
|
||||
Mod_LoadNodes(mod->name, mod->planes, mod->numplanes, mod->leafs,
|
||||
mod->numleafs, &mod->nodes, &mod->numnodes, mod_base,
|
||||
&header->lumps[LUMP_NODES]);
|
||||
Mod_LoadSubmodels (mod, mod_base, &header->lumps[LUMP_MODELS]);
|
||||
mod->numframes = 2; /* regular and alternate animation */
|
||||
}
|
||||
|
||||
static void
|
||||
Mod_Free(gl4model_t *mod)
|
||||
{
|
||||
Hunk_Free(mod->extradata);
|
||||
memset(mod, 0, sizeof(*mod));
|
||||
}
|
||||
|
||||
void
|
||||
GL4_Mod_FreeAll(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mod_numknown; i++)
|
||||
{
|
||||
if (mod_known[i].extradatasize)
|
||||
{
|
||||
Mod_Free(&mod_known[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads in a model for the given name
|
||||
*/
|
||||
static gl4model_t *
|
||||
Mod_ForName (char *name, gl4model_t *parent_model, qboolean crash)
|
||||
{
|
||||
gl4model_t *mod;
|
||||
void *buf;
|
||||
int i, modfilelen;
|
||||
|
||||
if (!name[0])
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: NULL name", __func__);
|
||||
}
|
||||
|
||||
/* inline models are grabbed only from worldmodel */
|
||||
if (name[0] == '*' && parent_model)
|
||||
{
|
||||
i = (int)strtol(name + 1, (char **)NULL, 10);
|
||||
|
||||
if (i < 1 || i >= parent_model->numsubmodels)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: bad inline model number",
|
||||
__func__);
|
||||
}
|
||||
|
||||
return &parent_model->submodels[i];
|
||||
}
|
||||
|
||||
/* search the currently loaded models */
|
||||
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
|
||||
{
|
||||
if (!mod->name[0])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(mod->name, name))
|
||||
{
|
||||
return mod;
|
||||
}
|
||||
}
|
||||
|
||||
/* find a free model slot spot */
|
||||
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
|
||||
{
|
||||
if (!mod->name[0])
|
||||
{
|
||||
break; /* free spot */
|
||||
}
|
||||
}
|
||||
|
||||
if (i == mod_numknown)
|
||||
{
|
||||
if (mod_numknown == MAX_MOD_KNOWN)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "mod_numknown == MAX_MOD_KNOWN");
|
||||
}
|
||||
|
||||
mod_numknown++;
|
||||
}
|
||||
|
||||
strcpy(mod->name, name);
|
||||
|
||||
/* load the file */
|
||||
modfilelen = ri.FS_LoadFile(mod->name, (void **)&buf);
|
||||
|
||||
if (!buf)
|
||||
{
|
||||
if (crash)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: %s not found",
|
||||
__func__, mod->name);
|
||||
}
|
||||
|
||||
memset(mod->name, 0, sizeof(mod->name));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* call the apropriate loader */
|
||||
switch (LittleLong(*(unsigned *)buf))
|
||||
{
|
||||
case DKMHEADER:
|
||||
/* fall through */
|
||||
case RAVENFMHEADER:
|
||||
/* fall through */
|
||||
case IDALIASHEADER:
|
||||
/* fall through */
|
||||
case IDMDLHEADER:
|
||||
{
|
||||
mod->extradata = Mod_LoadAliasModel(mod->name, buf, modfilelen,
|
||||
mod->mins, mod->maxs,
|
||||
(struct image_s **)mod->skins, (findimage_t)GL4_FindImage,
|
||||
&(mod->type));
|
||||
if (!mod->extradata)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: Failed to load %s",
|
||||
__func__, mod->name);
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
||||
case IDSPRITEHEADER:
|
||||
{
|
||||
mod->extradata = Mod_LoadSP2(mod->name, buf, modfilelen,
|
||||
(struct image_s **)mod->skins, (findimage_t)GL4_FindImage,
|
||||
&(mod->type));
|
||||
if (!mod->extradata)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "%s: Failed to load %s",
|
||||
__func__, mod->name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IDBSPHEADER:
|
||||
Mod_LoadBrushModel(mod, buf, modfilelen);
|
||||
break;
|
||||
|
||||
default:
|
||||
ri.Sys_Error(ERR_DROP, "%s: unknown fileid for %s",
|
||||
__func__, mod->name);
|
||||
break;
|
||||
}
|
||||
|
||||
mod->extradatasize = Hunk_End();
|
||||
|
||||
ri.FS_FreeFile(buf);
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
/*
|
||||
* Specifies the model that will be used as the world
|
||||
*/
|
||||
void
|
||||
GL4_BeginRegistration(char *model)
|
||||
{
|
||||
char fullname[MAX_QPATH];
|
||||
cvar_t *flushmap;
|
||||
|
||||
registration_sequence++;
|
||||
gl4_oldviewcluster = -1; /* force markleafs */
|
||||
|
||||
gl4state.currentlightmap = -1;
|
||||
|
||||
Com_sprintf(fullname, sizeof(fullname), "maps/%s.bsp", model);
|
||||
|
||||
/* explicitly free the old map if different
|
||||
this guarantees that mod_known[0] is the
|
||||
world map */
|
||||
flushmap = ri.Cvar_Get("flushmap", "0", 0);
|
||||
|
||||
if (strcmp(mod_known[0].name, fullname) || flushmap->value)
|
||||
{
|
||||
Mod_Free(&mod_known[0]);
|
||||
}
|
||||
|
||||
gl4_worldmodel = Mod_ForName(fullname, NULL, true);
|
||||
|
||||
gl4_viewcluster = -1;
|
||||
}
|
||||
|
||||
struct model_s *
|
||||
GL4_RegisterModel(char *name)
|
||||
{
|
||||
gl4model_t *mod;
|
||||
|
||||
mod = Mod_ForName(name, gl4_worldmodel, false);
|
||||
|
||||
if (mod)
|
||||
{
|
||||
mod->registration_sequence = registration_sequence;
|
||||
|
||||
/* register any images used by the models */
|
||||
if (mod->type == mod_brush)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mod->numtexinfo; i++)
|
||||
{
|
||||
mod->texinfo[i].image->registration_sequence = registration_sequence;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* numframes is unused for SP2 but lets set it also */
|
||||
mod->numframes = Mod_ReLoadSkins((struct image_s **)mod->skins,
|
||||
(findimage_t)GL4_FindImage, mod->extradata, mod->type);
|
||||
}
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
void
|
||||
GL4_EndRegistration(void)
|
||||
{
|
||||
int i;
|
||||
gl4model_t *mod;
|
||||
|
||||
if (Mod_HasFreeSpace() && GL4_ImageHasFreeSpace())
|
||||
{
|
||||
// should be enough space for load next maps
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
|
||||
{
|
||||
if (!mod->name[0])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mod->registration_sequence != registration_sequence)
|
||||
{
|
||||
/* don't need this model */
|
||||
Mod_Free(mod);
|
||||
}
|
||||
}
|
||||
|
||||
GL4_FreeUnusedImages();
|
||||
}
|
436
src/client/refresh/gl4/gl4_sdl.c
Normal file
436
src/client/refresh/gl4/gl4_sdl.c
Normal file
|
@ -0,0 +1,436 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (C) 2016-2017 Daniel Gibson
|
||||
*
|
||||
* This program 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 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* SDL backend for the GL4 renderer. Everything that needs to be on the
|
||||
* renderer side of thing. Also all glad (or whatever OpenGL loader I
|
||||
* end up using) specific things.
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#include "header/local.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
static SDL_Window* window = NULL;
|
||||
static SDL_GLContext context = NULL;
|
||||
static qboolean vsyncActive = false;
|
||||
qboolean IsHighDPIaware = false;
|
||||
|
||||
// --------
|
||||
|
||||
enum {
|
||||
// Not all GL.h header know about GL_DEBUG_SEVERITY_NOTIFICATION_*.
|
||||
// DG: yes, it's the same value in GLES3.2
|
||||
QGL_DEBUG_SEVERITY_NOTIFICATION = 0x826B
|
||||
};
|
||||
|
||||
/*
|
||||
* Callback function for debug output.
|
||||
*/
|
||||
static void APIENTRY
|
||||
DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
||||
const GLchar *message, const void *userParam)
|
||||
{
|
||||
const char* sourceStr = "Source: Unknown";
|
||||
const char* typeStr = "Type: Unknown";
|
||||
const char* severityStr = "Severity: Unknown";
|
||||
|
||||
switch (severity)
|
||||
{
|
||||
#ifdef YQ2_GL3_GLES
|
||||
#define SVRCASE(X, STR) case GL_DEBUG_SEVERITY_ ## X ## _KHR : severityStr = STR; break;
|
||||
#else // Desktop GL
|
||||
#define SVRCASE(X, STR) case GL_DEBUG_SEVERITY_ ## X ## _ARB : severityStr = STR; break;
|
||||
#endif
|
||||
|
||||
case QGL_DEBUG_SEVERITY_NOTIFICATION: return;
|
||||
SVRCASE(HIGH, "Severity: High")
|
||||
SVRCASE(MEDIUM, "Severity: Medium")
|
||||
SVRCASE(LOW, "Severity: Low")
|
||||
#undef SVRCASE
|
||||
}
|
||||
|
||||
switch (source)
|
||||
{
|
||||
#ifdef YQ2_GL3_GLES
|
||||
#define SRCCASE(X) case GL_DEBUG_SOURCE_ ## X ## _KHR: sourceStr = "Source: " #X; break;
|
||||
#else
|
||||
#define SRCCASE(X) case GL_DEBUG_SOURCE_ ## X ## _ARB: sourceStr = "Source: " #X; break;
|
||||
#endif
|
||||
SRCCASE(API);
|
||||
SRCCASE(WINDOW_SYSTEM);
|
||||
SRCCASE(SHADER_COMPILER);
|
||||
SRCCASE(THIRD_PARTY);
|
||||
SRCCASE(APPLICATION);
|
||||
SRCCASE(OTHER);
|
||||
#undef SRCCASE
|
||||
}
|
||||
|
||||
switch(type)
|
||||
{
|
||||
#ifdef YQ2_GL3_GLES
|
||||
#define TYPECASE(X) case GL_DEBUG_TYPE_ ## X ## _KHR: typeStr = "Type: " #X; break;
|
||||
#else
|
||||
#define TYPECASE(X) case GL_DEBUG_TYPE_ ## X ## _ARB: typeStr = "Type: " #X; break;
|
||||
#endif
|
||||
TYPECASE(ERROR);
|
||||
TYPECASE(DEPRECATED_BEHAVIOR);
|
||||
TYPECASE(UNDEFINED_BEHAVIOR);
|
||||
TYPECASE(PORTABILITY);
|
||||
TYPECASE(PERFORMANCE);
|
||||
TYPECASE(OTHER);
|
||||
#undef TYPECASE
|
||||
}
|
||||
|
||||
// use PRINT_ALL - this is only called with gl4_debugcontext != 0 anyway.
|
||||
R_Printf(PRINT_ALL, "GLDBG %s %s %s: %s\n", sourceStr, typeStr, severityStr, message);
|
||||
}
|
||||
|
||||
// ---------
|
||||
|
||||
/*
|
||||
* Swaps the buffers and shows the next frame.
|
||||
*/
|
||||
void GL4_EndFrame(void)
|
||||
{
|
||||
if(gl4config.useBigVBO)
|
||||
{
|
||||
// I think this is a good point to orphan the VBO and get a fresh one
|
||||
GL4_BindVAO(gl4state.vao3D);
|
||||
GL4_BindVBO(gl4state.vbo3D);
|
||||
glBufferData(GL_ARRAY_BUFFER, gl4state.vbo3Dsize, NULL, GL_STREAM_DRAW);
|
||||
gl4state.vbo3DcurOffset = 0;
|
||||
}
|
||||
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the vsync is enabled.
|
||||
*/
|
||||
qboolean GL4_IsVsyncActive(void)
|
||||
{
|
||||
return vsyncActive;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enables or disabes the vsync.
|
||||
*/
|
||||
void GL4_SetVsync(void)
|
||||
{
|
||||
// Make sure that the user given
|
||||
// value is SDL compatible...
|
||||
int vsync = 0;
|
||||
|
||||
if (r_vsync->value == 1)
|
||||
{
|
||||
vsync = 1;
|
||||
}
|
||||
else if (r_vsync->value == 2)
|
||||
{
|
||||
vsync = -1;
|
||||
}
|
||||
|
||||
if (SDL_GL_SetSwapInterval(vsync) == -1)
|
||||
{
|
||||
if (vsync == -1)
|
||||
{
|
||||
// Not every system supports adaptive
|
||||
// vsync, fallback to normal vsync.
|
||||
R_Printf(PRINT_ALL, "Failed to set adaptive vsync, reverting to normal vsync.\n");
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
}
|
||||
}
|
||||
|
||||
vsyncActive = SDL_GL_GetSwapInterval() != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns the flags used at the SDL window
|
||||
* creation by GLimp_InitGraphics(). In case of error -1
|
||||
* is returned.
|
||||
*/
|
||||
int GL4_PrepareForWindow(void)
|
||||
{
|
||||
// Mkay, let's try to load the libGL,
|
||||
const char *libgl;
|
||||
cvar_t *gl4_libgl = ri.Cvar_Get("gl4_libgl", "", CVAR_ARCHIVE);
|
||||
|
||||
if (strlen(gl4_libgl->string) == 0)
|
||||
{
|
||||
libgl = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
libgl = gl4_libgl->string;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (SDL_GL_LoadLibrary(libgl) < 0)
|
||||
{
|
||||
if (libgl == NULL)
|
||||
{
|
||||
ri.Sys_Error(ERR_FATAL, "%s: Couldn't load libGL: %s!",
|
||||
__func__, SDL_GetError());
|
||||
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
R_Printf(PRINT_ALL, "%s: Couldn't load libGL: %s!\n",
|
||||
__func__, SDL_GetError());
|
||||
R_Printf(PRINT_ALL, "Retrying with default...\n");
|
||||
|
||||
ri.Cvar_Set("gl3_libgl", "");
|
||||
libgl = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set GL context attributs bound to the window.
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
||||
if (SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8) == 0)
|
||||
{
|
||||
gl4config.stencil = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
gl4config.stencil = false;
|
||||
}
|
||||
|
||||
#ifdef YQ2_GL3_GLES3
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
#else // Desktop GL
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
#endif
|
||||
|
||||
// Set GL context flags.
|
||||
int contextFlags = 0;
|
||||
|
||||
#ifndef YQ2_GL3_GLES // Desktop GL (at least RPi4 doesn't like this for GLES3)
|
||||
contextFlags |= SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG;
|
||||
#endif
|
||||
|
||||
if (gl4_debugcontext && gl4_debugcontext->value)
|
||||
{
|
||||
contextFlags |= SDL_GL_CONTEXT_DEBUG_FLAG;
|
||||
}
|
||||
|
||||
if (contextFlags != 0)
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, contextFlags);
|
||||
}
|
||||
|
||||
// Let's see if the driver supports MSAA.
|
||||
int msaa_samples = 0;
|
||||
|
||||
if (gl_msaa_samples->value)
|
||||
{
|
||||
msaa_samples = gl_msaa_samples->value;
|
||||
|
||||
if (SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1) < 0)
|
||||
{
|
||||
R_Printf(PRINT_ALL, "MSAA is unsupported: %s\n", SDL_GetError());
|
||||
|
||||
ri.Cvar_SetValue ("r_msaa_samples", 0);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
|
||||
}
|
||||
else if (SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, msaa_samples) < 0)
|
||||
{
|
||||
R_Printf(PRINT_ALL, "MSAA %ix is unsupported: %s\n", msaa_samples, SDL_GetError());
|
||||
|
||||
ri.Cvar_SetValue("r_msaa_samples", 0);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
|
||||
}
|
||||
|
||||
return SDL_WINDOW_OPENGL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes the OpenGL context. Returns true at
|
||||
* success and false at failure.
|
||||
*/
|
||||
int GL4_InitContext(void* win)
|
||||
{
|
||||
// Coders are stupid.
|
||||
if (win == NULL)
|
||||
{
|
||||
ri.Sys_Error(ERR_FATAL, "R_InitContext() must not be called with NULL argument!");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
window = (SDL_Window *)win;
|
||||
|
||||
// Initialize GL context.
|
||||
context = SDL_GL_CreateContext(window);
|
||||
|
||||
if(context == NULL)
|
||||
{
|
||||
R_Printf(PRINT_ALL, "GL4_InitContext(): Creating OpenGL Context failed: %s\n", SDL_GetError());
|
||||
|
||||
window = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we've got the requested MSAA.
|
||||
int msaa_samples = 0;
|
||||
|
||||
if (gl_msaa_samples->value)
|
||||
{
|
||||
if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &msaa_samples) == 0)
|
||||
{
|
||||
ri.Cvar_SetValue("r_msaa_samples", msaa_samples);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we've got at least 8 stencil bits
|
||||
int stencil_bits = 0;
|
||||
|
||||
if (gl4config.stencil)
|
||||
{
|
||||
if (SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil_bits) < 0 || stencil_bits < 8)
|
||||
{
|
||||
gl4config.stencil = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Enable vsync if requested.
|
||||
GL4_SetVsync();
|
||||
|
||||
// Load GL pointrs through GLAD and check context.
|
||||
#ifdef YQ2_GL3_GLES
|
||||
if( !gladLoadGLES2Loader(SDL_GL_GetProcAddress))
|
||||
#else // Desktop GL
|
||||
if( !gladLoadGLLoader(SDL_GL_GetProcAddress))
|
||||
#endif
|
||||
{
|
||||
R_Printf(PRINT_ALL, "GL4_InitContext(): ERROR: loading OpenGL function pointers failed!\n");
|
||||
|
||||
return false;
|
||||
}
|
||||
#ifdef YQ2_GL3_GLES3
|
||||
else if (GLVersion.major < 3)
|
||||
#else // Desktop GL
|
||||
else if (GLVersion.major < 4 || (GLVersion.major == 4 && GLVersion.minor < 6))
|
||||
#endif
|
||||
{
|
||||
R_Printf(PRINT_ALL, "GL4_InitContext(): ERROR: glad only got GL version %d.%d!\n", GLVersion.major, GLVersion.minor);
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
R_Printf(PRINT_ALL, "Successfully loaded OpenGL function pointers using glad, got version %d.%d!\n", GLVersion.major, GLVersion.minor);
|
||||
}
|
||||
|
||||
#ifdef YQ2_GL3_GLES
|
||||
gl4config.debug_output = GLAD_GL_KHR_debug != 0;
|
||||
#else // Desktop GL
|
||||
gl4config.debug_output = GLAD_GL_ARB_debug_output != 0;
|
||||
#endif
|
||||
gl4config.anisotropic = GLAD_GL_ARB_texture_filter_anisotropic != 0;
|
||||
|
||||
gl4config.major_version = GLVersion.major;
|
||||
gl4config.minor_version = GLVersion.minor;
|
||||
|
||||
// Debug context setup.
|
||||
if (gl4_debugcontext && gl4_debugcontext->value && gl4config.debug_output)
|
||||
{
|
||||
#ifdef YQ2_GL3_GLES
|
||||
glDebugMessageCallbackKHR(DebugCallback, NULL);
|
||||
|
||||
// Call GL3_DebugCallback() synchronously, i.e. directly when and
|
||||
// where the error happens (so we can get the cause in a backtrace)
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
|
||||
#else // Desktop GL
|
||||
glDebugMessageCallbackARB(DebugCallback, NULL);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Window title - set here so we can display renderer name in it.
|
||||
char title[40] = {0};
|
||||
#ifdef YQ2_GL3_GLES3
|
||||
snprintf(title, sizeof(title), "Yamagi Quake II %s - OpenGL ES 3.0", YQ2VERSION);
|
||||
#else
|
||||
snprintf(title, sizeof(title), "Yamagi Quake II %s - OpenGL 4.6", YQ2VERSION);
|
||||
#endif
|
||||
SDL_SetWindowTitle(window, title);
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 26, 0)
|
||||
// Figure out if we are high dpi aware.
|
||||
int flags = SDL_GetWindowFlags(win);
|
||||
IsHighDPIaware = (flags & SDL_WINDOW_ALLOW_HIGHDPI) ? true : false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills the actual size of the drawable into width and height.
|
||||
*/
|
||||
void GL4_GetDrawableSize(int* width, int* height)
|
||||
{
|
||||
SDL_GL_GetDrawableSize(window, width, height);
|
||||
}
|
||||
|
||||
/*
|
||||
* Shuts the GL context down.
|
||||
*/
|
||||
void GL4_ShutdownContext()
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
if(context)
|
||||
{
|
||||
SDL_GL_DeleteContext(context);
|
||||
context = NULL;
|
||||
}
|
||||
}
|
||||
}
|
1365
src/client/refresh/gl4/gl4_shaders.c
Normal file
1365
src/client/refresh/gl4/gl4_shaders.c
Normal file
File diff suppressed because it is too large
Load diff
885
src/client/refresh/gl4/gl4_surf.c
Normal file
885
src/client/refresh/gl4/gl4_surf.c
Normal file
|
@ -0,0 +1,885 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (C) 2016-2017 Daniel Gibson
|
||||
*
|
||||
* This program 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 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Surface generation and drawing
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h> // ofsetof()
|
||||
|
||||
#include "header/local.h"
|
||||
|
||||
int c_visible_lightmaps;
|
||||
int c_visible_textures;
|
||||
static vec3_t modelorg; /* relative to viewpoint */
|
||||
static msurface_t *gl4_alpha_surfaces;
|
||||
|
||||
gl4lightmapstate_t gl4_lms;
|
||||
|
||||
#define BACKFACE_EPSILON 0.01
|
||||
|
||||
extern gl4image_t gl4textures[MAX_GL4TEXTURES];
|
||||
extern int numgl4textures;
|
||||
|
||||
void GL4_SurfInit(void)
|
||||
{
|
||||
// init the VAO and VBO for the standard vertexdata: 10 floats and 1 uint
|
||||
// (X, Y, Z), (S, T), (LMS, LMT), (normX, normY, normZ) ; lightFlags - last two groups for lightmap/dynlights
|
||||
|
||||
glGenVertexArrays(1, &gl4state.vao3D);
|
||||
GL4_BindVAO(gl4state.vao3D);
|
||||
|
||||
glGenBuffers(1, &gl4state.vbo3D);
|
||||
GL4_BindVBO(gl4state.vbo3D);
|
||||
|
||||
if(gl4config.useBigVBO)
|
||||
{
|
||||
gl4state.vbo3Dsize = 5*1024*1024; // a 5MB buffer seems to work well?
|
||||
gl4state.vbo3DcurOffset = 0;
|
||||
glBufferData(GL_ARRAY_BUFFER, gl4state.vbo3Dsize, NULL, GL_STREAM_DRAW); // allocate/reserve that data
|
||||
}
|
||||
|
||||
glEnableVertexAttribArray(GL4_ATTRIB_POSITION);
|
||||
qglVertexAttribPointer(GL4_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(gl4_3D_vtx_t), 0);
|
||||
|
||||
glEnableVertexAttribArray(GL4_ATTRIB_TEXCOORD);
|
||||
qglVertexAttribPointer(GL4_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(gl4_3D_vtx_t), offsetof(gl4_3D_vtx_t, texCoord));
|
||||
|
||||
glEnableVertexAttribArray(GL4_ATTRIB_LMTEXCOORD);
|
||||
qglVertexAttribPointer(GL4_ATTRIB_LMTEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(gl4_3D_vtx_t), offsetof(gl4_3D_vtx_t, lmTexCoord));
|
||||
|
||||
glEnableVertexAttribArray(GL4_ATTRIB_NORMAL);
|
||||
qglVertexAttribPointer(GL4_ATTRIB_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(gl4_3D_vtx_t), offsetof(gl4_3D_vtx_t, normal));
|
||||
|
||||
glEnableVertexAttribArray(GL4_ATTRIB_LIGHTFLAGS);
|
||||
qglVertexAttribIPointer(GL4_ATTRIB_LIGHTFLAGS, 1, GL_UNSIGNED_INT, sizeof(gl4_3D_vtx_t), offsetof(gl4_3D_vtx_t, lightFlags));
|
||||
|
||||
|
||||
|
||||
// init VAO and VBO for model vertexdata: 9 floats
|
||||
// (X,Y,Z), (S,T), (R,G,B,A)
|
||||
|
||||
glGenVertexArrays(1, &gl4state.vaoAlias);
|
||||
GL4_BindVAO(gl4state.vaoAlias);
|
||||
|
||||
glGenBuffers(1, &gl4state.vboAlias);
|
||||
GL4_BindVBO(gl4state.vboAlias);
|
||||
|
||||
glEnableVertexAttribArray(GL4_ATTRIB_POSITION);
|
||||
qglVertexAttribPointer(GL4_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 0);
|
||||
|
||||
glEnableVertexAttribArray(GL4_ATTRIB_TEXCOORD);
|
||||
qglVertexAttribPointer(GL4_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 3*sizeof(GLfloat));
|
||||
|
||||
glEnableVertexAttribArray(GL4_ATTRIB_COLOR);
|
||||
qglVertexAttribPointer(GL4_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 5*sizeof(GLfloat));
|
||||
|
||||
glGenBuffers(1, &gl4state.eboAlias);
|
||||
|
||||
// init VAO and VBO for particle vertexdata: 9 floats
|
||||
// (X,Y,Z), (point_size,distace_to_camera), (R,G,B,A)
|
||||
|
||||
glGenVertexArrays(1, &gl4state.vaoParticle);
|
||||
GL4_BindVAO(gl4state.vaoParticle);
|
||||
|
||||
glGenBuffers(1, &gl4state.vboParticle);
|
||||
GL4_BindVBO(gl4state.vboParticle);
|
||||
|
||||
glEnableVertexAttribArray(GL4_ATTRIB_POSITION);
|
||||
qglVertexAttribPointer(GL4_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 0);
|
||||
|
||||
// TODO: maybe move point size and camera origin to UBO and calculate distance in vertex shader
|
||||
glEnableVertexAttribArray(GL4_ATTRIB_TEXCOORD); // it's abused for (point_size, distance) here..
|
||||
qglVertexAttribPointer(GL4_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 3*sizeof(GLfloat));
|
||||
|
||||
glEnableVertexAttribArray(GL4_ATTRIB_COLOR);
|
||||
qglVertexAttribPointer(GL4_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 5*sizeof(GLfloat));
|
||||
}
|
||||
|
||||
void GL4_SurfShutdown(void)
|
||||
{
|
||||
glDeleteBuffers(1, &gl4state.vbo3D);
|
||||
gl4state.vbo3D = 0;
|
||||
glDeleteVertexArrays(1, &gl4state.vao3D);
|
||||
gl4state.vao3D = 0;
|
||||
|
||||
glDeleteBuffers(1, &gl4state.eboAlias);
|
||||
gl4state.eboAlias = 0;
|
||||
glDeleteBuffers(1, &gl4state.vboAlias);
|
||||
gl4state.vboAlias = 0;
|
||||
glDeleteVertexArrays(1, &gl4state.vaoAlias);
|
||||
gl4state.vaoAlias = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
SetLightFlags(msurface_t *surf)
|
||||
{
|
||||
unsigned int lightFlags = 0;
|
||||
if (surf->dlightframe == gl4_framecount)
|
||||
{
|
||||
lightFlags = surf->dlightbits;
|
||||
}
|
||||
|
||||
gl4_3D_vtx_t* verts = surf->polys->vertices;
|
||||
|
||||
int numVerts = surf->polys->numverts;
|
||||
for(int i=0; i<numVerts; ++i)
|
||||
{
|
||||
verts[i].lightFlags = lightFlags;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SetAllLightFlags(msurface_t *surf)
|
||||
{
|
||||
unsigned int lightFlags = 0xffffffff;
|
||||
|
||||
gl4_3D_vtx_t* verts = surf->polys->vertices;
|
||||
|
||||
int numVerts = surf->polys->numverts;
|
||||
for(int i=0; i<numVerts; ++i)
|
||||
{
|
||||
verts[i].lightFlags = lightFlags;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GL4_DrawGLPoly(msurface_t *fa)
|
||||
{
|
||||
glpoly_t *p = fa->polys;
|
||||
|
||||
GL4_BindVAO(gl4state.vao3D);
|
||||
GL4_BindVBO(gl4state.vbo3D);
|
||||
|
||||
GL4_BufferAndDraw3D(p->vertices, p->numverts, GL_TRIANGLE_FAN);
|
||||
}
|
||||
|
||||
void
|
||||
GL4_DrawGLFlowingPoly(msurface_t *fa)
|
||||
{
|
||||
glpoly_t *p;
|
||||
float scroll;
|
||||
|
||||
p = fa->polys;
|
||||
|
||||
scroll = -64.0f * ((gl4_newrefdef.time / 40.0f) - (int)(gl4_newrefdef.time / 40.0f));
|
||||
|
||||
if (scroll == 0.0f)
|
||||
{
|
||||
scroll = -64.0f;
|
||||
}
|
||||
|
||||
if(gl4state.uni3DData.scroll != scroll)
|
||||
{
|
||||
gl4state.uni3DData.scroll = scroll;
|
||||
GL4_UpdateUBO3D();
|
||||
}
|
||||
|
||||
GL4_BindVAO(gl4state.vao3D);
|
||||
GL4_BindVBO(gl4state.vbo3D);
|
||||
|
||||
GL4_BufferAndDraw3D(p->vertices, p->numverts, GL_TRIANGLE_FAN);
|
||||
}
|
||||
|
||||
static void
|
||||
DrawTriangleOutlines(void)
|
||||
{
|
||||
STUB_ONCE("TODO: Implement for gl_showtris support!");
|
||||
#if 0
|
||||
int i, j;
|
||||
glpoly_t *p;
|
||||
|
||||
if (!gl_showtris->value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glColor4f(1, 1, 1, 1);
|
||||
|
||||
for (i = 0; i < MAX_LIGHTMAPS; i++)
|
||||
{
|
||||
msurface_t *surf;
|
||||
|
||||
for (surf = gl4_lms.lightmap_surfaces[i];
|
||||
surf != 0;
|
||||
surf = surf->lightmapchain)
|
||||
{
|
||||
p = surf->polys;
|
||||
|
||||
for ( ; p; p = p->chain)
|
||||
{
|
||||
for (j = 2; j < p->numverts; j++)
|
||||
{
|
||||
GLfloat vtx[12];
|
||||
unsigned int k;
|
||||
|
||||
for (k=0; k<3; k++)
|
||||
{
|
||||
vtx[0+k] = p->vertices [ 0 ][ k ];
|
||||
vtx[3+k] = p->vertices [ j - 1 ][ k ];
|
||||
vtx[6+k] = p->vertices [ j ][ k ];
|
||||
vtx[9+k] = p->vertices [ 0 ][ k ];
|
||||
}
|
||||
|
||||
glEnableClientState( GL_VERTEX_ARRAY );
|
||||
|
||||
glVertexPointer( 3, GL_FLOAT, 0, vtx );
|
||||
glDrawArrays( GL_LINE_STRIP, 0, 4 );
|
||||
|
||||
glDisableClientState( GL_VERTEX_ARRAY );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
#endif // 0
|
||||
}
|
||||
|
||||
static void
|
||||
UpdateLMscales(const hmm_vec4 lmScales[MAX_LIGHTMAPS_PER_SURFACE], gl4ShaderInfo_t* si)
|
||||
{
|
||||
int i;
|
||||
qboolean hasChanged = false;
|
||||
|
||||
for(i=0; i<MAX_LIGHTMAPS_PER_SURFACE; ++i)
|
||||
{
|
||||
if(hasChanged)
|
||||
{
|
||||
si->lmScales[i] = lmScales[i];
|
||||
}
|
||||
else if( si->lmScales[i].R != lmScales[i].R
|
||||
|| si->lmScales[i].G != lmScales[i].G
|
||||
|| si->lmScales[i].B != lmScales[i].B
|
||||
|| si->lmScales[i].A != lmScales[i].A )
|
||||
{
|
||||
si->lmScales[i] = lmScales[i];
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(hasChanged)
|
||||
{
|
||||
glUniform4fv(si->uniLmScalesOrTime, MAX_LIGHTMAPS_PER_SURFACE, si->lmScales[0].Elements);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
RenderBrushPoly(entity_t *currententity, msurface_t *fa)
|
||||
{
|
||||
int map;
|
||||
gl4image_t *image;
|
||||
|
||||
c_brush_polys++;
|
||||
|
||||
image = R_TextureAnimation(currententity, fa->texinfo);
|
||||
|
||||
if (fa->flags & SURF_DRAWTURB)
|
||||
{
|
||||
GL4_Bind(image->texnum);
|
||||
|
||||
GL4_EmitWaterPolys(fa);
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
GL4_Bind(image->texnum);
|
||||
}
|
||||
|
||||
hmm_vec4 lmScales[MAX_LIGHTMAPS_PER_SURFACE] = {0};
|
||||
lmScales[0] = HMM_Vec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
GL4_BindLightmap(fa->lightmaptexturenum);
|
||||
|
||||
// Any dynamic lights on this surface?
|
||||
for (map = 0; map < MAX_LIGHTMAPS_PER_SURFACE && fa->styles[map] != 255; map++)
|
||||
{
|
||||
lmScales[map].R = gl4_newrefdef.lightstyles[fa->styles[map]].rgb[0];
|
||||
lmScales[map].G = gl4_newrefdef.lightstyles[fa->styles[map]].rgb[1];
|
||||
lmScales[map].B = gl4_newrefdef.lightstyles[fa->styles[map]].rgb[2];
|
||||
lmScales[map].A = 1.0f;
|
||||
}
|
||||
|
||||
if (fa->texinfo->flags & SURF_FLOWING)
|
||||
{
|
||||
GL4_UseProgram(gl4state.si3DlmFlow.shaderProgram);
|
||||
UpdateLMscales(lmScales, &gl4state.si3DlmFlow);
|
||||
GL4_DrawGLFlowingPoly(fa);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL4_UseProgram(gl4state.si3Dlm.shaderProgram);
|
||||
UpdateLMscales(lmScales, &gl4state.si3Dlm);
|
||||
GL4_DrawGLPoly(fa);
|
||||
}
|
||||
|
||||
// Note: lightmap chains are gone, lightmaps are rendered together with normal texture in one pass
|
||||
}
|
||||
|
||||
/*
|
||||
* Draw water surfaces and windows.
|
||||
* The BSP tree is waled front to back, so unwinding the chain
|
||||
* of alpha_surfaces will draw back to front, giving proper ordering.
|
||||
*/
|
||||
void
|
||||
GL4_DrawAlphaSurfaces(void)
|
||||
{
|
||||
msurface_t *s;
|
||||
|
||||
/* go back to the world matrix */
|
||||
gl4state.uni3DData.transModelMat4 = gl4_identityMat4;
|
||||
GL4_UpdateUBO3D();
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
for (s = gl4_alpha_surfaces; s != NULL; s = s->texturechain)
|
||||
{
|
||||
GL4_Bind(s->texinfo->image->texnum);
|
||||
c_brush_polys++;
|
||||
float alpha = 1.0f;
|
||||
if (s->texinfo->flags & SURF_TRANS33)
|
||||
{
|
||||
alpha = 0.333f;
|
||||
}
|
||||
else if (s->texinfo->flags & SURF_TRANS66)
|
||||
{
|
||||
alpha = 0.666f;
|
||||
}
|
||||
if(alpha != gl4state.uni3DData.alpha)
|
||||
{
|
||||
gl4state.uni3DData.alpha = alpha;
|
||||
GL4_UpdateUBO3D();
|
||||
}
|
||||
|
||||
if (s->flags & SURF_DRAWTURB)
|
||||
{
|
||||
GL4_EmitWaterPolys(s);
|
||||
}
|
||||
else if (s->texinfo->flags & SURF_FLOWING)
|
||||
{
|
||||
GL4_UseProgram(gl4state.si3DtransFlow.shaderProgram);
|
||||
GL4_DrawGLFlowingPoly(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL4_UseProgram(gl4state.si3Dtrans.shaderProgram);
|
||||
GL4_DrawGLPoly(s);
|
||||
}
|
||||
}
|
||||
|
||||
gl4state.uni3DData.alpha = 1.0f;
|
||||
GL4_UpdateUBO3D();
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
gl4_alpha_surfaces = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
DrawTextureChains(entity_t *currententity)
|
||||
{
|
||||
int i;
|
||||
msurface_t *s;
|
||||
gl4image_t *image;
|
||||
|
||||
c_visible_textures = 0;
|
||||
|
||||
for (i = 0, image = gl4textures; i < numgl4textures; i++, image++)
|
||||
{
|
||||
if (!image->registration_sequence)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
s = image->texturechain;
|
||||
|
||||
if (!s)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
c_visible_textures++;
|
||||
|
||||
for ( ; s; s = s->texturechain)
|
||||
{
|
||||
SetLightFlags(s);
|
||||
RenderBrushPoly(currententity, s);
|
||||
}
|
||||
|
||||
image->texturechain = NULL;
|
||||
}
|
||||
|
||||
// TODO: maybe one loop for normal faces and one for SURF_DRAWTURB ???
|
||||
}
|
||||
|
||||
static void
|
||||
RenderLightmappedPoly(entity_t *currententity, msurface_t *surf)
|
||||
{
|
||||
int map;
|
||||
gl4image_t *image = R_TextureAnimation(currententity, surf->texinfo);
|
||||
|
||||
hmm_vec4 lmScales[MAX_LIGHTMAPS_PER_SURFACE] = {0};
|
||||
lmScales[0] = HMM_Vec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
assert((surf->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP)) == 0
|
||||
&& "RenderLightMappedPoly mustn't be called with transparent, sky or warping surfaces!");
|
||||
|
||||
// Any dynamic lights on this surface?
|
||||
for (map = 0; map < MAX_LIGHTMAPS_PER_SURFACE && surf->styles[map] != 255; map++)
|
||||
{
|
||||
lmScales[map].R = gl4_newrefdef.lightstyles[surf->styles[map]].rgb[0];
|
||||
lmScales[map].G = gl4_newrefdef.lightstyles[surf->styles[map]].rgb[1];
|
||||
lmScales[map].B = gl4_newrefdef.lightstyles[surf->styles[map]].rgb[2];
|
||||
lmScales[map].A = 1.0f;
|
||||
}
|
||||
|
||||
c_brush_polys++;
|
||||
|
||||
GL4_Bind(image->texnum);
|
||||
GL4_BindLightmap(surf->lightmaptexturenum);
|
||||
|
||||
if (surf->texinfo->flags & SURF_FLOWING)
|
||||
{
|
||||
GL4_UseProgram(gl4state.si3DlmFlow.shaderProgram);
|
||||
UpdateLMscales(lmScales, &gl4state.si3DlmFlow);
|
||||
GL4_DrawGLFlowingPoly(surf);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL4_UseProgram(gl4state.si3Dlm.shaderProgram);
|
||||
UpdateLMscales(lmScales, &gl4state.si3Dlm);
|
||||
GL4_DrawGLPoly(surf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
DrawInlineBModel(entity_t *currententity, gl4model_t *currentmodel)
|
||||
{
|
||||
int i, k;
|
||||
cplane_t *pplane;
|
||||
float dot;
|
||||
msurface_t *psurf;
|
||||
dlight_t *lt;
|
||||
|
||||
/* calculate dynamic lighting for bmodel */
|
||||
lt = gl4_newrefdef.dlights;
|
||||
|
||||
for (k = 0; k < gl4_newrefdef.num_dlights; k++, lt++)
|
||||
{
|
||||
R_MarkLights(lt, 1 << k, currentmodel->nodes + currentmodel->firstnode,
|
||||
r_dlightframecount, GL4_MarkSurfaceLights);
|
||||
}
|
||||
|
||||
psurf = ¤tmodel->surfaces[currentmodel->firstmodelsurface];
|
||||
|
||||
if (currententity->flags & RF_TRANSLUCENT)
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
/* TODO: should I care about the 0.25 part? we'll just set alpha to 0.33 or 0.66 depending on surface flag..
|
||||
glColor4f(1, 1, 1, 0.25);
|
||||
R_TexEnv(GL_MODULATE);
|
||||
*/
|
||||
}
|
||||
|
||||
/* draw texture */
|
||||
for (i = 0; i < currentmodel->nummodelsurfaces; i++, psurf++)
|
||||
{
|
||||
/* find which side of the node we are on */
|
||||
pplane = psurf->plane;
|
||||
|
||||
dot = DotProduct(modelorg, pplane->normal) - pplane->dist;
|
||||
|
||||
/* draw the polygon */
|
||||
if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
|
||||
(!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
|
||||
{
|
||||
if (psurf->texinfo->flags & (SURF_TRANS33 | SURF_TRANS66))
|
||||
{
|
||||
/* add to the translucent chain */
|
||||
psurf->texturechain = gl4_alpha_surfaces;
|
||||
gl4_alpha_surfaces = psurf;
|
||||
}
|
||||
else if(!(psurf->flags & SURF_DRAWTURB))
|
||||
{
|
||||
SetAllLightFlags(psurf);
|
||||
RenderLightmappedPoly(currententity, psurf);
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderBrushPoly(currententity, psurf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currententity->flags & RF_TRANSLUCENT)
|
||||
{
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GL4_DrawBrushModel(entity_t *e, gl4model_t *currentmodel)
|
||||
{
|
||||
vec3_t mins, maxs;
|
||||
int i;
|
||||
qboolean rotated;
|
||||
|
||||
if (currentmodel->nummodelsurfaces == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gl4state.currenttexture = -1;
|
||||
|
||||
if (e->angles[0] || e->angles[1] || e->angles[2])
|
||||
{
|
||||
rotated = true;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
mins[i] = e->origin[i] - currentmodel->radius;
|
||||
maxs[i] = e->origin[i] + currentmodel->radius;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rotated = false;
|
||||
VectorAdd(e->origin, currentmodel->mins, mins);
|
||||
VectorAdd(e->origin, currentmodel->maxs, maxs);
|
||||
}
|
||||
|
||||
if (r_cull->value && R_CullBox(mins, maxs, frustum))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (gl_zfix->value)
|
||||
{
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
}
|
||||
|
||||
VectorSubtract(gl4_newrefdef.vieworg, e->origin, modelorg);
|
||||
|
||||
if (rotated)
|
||||
{
|
||||
vec3_t temp;
|
||||
vec3_t forward, right, up;
|
||||
|
||||
VectorCopy(modelorg, temp);
|
||||
AngleVectors(e->angles, forward, right, up);
|
||||
modelorg[0] = DotProduct(temp, forward);
|
||||
modelorg[1] = -DotProduct(temp, right);
|
||||
modelorg[2] = DotProduct(temp, up);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//glPushMatrix();
|
||||
hmm_mat4 oldMat = gl4state.uni3DData.transModelMat4;
|
||||
|
||||
e->angles[0] = -e->angles[0];
|
||||
e->angles[2] = -e->angles[2];
|
||||
GL4_RotateForEntity(e);
|
||||
e->angles[0] = -e->angles[0];
|
||||
e->angles[2] = -e->angles[2];
|
||||
|
||||
DrawInlineBModel(e, currentmodel);
|
||||
|
||||
// glPopMatrix();
|
||||
gl4state.uni3DData.transModelMat4 = oldMat;
|
||||
GL4_UpdateUBO3D();
|
||||
|
||||
if (gl_zfix->value)
|
||||
{
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
RecursiveWorldNode(entity_t *currententity, mnode_t *node)
|
||||
{
|
||||
int c, side, sidebit;
|
||||
cplane_t *plane;
|
||||
msurface_t *surf, **mark;
|
||||
mleaf_t *pleaf;
|
||||
float dot;
|
||||
gl4image_t *image;
|
||||
|
||||
if (node->contents == CONTENTS_SOLID)
|
||||
{
|
||||
return; /* solid */
|
||||
}
|
||||
|
||||
if (node->visframe != gl4_visframecount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (r_cull->value && R_CullBox(node->minmaxs, node->minmaxs + 3, frustum))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* if a leaf node, draw stuff */
|
||||
if (node->contents != CONTENTS_NODE)
|
||||
{
|
||||
pleaf = (mleaf_t *)node;
|
||||
|
||||
/* check for door connected areas */
|
||||
// check for door connected areas
|
||||
if (!R_AreaVisible(gl4_newrefdef.areabits, pleaf))
|
||||
return; // not visible
|
||||
|
||||
mark = pleaf->firstmarksurface;
|
||||
c = pleaf->nummarksurfaces;
|
||||
|
||||
if (c)
|
||||
{
|
||||
do
|
||||
{
|
||||
(*mark)->visframe = gl4_framecount;
|
||||
mark++;
|
||||
}
|
||||
while (--c);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* node is just a decision point, so go down the apropriate
|
||||
sides find which side of the node we are on */
|
||||
plane = node->plane;
|
||||
|
||||
switch (plane->type)
|
||||
{
|
||||
case PLANE_X:
|
||||
dot = modelorg[0] - plane->dist;
|
||||
break;
|
||||
case PLANE_Y:
|
||||
dot = modelorg[1] - plane->dist;
|
||||
break;
|
||||
case PLANE_Z:
|
||||
dot = modelorg[2] - plane->dist;
|
||||
break;
|
||||
default:
|
||||
dot = DotProduct(modelorg, plane->normal) - plane->dist;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dot >= 0)
|
||||
{
|
||||
side = 0;
|
||||
sidebit = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
side = 1;
|
||||
sidebit = SURF_PLANEBACK;
|
||||
}
|
||||
|
||||
/* recurse down the children, front side first */
|
||||
RecursiveWorldNode(currententity, node->children[side]);
|
||||
|
||||
/* draw stuff */
|
||||
for (c = node->numsurfaces,
|
||||
surf = gl4_worldmodel->surfaces + node->firstsurface;
|
||||
c; c--, surf++)
|
||||
{
|
||||
if (surf->visframe != gl4_framecount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((surf->flags & SURF_PLANEBACK) != sidebit)
|
||||
{
|
||||
continue; /* wrong side */
|
||||
}
|
||||
|
||||
if (surf->texinfo->flags & SURF_SKY)
|
||||
{
|
||||
/* just adds to visible sky bounds */
|
||||
GL4_AddSkySurface(surf);
|
||||
}
|
||||
else if (surf->texinfo->flags & (SURF_TRANS33 | SURF_TRANS66))
|
||||
{
|
||||
/* add to the translucent chain */
|
||||
surf->texturechain = gl4_alpha_surfaces;
|
||||
gl4_alpha_surfaces = surf;
|
||||
gl4_alpha_surfaces->texinfo->image = R_TextureAnimation(currententity, surf->texinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
// calling RenderLightmappedPoly() here probably isn't optimal, rendering everything
|
||||
// through texturechains should be faster, because far less glBindTexture() is needed
|
||||
// (and it might allow batching the drawcalls of surfaces with the same texture)
|
||||
#if 0
|
||||
if(!(surf->flags & SURF_DRAWTURB))
|
||||
{
|
||||
RenderLightmappedPoly(surf);
|
||||
}
|
||||
else
|
||||
#endif // 0
|
||||
{
|
||||
/* the polygon is visible, so add it to the texture sorted chain */
|
||||
image = R_TextureAnimation(currententity, surf->texinfo);
|
||||
surf->texturechain = image->texturechain;
|
||||
image->texturechain = surf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* recurse down the back side */
|
||||
RecursiveWorldNode(currententity, node->children[!side]);
|
||||
}
|
||||
|
||||
void
|
||||
GL4_DrawWorld(void)
|
||||
{
|
||||
entity_t ent;
|
||||
|
||||
if (!r_drawworld->value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (gl4_newrefdef.rdflags & RDF_NOWORLDMODEL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy(gl4_newrefdef.vieworg, modelorg);
|
||||
|
||||
/* auto cycle the world frame for texture animation */
|
||||
memset(&ent, 0, sizeof(ent));
|
||||
ent.frame = (int)(gl4_newrefdef.time * 2);
|
||||
|
||||
gl4state.currenttexture = -1;
|
||||
|
||||
GL4_ClearSkyBox();
|
||||
RecursiveWorldNode(&ent, gl4_worldmodel->nodes);
|
||||
DrawTextureChains(&ent);
|
||||
GL4_DrawSkyBox();
|
||||
DrawTriangleOutlines();
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark the leaves and nodes that are
|
||||
* in the PVS for the current cluster
|
||||
*/
|
||||
void
|
||||
GL4_MarkLeaves(void)
|
||||
{
|
||||
const byte *vis;
|
||||
YQ2_ALIGNAS_TYPE(int) byte fatvis[MAX_MAP_LEAFS / 8];
|
||||
mnode_t *node;
|
||||
int i, c;
|
||||
mleaf_t *leaf;
|
||||
int cluster;
|
||||
|
||||
if ((gl4_oldviewcluster == gl4_viewcluster) &&
|
||||
(gl4_oldviewcluster2 == gl4_viewcluster2) &&
|
||||
!r_novis->value &&
|
||||
(gl4_viewcluster != -1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* development aid to let you run around
|
||||
and see exactly where the pvs ends */
|
||||
if (r_lockpvs->value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gl4_visframecount++;
|
||||
gl4_oldviewcluster = gl4_viewcluster;
|
||||
gl4_oldviewcluster2 = gl4_viewcluster2;
|
||||
|
||||
if (r_novis->value || (gl4_viewcluster == -1) || !gl4_worldmodel->vis)
|
||||
{
|
||||
/* mark everything */
|
||||
for (i = 0; i < gl4_worldmodel->numleafs; i++)
|
||||
{
|
||||
gl4_worldmodel->leafs[i].visframe = gl4_visframecount;
|
||||
}
|
||||
|
||||
for (i = 0; i < gl4_worldmodel->numnodes; i++)
|
||||
{
|
||||
gl4_worldmodel->nodes[i].visframe = gl4_visframecount;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
vis = GL4_Mod_ClusterPVS(gl4_viewcluster, gl4_worldmodel);
|
||||
|
||||
/* may have to combine two clusters because of solid water boundaries */
|
||||
if (gl4_viewcluster2 != gl4_viewcluster)
|
||||
{
|
||||
memcpy(fatvis, vis, (gl4_worldmodel->numleafs + 7) / 8);
|
||||
vis = GL4_Mod_ClusterPVS(gl4_viewcluster2, gl4_worldmodel);
|
||||
c = (gl4_worldmodel->numleafs + 31) / 32;
|
||||
|
||||
for (i = 0; i < c; i++)
|
||||
{
|
||||
((int *)fatvis)[i] |= ((int *)vis)[i];
|
||||
}
|
||||
|
||||
vis = fatvis;
|
||||
}
|
||||
|
||||
for (i = 0, leaf = gl4_worldmodel->leafs;
|
||||
i < gl4_worldmodel->numleafs;
|
||||
i++, leaf++)
|
||||
{
|
||||
cluster = leaf->cluster;
|
||||
|
||||
if (cluster == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vis[cluster >> 3] & (1 << (cluster & 7)))
|
||||
{
|
||||
node = (mnode_t *)leaf;
|
||||
|
||||
do
|
||||
{
|
||||
if (node->visframe == gl4_visframecount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
node->visframe = gl4_visframecount;
|
||||
node = node->parent;
|
||||
}
|
||||
while (node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
754
src/client/refresh/gl4/gl4_warp.c
Normal file
754
src/client/refresh/gl4/gl4_warp.c
Normal file
|
@ -0,0 +1,754 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (C) 2016-2017 Daniel Gibson
|
||||
*
|
||||
* This program 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 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Warps. Used on water surfaces und for skybox rotation.
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#include "header/local.h"
|
||||
|
||||
static void
|
||||
R_BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
|
||||
{
|
||||
int i, j;
|
||||
float *v;
|
||||
|
||||
mins[0] = mins[1] = mins[2] = 9999;
|
||||
maxs[0] = maxs[1] = maxs[2] = -9999;
|
||||
v = verts;
|
||||
|
||||
for (i = 0; i < numverts; i++)
|
||||
{
|
||||
for (j = 0; j < 3; j++, v++)
|
||||
{
|
||||
if (*v < mins[j])
|
||||
{
|
||||
mins[j] = *v;
|
||||
}
|
||||
|
||||
if (*v > maxs[j])
|
||||
{
|
||||
maxs[j] = *v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const float SUBDIVIDE_SIZE = 64.0f;
|
||||
|
||||
static void
|
||||
R_SubdividePolygon(int numverts, float *verts, msurface_t *warpface)
|
||||
{
|
||||
int i, j, k;
|
||||
vec3_t mins, maxs;
|
||||
float m;
|
||||
float *v;
|
||||
vec3_t front[64], back[64];
|
||||
int f, b;
|
||||
float dist[64];
|
||||
float frac;
|
||||
glpoly_t *poly;
|
||||
float s, t;
|
||||
vec3_t total;
|
||||
float total_s, total_t;
|
||||
vec3_t normal;
|
||||
|
||||
VectorCopy(warpface->plane->normal, normal);
|
||||
|
||||
if (numverts > 60)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "numverts = %i", numverts);
|
||||
}
|
||||
|
||||
R_BoundPoly(numverts, verts, mins, maxs);
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
m = (mins[i] + maxs[i]) * 0.5;
|
||||
m = SUBDIVIDE_SIZE * floor(m / SUBDIVIDE_SIZE + 0.5);
|
||||
|
||||
if (maxs[i] - m < 8)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m - mins[i] < 8)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* cut it */
|
||||
v = verts + i;
|
||||
|
||||
for (j = 0; j < numverts; j++, v += 3)
|
||||
{
|
||||
dist[j] = *v - m;
|
||||
}
|
||||
|
||||
/* wrap cases */
|
||||
dist[j] = dist[0];
|
||||
v -= i;
|
||||
VectorCopy(verts, v);
|
||||
|
||||
f = b = 0;
|
||||
v = verts;
|
||||
|
||||
for (j = 0; j < numverts; j++, v += 3)
|
||||
{
|
||||
if (dist[j] >= 0)
|
||||
{
|
||||
VectorCopy(v, front[f]);
|
||||
f++;
|
||||
}
|
||||
|
||||
if (dist[j] <= 0)
|
||||
{
|
||||
VectorCopy(v, back[b]);
|
||||
b++;
|
||||
}
|
||||
|
||||
if ((dist[j] == 0) || (dist[j + 1] == 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((dist[j] > 0) != (dist[j + 1] > 0))
|
||||
{
|
||||
/* clip point */
|
||||
frac = dist[j] / (dist[j] - dist[j + 1]);
|
||||
|
||||
for (k = 0; k < 3; k++)
|
||||
{
|
||||
front[f][k] = back[b][k] = v[k] + frac * (v[3 + k] - v[k]);
|
||||
}
|
||||
|
||||
f++;
|
||||
b++;
|
||||
}
|
||||
}
|
||||
|
||||
R_SubdividePolygon(f, front[0], warpface);
|
||||
R_SubdividePolygon(b, back[0], warpface);
|
||||
return;
|
||||
}
|
||||
|
||||
/* add a point in the center to help keep warp valid */
|
||||
poly = Hunk_Alloc(sizeof(glpoly_t) + ((numverts - 4) + 2) * sizeof(gl4_3D_vtx_t));
|
||||
poly->next = warpface->polys;
|
||||
warpface->polys = poly;
|
||||
poly->numverts = numverts + 2;
|
||||
VectorClear(total);
|
||||
total_s = 0;
|
||||
total_t = 0;
|
||||
|
||||
for (i = 0; i < numverts; i++, verts += 3)
|
||||
{
|
||||
VectorCopy(verts, poly->vertices[i + 1].pos);
|
||||
s = DotProduct(verts, warpface->texinfo->vecs[0]);
|
||||
t = DotProduct(verts, warpface->texinfo->vecs[1]);
|
||||
|
||||
total_s += s;
|
||||
total_t += t;
|
||||
VectorAdd(total, verts, total);
|
||||
|
||||
poly->vertices[i + 1].texCoord[0] = s;
|
||||
poly->vertices[i + 1].texCoord[1] = t;
|
||||
VectorCopy(normal, poly->vertices[i + 1].normal);
|
||||
poly->vertices[i + 1].lightFlags = 0;
|
||||
}
|
||||
|
||||
VectorScale(total, (1.0 / numverts), poly->vertices[0].pos);
|
||||
poly->vertices[0].texCoord[0] = total_s / numverts;
|
||||
poly->vertices[0].texCoord[1] = total_t / numverts;
|
||||
VectorCopy(normal, poly->vertices[0].normal);
|
||||
|
||||
/* copy first vertex to last */
|
||||
//memcpy(poly->vertices[i + 1], poly->vertices[1], sizeof(poly->vertices[0]));
|
||||
poly->vertices[i + 1] = poly->vertices[1];
|
||||
}
|
||||
|
||||
/*
|
||||
* Breaks a polygon up along axial 64 unit
|
||||
* boundaries so that turbulent and sky warps
|
||||
* can be done reasonably.
|
||||
*/
|
||||
void
|
||||
GL4_SubdivideSurface(msurface_t *fa, gl4model_t* loadmodel)
|
||||
{
|
||||
vec3_t verts[64];
|
||||
int numverts;
|
||||
int i;
|
||||
int lindex;
|
||||
float *vec;
|
||||
|
||||
/* convert edges back to a normal polygon */
|
||||
numverts = 0;
|
||||
|
||||
for (i = 0; i < fa->numedges; i++)
|
||||
{
|
||||
lindex = loadmodel->surfedges[fa->firstedge + i];
|
||||
|
||||
if (lindex > 0)
|
||||
{
|
||||
vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
|
||||
}
|
||||
else
|
||||
{
|
||||
vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
|
||||
}
|
||||
|
||||
VectorCopy(vec, verts[numverts]);
|
||||
numverts++;
|
||||
}
|
||||
|
||||
R_SubdividePolygon(numverts, verts[0], fa);
|
||||
}
|
||||
|
||||
/*
|
||||
* Does a water warp on the pre-fragmented glpoly_t chain
|
||||
*/
|
||||
void
|
||||
GL4_EmitWaterPolys(msurface_t *fa)
|
||||
{
|
||||
glpoly_t *bp;
|
||||
float scroll = 0.0f;
|
||||
|
||||
if (fa->texinfo->flags & SURF_FLOWING)
|
||||
{
|
||||
scroll = -64.0f * ((gl4_newrefdef.time * 0.5) - (int)(gl4_newrefdef.time * 0.5));
|
||||
if (scroll == 0.0f) // this is done in GL4_DrawGLFlowingPoly() TODO: keep?
|
||||
{
|
||||
scroll = -64.0f;
|
||||
}
|
||||
}
|
||||
|
||||
qboolean updateUni3D = false;
|
||||
if(gl4state.uni3DData.scroll != scroll)
|
||||
{
|
||||
gl4state.uni3DData.scroll = scroll;
|
||||
updateUni3D = true;
|
||||
}
|
||||
// these surfaces (mostly water and lava, I think?) don't have a lightmap.
|
||||
// rendering water at full brightness looks bad (esp. for water in dark environments)
|
||||
// so default use a factor of 0.5 (ontop of intensity)
|
||||
// but lava should be bright and glowing, so use full brightness there
|
||||
float lightScale = fa->texinfo->image->is_lava ? 1.0f : 0.5f;
|
||||
if(lightScale != gl4state.uni3DData.lightScaleForTurb)
|
||||
{
|
||||
gl4state.uni3DData.lightScaleForTurb = lightScale;
|
||||
updateUni3D = true;
|
||||
}
|
||||
|
||||
if(updateUni3D)
|
||||
{
|
||||
GL4_UpdateUBO3D();
|
||||
}
|
||||
|
||||
GL4_UseProgram(gl4state.si3Dturb.shaderProgram);
|
||||
|
||||
GL4_BindVAO(gl4state.vao3D);
|
||||
GL4_BindVBO(gl4state.vbo3D);
|
||||
|
||||
for (bp = fa->polys; bp != NULL; bp = bp->next)
|
||||
{
|
||||
GL4_BufferAndDraw3D(bp->vertices, bp->numverts, GL_TRIANGLE_FAN);
|
||||
}
|
||||
}
|
||||
|
||||
// ########### below: Sky-specific stuff ##########
|
||||
|
||||
#define ON_EPSILON 0.1 /* point on plane side epsilon */
|
||||
enum { MAX_CLIP_VERTS = 64 };
|
||||
|
||||
|
||||
static const int skytexorder[6] = {0, 2, 1, 3, 4, 5};
|
||||
|
||||
static float skymins[2][6], skymaxs[2][6];
|
||||
static float sky_min, sky_max;
|
||||
|
||||
static float skyrotate;
|
||||
static int skyautorotate;
|
||||
static vec3_t skyaxis;
|
||||
static gl4image_t* sky_images[6];
|
||||
|
||||
/* 3dstudio environment map names */
|
||||
static const char* suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
|
||||
|
||||
vec3_t skyclip[6] = {
|
||||
{1, 1, 0},
|
||||
{1, -1, 0},
|
||||
{0, -1, 1},
|
||||
{0, 1, 1},
|
||||
{1, 0, 1},
|
||||
{-1, 0, 1}
|
||||
};
|
||||
int c_sky;
|
||||
|
||||
int st_to_vec[6][3] = {
|
||||
{3, -1, 2},
|
||||
{-3, 1, 2},
|
||||
|
||||
{1, 3, 2},
|
||||
{-1, -3, 2},
|
||||
|
||||
{-2, -1, 3}, /* 0 degrees yaw, look straight up */
|
||||
{2, -1, -3} /* look straight down */
|
||||
};
|
||||
|
||||
int vec_to_st[6][3] = {
|
||||
{-2, 3, 1},
|
||||
{2, 3, -1},
|
||||
|
||||
{1, 3, 2},
|
||||
{-1, 3, -2},
|
||||
|
||||
{-2, -1, 3},
|
||||
{-2, 1, -3}
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
GL4_SetSky(const char *name, float rotate, int autorotate, const vec3_t axis)
|
||||
{
|
||||
char skyname[MAX_QPATH];
|
||||
int i;
|
||||
|
||||
Q_strlcpy(skyname, name, sizeof(skyname));
|
||||
skyrotate = rotate;
|
||||
skyautorotate = autorotate;
|
||||
VectorCopy(axis, skyaxis);
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
gl4image_t *image;
|
||||
|
||||
image = (gl4image_t *)GetSkyImage(skyname, suf[i],
|
||||
r_palettedtexture->value, (findimage_t)GL4_FindImage);
|
||||
|
||||
if (!image)
|
||||
{
|
||||
R_Printf(PRINT_ALL, "%s: can't load %s:%s sky\n",
|
||||
__func__, skyname, suf[i]);
|
||||
image = gl4_notexture;
|
||||
}
|
||||
|
||||
sky_images[i] = image;
|
||||
}
|
||||
|
||||
sky_min = 1.0 / 512;
|
||||
sky_max = 511.0 / 512;
|
||||
}
|
||||
|
||||
static void
|
||||
DrawSkyPolygon(int nump, vec3_t vecs)
|
||||
{
|
||||
int i, j;
|
||||
vec3_t v, av;
|
||||
float s, t, dv;
|
||||
int axis;
|
||||
float *vp;
|
||||
|
||||
c_sky++;
|
||||
|
||||
/* decide which face it maps to */
|
||||
VectorCopy(vec3_origin, v);
|
||||
|
||||
for (i = 0, vp = vecs; i < nump; i++, vp += 3)
|
||||
{
|
||||
VectorAdd(vp, v, v);
|
||||
}
|
||||
|
||||
av[0] = fabs(v[0]);
|
||||
av[1] = fabs(v[1]);
|
||||
av[2] = fabs(v[2]);
|
||||
|
||||
if ((av[0] > av[1]) && (av[0] > av[2]))
|
||||
{
|
||||
if (v[0] < 0)
|
||||
{
|
||||
axis = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
axis = 0;
|
||||
}
|
||||
}
|
||||
else if ((av[1] > av[2]) && (av[1] > av[0]))
|
||||
{
|
||||
if (v[1] < 0)
|
||||
{
|
||||
axis = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
axis = 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v[2] < 0)
|
||||
{
|
||||
axis = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
axis = 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* project new texture coords */
|
||||
for (i = 0; i < nump; i++, vecs += 3)
|
||||
{
|
||||
j = vec_to_st[axis][2];
|
||||
|
||||
if (j > 0)
|
||||
{
|
||||
dv = vecs[j - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
dv = -vecs[-j - 1];
|
||||
}
|
||||
|
||||
if (dv < 0.001)
|
||||
{
|
||||
continue; /* don't divide by zero */
|
||||
}
|
||||
|
||||
j = vec_to_st[axis][0];
|
||||
|
||||
if (j < 0)
|
||||
{
|
||||
s = -vecs[-j - 1] / dv;
|
||||
}
|
||||
else
|
||||
{
|
||||
s = vecs[j - 1] / dv;
|
||||
}
|
||||
|
||||
j = vec_to_st[axis][1];
|
||||
|
||||
if (j < 0)
|
||||
{
|
||||
t = -vecs[-j - 1] / dv;
|
||||
}
|
||||
else
|
||||
{
|
||||
t = vecs[j - 1] / dv;
|
||||
}
|
||||
|
||||
if (s < skymins[0][axis])
|
||||
{
|
||||
skymins[0][axis] = s;
|
||||
}
|
||||
|
||||
if (t < skymins[1][axis])
|
||||
{
|
||||
skymins[1][axis] = t;
|
||||
}
|
||||
|
||||
if (s > skymaxs[0][axis])
|
||||
{
|
||||
skymaxs[0][axis] = s;
|
||||
}
|
||||
|
||||
if (t > skymaxs[1][axis])
|
||||
{
|
||||
skymaxs[1][axis] = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ClipSkyPolygon(int nump, vec3_t vecs, int stage)
|
||||
{
|
||||
float *norm;
|
||||
float *v;
|
||||
qboolean front, back;
|
||||
float d, e;
|
||||
float dists[MAX_CLIP_VERTS];
|
||||
int sides[MAX_CLIP_VERTS];
|
||||
vec3_t newv[2][MAX_CLIP_VERTS];
|
||||
int newc[2];
|
||||
int i, j;
|
||||
|
||||
if (nump > MAX_CLIP_VERTS - 2)
|
||||
{
|
||||
ri.Sys_Error(ERR_DROP, "R_ClipSkyPolygon: MAX_CLIP_VERTS");
|
||||
}
|
||||
|
||||
if (stage == 6)
|
||||
{
|
||||
/* fully clipped, so draw it */
|
||||
DrawSkyPolygon(nump, vecs);
|
||||
return;
|
||||
}
|
||||
|
||||
front = back = false;
|
||||
norm = skyclip[stage];
|
||||
|
||||
for (i = 0, v = vecs; i < nump; i++, v += 3)
|
||||
{
|
||||
d = DotProduct(v, norm);
|
||||
|
||||
if (d > ON_EPSILON)
|
||||
{
|
||||
front = true;
|
||||
sides[i] = SIDE_FRONT;
|
||||
}
|
||||
else if (d < -ON_EPSILON)
|
||||
{
|
||||
back = true;
|
||||
sides[i] = SIDE_BACK;
|
||||
}
|
||||
else
|
||||
{
|
||||
sides[i] = SIDE_ON;
|
||||
}
|
||||
|
||||
dists[i] = d;
|
||||
}
|
||||
|
||||
if (!front || !back)
|
||||
{
|
||||
/* not clipped */
|
||||
ClipSkyPolygon(nump, vecs, stage + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* clip it */
|
||||
sides[i] = sides[0];
|
||||
dists[i] = dists[0];
|
||||
VectorCopy(vecs, (vecs + (i * 3)));
|
||||
newc[0] = newc[1] = 0;
|
||||
|
||||
for (i = 0, v = vecs; i < nump; i++, v += 3)
|
||||
{
|
||||
switch (sides[i])
|
||||
{
|
||||
case SIDE_FRONT:
|
||||
VectorCopy(v, newv[0][newc[0]]);
|
||||
newc[0]++;
|
||||
break;
|
||||
case SIDE_BACK:
|
||||
VectorCopy(v, newv[1][newc[1]]);
|
||||
newc[1]++;
|
||||
break;
|
||||
case SIDE_ON:
|
||||
VectorCopy(v, newv[0][newc[0]]);
|
||||
newc[0]++;
|
||||
VectorCopy(v, newv[1][newc[1]]);
|
||||
newc[1]++;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((sides[i] == SIDE_ON) ||
|
||||
(sides[i + 1] == SIDE_ON) ||
|
||||
(sides[i + 1] == sides[i]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
d = dists[i] / (dists[i] - dists[i + 1]);
|
||||
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
e = v[j] + d * (v[j + 3] - v[j]);
|
||||
newv[0][newc[0]][j] = e;
|
||||
newv[1][newc[1]][j] = e;
|
||||
}
|
||||
|
||||
newc[0]++;
|
||||
newc[1]++;
|
||||
}
|
||||
|
||||
/* continue */
|
||||
ClipSkyPolygon(newc[0], newv[0][0], stage + 1);
|
||||
ClipSkyPolygon(newc[1], newv[1][0], stage + 1);
|
||||
}
|
||||
|
||||
void
|
||||
GL4_AddSkySurface(msurface_t *fa)
|
||||
{
|
||||
int i;
|
||||
vec3_t verts[MAX_CLIP_VERTS];
|
||||
glpoly_t *p;
|
||||
|
||||
/* calculate vertex values for sky box */
|
||||
for (p = fa->polys; p; p = p->next)
|
||||
{
|
||||
for (i = 0; i < p->numverts; i++)
|
||||
{
|
||||
VectorSubtract(p->vertices[i].pos, gl4_origin, verts[i]);
|
||||
}
|
||||
|
||||
ClipSkyPolygon(p->numverts, verts[0], 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GL4_ClearSkyBox(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
skymins[0][i] = skymins[1][i] = 9999;
|
||||
skymaxs[0][i] = skymaxs[1][i] = -9999;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
MakeSkyVec(float s, float t, int axis, gl4_3D_vtx_t* vert)
|
||||
{
|
||||
vec3_t v, b;
|
||||
int j, k;
|
||||
|
||||
float dist = (r_farsee->value == 0) ? 2300.0f : 4096.0f;
|
||||
|
||||
b[0] = s * dist;
|
||||
b[1] = t * dist;
|
||||
b[2] = dist;
|
||||
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
k = st_to_vec[axis][j];
|
||||
|
||||
if (k < 0)
|
||||
{
|
||||
v[j] = -b[-k - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
v[j] = b[k - 1];
|
||||
}
|
||||
}
|
||||
|
||||
/* avoid bilerp seam */
|
||||
s = (s + 1) * 0.5;
|
||||
t = (t + 1) * 0.5;
|
||||
|
||||
if (s < sky_min)
|
||||
{
|
||||
s = sky_min;
|
||||
}
|
||||
else if (s > sky_max)
|
||||
{
|
||||
s = sky_max;
|
||||
}
|
||||
|
||||
if (t < sky_min)
|
||||
{
|
||||
t = sky_min;
|
||||
}
|
||||
else if (t > sky_max)
|
||||
{
|
||||
t = sky_max;
|
||||
}
|
||||
|
||||
t = 1.0 - t;
|
||||
|
||||
VectorCopy(v, vert->pos);
|
||||
|
||||
vert->texCoord[0] = s;
|
||||
vert->texCoord[1] = t;
|
||||
|
||||
vert->lmTexCoord[0] = vert->lmTexCoord[1] = 0.0f;
|
||||
}
|
||||
|
||||
void
|
||||
GL4_DrawSkyBox(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (skyrotate)
|
||||
{ /* check for no sky at all */
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
if ((skymins[0][i] < skymaxs[0][i]) &&
|
||||
(skymins[1][i] < skymaxs[1][i]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 6)
|
||||
{
|
||||
return; /* nothing visible */
|
||||
}
|
||||
}
|
||||
|
||||
// glPushMatrix();
|
||||
hmm_mat4 origModelMat = gl4state.uni3DData.transModelMat4;
|
||||
|
||||
// glTranslatef(gl4_origin[0], gl4_origin[1], gl4_origin[2]);
|
||||
hmm_vec3 transl = HMM_Vec3(gl4_origin[0], gl4_origin[1], gl4_origin[2]);
|
||||
hmm_mat4 modMVmat = HMM_MultiplyMat4(origModelMat, HMM_Translate(transl));
|
||||
if(skyrotate != 0.0f)
|
||||
{
|
||||
// glRotatef(gl4_newrefdef.time * skyrotate, skyaxis[0], skyaxis[1], skyaxis[2]);
|
||||
hmm_vec3 rotAxis = HMM_Vec3(skyaxis[0], skyaxis[1], skyaxis[2]);
|
||||
modMVmat = HMM_MultiplyMat4(modMVmat, HMM_Rotate(
|
||||
(skyautorotate ? gl4_newrefdef.time : 1.f) * skyrotate, rotAxis));
|
||||
}
|
||||
gl4state.uni3DData.transModelMat4 = modMVmat;
|
||||
GL4_UpdateUBO3D();
|
||||
|
||||
GL4_UseProgram(gl4state.si3Dsky.shaderProgram);
|
||||
GL4_BindVAO(gl4state.vao3D);
|
||||
GL4_BindVBO(gl4state.vbo3D);
|
||||
|
||||
// TODO: this could all be done in one drawcall.. but.. whatever, it's <= 6 drawcalls/frame
|
||||
|
||||
gl4_3D_vtx_t skyVertices[4];
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
if (skyrotate != 0.0f)
|
||||
{
|
||||
skymins[0][i] = -1;
|
||||
skymins[1][i] = -1;
|
||||
skymaxs[0][i] = 1;
|
||||
skymaxs[1][i] = 1;
|
||||
}
|
||||
|
||||
if ((skymins[0][i] >= skymaxs[0][i]) ||
|
||||
(skymins[1][i] >= skymaxs[1][i]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GL4_Bind(sky_images[skytexorder[i]]->texnum);
|
||||
|
||||
MakeSkyVec( skymins [ 0 ] [ i ], skymins [ 1 ] [ i ], i, &skyVertices[0] );
|
||||
MakeSkyVec( skymins [ 0 ] [ i ], skymaxs [ 1 ] [ i ], i, &skyVertices[1] );
|
||||
MakeSkyVec( skymaxs [ 0 ] [ i ], skymaxs [ 1 ] [ i ], i, &skyVertices[2] );
|
||||
MakeSkyVec( skymaxs [ 0 ] [ i ], skymins [ 1 ] [ i ], i, &skyVertices[3] );
|
||||
|
||||
GL4_BufferAndDraw3D(skyVertices, 4, GL_TRIANGLE_FAN);
|
||||
}
|
||||
|
||||
// glPopMatrix();
|
||||
gl4state.uni3DData.transModelMat4 = origModelMat;
|
||||
GL4_UpdateUBO3D();
|
||||
}
|
311
src/client/refresh/gl4/glad/include/KHR/khrplatform.h
Normal file
311
src/client/refresh/gl4/glad/include/KHR/khrplatform.h
Normal file
|
@ -0,0 +1,311 @@
|
|||
#ifndef __khrplatform_h_
|
||||
#define __khrplatform_h_
|
||||
|
||||
/*
|
||||
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
** "Materials"), to deal in the Materials without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
** permit persons to whom the Materials are furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included
|
||||
** in all copies or substantial portions of the Materials.
|
||||
**
|
||||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||
* The last semantic modification to khrplatform.h was at commit ID:
|
||||
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
* group so that they can be included in future versions of this file.
|
||||
* Please submit changes by filing pull requests or issues on
|
||||
* the EGL Registry repository linked above.
|
||||
*
|
||||
*
|
||||
* See the Implementer's Guidelines for information about where this file
|
||||
* should be located on your system and for more details of its use:
|
||||
* http://www.khronos.org/registry/implementers_guide.pdf
|
||||
*
|
||||
* This file should be included as
|
||||
* #include <KHR/khrplatform.h>
|
||||
* by Khronos client API header files that use its types and defines.
|
||||
*
|
||||
* The types in khrplatform.h should only be used to define API-specific types.
|
||||
*
|
||||
* Types defined in khrplatform.h:
|
||||
* khronos_int8_t signed 8 bit
|
||||
* khronos_uint8_t unsigned 8 bit
|
||||
* khronos_int16_t signed 16 bit
|
||||
* khronos_uint16_t unsigned 16 bit
|
||||
* khronos_int32_t signed 32 bit
|
||||
* khronos_uint32_t unsigned 32 bit
|
||||
* khronos_int64_t signed 64 bit
|
||||
* khronos_uint64_t unsigned 64 bit
|
||||
* khronos_intptr_t signed same number of bits as a pointer
|
||||
* khronos_uintptr_t unsigned same number of bits as a pointer
|
||||
* khronos_ssize_t signed size
|
||||
* khronos_usize_t unsigned size
|
||||
* khronos_float_t signed 32 bit floating point
|
||||
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
||||
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
||||
* nanoseconds
|
||||
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
||||
* khronos_boolean_enum_t enumerated boolean type. This should
|
||||
* only be used as a base type when a client API's boolean type is
|
||||
* an enum. Client APIs which use an integer or other type for
|
||||
* booleans cannot use this as the base type for their boolean.
|
||||
*
|
||||
* Tokens defined in khrplatform.h:
|
||||
*
|
||||
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
||||
*
|
||||
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
||||
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
||||
*
|
||||
* Calling convention macros defined in this file:
|
||||
* KHRONOS_APICALL
|
||||
* KHRONOS_APIENTRY
|
||||
* KHRONOS_APIATTRIBUTES
|
||||
*
|
||||
* These may be used in function prototypes as:
|
||||
*
|
||||
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
||||
* int arg1,
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||
# define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
# define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
# define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
#if defined (__ARMCC_2__)
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
/*
|
||||
* To support platform where unsigned long cannot be used interchangeably with
|
||||
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||
* unsigned long long or similar (this results in different C++ name mangling).
|
||||
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||
* platforms where the size of a pointer is larger than the size of long.
|
||||
*/
|
||||
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||
#define KHRONOS_USE_INTPTR_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
typedef __int32 khronos_int32_t;
|
||||
typedef unsigned __int32 khronos_uint32_t;
|
||||
typedef __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
typedef long int khronos_int64_t;
|
||||
typedef unsigned long int khronos_uint64_t;
|
||||
#else
|
||||
typedef long long int khronos_int64_t;
|
||||
typedef unsigned long long int khronos_uint64_t;
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif 0
|
||||
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Types that are (so far) the same on all platforms
|
||||
*/
|
||||
typedef signed char khronos_int8_t;
|
||||
typedef unsigned char khronos_uint8_t;
|
||||
typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
#ifdef KHRONOS_USE_INTPTR_T
|
||||
typedef intptr_t khronos_intptr_t;
|
||||
typedef uintptr_t khronos_uintptr_t;
|
||||
#elif defined(_WIN64)
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef unsigned long long int khronos_uintptr_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef unsigned long int khronos_uintptr_t;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_ssize_t;
|
||||
typedef unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_FLOAT
|
||||
/*
|
||||
* Float type
|
||||
*/
|
||||
typedef float khronos_float_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_INT64
|
||||
/* Time types
|
||||
*
|
||||
* These types can be used to represent a time interval in nanoseconds or
|
||||
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
||||
* of nanoseconds since some arbitrary system event (e.g. since the last
|
||||
* time the system booted). The Unadjusted System Time is an unsigned
|
||||
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
||||
* may be either signed or unsigned.
|
||||
*/
|
||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dummy value used to pad enum types to 32 bits.
|
||||
*/
|
||||
#ifndef KHRONOS_MAX_ENUM
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
* Values other than zero should be considered to be true. Therefore
|
||||
* comparisons should not be made against KHRONOS_TRUE.
|
||||
*/
|
||||
typedef enum {
|
||||
KHRONOS_FALSE = 0,
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||
} khronos_boolean_enum_t;
|
||||
|
||||
#endif /* __khrplatform_h_ */
|
3734
src/client/refresh/gl4/glad/include/glad/glad.h
Normal file
3734
src/client/refresh/gl4/glad/include/glad/glad.h
Normal file
File diff suppressed because it is too large
Load diff
1717
src/client/refresh/gl4/glad/src/glad.c
Normal file
1717
src/client/refresh/gl4/glad/src/glad.c
Normal file
File diff suppressed because it is too large
Load diff
960
src/client/refresh/gl4/header/DG_dynarr.h
Normal file
960
src/client/refresh/gl4/header/DG_dynarr.h
Normal file
|
@ -0,0 +1,960 @@
|
|||
/*
|
||||
* A header-only typesafe dynamic array implementation for plain C,
|
||||
* kinda like C++ std::vector. This code is compatible with C++, but should
|
||||
* only be used with POD (plain old data) types, as it uses memcpy() etc
|
||||
* instead of copy/move construction/assignment.
|
||||
* It requires a new type (created with the DA_TYPEDEF(ELEMENT_TYPE, ARRAY_TYPE_NAME)
|
||||
* macro) for each kind of element you want to put in a dynamic array; however
|
||||
* the "functions" to manipulate the array are actually macros and the same
|
||||
* for all element types.
|
||||
* The array elements are accessed via dynArr.p[i] or da_get(dynArr, i)
|
||||
* - the latter checks whether i is a valid index and asserts if not.
|
||||
*
|
||||
* One thing to keep in mind is that, because of using macros, the arguments to
|
||||
* the "functions" are usually evaluated more than once, so you should avoid
|
||||
* putting things with side effect (like function-calls with side effects or i++)
|
||||
* into them. Notable exceptions are the value arguments (v) of da_push()
|
||||
* and da_insert(), so it's still ok to do da_push(arr, fun_with_sideffects());
|
||||
* or da_insert(a, 3, x++);
|
||||
*
|
||||
* The function-like da_* macros are short aliases of dg_dynarr_* macros.
|
||||
* If the short names clash with anything in your code or other headers
|
||||
* you are using, you can, before #including this header, do
|
||||
* #define DG_DYNARR_NO_SHORTNAMES
|
||||
* and use the long dg_dynarr_* forms of the macros instead.
|
||||
*
|
||||
* Using this library in your project:
|
||||
* Put this file somewhere in your project.
|
||||
* In *one* of your .c/.cpp files, do
|
||||
* #define DG_DYNARR_IMPLEMENTATION
|
||||
* #include "DG_dynarr.h"
|
||||
* to create the implementation of this library in that file.
|
||||
* You can just #include "DG_dynarr.h" (without the #define) in other source
|
||||
* files to use it there.
|
||||
*
|
||||
* See below this comment block for a usage example.
|
||||
*
|
||||
* You can #define your own allocators, assertion and the amount of runtime
|
||||
* checking of indexes, see CONFIGURATION section in the code for more information.
|
||||
*
|
||||
*
|
||||
* This is heavily inspired by Sean Barrett's stretchy_buffer.h
|
||||
* ( see: https://github.com/nothings/stb/blob/master/stretchy_buffer.h )
|
||||
* However I wanted to have a struct that holds the array pointer and the length
|
||||
* and capacity, so that struct always remains at the same address while the
|
||||
* array memory might be reallocated.
|
||||
* I can live with arr.p[i] instead of arr[i], but I like how he managed to use
|
||||
* macros to create an API that doesn't force the user to specify the stored
|
||||
* type over and over again, so I stole some of his tricks :-)
|
||||
*
|
||||
* This has been tested with GCC 4.8 and clang 3.8 (-std=gnu89, -std=c99 and as C++;
|
||||
* -std=c89 works if you convert the C++-style comments to C comments) and
|
||||
* Microsoft Visual Studio 6 and 2010 (32bit) and 2013 (32bit and 64bit).
|
||||
* I guess it works with all (recentish) C++ compilers and C compilers supporting
|
||||
* C99 or even C89 + C++ comments (otherwise converting the comments should help).
|
||||
*
|
||||
* (C) 2016 Daniel Gibson
|
||||
*
|
||||
* LICENSE
|
||||
* This software is dual-licensed to the public domain and under the following
|
||||
* license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
* publish, and distribute this file as you see fit.
|
||||
* No warranty implied; use at your own risk.
|
||||
*/
|
||||
#if 0 // Usage Example:
|
||||
#define DG_DYNARR_IMPLEMENTATION // this define is only needed in *one* .c/.cpp file!
|
||||
#include "DG_dynarr.h"
|
||||
|
||||
DA_TYPEDEF(int, MyIntArrType); // creates MyIntArrType - a dynamic array for ints
|
||||
|
||||
void printIntArr(MyIntArrType* arr, const char* name)
|
||||
{
|
||||
// note that arr is a pointer here, so use *arr in the da_*() functions.
|
||||
printf("%s = {", name);
|
||||
if(da_count(*arr) > 0)
|
||||
printf(" %d", arr->p[0]);
|
||||
for(int i=1; i<da_count(*arr); ++i)
|
||||
printf(", %d", arr->p[i]);
|
||||
printf(" }\n");
|
||||
}
|
||||
|
||||
void myFunction()
|
||||
{
|
||||
MyIntArrType a1 = {0}; // make sure to zero out the struct
|
||||
// instead of = {0}; you could also call da_init(a1);
|
||||
|
||||
da_push(a1, 42);
|
||||
assert(da_count(a1) == 1 && a1.p[0] == 42);
|
||||
|
||||
int* addedElements = da_addn_uninit(a1, 3);
|
||||
assert(da_count(a1) == 4);
|
||||
for(size_t i=0; i<3; ++i)
|
||||
addedElements[i] = i+5;
|
||||
|
||||
printIntArr(&a1, "a1"); // "a1 = { 42, 5, 6, 7 }"
|
||||
|
||||
MyIntArrType a2;
|
||||
da_init(a2);
|
||||
|
||||
da_addn(a2, a1.p, da_count(a1)); // copy all elements from a1 to a2
|
||||
assert(da_count(a2) == 4);
|
||||
|
||||
da_insert(a2, 1, 11);
|
||||
printIntArr(&a2, "a2"); // "a2 = { 42, 11, 5, 6, 7 }"
|
||||
|
||||
da_delete(a2, 2);
|
||||
printIntArr(&a2, "a2"); // "a2 = { 42, 11, 6, 7 }"
|
||||
|
||||
da_deletefast(a2, 0);
|
||||
printIntArr(&a2, "a2"); // "a2 = { 7, 11, 6 }"
|
||||
|
||||
da_push(a1, 3);
|
||||
printIntArr(&a1, "a1"); // "a1 = { 42, 5, 6, 7, 3 }"
|
||||
|
||||
int x=da_pop(a1);
|
||||
printf("x = %d\n", x); // "x = 3"
|
||||
printIntArr(&a1, "a1"); // "a1 = { 42, 5, 6, 7 }"
|
||||
|
||||
da_free(a1); // make sure not to leak memory!
|
||||
da_free(a2);
|
||||
}
|
||||
#endif // 0 (usage example)
|
||||
|
||||
#ifndef DG__DYNARR_H
|
||||
#define DG__DYNARR_H
|
||||
|
||||
// ######### CONFIGURATION #########
|
||||
|
||||
// following: some #defines that you can tweak to your liking
|
||||
|
||||
// you can reduce some overhead by defining DG_DYNARR_INDEX_CHECK_LEVEL to 2, 1 or 0
|
||||
#ifndef DG_DYNARR_INDEX_CHECK_LEVEL
|
||||
|
||||
// 0: (almost) no index checking
|
||||
// 1: macros "returning" something return a.p[0] or NULL if the index was invalid
|
||||
// 2: assertions in all macros taking indexes that make sure they're valid
|
||||
// 3: 1 and 2
|
||||
#define DG_DYNARR_INDEX_CHECK_LEVEL 3
|
||||
|
||||
#endif // DG_DYNARR_INDEX_CHECK_LEVEL
|
||||
|
||||
// you can #define your own DG_DYNARR_ASSERT(condition, msgstring)
|
||||
// that will be used for all assertions in this code.
|
||||
#ifndef DG_DYNARR_ASSERT
|
||||
#include <assert.h>
|
||||
#define DG_DYNARR_ASSERT(cond, msg) assert((cond) && msg)
|
||||
#endif
|
||||
|
||||
// you can #define DG_DYNARR_OUT_OF_MEMORY to some code that will be executed
|
||||
// if allocating memory fails
|
||||
// it's needed only before the #define DG_DYNARR_IMPLEMENTATION #include of
|
||||
// this header, so the following is here only for reference and commented out
|
||||
/*
|
||||
#ifndef DG_DYNARR_OUT_OF_MEMORY
|
||||
#define DG_DYNARR_OUT_OF_MEMORY DG_DYNARR_ASSERT(0, "Out of Memory!");
|
||||
#endif
|
||||
*/
|
||||
|
||||
// By default, C's malloc(), realloc() and free() is used to allocate/free heap memory
|
||||
// (see beginning of "#ifdef DG_DYNARR_IMPLEMENTATION" block below).
|
||||
// You can #define DG_DYNARR_MALLOC, DG_DYNARR_REALLOC and DG_DYNARR_FREE yourself
|
||||
// to provide alternative implementations like Win32 Heap(Re)Alloc/HeapFree
|
||||
// it's needed only before the #define DG_DYNARR_IMPLEMENTATION #include of
|
||||
// this header, so the following is here only for reference and commented out
|
||||
/*
|
||||
#define DG_DYNARR_MALLOC(elemSize, numElems) malloc(elemSize*numElems)
|
||||
|
||||
// oldNumElems is not used for C's realloc, but maybe you need it for
|
||||
// your allocator to copy the old elements over
|
||||
#define DG_DYNARR_REALLOC(ptr, elemSize, oldNumElems, newCapacity) \
|
||||
realloc(ptr, elemSize*newCapacity);
|
||||
|
||||
#define DG_DYNARR_FREE(ptr) free(ptr)
|
||||
*/
|
||||
|
||||
// if you want to prepend something to the non inline (DG_DYNARR_INLINE) functions,
|
||||
// like "__declspec(dllexport)" or whatever, #define DG_DYNARR_DEF
|
||||
#ifndef DG_DYNARR_DEF
|
||||
// by defaults it's empty.
|
||||
#define DG_DYNARR_DEF
|
||||
#endif
|
||||
|
||||
// some functions are inline, in case your compiler doesn't like "static inline"
|
||||
// but wants "__inline__" or something instead, #define DG_DYNARR_INLINE accordingly.
|
||||
#ifndef DG_DYNARR_INLINE
|
||||
// for pre-C99 compilers you might have to use something compiler-specific (or maybe only "static")
|
||||
#ifdef _MSC_VER
|
||||
#define DG_DYNARR_INLINE static __inline
|
||||
#else
|
||||
#define DG_DYNARR_INLINE static inline
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// ############### Short da_* aliases for the long names ###############
|
||||
|
||||
#ifndef DG_DYNARR_NO_SHORTNAMES
|
||||
|
||||
// this macro is used to create an array type (struct) for elements of TYPE
|
||||
// use like DA_TYPEDEF(int, MyIntArrType); MyIntArrType ia = {0}; da_push(ia, 42); ...
|
||||
#define DA_TYPEDEF(TYPE, NewArrayTypeName) \
|
||||
DG_DYNARR_TYPEDEF(TYPE, NewArrayTypeName)
|
||||
|
||||
// makes sure the array is initialized and can be used.
|
||||
// either do YourArray arr = {0}; or YourArray arr; da_init(arr);
|
||||
#define da_init(a) \
|
||||
dg_dynarr_init(a)
|
||||
|
||||
/*
|
||||
* This allows you to provide an external buffer that'll be used as long as it's big enough
|
||||
* once you add more elements than buf can hold, fresh memory will be allocated on the heap
|
||||
* Use like:
|
||||
* DA_TYPEDEF(double, MyDoubleArrType);
|
||||
* MyDoubleArrType arr;
|
||||
* double buf[8];
|
||||
* dg_dynarr_init_external(arr, buf, 8);
|
||||
* dg_dynarr_push(arr, 1.23);
|
||||
* ...
|
||||
*/
|
||||
#define da_init_external(a, buf, buf_cap) \
|
||||
dg_dynarr_init_external(a, buf, buf_cap)
|
||||
|
||||
// use this to free the memory allocated by dg_dynarr once you don't need the array anymore
|
||||
// Note: it is safe to add new elements to the array after da_free()
|
||||
// it will allocate new memory, just like it would directly after da_init()
|
||||
#define da_free(a) \
|
||||
dg_dynarr_free(a)
|
||||
|
||||
|
||||
// add an element to the array (appended at the end)
|
||||
#define da_push(a, v) \
|
||||
dg_dynarr_push(a, v)
|
||||
|
||||
// add an element to the array (appended at the end)
|
||||
// does the same as push, just for consistency with addn (like insert and insertn)
|
||||
#define da_add(a, v) \
|
||||
dg_dynarr_add(a, v)
|
||||
|
||||
// append n elements to a and initialize them from array vals, doesn't return anything
|
||||
// ! vals (and all other args) are evaluated multiple times !
|
||||
#define da_addn(a, vals, n) \
|
||||
dg_dynarr_addn(a, vals, n)
|
||||
|
||||
// add n elements to the end of the array and zeroes them with memset()
|
||||
// returns pointer to first added element, NULL if out of memory (array is empty then)
|
||||
#define da_addn_zeroed(a, n) \
|
||||
dg_dynarr_addn_zeroed(a, n)
|
||||
|
||||
// add n elements to the end of the array, will remain uninitialized
|
||||
// returns pointer to first added element, NULL if out of memory (array is empty then)
|
||||
#define da_addn_uninit(a, n) \
|
||||
dg_dynarr_addn_uninit(a, n)
|
||||
|
||||
|
||||
// insert a single value v at index idx
|
||||
#define da_insert(a, idx, v) \
|
||||
dg_dynarr_insert(a, idx, v)
|
||||
|
||||
// insert n elements into a at idx, initialize them from array vals
|
||||
// doesn't return anything
|
||||
// ! vals (and all other args) is evaluated multiple times !
|
||||
#define da_insertn(a, idx, vals, n) \
|
||||
dg_dynarr_insertn(a, idx, vals, n)
|
||||
|
||||
// insert n elements into a at idx and zeroe them with memset()
|
||||
// returns pointer to first inserted element or NULL if out of memory
|
||||
#define da_insertn_zeroed(a, idx, n) \
|
||||
dg_dynarr_insertn_zeroed(a, idx, n)
|
||||
|
||||
// insert n uninitialized elements into a at idx;
|
||||
// returns pointer to first inserted element or NULL if out of memory
|
||||
#define da_insertn_uninit(a, idx, n) \
|
||||
dg_dynarr_insertn_uninit(a, idx, n)
|
||||
|
||||
// set a single value v at index idx - like "a.p[idx] = v;" but with checks (unless disabled)
|
||||
#define da_set(a, idx, v) \
|
||||
dg_dynarr_set(a, idx, v)
|
||||
|
||||
// overwrite n elements of a, starting at idx, with values from array vals
|
||||
// doesn't return anything
|
||||
// ! vals (and all other args) is evaluated multiple times !
|
||||
#define da_setn(a, idx, vals, n) \
|
||||
dg_dynarr_setn(a, idx, vals, n)
|
||||
|
||||
// delete the element at idx, moving all following elements (=> keeps order)
|
||||
#define da_delete(a, idx) \
|
||||
dg_dynarr_delete(a, idx)
|
||||
|
||||
// delete n elements starting at idx, moving all following elements (=> keeps order)
|
||||
#define da_deleten(a, idx, n) \
|
||||
dg_dynarr_deleten(a, idx, n)
|
||||
|
||||
// delete the element at idx, move the last element there (=> doesn't keep order)
|
||||
#define da_deletefast(a, idx) \
|
||||
dg_dynarr_deletefast(a, idx)
|
||||
|
||||
// delete n elements starting at idx, move the last n elements there (=> doesn't keep order)
|
||||
#define da_deletenfast(a, idx, n) \
|
||||
dg_dynarr_deletenfast(a, idx, n)
|
||||
|
||||
// removes all elements from the array, but does not free the buffer
|
||||
// (if you want to free the buffer too, just use da_free())
|
||||
#define da_clear(a) \
|
||||
dg_dynarr_clear(a)
|
||||
|
||||
// sets the logical number of elements in the array
|
||||
// if cnt > dg_dynarr_count(a), the logical count will be increased accordingly
|
||||
// and the new elements will be uninitialized
|
||||
#define da_setcount(a, cnt) \
|
||||
dg_dynarr_setcount(a, cnt)
|
||||
|
||||
// make sure the array can store cap elements without reallocating
|
||||
// logical count remains unchanged
|
||||
#define da_reserve(a, cap) \
|
||||
dg_dynarr_reserve(a, cap)
|
||||
|
||||
// this makes sure a only uses as much memory as for its elements
|
||||
// => maybe useful if a used to contain a huge amount of elements,
|
||||
// but you deleted most of them and want to free some memory
|
||||
// Note however that this implies an allocation and copying the remaining
|
||||
// elements, so only do this if it frees enough memory to be worthwhile!
|
||||
#define da_shrink_to_fit(a) \
|
||||
dg_dynarr_shrink_to_fit(a)
|
||||
|
||||
|
||||
// removes and returns the last element of the array
|
||||
#define da_pop(a) \
|
||||
dg_dynarr_pop(a)
|
||||
|
||||
// returns the last element of the array
|
||||
#define da_last(a) \
|
||||
dg_dynarr_last(a)
|
||||
|
||||
// returns the pointer *to* the last element of the array
|
||||
// (in contrast to dg_dynarr_end() which returns a pointer *after* the last element)
|
||||
// returns NULL if array is empty
|
||||
#define da_lastptr(a) \
|
||||
dg_dynarr_lastptr(a)
|
||||
|
||||
// get element at index idx (like a.p[idx]), but with checks
|
||||
// (unless you disabled them with #define DG_DYNARR_INDEX_CHECK_LEVEL 0)
|
||||
#define da_get(a, idx) \
|
||||
dg_dynarr_get(a,idx)
|
||||
|
||||
// get pointer to element at index idx (like &a.p[idx]), but with checks
|
||||
// and it returns NULL if idx is invalid
|
||||
#define da_getptr(a, idx) \
|
||||
dg_dynarr_getptr(a, idx)
|
||||
|
||||
// returns a pointer to the first element of the array
|
||||
// (together with dg_dynarr_end() you can do C++-style iterating)
|
||||
#define da_begin(a) \
|
||||
dg_dynarr_begin(a)
|
||||
|
||||
// returns a pointer to the past-the-end element of the array
|
||||
// Allows C++-style iterating, in case you're into that kind of thing:
|
||||
// for(T *it=da_begin(a), *end=da_end(a); it!=end; ++it) foo(*it);
|
||||
// (see da_lastptr() to get a pointer *to* the last element)
|
||||
#define da_end(a) \
|
||||
dg_dynarr_end(a)
|
||||
|
||||
|
||||
// returns (logical) number of elements currently in the array
|
||||
#define da_count(a) \
|
||||
dg_dynarr_count(a)
|
||||
|
||||
// get the current reserved capacity of the array
|
||||
#define da_capacity(a) \
|
||||
dg_dynarr_capacity(a)
|
||||
|
||||
// returns 1 if the array is empty, else 0
|
||||
#define da_empty(a) \
|
||||
dg_dynarr_empty(a)
|
||||
|
||||
// returns 1 if the last (re)allocation when inserting failed (Out Of Memory)
|
||||
// or if the array has never allocated any memory yet, else 0
|
||||
// deleting the contents when growing fails instead of keeping old may seem
|
||||
// a bit uncool, but it's simple and OOM should rarely happen on modern systems
|
||||
// anyway - after all you need to deplete both RAM and swap/pagefile.sys
|
||||
#define da_oom(a) \
|
||||
dg_dynarr_oom(a)
|
||||
|
||||
|
||||
// sort a using the given qsort()-comparator cmp
|
||||
// (just a slim wrapper around qsort())
|
||||
#define da_sort(a, cmp) \
|
||||
dg_dynarr_sort(a, cmp)
|
||||
|
||||
#endif // DG_DYNARR_NO_SHORTNAMES
|
||||
|
||||
|
||||
// ######### Implementation of the actual macros (using the long names) ##########
|
||||
|
||||
// use like DG_DYNARR_TYPEDEF(int, MyIntArrType); MyIntArrType ia = {0}; dg_dynarr_push(ia, 42); ...
|
||||
#define DG_DYNARR_TYPEDEF(TYPE, NewArrayTypeName) \
|
||||
typedef struct { TYPE* p; dg__dynarr_md md; } NewArrayTypeName;
|
||||
|
||||
// makes sure the array is initialized and can be used.
|
||||
// either do YourArray arr = {0}; or YourArray arr; dg_dynarr_init(arr);
|
||||
#define dg_dynarr_init(a) \
|
||||
dg__dynarr_init((void**)&(a).p, &(a).md, NULL, 0)
|
||||
|
||||
// this allows you to provide an external buffer that'll be used as long as it's big enough
|
||||
// once you add more elements than buf can hold, fresh memory will be allocated on the heap
|
||||
#define dg_dynarr_init_external(a, buf, buf_cap) \
|
||||
dg__dynarr_init((void**)&(a).p, &(a).md, (buf), (buf_cap))
|
||||
|
||||
// use this to free the memory allocated by dg_dynarr
|
||||
// Note: it is safe to add new elements to the array after dg_dynarr_free()
|
||||
// it will allocate new memory, just like it would directly after dg_dynarr_init()
|
||||
#define dg_dynarr_free(a) \
|
||||
dg__dynarr_free((void**)&(a).p, &(a).md)
|
||||
|
||||
|
||||
// add an element to the array (appended at the end)
|
||||
#define dg_dynarr_push(a, v) \
|
||||
(dg__dynarr_maybegrowadd(dg__dynarr_unp(a), 1) ? (((a).p[(a).md.cnt++] = (v)),0) : 0)
|
||||
|
||||
// add an element to the array (appended at the end)
|
||||
// does the same as push, just for consistency with addn (like insert and insertn)
|
||||
#define dg_dynarr_add(a, v) \
|
||||
dg_dynarr_push((a), (v))
|
||||
|
||||
// append n elements to a and initialize them from array vals, doesn't return anything
|
||||
// ! vals (and all other args) are evaluated multiple times !
|
||||
#define dg_dynarr_addn(a, vals, n) do { \
|
||||
DG_DYNARR_ASSERT((vals)!=NULL, "Don't pass NULL als vals to dg_dynarr_addn!"); \
|
||||
if((vals)!=NULL && dg__dynarr_add(dg__dynarr_unp(a), n, 0)) { \
|
||||
size_t i_=(a).md.cnt-(n), v_=0; \
|
||||
while(i_<(a).md.cnt) (a).p[i_++]=(vals)[v_++]; \
|
||||
} } DG__DYNARR_WHILE0
|
||||
|
||||
// add n elements to the end of the array and zeroe them with memset()
|
||||
// returns pointer to first added element, NULL if out of memory (array is empty then)
|
||||
#define dg_dynarr_addn_zeroed(a, n) \
|
||||
(dg__dynarr_add(dg__dynarr_unp(a), (n), 1) ? &(a).p[(a).md.cnt-(size_t)(n)] : NULL)
|
||||
|
||||
// add n elements to the end of the array, which are uninitialized
|
||||
// returns pointer to first added element, NULL if out of memory (array is empty then)
|
||||
#define dg_dynarr_addn_uninit(a, n) \
|
||||
(dg__dynarr_add(dg__dynarr_unp(a), (n), 0) ? &(a).p[(a).md.cnt-(size_t)(n)] : NULL)
|
||||
|
||||
// insert a single value v at index idx
|
||||
#define dg_dynarr_insert(a, idx, v) \
|
||||
(dg__dynarr_checkidxle((a),(idx)), \
|
||||
dg__dynarr_insert(dg__dynarr_unp(a), (idx), 1, 0), \
|
||||
(a).p[dg__dynarr_idx((a).md, (idx))] = (v))
|
||||
|
||||
// insert n elements into a at idx, initialize them from array vals
|
||||
// doesn't return anything
|
||||
// ! vals (and all other args) is evaluated multiple times !
|
||||
#define dg_dynarr_insertn(a, idx, vals, n) do { \
|
||||
DG_DYNARR_ASSERT((vals)!=NULL, "Don't pass NULL as vals to dg_dynarr_insertn!"); \
|
||||
dg__dynarr_checkidxle((a),(idx)); \
|
||||
if((vals)!=NULL && dg__dynarr_insert(dg__dynarr_unp(a), (idx), (n), 0)){ \
|
||||
size_t i_=(idx), v_=0, e_=(idx)+(n); \
|
||||
while(i_ < e_) (a).p[i_++] = (vals)[v_++]; \
|
||||
}} DG__DYNARR_WHILE0
|
||||
|
||||
// insert n elements into a at idx and zeroe them with memset()
|
||||
// returns pointer to first inserted element or NULL if out of memory
|
||||
#define dg_dynarr_insertn_zeroed(a, idx, n) \
|
||||
(dg__dynarr_checkidxle((a),(idx)), \
|
||||
dg__dynarr_insert(dg__dynarr_unp(a), (idx), (n), 1) \
|
||||
? &(a).p[dg__dynarr_idx((a).md, (idx))] : NULL)
|
||||
|
||||
// insert n uninitialized elements into a at idx;
|
||||
// returns pointer to first inserted element or NULL if out of memory
|
||||
#define dg_dynarr_insertn_uninit(a, idx, n) \
|
||||
(dg__dynarr_checkidxle((a),(idx)), \
|
||||
dg__dynarr_insert(dg__dynarr_unp(a), idx, n, 0) \
|
||||
? &(a).p[dg__dynarr_idx((a).md, (idx))] : NULL)
|
||||
|
||||
// set a single value v at index idx - like "a.p[idx] = v;" but with checks (unless disabled)
|
||||
#define dg_dynarr_set(a, idx, v) \
|
||||
(dg__dynarr_checkidx((a),(idx)), \
|
||||
(a).p[dg__dynarr_idx((a).md, (idx))] = (v))
|
||||
|
||||
// overwrite n elements of a, starting at idx, with values from array vals
|
||||
// doesn't return anything
|
||||
// ! vals (and all other args) is evaluated multiple times !
|
||||
#define dg_dynarr_setn(a, idx, vals, n) do { \
|
||||
DG_DYNARR_ASSERT((vals)!=NULL, "Don't pass NULL as vals to dg_dynarr_setn!"); \
|
||||
size_t idx_=(idx); size_t end_=idx_+(size_t)n; \
|
||||
dg__dynarr_checkidx((a),idx_); dg__dynarr_checkidx((a),end_-1); \
|
||||
if((vals)!=NULL && idx_ < (a).md.cnt && end_ <= (a).md.cnt) { \
|
||||
size_t v_=0; \
|
||||
while(idx_ < end_) (a).p[idx_++] = (vals)[v_++]; \
|
||||
}} DG__DYNARR_WHILE0
|
||||
|
||||
|
||||
// delete the element at idx, moving all following elements (=> keeps order)
|
||||
#define dg_dynarr_delete(a, idx) \
|
||||
(dg__dynarr_checkidx((a),(idx)), dg__dynarr_delete(dg__dynarr_unp(a), (idx), 1))
|
||||
|
||||
// delete n elements starting at idx, moving all following elements (=> keeps order)
|
||||
#define dg_dynarr_deleten(a, idx, n) \
|
||||
(dg__dynarr_checkidx((a),(idx)), dg__dynarr_delete(dg__dynarr_unp(a), (idx), (n)))
|
||||
// TODO: check whether idx+n < count?
|
||||
|
||||
// delete the element at idx, move the last element there (=> doesn't keep order)
|
||||
#define dg_dynarr_deletefast(a, idx) \
|
||||
(dg__dynarr_checkidx((a),(idx)), dg__dynarr_deletefast(dg__dynarr_unp(a), (idx), 1))
|
||||
|
||||
// delete n elements starting at idx, move the last n elements there (=> doesn't keep order)
|
||||
#define dg_dynarr_deletenfast(a, idx, n) \
|
||||
(dg__dynarr_checkidx((a),(idx)), dg__dynarr_deletefast(dg__dynarr_unp(a), idx, n))
|
||||
// TODO: check whether idx+n < count?
|
||||
|
||||
// removes all elements from the array, but does not free the buffer
|
||||
// (if you want to free the buffer too, just use dg_dynarr_free())
|
||||
#define dg_dynarr_clear(a) \
|
||||
((a).md.cnt=0)
|
||||
|
||||
// sets the logical number of elements in the array
|
||||
// if cnt > dg_dynarr_count(a), the logical count will be increased accordingly
|
||||
// and the new elements will be uninitialized
|
||||
#define dg_dynarr_setcount(a, n) \
|
||||
(dg__dynarr_maybegrow(dg__dynarr_unp(a), (n)) ? ((a).md.cnt = (n)) : 0)
|
||||
|
||||
// make sure the array can store cap elements without reallocating
|
||||
// logical count remains unchanged
|
||||
#define dg_dynarr_reserve(a, cap) \
|
||||
dg__dynarr_maybegrow(dg__dynarr_unp(a), (cap))
|
||||
|
||||
// this makes sure a only uses as much memory as for its elements
|
||||
// => maybe useful if a used to contain a huge amount of elements,
|
||||
// but you deleted most of them and want to free some memory
|
||||
// Note however that this implies an allocation and copying the remaining
|
||||
// elements, so only do this if it frees enough memory to be worthwhile!
|
||||
#define dg_dynarr_shrink_to_fit(a) \
|
||||
dg__dynarr_shrink_to_fit(dg__dynarr_unp(a))
|
||||
|
||||
|
||||
#if (DG_DYNARR_INDEX_CHECK_LEVEL == 1) || (DG_DYNARR_INDEX_CHECK_LEVEL == 3)
|
||||
|
||||
// removes and returns the last element of the array
|
||||
#define dg_dynarr_pop(a) \
|
||||
(dg__dynarr_check_notempty((a), "Don't pop an empty array!"), \
|
||||
(a).p[((a).md.cnt > 0) ? (--(a).md.cnt) : 0])
|
||||
|
||||
// returns the last element of the array
|
||||
#define dg_dynarr_last(a) \
|
||||
(dg__dynarr_check_notempty((a), "Don't call da_last() on an empty array!"), \
|
||||
(a).p[((a).md.cnt > 0) ? ((a).md.cnt-1) : 0])
|
||||
|
||||
#elif (DG_DYNARR_INDEX_CHECK_LEVEL == 0) || (DG_DYNARR_INDEX_CHECK_LEVEL == 2)
|
||||
|
||||
// removes and returns the last element of the array
|
||||
#define dg_dynarr_pop(a) \
|
||||
(dg__dynarr_check_notempty((a), "Don't pop an empty array!"), \
|
||||
(a).p[--(a).md.cnt])
|
||||
|
||||
// returns the last element of the array
|
||||
#define dg_dynarr_last(a) \
|
||||
(dg__dynarr_check_notempty((a), "Don't call da_last() on an empty array!"), \
|
||||
(a).p[(a).md.cnt-1])
|
||||
|
||||
#else // invalid DG_DYNARR_INDEX_CHECK_LEVEL
|
||||
#error Invalid index check level DG_DYNARR_INDEX_CHECK_LEVEL (must be 0-3) !
|
||||
#endif // DG_DYNARR_INDEX_CHECK_LEVEL
|
||||
|
||||
// returns the pointer *to* the last element of the array
|
||||
// (in contrast to dg_dynarr_end() which returns a pointer *after* the last element)
|
||||
// returns NULL if array is empty
|
||||
#define dg_dynarr_lastptr(a) \
|
||||
(((a).md.cnt > 0) ? ((a).p + (a).md.cnt - 1) : NULL)
|
||||
|
||||
// get element at index idx (like a.p[idx]), but with checks
|
||||
// (unless you disabled them with #define DG_DYNARR_INDEX_CHECK_LEVEL 0)
|
||||
#define dg_dynarr_get(a, idx) \
|
||||
(dg__dynarr_checkidx((a),(idx)), (a).p[dg__dynarr_idx((a).md, (idx))])
|
||||
|
||||
// get pointer to element at index idx (like &a.p[idx]), but with checks
|
||||
// (unless you disabled them with #define DG_DYNARR_INDEX_CHECK_LEVEL 0)
|
||||
// if index-checks are disabled, it returns NULL on invalid index (else it asserts() before returning)
|
||||
#define dg_dynarr_getptr(a, idx) \
|
||||
(dg__dynarr_checkidx((a),(idx)), \
|
||||
((size_t)(idx) < (a).md.cnt) ? ((a).p+(size_t)(idx)) : NULL)
|
||||
|
||||
// returns a pointer to the first element of the array
|
||||
// (together with dg_dynarr_end() you can do C++-style iterating)
|
||||
#define dg_dynarr_begin(a) \
|
||||
((a).p)
|
||||
|
||||
// returns a pointer to the past-the-end element of the array
|
||||
// Allows C++-style iterating, in case you're into that kind of thing:
|
||||
// for(T *it=dg_dynarr_begin(a), *end=dg_dynarr_end(a); it!=end; ++it) foo(*it);
|
||||
// (see dg_dynarr_lastptr() to get a pointer *to* the last element)
|
||||
#define dg_dynarr_end(a) \
|
||||
((a).p + (a).md.cnt)
|
||||
|
||||
|
||||
// returns (logical) number of elements currently in the array
|
||||
#define dg_dynarr_count(a) \
|
||||
((a).md.cnt)
|
||||
|
||||
// get the current reserved capacity of the array
|
||||
#define dg_dynarr_capacity(a) \
|
||||
((a).md.cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB)
|
||||
|
||||
// returns 1 if the array is empty, else 0
|
||||
#define dg_dynarr_empty(a) \
|
||||
((a).md.cnt == 0)
|
||||
|
||||
// returns 1 if the last (re)allocation when inserting failed (Out Of Memory)
|
||||
// or if the array has never allocated any memory yet, else 0
|
||||
// deleting the contents when growing fails instead of keeping old may seem
|
||||
// a bit uncool, but it's simple and OOM should rarely happen on modern systems
|
||||
// anyway - after all you need to deplete both RAM and swap/pagefile.sys
|
||||
// or deplete the address space, which /might/ happen with 32bit applications
|
||||
// but probably not with 64bit (at least in the foreseeable future)
|
||||
#define dg_dynarr_oom(a) \
|
||||
((a).md.cap == 0)
|
||||
|
||||
|
||||
// sort a using the given qsort()-comparator cmp
|
||||
// (just a slim wrapper around qsort())
|
||||
#define dg_dynarr_sort(a, cmp) \
|
||||
qsort((a).p, (a).md.cnt, sizeof((a).p[0]), (cmp))
|
||||
|
||||
|
||||
// ######### Implementation-Details that are not part of the API ##########
|
||||
|
||||
#include <stdlib.h> // size_t, malloc(), free(), realloc()
|
||||
#include <string.h> // memset(), memcpy(), memmove()
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
size_t cnt; // logical number of elements
|
||||
size_t cap; // cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB is actual capacity (in elements, *not* bytes!)
|
||||
// if(cap & DG__DYNARR_SIZE_T_MSB) the current memory is not allocated by dg_dynarr,
|
||||
// but was set with dg_dynarr_init_external()
|
||||
// that's handy to give an array a base-element storage on the stack, for example
|
||||
// TODO: alternatively, we could introduce a flag field to this struct and use that,
|
||||
// so we don't have to calculate & everytime cap is needed
|
||||
} dg__dynarr_md;
|
||||
|
||||
// I used to have the following in an enum, but MSVC assumes enums are always 32bit ints
|
||||
static const size_t DG__DYNARR_SIZE_T_MSB = ((size_t)1) << (sizeof(size_t)*8 - 1);
|
||||
static const size_t DG__DYNARR_SIZE_T_ALL_BUT_MSB = (((size_t)1) << (sizeof(size_t)*8 - 1))-1;
|
||||
|
||||
// "unpack" the elements of an array struct for use with helper functions
|
||||
// (to void** arr, dg__dynarr_md* md, size_t itemsize)
|
||||
#define dg__dynarr_unp(a) \
|
||||
(void**)&(a).p, &(a).md, sizeof((a).p[0])
|
||||
|
||||
// MSVC warns about "conditional expression is constant" when using the
|
||||
// do { ... } while(0) idiom in macros..
|
||||
#ifdef _MSC_VER
|
||||
#if _MSC_VER >= 1400 // MSVC 2005 and newer
|
||||
// people claim MSVC 2005 and newer support __pragma, even though it's only documented
|
||||
// for 2008+ (https://msdn.microsoft.com/en-us/library/d9x1s805%28v=vs.90%29.aspx)
|
||||
// the following workaround is based on
|
||||
// http://cnicholson.net/2009/03/stupid-c-tricks-dowhile0-and-c4127/
|
||||
#define DG__DYNARR_WHILE0 \
|
||||
__pragma(warning(push)) \
|
||||
__pragma(warning(disable:4127)) \
|
||||
while(0) \
|
||||
__pragma(warning(pop))
|
||||
#else // older MSVC versions don't support __pragma - I heard this helps for them
|
||||
#define DG__DYNARR_WHILE0 while(0,0)
|
||||
#endif
|
||||
|
||||
#else // other compilers
|
||||
|
||||
#define DG__DYNARR_WHILE0 while(0)
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
|
||||
#if (DG_DYNARR_INDEX_CHECK_LEVEL == 2) || (DG_DYNARR_INDEX_CHECK_LEVEL == 3)
|
||||
|
||||
#define dg__dynarr_checkidx(a,i) \
|
||||
DG_DYNARR_ASSERT((size_t)i < a.md.cnt, "index out of bounds!")
|
||||
|
||||
// special case for insert operations: == cnt is also ok, insert will append then
|
||||
#define dg__dynarr_checkidxle(a,i) \
|
||||
DG_DYNARR_ASSERT((size_t)i <= a.md.cnt, "index out of bounds!")
|
||||
|
||||
#define dg__dynarr_check_notempty(a, msg) \
|
||||
DG_DYNARR_ASSERT(a.md.cnt > 0, msg)
|
||||
|
||||
#elif (DG_DYNARR_INDEX_CHECK_LEVEL == 0) || (DG_DYNARR_INDEX_CHECK_LEVEL == 1)
|
||||
|
||||
// no assertions that check if index is valid
|
||||
#define dg__dynarr_checkidx(a,i) (void)0
|
||||
#define dg__dynarr_checkidxle(a,i) (void)0
|
||||
|
||||
#define dg__dynarr_check_notempty(a, msg) (void)0
|
||||
|
||||
#else // invalid DG_DYNARR_INDEX_CHECK_LEVEL
|
||||
#error Invalid index check level DG_DYNARR_INDEX_CHECK_LEVEL (must be 0-3) !
|
||||
#endif // DG_DYNARR_INDEX_CHECK_LEVEL
|
||||
|
||||
|
||||
#if (DG_DYNARR_INDEX_CHECK_LEVEL == 1) || (DG_DYNARR_INDEX_CHECK_LEVEL == 3)
|
||||
|
||||
// the given index, if valid, else 0
|
||||
#define dg__dynarr_idx(md,i) \
|
||||
(((size_t)(i) < md.cnt) ? (size_t)(i) : 0)
|
||||
|
||||
#elif (DG_DYNARR_INDEX_CHECK_LEVEL == 0) || (DG_DYNARR_INDEX_CHECK_LEVEL == 2)
|
||||
|
||||
// don't check and default to 0 if invalid, but just use the given value
|
||||
#define dg__dynarr_idx(md,i) (size_t)(i)
|
||||
|
||||
#else // invalid DG_DYNARR_INDEX_CHECK_LEVEL
|
||||
#error Invalid index check level DG_DYNARR_INDEX_CHECK_LEVEL (must be 0-3) !
|
||||
#endif // DG_DYNARR_INDEX_CHECK_LEVEL
|
||||
|
||||
// the functions allocating/freeing memory are not implemented inline, but
|
||||
// in the #ifdef DG_DYNARR_IMPLEMENTATION section
|
||||
// one reason is that dg__dynarr_grow has the most code in it, the other is
|
||||
// that windows has weird per-dll heaps so free() or realloc() should be
|
||||
// called from code in the same dll that allocated the memory - these kind
|
||||
// of wrapper functions that end up compiled into the exe or *one* dll
|
||||
// (instead of inline functions compiled into everything) should ensure that.
|
||||
|
||||
DG_DYNARR_DEF void
|
||||
dg__dynarr_free(void** p, dg__dynarr_md* md);
|
||||
|
||||
DG_DYNARR_DEF void
|
||||
dg__dynarr_shrink_to_fit(void** arr, dg__dynarr_md* md, size_t itemsize);
|
||||
|
||||
// grow array to have enough space for at least min_needed elements
|
||||
// if it fails (OOM), the array will be deleted, a.p will be NULL, a.md.cap and a.md.cnt will be 0
|
||||
// and the functions returns 0; else (on success) it returns 1
|
||||
DG_DYNARR_DEF int
|
||||
dg__dynarr_grow(void** arr, dg__dynarr_md* md, size_t itemsize, size_t min_needed);
|
||||
|
||||
|
||||
// the following functions are implemented inline, because they're quite short
|
||||
// and mosty implemented in functions so the macros don't get too ugly
|
||||
|
||||
DG_DYNARR_INLINE void
|
||||
dg__dynarr_init(void** p, dg__dynarr_md* md, void* buf, size_t buf_cap)
|
||||
{
|
||||
*p = buf;
|
||||
md->cnt = 0;
|
||||
if(buf == NULL) md->cap = 0;
|
||||
else md->cap = (DG__DYNARR_SIZE_T_MSB | buf_cap);
|
||||
}
|
||||
|
||||
DG_DYNARR_INLINE int
|
||||
dg__dynarr_maybegrow(void** arr, dg__dynarr_md* md, size_t itemsize, size_t min_needed)
|
||||
{
|
||||
if((md->cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB) >= min_needed) return 1;
|
||||
else return dg__dynarr_grow(arr, md, itemsize, min_needed);
|
||||
}
|
||||
|
||||
DG_DYNARR_INLINE int
|
||||
dg__dynarr_maybegrowadd(void** arr, dg__dynarr_md* md, size_t itemsize, size_t num_add)
|
||||
{
|
||||
size_t min_needed = md->cnt+num_add;
|
||||
if((md->cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB) >= min_needed) return 1;
|
||||
else return dg__dynarr_grow(arr, md, itemsize, min_needed);
|
||||
}
|
||||
|
||||
DG_DYNARR_INLINE int
|
||||
dg__dynarr_insert(void** arr, dg__dynarr_md* md, size_t itemsize, size_t idx, size_t n, int init0)
|
||||
{
|
||||
// allow idx == md->cnt to append
|
||||
size_t oldCount = md->cnt;
|
||||
size_t newCount = oldCount+n;
|
||||
if(idx <= oldCount && dg__dynarr_maybegrow(arr, md, itemsize, newCount))
|
||||
{
|
||||
unsigned char* p = (unsigned char*)*arr; // *arr might have changed in dg__dynarr_grow()!
|
||||
// move all existing items after a[idx] to a[idx+n]
|
||||
if(idx < oldCount) memmove(p+(idx+n)*itemsize, p+idx*itemsize, itemsize*(oldCount - idx));
|
||||
|
||||
// if the memory is supposed to be zeroed, do that
|
||||
if(init0) memset(p+idx*itemsize, 0, n*itemsize);
|
||||
|
||||
md->cnt = newCount;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DG_DYNARR_INLINE int
|
||||
dg__dynarr_add(void** arr, dg__dynarr_md* md, size_t itemsize, size_t n, int init0)
|
||||
{
|
||||
size_t cnt = md->cnt;
|
||||
if(dg__dynarr_maybegrow(arr, md, itemsize, cnt+n))
|
||||
{
|
||||
unsigned char* p = (unsigned char*)*arr; // *arr might have changed in dg__dynarr_grow()!
|
||||
// if the memory is supposed to be zeroed, do that
|
||||
if(init0) memset(p+cnt*itemsize, 0, n*itemsize);
|
||||
|
||||
md->cnt += n;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DG_DYNARR_INLINE void
|
||||
dg__dynarr_delete(void** arr, dg__dynarr_md* md, size_t itemsize, size_t idx, size_t n)
|
||||
{
|
||||
size_t cnt = md->cnt;
|
||||
if(idx < cnt)
|
||||
{
|
||||
if(idx+n >= cnt) md->cnt = idx; // removing last element(s) => just reduce count
|
||||
else
|
||||
{
|
||||
unsigned char* p = (unsigned char*)*arr;
|
||||
// move all items following a[idx+n] to a[idx]
|
||||
memmove(p+itemsize*idx, p+itemsize*(idx+n), itemsize*(cnt - (idx+n)));
|
||||
md->cnt -= n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DG_DYNARR_INLINE void
|
||||
dg__dynarr_deletefast(void** arr, dg__dynarr_md* md, size_t itemsize, size_t idx, size_t n)
|
||||
{
|
||||
size_t cnt = md->cnt;
|
||||
if(idx < cnt)
|
||||
{
|
||||
if(idx+n >= cnt) md->cnt = idx; // removing last element(s) => just reduce count
|
||||
else
|
||||
{
|
||||
unsigned char* p = (unsigned char*)*arr;
|
||||
// copy the last n items to a[idx] - but handle the case that
|
||||
// the array has less than n elements left after the deleted elements
|
||||
size_t numItemsAfterDeleted = cnt - (idx+n);
|
||||
size_t m = (n < numItemsAfterDeleted) ? n : numItemsAfterDeleted;
|
||||
memcpy(p+itemsize*idx, p+itemsize*(cnt - m), itemsize*m);
|
||||
md->cnt -= n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // DG__DYNARR_H
|
||||
|
||||
|
||||
// ############## Implementation of non-inline functions ##############
|
||||
|
||||
#ifdef DG_DYNARR_IMPLEMENTATION
|
||||
|
||||
// by default, C's malloc(), realloc() and free() is used to allocate/free heap memory.
|
||||
// you can #define DG_DYNARR_MALLOC, DG_DYNARR_REALLOC and DG_DYNARR_FREE
|
||||
// to provide alternative implementations like Win32 Heap(Re)Alloc/HeapFree
|
||||
//
|
||||
#ifndef DG_DYNARR_MALLOC
|
||||
#define DG_DYNARR_MALLOC(elemSize, numElems) malloc(elemSize*numElems)
|
||||
|
||||
// oldNumElems is not used here, but maybe you need it for your allocator
|
||||
// to copy the old elements over
|
||||
#define DG_DYNARR_REALLOC(ptr, elemSize, oldNumElems, newCapacity) \
|
||||
realloc(ptr, elemSize*newCapacity);
|
||||
|
||||
#define DG_DYNARR_FREE(ptr) free(ptr)
|
||||
#endif
|
||||
|
||||
// you can #define DG_DYNARR_OUT_OF_MEMORY to some code that will be executed
|
||||
// if allocating memory fails
|
||||
#ifndef DG_DYNARR_OUT_OF_MEMORY
|
||||
#define DG_DYNARR_OUT_OF_MEMORY DG_DYNARR_ASSERT(0, "Out of Memory!");
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DG_DYNARR_DEF void
|
||||
dg__dynarr_free(void** p, dg__dynarr_md* md)
|
||||
{
|
||||
// only free memory if it doesn't point to external memory
|
||||
if(!(md->cap & DG__DYNARR_SIZE_T_MSB))
|
||||
{
|
||||
DG_DYNARR_FREE(*p);
|
||||
*p = NULL;
|
||||
md->cap = 0;
|
||||
}
|
||||
md->cnt = 0;
|
||||
}
|
||||
|
||||
|
||||
DG_DYNARR_DEF int
|
||||
dg__dynarr_grow(void** arr, dg__dynarr_md* md, size_t itemsize, size_t min_needed)
|
||||
{
|
||||
size_t cap = md->cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB;
|
||||
|
||||
DG_DYNARR_ASSERT(min_needed > cap, "dg__dynarr_grow() should only be called if storage actually needs to grow!");
|
||||
|
||||
if(min_needed < DG__DYNARR_SIZE_T_MSB)
|
||||
{
|
||||
size_t newcap = (cap > 4) ? (2*cap) : 8; // allocate for at least 8 elements
|
||||
// make sure not to set DG__DYNARR_SIZE_T_MSB (unlikely anyway)
|
||||
if(newcap >= DG__DYNARR_SIZE_T_MSB) newcap = DG__DYNARR_SIZE_T_MSB-1;
|
||||
if(min_needed > newcap) newcap = min_needed;
|
||||
|
||||
// the memory was allocated externally, don't free it, just copy contents
|
||||
if(md->cap & DG__DYNARR_SIZE_T_MSB)
|
||||
{
|
||||
void* p = DG_DYNARR_MALLOC(itemsize, newcap);
|
||||
if(p != NULL) memcpy(p, *arr, itemsize*md->cnt);
|
||||
*arr = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
void* p = DG_DYNARR_REALLOC(*arr, itemsize, md->cnt, newcap);
|
||||
if(p == NULL) DG_DYNARR_FREE(*arr); // realloc failed, at least don't leak memory
|
||||
*arr = p;
|
||||
}
|
||||
|
||||
// TODO: handle OOM by setting highest bit of count and keeping old data?
|
||||
|
||||
if(*arr) md->cap = newcap;
|
||||
else
|
||||
{
|
||||
md->cap = 0;
|
||||
md->cnt = 0;
|
||||
|
||||
DG_DYNARR_OUT_OF_MEMORY ;
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
DG_DYNARR_ASSERT(min_needed < DG__DYNARR_SIZE_T_MSB, "Arrays must stay below SIZE_T_MAX/2 elements!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DG_DYNARR_DEF void
|
||||
dg__dynarr_shrink_to_fit(void** arr, dg__dynarr_md* md, size_t itemsize)
|
||||
{
|
||||
// only do this if we allocated the memory ourselves
|
||||
if(!(md->cap & DG__DYNARR_SIZE_T_MSB))
|
||||
{
|
||||
size_t cnt = md->cnt;
|
||||
if(cnt == 0) dg__dynarr_free(arr, md);
|
||||
else if((md->cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB) > cnt)
|
||||
{
|
||||
void* p = DG_DYNARR_MALLOC(itemsize, cnt);
|
||||
if(p != NULL)
|
||||
{
|
||||
memcpy(p, *arr, cnt*itemsize);
|
||||
md->cap = cnt;
|
||||
DG_DYNARR_FREE(*arr);
|
||||
*arr = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // DG_DYNARR_IMPLEMENTATION
|
2453
src/client/refresh/gl4/header/HandmadeMath.h
Normal file
2453
src/client/refresh/gl4/header/HandmadeMath.h
Normal file
File diff suppressed because it is too large
Load diff
562
src/client/refresh/gl4/header/local.h
Normal file
562
src/client/refresh/gl4/header/local.h
Normal file
|
@ -0,0 +1,562 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (C) 2016-2017 Daniel Gibson
|
||||
*
|
||||
* This program 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 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Local header for the OpenGL4 refresher.
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SRC_CLIENT_REFRESH_GL4_HEADER_LOCAL_H_
|
||||
#define SRC_CLIENT_REFRESH_GL4_HEADER_LOCAL_H_
|
||||
|
||||
#ifdef IN_IDE_PARSER
|
||||
// this is just a hack to get proper auto-completion in IDEs:
|
||||
// using system headers for their parsers/indexers but glad for real build
|
||||
// (in glad glFoo is just a #define to glad_glFoo or sth, which screws up autocompletion)
|
||||
// (you may have to configure your IDE to #define IN_IDE_PARSER, but not for building!)
|
||||
#ifdef YQ2_GL3_GLES3
|
||||
#include <GLES3/gl32.h>
|
||||
#else // desktop GL4
|
||||
#define GL_GLEXT_PROTOTYPES 1
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifdef YQ2_GL3_GLES3
|
||||
#include "../glad-gles3/include/glad/glad.h"
|
||||
// yes, this is a bit hacky, but it works :-P
|
||||
#define glDepthRange glDepthRangef
|
||||
#else // desktop GL4
|
||||
#include "../glad/include/glad/glad.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#include "../../ref_shared.h"
|
||||
|
||||
#include "HandmadeMath.h"
|
||||
|
||||
#if 0 // only use this for development ..
|
||||
#define STUB_ONCE(msg) do { \
|
||||
static int show=1; \
|
||||
if(show) { \
|
||||
show = 0; \
|
||||
R_Printf(PRINT_ALL, "STUB: %s() %s\n", __FUNCTION__, msg); \
|
||||
} \
|
||||
} while(0);
|
||||
#else // .. so make this a no-op in released code
|
||||
#define STUB_ONCE(msg)
|
||||
#endif
|
||||
|
||||
// a wrapper around glVertexAttribPointer() to stay sane
|
||||
// (caller doesn't have to cast to GLintptr and then void*)
|
||||
static inline void
|
||||
qglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset)
|
||||
{
|
||||
glVertexAttribPointer(index, size, type, normalized, stride, (const void*)offset);
|
||||
}
|
||||
|
||||
static inline void
|
||||
qglVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset)
|
||||
{
|
||||
glVertexAttribIPointer(index, size, type, stride, (void*)offset);
|
||||
}
|
||||
|
||||
// attribute locations for vertex shaders
|
||||
enum {
|
||||
GL4_ATTRIB_POSITION = 0,
|
||||
GL4_ATTRIB_TEXCOORD = 1, // for normal texture
|
||||
GL4_ATTRIB_LMTEXCOORD = 2, // for lightmap
|
||||
GL4_ATTRIB_COLOR = 3, // per-vertex color
|
||||
GL4_ATTRIB_NORMAL = 4, // vertex normal
|
||||
GL4_ATTRIB_LIGHTFLAGS = 5 // uint, each set bit means "dyn light i affects this surface"
|
||||
};
|
||||
|
||||
// always using RGBA now, GLES3 on RPi4 doesn't work otherwise
|
||||
// and I think all modern GPUs prefer 4byte pixels over 3bytes
|
||||
static const int gl4_solid_format = GL_RGBA;
|
||||
static const int gl4_alpha_format = GL_RGBA;
|
||||
static const int gl4_tex_solid_format = GL_RGBA;
|
||||
static const int gl4_tex_alpha_format = GL_RGBA;
|
||||
|
||||
extern unsigned gl4_rawpalette[256];
|
||||
extern unsigned d_8to24table[256];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *renderer_string;
|
||||
const char *vendor_string;
|
||||
const char *version_string;
|
||||
const char *glsl_version_string;
|
||||
|
||||
int major_version;
|
||||
int minor_version;
|
||||
|
||||
// ----
|
||||
|
||||
qboolean anisotropic; // is GL_EXT_texture_filter_anisotropic supported?
|
||||
qboolean debug_output; // is GL_ARB_debug_output supported?
|
||||
qboolean stencil; // Do we have a stencil buffer?
|
||||
|
||||
qboolean useBigVBO; // workaround for AMDs windows driver for fewer calls to glBufferData()
|
||||
|
||||
// ----
|
||||
|
||||
float max_anisotropy;
|
||||
} gl4config_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GLuint shaderProgram;
|
||||
GLint uniVblend;
|
||||
GLint uniLmScalesOrTime; // for 3D it's lmScales, for 2D underwater PP it's time
|
||||
hmm_vec4 lmScales[4];
|
||||
} gl4ShaderInfo_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GLfloat gamma;
|
||||
GLfloat intensity;
|
||||
GLfloat intensity2D; // for HUD, menus etc
|
||||
|
||||
// entries of std430 UBOs are aligned to multiples of their own size
|
||||
// so we'll need to pad accordingly for following vec4
|
||||
GLfloat _padding;
|
||||
|
||||
hmm_vec4 color;
|
||||
} gl4UniCommon_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
hmm_mat4 transMat4;
|
||||
} gl4Uni2D_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
hmm_mat4 transProjViewMat4; // gl4state.projMat3D * gl4state.viewMat3D - so we don't have to do this in the shader
|
||||
hmm_mat4 transModelMat4;
|
||||
|
||||
GLfloat scroll; // for SURF_FLOWING
|
||||
GLfloat time; // for warping surfaces like water & possibly other things
|
||||
GLfloat alpha; // for translucent surfaces (water, glass, ..)
|
||||
GLfloat overbrightbits; // gl4_overbrightbits, applied to lightmaps (and elsewhere to models)
|
||||
GLfloat particleFadeFactor; // gl4_particle_fade_factor, higher => less fading out towards edges
|
||||
|
||||
GLfloat lightScaleForTurb; // surfaces with SURF_DRAWTURB (water, lava) don't have lightmaps, use this instead
|
||||
GLfloat _padding[2]; // again, some padding to ensure this has right size
|
||||
} gl4Uni3D_t;
|
||||
|
||||
extern const hmm_mat4 gl4_identityMat4;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec3_t origin;
|
||||
GLfloat _padding;
|
||||
vec3_t color;
|
||||
GLfloat intensity;
|
||||
} gl4UniDynLight;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gl4UniDynLight dynLights[MAX_DLIGHTS];
|
||||
GLuint numDynLights;
|
||||
GLfloat _padding[3];
|
||||
} gl4UniLights_t;
|
||||
|
||||
enum {
|
||||
// width and height used to be 128, so now we should be able to get the same lightmap data
|
||||
// that used 32 lightmaps before into one, so 4 lightmaps should be enough
|
||||
BLOCK_WIDTH = 1024,
|
||||
BLOCK_HEIGHT = 512,
|
||||
LIGHTMAP_BYTES = 4,
|
||||
MAX_LIGHTMAPS = 4,
|
||||
MAX_LIGHTMAPS_PER_SURFACE = MAXLIGHTMAPS // 4
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// TODO: what of this do we need?
|
||||
qboolean fullscreen;
|
||||
|
||||
int prev_mode;
|
||||
|
||||
// each lightmap consists of 4 sub-lightmaps allowing changing shadows on the same surface
|
||||
// used for switching on/off light and stuff like that.
|
||||
// most surfaces only have one really and the remaining for are filled with dummy data
|
||||
GLuint lightmap_textureIDs[MAX_LIGHTMAPS][MAX_LIGHTMAPS_PER_SURFACE]; // instead of lightmap_textures+i use lightmap_textureIDs[i]
|
||||
|
||||
GLuint currenttexture; // bound to GL_TEXTURE0
|
||||
int currentlightmap; // lightmap_textureIDs[currentlightmap] bound to GL_TEXTURE1
|
||||
GLuint currenttmu; // GL_TEXTURE0 or GL_TEXTURE1
|
||||
|
||||
// FBO for postprocess effects (like under-water-warping)
|
||||
GLuint ppFBO;
|
||||
GLuint ppFBtex; // ppFBO's texture for color buffer
|
||||
int ppFBtexWidth, ppFBtexHeight;
|
||||
GLuint ppFBrbo; // ppFBO's renderbuffer object for depth and stencil buffer
|
||||
qboolean ppFBObound; // is it currently bound (rendered to)?
|
||||
|
||||
//float camera_separation;
|
||||
//enum stereo_modes stereo_mode;
|
||||
|
||||
GLuint currentVAO;
|
||||
GLuint currentVBO;
|
||||
GLuint currentEBO;
|
||||
GLuint currentShaderProgram;
|
||||
GLuint currentUBO;
|
||||
|
||||
// NOTE: make sure si2D is always the first shaderInfo (or adapt GL4_ShutdownShaders())
|
||||
gl4ShaderInfo_t si2D; // shader for rendering 2D with textures
|
||||
gl4ShaderInfo_t si2Dcolor; // shader for rendering 2D with flat colors
|
||||
gl4ShaderInfo_t si2DpostProcess; // shader to render postprocess FBO, when *not* underwater
|
||||
gl4ShaderInfo_t si2DpostProcessWater; // shader to apply water-warp postprocess effect
|
||||
|
||||
gl4ShaderInfo_t si3Dlm; // a regular opaque face (e.g. from brush) with lightmap
|
||||
// TODO: lm-only variants for gl_lightmap 1
|
||||
gl4ShaderInfo_t si3Dtrans; // transparent is always w/o lightmap
|
||||
gl4ShaderInfo_t si3DcolorOnly; // used for beams - no lightmaps
|
||||
gl4ShaderInfo_t si3Dturb; // for water etc - always without lightmap
|
||||
gl4ShaderInfo_t si3DlmFlow; // for flowing/scrolling things with lightmap (conveyor, ..?)
|
||||
gl4ShaderInfo_t si3DtransFlow; // for transparent flowing/scrolling things (=> no lightmap)
|
||||
gl4ShaderInfo_t si3Dsky; // guess what..
|
||||
gl4ShaderInfo_t si3Dsprite; // for sprites
|
||||
gl4ShaderInfo_t si3DspriteAlpha; // for sprites with alpha-testing
|
||||
|
||||
gl4ShaderInfo_t si3Dalias; // for models
|
||||
gl4ShaderInfo_t si3DaliasColor; // for models w/ flat colors
|
||||
|
||||
// NOTE: make sure siParticle is always the last shaderInfo (or adapt GL4_ShutdownShaders())
|
||||
gl4ShaderInfo_t siParticle; // for particles. surprising, right?
|
||||
|
||||
GLuint vao3D, vbo3D; // for brushes etc, using 10 floats and one uint as vertex input (x,y,z, s,t, lms,lmt, normX,normY,normZ ; lightFlags)
|
||||
|
||||
// the next two are for gl4config.useBigVBO == true
|
||||
int vbo3Dsize;
|
||||
int vbo3DcurOffset;
|
||||
|
||||
GLuint vaoAlias, vboAlias, eboAlias; // for models, using 9 floats as (x,y,z, s,t, r,g,b,a)
|
||||
GLuint vaoParticle, vboParticle; // for particles, using 9 floats (x,y,z, size,distance, r,g,b,a)
|
||||
|
||||
// UBOs and their data
|
||||
gl4UniCommon_t uniCommonData;
|
||||
gl4Uni2D_t uni2DData;
|
||||
gl4Uni3D_t uni3DData;
|
||||
gl4UniLights_t uniLightsData;
|
||||
GLuint uniCommonUBO;
|
||||
GLuint uni2DUBO;
|
||||
GLuint uni3DUBO;
|
||||
GLuint uniLightsUBO;
|
||||
|
||||
hmm_mat4 projMat3D;
|
||||
hmm_mat4 viewMat3D;
|
||||
} gl4state_t;
|
||||
|
||||
extern gl4config_t gl4config;
|
||||
extern gl4state_t gl4state;
|
||||
|
||||
extern viddef_t vid;
|
||||
|
||||
extern refdef_t gl4_newrefdef;
|
||||
|
||||
extern int gl4_visframecount; /* bumped when going to a new PVS */
|
||||
extern int gl4_framecount; /* used for dlight push checking */
|
||||
|
||||
extern int gl4_viewcluster, gl4_viewcluster2, gl4_oldviewcluster, gl4_oldviewcluster2;
|
||||
|
||||
extern int c_brush_polys, c_alias_polys;
|
||||
|
||||
extern qboolean IsHighDPIaware;
|
||||
|
||||
/* NOTE: struct image_s* is what re.RegisterSkin() etc return so no gl4image_s!
|
||||
* (I think the client only passes the pointer around and doesn't know the
|
||||
* definition of this struct, so this being different from struct image_s
|
||||
* in ref_gl should be ok)
|
||||
*/
|
||||
typedef struct image_s
|
||||
{
|
||||
char name[MAX_QPATH]; /* game path, including extension */
|
||||
imagetype_t type;
|
||||
int width, height; /* source image */
|
||||
//int upload_width, upload_height; /* after power of two and picmip */
|
||||
int registration_sequence; /* 0 = free */
|
||||
struct msurface_s *texturechain; /* for sort-by-texture world drawing */
|
||||
GLuint texnum; /* gl texture binding */
|
||||
float sl, tl, sh, th; /* 0,0 - 1,1 unless part of the scrap */
|
||||
// qboolean scrap; // currently unused
|
||||
qboolean has_alpha;
|
||||
qboolean is_lava; // DG: added for lava brightness hack
|
||||
|
||||
} gl4image_t;
|
||||
|
||||
enum {MAX_GL4TEXTURES = 1024};
|
||||
|
||||
// include this down here so it can use gl4image_t
|
||||
#include "model.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int internal_format;
|
||||
int current_lightmap_texture; // index into gl4state.lightmap_textureIDs[]
|
||||
|
||||
//msurface_t *lightmap_surfaces[MAX_LIGHTMAPS]; - no more lightmap chains, lightmaps are rendered multitextured
|
||||
|
||||
int allocated[BLOCK_WIDTH];
|
||||
|
||||
/* the lightmap texture data needs to be kept in
|
||||
main memory so texsubimage can update properly */
|
||||
byte lightmap_buffers[MAX_LIGHTMAPS_PER_SURFACE][4 * BLOCK_WIDTH * BLOCK_HEIGHT];
|
||||
} gl4lightmapstate_t;
|
||||
|
||||
extern gl4model_t *gl4_worldmodel;
|
||||
|
||||
extern float gl4depthmin, gl4depthmax;
|
||||
|
||||
extern cplane_t frustum[4];
|
||||
|
||||
extern vec3_t gl4_origin;
|
||||
|
||||
extern gl4image_t *gl4_notexture; /* use for bad textures */
|
||||
extern gl4image_t *gl4_particletexture; /* little dot for particles */
|
||||
|
||||
extern int gl_filter_min;
|
||||
extern int gl_filter_max;
|
||||
|
||||
static inline void
|
||||
GL4_UseProgram(GLuint shaderProgram)
|
||||
{
|
||||
if(shaderProgram != gl4state.currentShaderProgram)
|
||||
{
|
||||
gl4state.currentShaderProgram = shaderProgram;
|
||||
glUseProgram(shaderProgram);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
GL4_BindVAO(GLuint vao)
|
||||
{
|
||||
if(vao != gl4state.currentVAO)
|
||||
{
|
||||
gl4state.currentVAO = vao;
|
||||
glBindVertexArray(vao);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
GL4_BindVBO(GLuint vbo)
|
||||
{
|
||||
if(vbo != gl4state.currentVBO)
|
||||
{
|
||||
gl4state.currentVBO = vbo;
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
GL4_BindEBO(GLuint ebo)
|
||||
{
|
||||
if(ebo != gl4state.currentEBO)
|
||||
{
|
||||
gl4state.currentEBO = ebo;
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
}
|
||||
}
|
||||
|
||||
extern void GL4_BufferAndDraw3D(const gl4_3D_vtx_t* verts, int numVerts, GLenum drawMode);
|
||||
|
||||
extern void GL4_RotateForEntity(entity_t *e);
|
||||
|
||||
// gl4_sdl.c
|
||||
extern int GL4_InitContext(void* win);
|
||||
extern void GL4_GetDrawableSize(int* width, int* height);
|
||||
extern int GL4_PrepareForWindow(void);
|
||||
extern qboolean GL4_IsVsyncActive(void);
|
||||
extern void GL4_EndFrame(void);
|
||||
extern void GL4_SetVsync(void);
|
||||
extern void GL4_ShutdownContext(void);
|
||||
|
||||
// gl4_misc.c
|
||||
extern void GL4_InitParticleTexture(void);
|
||||
extern void GL4_ScreenShot(void);
|
||||
extern void GL4_SetDefaultState(void);
|
||||
|
||||
// gl4_model.c
|
||||
extern int registration_sequence;
|
||||
extern void GL4_Mod_Init(void);
|
||||
extern void GL4_Mod_FreeAll(void);
|
||||
extern void GL4_BeginRegistration(char *model);
|
||||
extern struct model_s * GL4_RegisterModel(char *name);
|
||||
extern void GL4_EndRegistration(void);
|
||||
extern void GL4_Mod_Modellist_f(void);
|
||||
extern const byte* GL4_Mod_ClusterPVS(int cluster, const gl4model_t *model);
|
||||
|
||||
// gl4_draw.c
|
||||
extern void GL4_Draw_InitLocal(void);
|
||||
extern void GL4_Draw_ShutdownLocal(void);
|
||||
extern gl4image_t * GL4_Draw_FindPic(char *name);
|
||||
extern void GL4_Draw_GetPicSize(int *w, int *h, char *pic);
|
||||
|
||||
extern void GL4_Draw_PicScaled(int x, int y, char *pic, float factor);
|
||||
extern void GL4_Draw_StretchPic(int x, int y, int w, int h, char *pic);
|
||||
extern void GL4_Draw_CharScaled(int x, int y, int num, float scale);
|
||||
extern void GL4_Draw_TileClear(int x, int y, int w, int h, char *pic);
|
||||
extern void GL4_DrawFrameBufferObject(int x, int y, int w, int h, GLuint fboTexture, const float v_blend[4]);
|
||||
extern void GL4_Draw_Fill(int x, int y, int w, int h, int c);
|
||||
extern void GL4_Draw_FadeScreen(void);
|
||||
extern void GL4_Draw_Flash(const float color[4], float x, float y, float w, float h);
|
||||
extern void GL4_Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, const byte *data, int bits);
|
||||
|
||||
// gl4_image.c
|
||||
|
||||
static inline void
|
||||
GL4_SelectTMU(GLenum tmu)
|
||||
{
|
||||
if(gl4state.currenttmu != tmu)
|
||||
{
|
||||
glActiveTexture(tmu);
|
||||
gl4state.currenttmu = tmu;
|
||||
}
|
||||
}
|
||||
|
||||
extern void GL4_TextureMode(char *string);
|
||||
extern void GL4_Bind(GLuint texnum);
|
||||
extern void GL4_BindLightmap(int lightmapnum);
|
||||
extern gl4image_t *GL4_LoadPic(char *name, byte *pic, int width, int realwidth,
|
||||
int height, int realheight, size_t data_size,
|
||||
imagetype_t type, int bits);
|
||||
extern gl4image_t *GL4_FindImage(const char *name, imagetype_t type);
|
||||
extern gl4image_t *GL4_RegisterSkin(char *name);
|
||||
extern void GL4_ShutdownImages(void);
|
||||
extern void GL4_FreeUnusedImages(void);
|
||||
extern qboolean GL4_ImageHasFreeSpace(void);
|
||||
extern void GL4_ImageList_f(void);
|
||||
|
||||
// gl4_light.c
|
||||
extern int r_dlightframecount;
|
||||
extern void GL4_MarkSurfaceLights(dlight_t *light, int bit, mnode_t *node,
|
||||
int r_dlightframecount);
|
||||
extern void GL4_PushDlights(void);
|
||||
extern void GL4_LightPoint(entity_t *currententity, vec3_t p, vec3_t color);
|
||||
extern void GL4_BuildLightMap(msurface_t *surf, int offsetInLMbuf, int stride);
|
||||
|
||||
// gl4_lightmap.c
|
||||
#define GL_LIGHTMAP_FORMAT GL_RGBA
|
||||
|
||||
extern void GL4_LM_InitBlock(void);
|
||||
extern void GL4_LM_UploadBlock(void);
|
||||
extern qboolean GL4_LM_AllocBlock(int w, int h, int *x, int *y);
|
||||
extern void GL4_LM_BuildPolygonFromSurface(gl4model_t *currentmodel, msurface_t *fa);
|
||||
extern void GL4_LM_CreateSurfaceLightmap(msurface_t *surf);
|
||||
extern void GL4_LM_BeginBuildingLightmaps(gl4model_t *m);
|
||||
extern void GL4_LM_EndBuildingLightmaps(void);
|
||||
|
||||
// gl4_warp.c
|
||||
extern void GL4_EmitWaterPolys(msurface_t *fa);
|
||||
extern void GL4_SubdivideSurface(msurface_t *fa, gl4model_t* loadmodel);
|
||||
|
||||
extern void GL4_SetSky(const char *name, float rotate, int autorotate, const vec3_t axis);
|
||||
extern void GL4_DrawSkyBox(void);
|
||||
extern void GL4_ClearSkyBox(void);
|
||||
extern void GL4_AddSkySurface(msurface_t *fa);
|
||||
|
||||
|
||||
// gl4_surf.c
|
||||
extern void GL4_SurfInit(void);
|
||||
extern void GL4_SurfShutdown(void);
|
||||
extern void GL4_DrawGLPoly(msurface_t *fa);
|
||||
extern void GL4_DrawGLFlowingPoly(msurface_t *fa);
|
||||
extern void GL4_DrawTriangleOutlines(void);
|
||||
extern void GL4_DrawAlphaSurfaces(void);
|
||||
extern void GL4_DrawBrushModel(entity_t *e, gl4model_t *currentmodel);
|
||||
extern void GL4_DrawWorld(void);
|
||||
extern void GL4_MarkLeaves(void);
|
||||
|
||||
// gl4_mesh.c
|
||||
extern void GL4_DrawAliasModel(entity_t *e);
|
||||
extern void GL4_ResetShadowAliasModels(void);
|
||||
extern void GL4_DrawAliasShadows(void);
|
||||
extern void GL4_ShutdownMeshes(void);
|
||||
|
||||
// gl4_shaders.c
|
||||
|
||||
extern qboolean GL4_RecreateShaders(void);
|
||||
extern qboolean GL4_InitShaders(void);
|
||||
extern void GL4_ShutdownShaders(void);
|
||||
extern void GL4_UpdateUBOCommon(void);
|
||||
extern void GL4_UpdateUBO2D(void);
|
||||
extern void GL4_UpdateUBO3D(void);
|
||||
extern void GL4_UpdateUBOLights(void);
|
||||
|
||||
// ############ Cvars ###########
|
||||
|
||||
extern cvar_t *gl_msaa_samples;
|
||||
extern cvar_t *r_vsync;
|
||||
extern cvar_t *r_retexturing;
|
||||
extern cvar_t *r_scale8bittextures;
|
||||
extern cvar_t *vid_fullscreen;
|
||||
extern cvar_t *r_mode;
|
||||
extern cvar_t *r_customwidth;
|
||||
extern cvar_t *r_customheight;
|
||||
|
||||
extern cvar_t *r_2D_unfiltered;
|
||||
extern cvar_t *r_videos_unfiltered;
|
||||
extern cvar_t *gl_nolerp_list;
|
||||
extern cvar_t *r_lerp_list;
|
||||
extern cvar_t *gl_nobind;
|
||||
extern cvar_t *r_lockpvs;
|
||||
extern cvar_t *r_novis;
|
||||
|
||||
extern cvar_t *r_cull;
|
||||
extern cvar_t *gl_zfix;
|
||||
extern cvar_t *r_fullbright;
|
||||
|
||||
extern cvar_t *r_norefresh;
|
||||
extern cvar_t *gl_lefthand;
|
||||
extern cvar_t *r_gunfov;
|
||||
extern cvar_t *r_farsee;
|
||||
extern cvar_t *r_drawworld;
|
||||
|
||||
extern cvar_t *vid_gamma;
|
||||
extern cvar_t *gl4_intensity;
|
||||
extern cvar_t *gl4_intensity_2D;
|
||||
extern cvar_t *gl_anisotropic;
|
||||
extern cvar_t *gl_texturemode;
|
||||
|
||||
extern cvar_t *r_lightlevel;
|
||||
extern cvar_t *gl4_overbrightbits;
|
||||
extern cvar_t *gl4_particle_fade_factor;
|
||||
extern cvar_t *gl4_particle_square;
|
||||
extern cvar_t *gl4_colorlight;
|
||||
extern cvar_t *gl_polyblend;
|
||||
|
||||
extern cvar_t *r_modulate;
|
||||
extern cvar_t *gl_lightmap;
|
||||
extern cvar_t *gl_shadows;
|
||||
extern cvar_t *r_fixsurfsky;
|
||||
extern cvar_t *r_palettedtexture;
|
||||
extern cvar_t *r_validation;
|
||||
|
||||
extern cvar_t *gl4_debugcontext;
|
||||
|
||||
#endif /* SRC_CLIENT_REFRESH_GL4_HEADER_LOCAL_H_ */
|
163
src/client/refresh/gl4/header/model.h
Normal file
163
src/client/refresh/gl4/header/model.h
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
*
|
||||
* This program 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 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Header for the model stuff.
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#ifndef SRC_CLIENT_REFRESH_GL4_HEADER_MODEL_H_
|
||||
#define SRC_CLIENT_REFRESH_GL4_HEADER_MODEL_H_
|
||||
|
||||
// used for vertex array elements when drawing brushes, sprites, sky and more
|
||||
// (ok, it has the layout used for rendering brushes, but is not used there)
|
||||
typedef struct gl4_3D_vtx_s {
|
||||
vec3_t pos;
|
||||
float texCoord[2];
|
||||
float lmTexCoord[2]; // lightmap texture coordinate (sometimes unused)
|
||||
vec3_t normal;
|
||||
GLuint lightFlags; // bit i set means: dynlight i affects surface
|
||||
} gl4_3D_vtx_t;
|
||||
|
||||
// used for vertex array elements when drawing models
|
||||
typedef struct gl4_alias_vtx_s {
|
||||
GLfloat pos[3];
|
||||
GLfloat texCoord[2];
|
||||
GLfloat color[4];
|
||||
} gl4_alias_vtx_t;
|
||||
|
||||
/* in memory representation */
|
||||
|
||||
typedef struct glpoly_s
|
||||
{
|
||||
struct glpoly_s *next;
|
||||
struct glpoly_s *chain;
|
||||
int numverts;
|
||||
int flags; /* for SURF_UNDERWATER (not needed anymore?) */
|
||||
gl4_3D_vtx_t vertices[4]; /* variable sized */
|
||||
} glpoly_t;
|
||||
|
||||
typedef struct msurface_s
|
||||
{
|
||||
int visframe; /* should be drawn when node is crossed */
|
||||
|
||||
cplane_t *plane;
|
||||
int flags;
|
||||
|
||||
int firstedge; /* look up in model->surfedges[], negative numbers */
|
||||
int numedges; /* are backwards edges */
|
||||
|
||||
short texturemins[2];
|
||||
short extents[2];
|
||||
|
||||
int light_s, light_t; /* gl lightmap coordinates */
|
||||
int dlight_s, dlight_t; /* gl lightmap coordinates for dynamic lightmaps */
|
||||
|
||||
glpoly_t *polys; /* multiple if warped */
|
||||
struct msurface_s *texturechain;
|
||||
// struct msurface_s *lightmapchain; not used/needed anymore
|
||||
|
||||
mtexinfo_t *texinfo;
|
||||
|
||||
/* lighting info */
|
||||
int dlightframe;
|
||||
int dlightbits;
|
||||
|
||||
int lightmaptexturenum;
|
||||
byte styles[MAXLIGHTMAPS]; // MAXLIGHTMAPS = MAX_LIGHTMAPS_PER_SURFACE (defined in local.h)
|
||||
// I think cached_light is not used/needed anymore
|
||||
//float cached_light[MAXLIGHTMAPS]; /* values currently used in lightmap */
|
||||
byte *samples; /* [numstyles*surfsize] */
|
||||
} msurface_t;
|
||||
|
||||
/* Whole model */
|
||||
|
||||
// this, must be struct model_s, not gl4model_s,
|
||||
// because struct model_s* is returned by re.RegisterModel()
|
||||
typedef struct model_s
|
||||
{
|
||||
char name[MAX_QPATH];
|
||||
|
||||
int registration_sequence;
|
||||
|
||||
modtype_t type;
|
||||
int numframes;
|
||||
|
||||
int flags;
|
||||
|
||||
/* volume occupied by the model graphics */
|
||||
vec3_t mins, maxs;
|
||||
float radius;
|
||||
|
||||
/* solid volume for clipping */
|
||||
qboolean clipbox;
|
||||
vec3_t clipmins, clipmaxs;
|
||||
|
||||
/* brush model */
|
||||
int firstmodelsurface, nummodelsurfaces;
|
||||
int lightmap; /* only for submodels */
|
||||
|
||||
int numsubmodels;
|
||||
struct model_s *submodels;
|
||||
|
||||
int numplanes;
|
||||
cplane_t *planes;
|
||||
|
||||
int numleafs; /* number of visible leafs, not counting 0 */
|
||||
mleaf_t *leafs;
|
||||
|
||||
int numvertexes;
|
||||
mvertex_t *vertexes;
|
||||
|
||||
int numedges;
|
||||
medge_t *edges;
|
||||
|
||||
int numnodes;
|
||||
int firstnode;
|
||||
mnode_t *nodes;
|
||||
|
||||
int numtexinfo;
|
||||
mtexinfo_t *texinfo;
|
||||
|
||||
int numsurfaces;
|
||||
msurface_t *surfaces;
|
||||
|
||||
int numsurfedges;
|
||||
int *surfedges;
|
||||
|
||||
int nummarksurfaces;
|
||||
msurface_t **marksurfaces;
|
||||
|
||||
dvis_t *vis;
|
||||
|
||||
byte *lightdata;
|
||||
|
||||
/* for alias models and skins */
|
||||
gl4image_t *skins[MAX_MD2SKINS];
|
||||
|
||||
int extradatasize;
|
||||
void *extradata;
|
||||
|
||||
// submodules
|
||||
vec3_t origin; // for sounds or lights
|
||||
} gl4model_t;
|
||||
|
||||
#endif /* SRC_CLIENT_REFRESH_GL4_HEADER_MODEL_H_ */
|
Loading…
Reference in a new issue