diff --git a/code/renderer/qgl.h b/code/renderer/qgl.h index 5296193..259733c 100644 --- a/code/renderer/qgl.h +++ b/code/renderer/qgl.h @@ -32,13 +32,415 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # include #endif +// MinGW headers may be missing some GL extensions +#ifndef GL_ARB_map_buffer_range +#define GL_ARB_map_buffer_range +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#endif + +#ifndef GL_EXT_timer_query +#define GL_EXT_timer_query +#define GL_TIME_ELAPSED_EXT 0x88BF +typedef int64_t GLint64EXT; +typedef uint64_t GLuint64EXT; +#endif + +#ifndef GL_EXT_geometry_shader4 +#define GL_EXT_geometry_shader4 +#define GL_GEOMETRY_SHADER_EXT 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC +#endif + +#ifndef GL_EXT_texture_buffer_object +#define GL_EXT_texture_buffer_object +#define GL_TEXTURE_BUFFER_EXT 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_EXT 0x8C2E +#endif + +#ifndef GL_ARB_debug_output +#define GL_ARB_debug_output +typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam); +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 +#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 +#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A +#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 +#endif + +#ifndef GL_AMD_debug_output +#define GL_AMD_debug_output +typedef void (APIENTRYP *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam); +#define GL_DEBUG_LOGGED_MESSAGES_AMD 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_AMD 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_AMD 0x9147 +#define GL_DEBUG_SEVERITY_LOW_AMD 0x9148 +#define GL_DEBUG_CATEGORY_APPLICATION_AMD 0x914f +#endif + +#ifndef WGL_ARB_create_context_profile +#define WGL_ARB_create_context_profile +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 +#endif + +#ifndef GL_ARB_uniform_buffer_object +#define GL_ARB_uniform_buffer_object +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFFu +#endif + +#ifndef GL_ARB_blend_func_extended +#define GL_ARB_blend_func_extended +#define GL_SRC1_COLOR 0x88F9 +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#endif + +// GL_EXT_draw_range_elements +extern void (APIENTRYP qglDrawRangeElementsEXT) (GLenum mode, GLsizei count, GLuint start, GLuint end, GLenum type, const GLvoid *indices); + extern void (APIENTRYP qglActiveTextureARB) (GLenum texture); extern void (APIENTRYP qglClientActiveTextureARB) (GLenum texture); extern void (APIENTRYP qglMultiTexCoord2fARB) (GLenum target, GLfloat s, GLfloat t); +extern void (APIENTRYP qglMultiTexCoord4fvARB) (GLenum target, GLfloat *v); extern void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count); extern void (APIENTRYP qglUnlockArraysEXT) (void); +// GL_ARB_vertex_buffer_object +extern void (APIENTRYP qglBindBufferARB) (GLenum target, GLuint buffer); +extern void (APIENTRYP qglDeleteBuffersARB) (GLsizei n, const GLuint *buffers); +extern void (APIENTRYP qglGenBuffersARB) (GLsizei n, GLuint *buffers); +extern GLboolean (APIENTRYP qglIsBufferARB) (GLuint buffer); +extern void (APIENTRYP qglBufferDataARB) (GLenum target, GLsizeiptrARB size, const GLvoid *data, GLenum usage); +extern void (APIENTRYP qglBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid *data); +extern void (APIENTRYP qglGetBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid *data); +extern GLvoid *(APIENTRYP qglMapBufferARB) (GLenum target, GLenum access); +extern GLboolean (APIENTRYP qglUnmapBufferARB) (GLenum target); +extern void (APIENTRYP qglGetBufferParameterivARB) (GLenum target, GLenum pname, GLint *params); +extern void (APIENTRYP qglGetBufferPointervARB) (GLenum target, GLenum pname, GLvoid **params); + +// GL_ARB_map_buffer_range +extern GLvoid *(APIENTRYP qglMapBufferRange) (GLenum target, GLintptr offset, + GLsizeiptr length, + GLbitfield access); +extern GLvoid (APIENTRYP qglFlushMappedBufferRange) (GLenum target, + GLintptr offset, + GLsizeiptr length); + +// GL_ARB_shader_objects +extern GLvoid (APIENTRYP qglDeleteShader) (GLuint shader); +extern GLvoid (APIENTRYP qglDeleteProgram) (GLuint program); +extern GLvoid (APIENTRYP qglDetachShader) (GLuint program, GLuint shader); +extern GLuint (APIENTRYP qglCreateShader) (GLenum type); +extern GLvoid (APIENTRYP qglShaderSource) (GLuint shader, GLsizei count, const char **string, + const GLint *length); +extern GLvoid (APIENTRYP qglCompileShader) (GLuint shader); +extern GLuint (APIENTRYP qglCreateProgram) (void); +extern GLvoid (APIENTRYP qglAttachShader) (GLuint program, GLuint shader); +extern GLvoid (APIENTRYP qglLinkProgram) (GLuint program); +extern GLvoid (APIENTRYP qglUseProgram) (GLuint program); +extern GLvoid (APIENTRYP qglValidateProgram) (GLuint program); +extern GLvoid (APIENTRYP qglUniform1f) (GLint location, GLfloat v0); +extern GLvoid (APIENTRYP qglUniform2f) (GLint location, GLfloat v0, GLfloat v1); +extern GLvoid (APIENTRYP qglUniform3f) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +extern GLvoid (APIENTRYP qglUniform4f) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +extern GLvoid (APIENTRYP qglUniform1i) (GLint location, GLint v0); +extern GLvoid (APIENTRYP qglUniform2i) (GLint location, GLint v0, GLint v1); +extern GLvoid (APIENTRYP qglUniform3i) (GLint location, GLint v0, GLint v1, GLint v2); +extern GLvoid (APIENTRYP qglUniform4i) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +extern GLvoid (APIENTRYP qglUniform1fv) (GLint location, GLsizei count, const GLfloat *value); +extern GLvoid (APIENTRYP qglUniform2fv) (GLint location, GLsizei count, const GLfloat *value); +extern GLvoid (APIENTRYP qglUniform3fv) (GLint location, GLsizei count, const GLfloat *value); +extern GLvoid (APIENTRYP qglUniform4fv) (GLint location, GLsizei count, const GLfloat *value); +extern GLvoid (APIENTRYP qglUniform1iv) (GLint location, GLsizei count, const GLint *value); +extern GLvoid (APIENTRYP qglUniform2iv) (GLint location, GLsizei count, const GLint *value); +extern GLvoid (APIENTRYP qglUniform3iv) (GLint location, GLsizei count, const GLint *value); +extern GLvoid (APIENTRYP qglUniform4iv) (GLint location, GLsizei count, const GLint *value); +extern GLvoid (APIENTRYP qglUniformMatrix2fv) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +extern GLvoid (APIENTRYP qglUniformMatrix3fv) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +extern GLvoid (APIENTRYP qglUniformMatrix4fv) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +extern GLvoid (APIENTRYP qglGetShaderiv) (GLuint shader, GLenum pname, GLint *params); +extern GLvoid (APIENTRYP qglGetProgramiv) (GLuint program, GLenum pname, GLint *params); +extern GLvoid (APIENTRYP qglGetShaderInfoLog) (GLuint shader, GLsizei maxLength, GLsizei *length, char *infoLog); +extern GLvoid (APIENTRYP qglGetProgramInfoLog) (GLuint program, GLsizei maxLength, GLsizei *length, char *infoLog); +extern GLvoid (APIENTRYP qglGetAttachedShaders) (GLuint program, GLsizei maxCount, GLsizei *count, + GLuint *shaders); +extern GLint (APIENTRYP qglGetUniformLocation) (GLuint program, const char *name); +extern GLvoid (APIENTRYP qglGetActiveUniform) (GLuint program, GLuint index, GLsizei maxLength, + GLsizei *length, GLint *size, GLenum *type, char *name); +extern GLvoid (APIENTRYP qglGetUniformfv) (GLuint program, GLint location, GLfloat *params); +extern GLvoid (APIENTRYP qglGetUniformiv) (GLuint program, GLint location, GLint *params); +extern GLvoid (APIENTRYP qglGetShaderSource) (GLuint shader, GLsizei maxLength, GLsizei *length, + char *source); + +// GL_ARB_vertex_shader +extern GLvoid (APIENTRYP qglVertexAttrib1fARB) (GLuint index, GLfloat v0); +extern GLvoid (APIENTRYP qglVertexAttrib1sARB) (GLuint index, GLshort v0); +extern GLvoid (APIENTRYP qglVertexAttrib1dARB) (GLuint index, GLdouble v0); +extern GLvoid (APIENTRYP qglVertexAttrib2fARB) (GLuint index, GLfloat v0, GLfloat v1); +extern GLvoid (APIENTRYP qglVertexAttrib2sARB) (GLuint index, GLshort v0, GLshort v1); +extern GLvoid (APIENTRYP qglVertexAttrib2dARB) (GLuint index, GLdouble v0, GLdouble v1); +extern GLvoid (APIENTRYP qglVertexAttrib3fARB) (GLuint index, GLfloat v0, GLfloat v1, GLfloat v2); +extern GLvoid (APIENTRYP qglVertexAttrib3sARB) (GLuint index, GLshort v0, GLshort v1, GLshort v2); +extern GLvoid (APIENTRYP qglVertexAttrib3dARB) (GLuint index, GLdouble v0, GLdouble v1, GLdouble v2); +extern GLvoid (APIENTRYP qglVertexAttrib4fARB) (GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +extern GLvoid (APIENTRYP qglVertexAttrib4sARB) (GLuint index, GLshort v0, GLshort v1, GLshort v2, GLshort v3); +extern GLvoid (APIENTRYP qglVertexAttrib4dARB) (GLuint index, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +extern GLvoid (APIENTRYP qglVertexAttrib4NubARB) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +extern GLvoid (APIENTRYP qglVertexAttrib1fvARB) (GLuint index, GLfloat *v); +extern GLvoid (APIENTRYP qglVertexAttrib1svARB) (GLuint index, GLshort *v); +extern GLvoid (APIENTRYP qglVertexAttrib1dvARB) (GLuint index, GLdouble *v); +extern GLvoid (APIENTRYP qglVertexAttrib2fvARB) (GLuint index, GLfloat *v); +extern GLvoid (APIENTRYP qglVertexAttrib2svARB) (GLuint index, GLshort *v); +extern GLvoid (APIENTRYP qglVertexAttrib2dvARB) (GLuint index, GLdouble *v); +extern GLvoid (APIENTRYP qglVertexAttrib3fvARB) (GLuint index, GLfloat *v); +extern GLvoid (APIENTRYP qglVertexAttrib3svARB) (GLuint index, GLshort *v); +extern GLvoid (APIENTRYP qglVertexAttrib3dvARB) (GLuint index, GLdouble *v); +extern GLvoid (APIENTRYP qglVertexAttrib4fvARB) (GLuint index, GLfloat *v); +extern GLvoid (APIENTRYP qglVertexAttrib4svARB) (GLuint index, GLshort *v); +extern GLvoid (APIENTRYP qglVertexAttrib4dvARB) (GLuint index, GLdouble *v); +extern GLvoid (APIENTRYP qglVertexAttrib4ivARB) (GLuint index, GLint *v); +extern GLvoid (APIENTRYP qglVertexAttrib4bvARB) (GLuint index, GLbyte *v); +extern GLvoid (APIENTRYP qglVertexAttrib4ubvARB) (GLuint index, GLubyte *v); +extern GLvoid (APIENTRYP qglVertexAttrib4usvARB) (GLuint index, GLushort *v); +extern GLvoid (APIENTRYP qglVertexAttrib4uivARB) (GLuint index, GLuint *v); +extern GLvoid (APIENTRYP qglVertexAttrib4NbvARB) (GLuint index, const GLbyte *v); +extern GLvoid (APIENTRYP qglVertexAttrib4NsvARB) (GLuint index, const GLshort *v); +extern GLvoid (APIENTRYP qglVertexAttrib4NivARB) (GLuint index, const GLint *v); +extern GLvoid (APIENTRYP qglVertexAttrib4NubvARB) (GLuint index, const GLubyte *v); +extern GLvoid (APIENTRYP qglVertexAttrib4NusvARB) (GLuint index, const GLushort *v); +extern GLvoid (APIENTRYP qglVertexAttrib4NuivARB) (GLuint index, const GLuint *v); +extern GLvoid (APIENTRYP qglVertexAttribPointerARB) (GLuint index, GLint size, GLenum type, GLboolean normalized, + GLsizei stride, const GLvoid *pointer); +extern GLvoid (APIENTRYP qglEnableVertexAttribArrayARB) (GLuint index); +extern GLvoid (APIENTRYP qglDisableVertexAttribArrayARB) (GLuint index); +extern GLvoid (APIENTRYP qglBindAttribLocationARB) (GLhandleARB programObj, GLuint index, const GLcharARB *name); +extern GLvoid (APIENTRYP qglGetActiveAttribARB) (GLhandleARB programObj, GLuint index, GLsizei maxLength, + GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +extern GLint (APIENTRYP qglGetAttribLocationARB) (GLhandleARB programObj, const GLcharARB *name); +extern GLvoid (APIENTRYP qglGetVertexAttribdvARB) (GLuint index, GLenum pname, GLdouble *params); +extern GLvoid (APIENTRYP qglGetVertexAttribfvARB) (GLuint index, GLenum pname, GLfloat *params); +extern GLvoid (APIENTRYP qglGetVertexAttribivARB) (GLuint index, GLenum pname, GLint *params); +extern GLvoid (APIENTRYP qglGetVertexAttribPointervARB) (GLuint index, GLenum pname, GLvoid **pointer); + +// GL_EXT_geometry_shader4 +extern GLvoid (APIENTRYP qglProgramParameteriEXT) (GLuint program, GLenum pname, GLint value); +extern GLvoid (APIENTRYP qglFramebufferTextureEXT) (GLenum target, GLenum attachment, + GLuint texture, GLint level); +extern GLvoid (APIENTRYP qglFramebufferTextureLayerEXT) (GLenum target, GLenum attachment, + GLuint texture, GLint level, GLint layer); +extern GLvoid (APIENTRYP qglFramebufferTextureFaceEXT) (GLenum target, GLenum attachment, + GLuint texture, GLint level, GLenum face); + +// GL_EXT_texture3D +extern GLvoid (APIENTRYP qglTexImage3DEXT) (GLenum target, GLint level, + GLenum internalformat, + GLsizei width, GLsizei height, + GLsizei depth, GLint border, + GLenum format, GLenum type, + const GLvoid *pixels); + +// GL_EXT_texture_buffer_object +extern GLvoid (APIENTRYP qglTexBufferEXT) (GLenum target, GLenum internalFormat, + GLuint buffer); + +// GL_ARB_uniform_buffer_object +extern GLvoid (APIENTRYP qglGetUniformIndices) (GLuint program, + GLsizei uniformCount, + const GLchar** uniformNames, + GLuint* uniformIndices); +extern GLvoid (APIENTRYP qglGetActiveUniformsiv) (GLuint program, + GLsizei uniformCount, + const GLuint* uniformIndices, + GLenum pname, + GLint* params); +extern GLvoid (APIENTRYP qglGetActiveUniformName) (GLuint program, + GLuint uniformIndex, + GLsizei bufSize, + GLsizei* length, + GLchar* uniformName); +extern GLuint (APIENTRYP qglGetUniformBlockIndex) (GLuint program, + const GLchar* uniformBlockName); +extern GLvoid (APIENTRYP qglGetActiveUniformBlockiv) (GLuint program, + GLuint uniformBlockIndex, + GLenum pname, + GLint* params); +extern GLvoid (APIENTRYP qglGetActiveUniformBlockName) (GLuint program, + GLuint uniformBlockIndex, + GLsizei bufSize, + GLsizei* length, + GLchar* uniformBlockName); +extern GLvoid (APIENTRYP qglBindBufferRange) (GLenum target, + GLuint index, + GLuint buffer, + GLintptr offset, + GLsizeiptr size); +extern GLvoid (APIENTRYP qglBindBufferBase) (GLenum target, + GLuint index, + GLuint buffer); +extern GLvoid (APIENTRYP qglGetIntegeri_v) (GLenum target, + GLuint index, + GLint* data); +extern GLvoid (APIENTRYP qglUniformBlockBinding) (GLuint program, + GLuint uniformBlockIndex, + GLuint uniformBlockBinding); + +// GL_ARB_framebuffer_object +extern GLboolean (APIENTRYP qglIsRenderbuffer) (GLuint renderbuffer); +extern GLvoid (APIENTRYP qglBindRenderbuffer) (GLenum target, GLuint renderbuffer); +extern GLvoid (APIENTRYP qglDeleteRenderbuffers) (GLsizei n, const GLuint *renderbuffers); +extern GLvoid (APIENTRYP qglGenRenderbuffers) (GLsizei n, GLuint *renderbuffers); +extern GLvoid (APIENTRYP qglRenderbufferStorage) (GLenum target, GLenum internalformat, + GLsizei width, GLsizei height); +extern GLvoid (APIENTRYP qglRenderbufferStorageMultisample) (GLenum target, GLsizei samples, + GLenum internalformat, + GLsizei width, GLsizei height); +extern GLvoid (APIENTRYP qglGetRenderbufferParameteriv) (GLenum target, GLenum pname, GLint *params); +extern GLboolean (APIENTRYP qglIsFramebuffer) (GLuint framebuffer); +extern GLvoid (APIENTRYP qglBindFramebuffer) (GLenum target, GLuint framebuffer); +extern GLvoid (APIENTRYP qglDeleteFramebuffers) (GLsizei n, const GLuint *framebuffers); +extern GLvoid (APIENTRYP qglGenFramebuffers) (GLsizei n, GLuint *framebuffers); +extern GLenum (APIENTRYP qglCheckFramebufferStatus) (GLenum target); +extern GLvoid (APIENTRYP qglFramebufferTexture1D) (GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, GLint level); +extern GLvoid (APIENTRYP qglFramebufferTexture2D) (GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, GLint level); +extern GLvoid (APIENTRYP qglFramebufferTexture3D) (GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, + GLint level, GLint layer); +extern GLvoid (APIENTRYP qglFramebufferTextureLayer) (GLenum target, GLenum attachment, + GLuint texture, GLint level, GLint layer); +extern GLvoid (APIENTRYP qglFramebufferRenderbuffer) (GLenum target, GLenum attachment, + GLenum renderbuffertarget, GLuint renderbuffer); +extern GLvoid (APIENTRYP qglGetFramebufferAttachmentParameteriv) (GLenum target, GLenum attachment, + GLenum pname, GLint *params); +extern GLvoid (APIENTRYP qglBlitFramebuffer) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter); +extern GLvoid (APIENTRYP qglGenerateMipmap) (GLenum target); + +// GL_EXT_timer_query +extern GLvoid (APIENTRYP qglGenQueriesARB) (GLsizei n, GLuint *ids); +extern GLvoid (APIENTRYP qglDeleteQueriesARB) (GLsizei n, const GLuint *ids); +extern GLboolean (APIENTRYP qglIsQueryARB) (GLuint id); +extern GLvoid (APIENTRYP qglBeginQueryARB) (GLenum target, GLuint id); +extern GLvoid (APIENTRYP qglEndQueryARB) (GLenum target); +extern GLvoid (APIENTRYP qglGetQueryivARB) (GLenum target, GLenum pname, GLint *params); +extern GLvoid (APIENTRYP qglGetQueryObjectivARB) (GLuint id, GLenum pname, GLint *params); +extern GLvoid (APIENTRYP qglGetQueryObjectuivARB) (GLuint id, GLenum pname, GLuint *params); +extern GLvoid (APIENTRYP qglGetQueryObjecti64vEXT) (GLuint id, GLenum pname, GLint64EXT *params); +extern GLvoid (APIENTRYP qglGetQueryObjectui64vEXT) (GLuint id, GLenum pname, GLuint64EXT *params); + +// GL_ARB_instanced_arrays +extern GLvoid (APIENTRYP qglVertexAttribDivisorARB) (GLuint index, GLuint divisor); +extern GLvoid (APIENTRYP qglDrawArraysInstancedARB) (GLenum mode, GLint first, GLsizei count, + GLsizei primcount); +extern GLvoid (APIENTRYP qglDrawElementsInstancedARB) (GLenum mode, GLsizei count, GLenum type, + const GLvoid *indices, GLsizei primcount); + +// GL_ARB_separate_stencil, does not really exists, part of 2.0 +extern GLvoid (APIENTRYP qglStencilOpSeparate) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +extern GLvoid (APIENTRYP qglStencilFuncSeparate) (GLenum face, GLenum func, GLint ref, GLuint mask); +extern GLvoid (APIENTRYP qglStencilMaskSeparate) (GLenum face, GLuint mask); + +// GL_ARB_debug_output, not part of core +extern GLvoid (APIENTRYP qglDebugMessageControlARB) (GLenum source, + GLenum type, + GLenum severity, + GLsizei count, + const GLuint* ids, + GLboolean enabled); +extern GLvoid (APIENTRYP qglDebugMessageInsertARB) (GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* buf); +extern GLvoid (APIENTRYP qglDebugMessageCallbackARB) (GLDEBUGPROCARB callback, + GLvoid *userParam); +extern GLuint (APIENTRYP qglGetDebugMessageLogARB) (GLuint count, + GLsizei bufsize, + GLenum *sources, + GLenum *types, + GLuint *ids, + GLenum *severities, + GLsizei *lengths, + GLchar *messageLog); +// GL_AMD_debug_output, predecessor to GL_ARB_debug_output, but has only +// a category parameter instead of source and type +extern GLvoid (APIENTRYP qglDebugMessageEnableAMD) (GLenum category, + GLenum severity, + GLsizei count, + const GLuint* ids, + GLboolean enabled); +extern GLvoid (APIENTRYP qglDebugMessageInsertAMD) (GLenum category, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* buf); +extern GLvoid (APIENTRYP qglDebugMessageCallbackAMD) (GLDEBUGPROCAMD callback, + GLvoid *userParam); +extern GLuint (APIENTRYP qglGetDebugMessageLogAMD) (GLuint count, + GLsizei bufsize, + GLenum *categories, + GLuint *ids, + GLenum *severities, + GLsizei *lengths, + GLchar *messageLog); + +// GL_ARB_blend_func_extended +extern GLvoid (APIENTRYP qglBindFragDataLocationIndexed) (GLuint program, + GLuint colorNumber, + GLuint index, + const GLchar *name); +extern GLint (APIENTRYP qglGetFragDataIndex) (GLuint program, + const GLchar *name); + //=========================================================================== diff --git a/code/renderer/tr_animation.c b/code/renderer/tr_animation.c index c123e9e..89d83dc 100644 --- a/code/renderer/tr_animation.c +++ b/code/renderer/tr_animation.c @@ -51,7 +51,7 @@ void R_AddAnimSurfaces( trRefEntity_t *ent ) { surface = (md4Surface_t *)( (byte *)lod + lod->ofsSurfaces ); for ( i = 0 ; i < lod->numSurfaces ; i++ ) { shader = R_GetShaderByHandle( surface->shaderIndex ); - R_AddDrawSurf( (void *)surface, shader, 0 /*fogNum*/, qfalse ); + R_AddDrawSurf( (void *)surface, shader, 0 /*fogNum*/, 0 ); surface = (md4Surface_t *)( (byte *)surface + surface->ofsEnd ); } } @@ -61,12 +61,13 @@ void R_AddAnimSurfaces( trRefEntity_t *ent ) { RB_SurfaceAnim ============== */ -void RB_SurfaceAnim( md4Surface_t *surface ) { +void RB_SurfaceAnim( tessMode_t mode, surfaceType_t *surf ) { + md4Surface_t *surface = (md4Surface_t *)surf; int i, j, k; float frontlerp, backlerp; int *triangles; int indexes; - int baseIndex, baseVertex; + int baseVertex; int numVerts; md4Vertex_t *v; md4Bone_t bones[MD4_MAX_BONES]; @@ -75,7 +76,7 @@ void RB_SurfaceAnim( md4Surface_t *surface ) { md4Frame_t *frame; md4Frame_t *oldFrame; int frameSize; - + glVertex_t *vertexPtr; if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) { backlerp = 0; @@ -93,77 +94,88 @@ void RB_SurfaceAnim( md4Surface_t *surface ) { oldFrame = (md4Frame_t *)((byte *)header + header->ofsFrames + backEnd.currentEntity->e.oldframe * frameSize ); - RB_CheckOverflow( surface->numVerts, surface->numTriangles * 3 ); - - triangles = (int *) ((byte *)surface + surface->ofsTriangles); - indexes = surface->numTriangles * 3; - baseIndex = tess.numIndexes; - baseVertex = tess.numVertexes; - for (j = 0 ; j < indexes ; j++) { - tess.indexes[baseIndex + j] = baseIndex + triangles[j]; - } - tess.numIndexes += indexes; - - // - // lerp all the needed bones - // - if ( !backlerp ) { - // no lerping needed - bonePtr = frame->bones; - } else { - bonePtr = bones; - for ( i = 0 ; i < header->numBones*12 ; i++ ) { - ((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i] + if ( mode & TESS_VERTEX ) { + baseVertex = tess.numVertexes; + vertexPtr = tess.vertexPtr + baseVertex; + + // + // lerp all the needed bones + // + if ( !backlerp ) { + // no lerping needed + bonePtr = frame->bones; + } else { + bonePtr = bones; + for ( i = 0 ; i < header->numBones*12 ; i++ ) { + ((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i] + backlerp * ((float *)oldFrame->bones)[i]; + } } - } - - // - // deform the vertexes by the lerped bones - // - numVerts = surface->numVerts; - // FIXME - // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left - // in for reference. - //v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts + 12); - v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts); - for ( j = 0; j < numVerts; j++ ) { - vec3_t tempVert, tempNormal; - md4Weight_t *w; - - VectorClear( tempVert ); - VectorClear( tempNormal ); - w = v->weights; - for ( k = 0 ; k < v->numWeights ; k++, w++ ) { - bone = bonePtr + w->boneIndex; - - tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] ); - tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] ); - tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] ); - - tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal ); - tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal ); - tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal ); - } - - tess.xyz[baseVertex + j][0] = tempVert[0]; - tess.xyz[baseVertex + j][1] = tempVert[1]; - tess.xyz[baseVertex + j][2] = tempVert[2]; - - tess.normal[baseVertex + j][0] = tempNormal[0]; - tess.normal[baseVertex + j][1] = tempNormal[1]; - tess.normal[baseVertex + j][2] = tempNormal[2]; - - tess.texCoords[baseVertex + j][0][0] = v->texCoords[0]; - tess.texCoords[baseVertex + j][0][1] = v->texCoords[1]; - + + // + // deform the vertexes by the lerped bones + // + numVerts = surface->numVerts; // FIXME // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left // in for reference. - //v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights] + 12 ); - v = (md4Vertex_t *)&v->weights[v->numWeights]; + //v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts + 12); + v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts); + for ( j = 0; j < numVerts; j++ ) { + vec4_t tempVert, tempNormal; + md4Weight_t *w; + + VectorClear( tempVert ); tempVert[3] = 0.0; + VectorClear( tempNormal ); tempNormal[3] = 0.0; + w = v->weights; + for ( k = 0 ; k < v->numWeights ; k++, w++ ) { + bone = bonePtr + w->boneIndex; + + tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] ); + tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] ); + tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] ); + + tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal ); + tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal ); + tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal ); + } + + VectorCopy( tempVert, vertexPtr->xyz ); + vertexPtr->fogNum = (float)tess.fogNum; + vertexPtr->tc1[0] = v->texCoords[0]; + vertexPtr->tc1[1] = v->texCoords[1]; + vertexPtr->tc2[0] = v->texCoords[0]; + vertexPtr->tc2[1] = v->texCoords[1]; + VectorCopy( tempNormal, vertexPtr->normal ); + vertexPtr->color[0] = 255; + vertexPtr->color[1] = 255; + vertexPtr->color[2] = 255; + vertexPtr->color[3] = 255; + + vertexPtr++; + + // FIXME + // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left + // in for reference. + //v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights] + 12 ); + v = (md4Vertex_t *)&v->weights[v->numWeights]; + } + } else { + baseVertex = 0; // ERROR } + + if ( mode & TESS_INDEX ) { + GLuint *indexPtr = tess.indexPtr + tess.numIndexes[tess.indexRange]; + triangles = (int *) ((byte *)surface + surface->ofsTriangles); + indexes = surface->numTriangles * 3; + + for (j = 0 ; j < indexes ; j++) { + *indexPtr++ = baseVertex + triangles[j]; + } + } + + tess.numIndexes[tess.indexRange] += surface->numTriangles * 3; tess.numVertexes += surface->numVerts; } @@ -444,8 +456,9 @@ void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) { RB_MDRSurfaceAnim ============== */ -void RB_MDRSurfaceAnim( md4Surface_t *surface ) +void RB_MDRSurfaceAnim( tessMode_t mode, surfaceType_t *surf ) { + md4Surface_t *surface = (md4Surface_t *) surf; int i, j, k; float frontlerp, backlerp; int *triangles; @@ -482,78 +495,79 @@ void RB_MDRSurfaceAnim( md4Surface_t *surface ) oldFrame = (mdrFrame_t *)((byte *)header + header->ofsFrames + backEnd.currentEntity->e.oldframe * frameSize ); - RB_CheckOverflow( surface->numVerts, surface->numTriangles ); - triangles = (int *) ((byte *)surface + surface->ofsTriangles); indexes = surface->numTriangles * 3; baseIndex = tess.numIndexes; baseVertex = tess.numVertexes; - // Set up all triangles. - for (j = 0 ; j < indexes ; j++) - { - tess.indexes[baseIndex + j] = baseVertex + triangles[j]; - } - tess.numIndexes += indexes; - - // - // lerp all the needed bones - // - if ( !backlerp ) - { - // no lerping needed - bonePtr = frame->bones; - } - else - { - bonePtr = bones; - - for ( i = 0 ; i < header->numBones*12 ; i++ ) + if( mode & TESS_VERTEX ) { + // + // lerp all the needed bones + // + if ( !backlerp ) { - ((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i] + backlerp * ((float *)oldFrame->bones)[i]; - } - } - - // - // deform the vertexes by the lerped bones - // - numVerts = surface->numVerts; - v = (mdrVertex_t *) ((byte *)surface + surface->ofsVerts); - for ( j = 0; j < numVerts; j++ ) - { - vec3_t tempVert, tempNormal; - mdrWeight_t *w; - - VectorClear( tempVert ); - VectorClear( tempNormal ); - w = v->weights; - for ( k = 0 ; k < v->numWeights ; k++, w++ ) + // no lerping needed + bonePtr = frame->bones; + } + else { - bone = bonePtr + w->boneIndex; - - tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] ); - tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] ); - tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] ); + bonePtr = bones; - tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal ); - tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal ); - tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal ); + for ( i = 0 ; i < header->numBones*12 ; i++ ) + { + ((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i] + backlerp * ((float *)oldFrame->bones)[i]; + } } - tess.xyz[baseVertex + j][0] = tempVert[0]; - tess.xyz[baseVertex + j][1] = tempVert[1]; - tess.xyz[baseVertex + j][2] = tempVert[2]; + // + // deform the vertexes by the lerped bones + // + numVerts = surface->numVerts; + v = (mdrVertex_t *) ((byte *)surface + surface->ofsVerts); + for ( j = 0; j < numVerts; j++ ) + { + vec3_t tempVert, tempNormal; + mdrWeight_t *w; + + VectorClear( tempVert ); + VectorClear( tempNormal ); + w = v->weights; + for ( k = 0 ; k < v->numWeights ; k++, w++ ) + { + bone = bonePtr + w->boneIndex; + + tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] ); + tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] ); + tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] ); - tess.normal[baseVertex + j][0] = tempNormal[0]; - tess.normal[baseVertex + j][1] = tempNormal[1]; - tess.normal[baseVertex + j][2] = tempNormal[2]; + tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal ); + tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal ); + tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal ); + } + + tess.xyz[baseVertex + j][0] = tempVert[0]; + tess.xyz[baseVertex + j][1] = tempVert[1]; + tess.xyz[baseVertex + j][2] = tempVert[2]; + + tess.normal[baseVertex + j][0] = tempNormal[0]; + tess.normal[baseVertex + j][1] = tempNormal[1]; + tess.normal[baseVertex + j][2] = tempNormal[2]; + + tess.texCoords[baseVertex + j][0][0] = v->texCoords[0]; + tess.texCoords[baseVertex + j][0][1] = v->texCoords[1]; - tess.texCoords[baseVertex + j][0][0] = v->texCoords[0]; - tess.texCoords[baseVertex + j][0][1] = v->texCoords[1]; + v = (mdrVertex_t *)&v->weights[v->numWeights]; + } + } - v = (mdrVertex_t *)&v->weights[v->numWeights]; + if( mode & TESS_INDEX ) { + // Set up all triangles. + for (j = 0 ; j < indexes ; j++) { + tess.indexes[baseIndex + j] = baseVertex + triangles[j]; + } } + tess.numIndexes += indexes; tess.numVertexes += surface->numVerts; } diff --git a/code/renderer/tr_backend.c b/code/renderer/tr_backend.c index 2b630a4..57775f6 100644 --- a/code/renderer/tr_backend.c +++ b/code/renderer/tr_backend.c @@ -24,217 +24,173 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA backEndData_t *backEndData[SMP_FRAMES]; backEndState_t backEnd; - -static float s_flipMatrix[16] = { - // convert from our coordinate system (looking down X) - // to OpenGL's coordinate system (looking down -Z) - 0, 0, -1, 0, - -1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 1 -}; - +static glIndex_t quadIndexes[6] = { 3, 0, 2, 2, 0, 1 }; /* -** GL_Bind +** GL_ActiveTexture */ -void GL_Bind( image_t *image ) { - int texnum; - - if ( !image ) { - ri.Printf( PRINT_WARNING, "GL_Bind: NULL image\n" ); - texnum = tr.defaultImage->texnum; - } else { - texnum = image->texnum; - } - - if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option - texnum = tr.dlightImage->texnum; - } - - if ( glState.currenttextures[glState.currenttmu] != texnum ) { - image->frameUsed = tr.frameCount; - glState.currenttextures[glState.currenttmu] = texnum; - qglBindTexture (GL_TEXTURE_2D, texnum); +void GL_ActiveTexture( int unit ) { + if( qglActiveTextureARB && glState.activeTexture != unit ) { + qglActiveTextureARB( GL_TEXTURE0_ARB + unit ); + glState.activeTexture = unit; } } - -/* -** GL_SelectTexture -*/ -void GL_SelectTexture( int unit ) -{ - if ( glState.currenttmu == unit ) - { - return; +void GL_ClientActiveTexture( int unit ) { + if( qglClientActiveTextureARB && glState.clientActiveTexture != unit ) { + qglClientActiveTextureARB( GL_TEXTURE0_ARB + unit ); + glState.clientActiveTexture = unit; } - - if ( unit == 0 ) - { - qglActiveTextureARB( GL_TEXTURE0_ARB ); - GLimp_LogComment( "glActiveTextureARB( GL_TEXTURE0_ARB )\n" ); - qglClientActiveTextureARB( GL_TEXTURE0_ARB ); - GLimp_LogComment( "glClientActiveTextureARB( GL_TEXTURE0_ARB )\n" ); - } - else if ( unit == 1 ) - { - qglActiveTextureARB( GL_TEXTURE1_ARB ); - GLimp_LogComment( "glActiveTextureARB( GL_TEXTURE1_ARB )\n" ); - qglClientActiveTextureARB( GL_TEXTURE1_ARB ); - GLimp_LogComment( "glClientActiveTextureARB( GL_TEXTURE1_ARB )\n" ); - } else { - ri.Error( ERR_DROP, "GL_SelectTexture: unit = %i", unit ); - } - - glState.currenttmu = unit; } - /* -** GL_BindMultitexture +** GL_BindTexture +** +** binds a texture to texture unit 0 for texture manipulation. +** This is called by the frontend, so it may use a separate context for SMP, +** in which case the glState must not be changed ! */ -void GL_BindMultitexture( image_t *image0, GLuint env0, image_t *image1, GLuint env1 ) { - int texnum0, texnum1; - - texnum0 = image0->texnum; - texnum1 = image1->texnum; - - if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option - texnum0 = texnum1 = tr.dlightImage->texnum; - } - - if ( glState.currenttextures[1] != texnum1 ) { - GL_SelectTexture( 1 ); - image1->frameUsed = tr.frameCount; - glState.currenttextures[1] = texnum1; - qglBindTexture( GL_TEXTURE_2D, texnum1 ); - } - if ( glState.currenttextures[0] != texnum0 ) { - GL_SelectTexture( 0 ); - image0->frameUsed = tr.frameCount; - glState.currenttextures[0] = texnum0; - qglBindTexture( GL_TEXTURE_2D, texnum0 ); +void GL_BindTexture( int texnum ) { + if( !GLimp_InBackend() ) { + qglBindTexture( GL_TEXTURE_2D, texnum ); + } else if ( glState.currenttextures[0] != texnum ) { + GL_ActiveTexture( 0 ); + glState.currenttextures[0] = texnum; + qglBindTexture( GL_TEXTURE_2D, texnum ); } } - /* -** GL_Cull +** GL_UnbindAllTextures +** +** unbind all texture units for renderer cleanup. */ -void GL_Cull( int cullType ) { - if ( glState.faceCulling == cullType ) { - return; - } +void GL_UnbindAllTextures( void ) { + int i; - glState.faceCulling = cullType; - - if ( cullType == CT_TWO_SIDED ) - { - qglDisable( GL_CULL_FACE ); - } - else - { - qglEnable( GL_CULL_FACE ); + for( i = 0; i < MAX_SHADER_STAGES; i++ ) { + if( i >= glGlobals.maxTextureImageUnits ) + break; - if ( cullType == CT_BACK_SIDED ) - { - if ( backEnd.viewParms.isMirror ) - { - qglCullFace( GL_FRONT ); - } - else - { - qglCullFace( GL_BACK ); - } - } - else - { - if ( backEnd.viewParms.isMirror ) - { - qglCullFace( GL_BACK ); - } - else - { - qglCullFace( GL_FRONT ); + if( glState.currenttextures[i] ) { + glState.currenttextures[i] = 0; + GL_ActiveTexture( i ); + qglBindTexture( glState.texTargets[i], 0 ); + if( i < NUM_TEXTURE_BUNDLES ) { + glState.texEnabled[i] = qfalse; + qglDisable( GL_TEXTURE_2D ); } } } } /* -** GL_TexEnv +** GL_Bind +** +** bind a list of images to the texture units. For GLSL shader it's not +** required to glEnable them and we may keep unused textures bound, so +** we can avoid to rebind them later. */ -void GL_TexEnv( int env ) -{ - if ( env == glState.texEnv[glState.currenttmu] ) - { - return; +static void GL_BindImages( int count, image_t **images, qboolean isGLSL ) { + int i, texnum; + GLenum target; + + if( !qglActiveTextureARB && count > 1 ) { + ri.Printf( PRINT_WARNING, "GL_BindImages: Multitexturing not enabled\n" ); + count = 1; + } + + for( i = 0; i < count; i++ ) { + if ( !images[i] ) { + ri.Printf( PRINT_WARNING, "GL_BindImages: NULL image\n" ); + target = GL_TEXTURE_2D; + texnum = tr.defaultImage->texnum; + } else { + target = images[i]->target; + texnum = images[i]->texnum; + } + + if ( r_nobind->integer && + tr.dlightImage && + target == GL_TEXTURE_2D ) { // performance evaluation option + texnum = tr.dlightImage->texnum; + } + + if ( glState.texTargets[i] && + glState.texTargets[i] != target ) { + GL_ActiveTexture( i ); + qglBindTexture( glState.texTargets[i], 0 ); + glState.currenttextures[i] = 0; + } + if ( glState.currenttextures[i] != texnum ) { + images[i]->frameUsed = tr.frameCount; + glState.currenttextures[i] = texnum; + glState.texTargets[i] = target; + GL_ActiveTexture( i ); + qglBindTexture( target, texnum ); + } + if( !isGLSL && !glState.texEnabled[i] && + target == GL_TEXTURE_2D ) { + GL_ActiveTexture( i ); + qglEnable( GL_TEXTURE_2D ); + glState.texEnabled[i] = qtrue; + } } - glState.texEnv[glState.currenttmu] = env; - + if( !isGLSL ) { + // have to disable further textures for non-GLSL shaders + for( ; i < NUM_TEXTURE_BUNDLES; i++ ) { + if( i >= glGlobals.maxTextureImageUnits ) + break; - switch ( env ) - { - case GL_MODULATE: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - break; - case GL_REPLACE: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); - break; - case GL_DECAL: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL ); - break; - case GL_ADD: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD ); - break; - default: - ri.Error( ERR_DROP, "GL_TexEnv: invalid env '%d' passed", env ); - break; + if( glState.texEnabled[i] ) { + GL_ActiveTexture( i ); + qglDisable( GL_TEXTURE_2D ); + glState.texEnabled[i] = qfalse; + } + } } } /* -** GL_State -** -** This routine is responsible for setting the most commonly changed state -** in Q3. +================== +SetRenderState + +set all OpenGL state to the values passed in state, avoid calling gl functions +if the state doesn't actually change +================== */ -void GL_State( unsigned long stateBits ) -{ +static void GL_State( unsigned long stateBits ) { unsigned long diff = stateBits ^ glState.glStateBits; - if ( !diff ) - { + if ( !diff ) { return; } // // check depthFunc bits // - if ( diff & GLS_DEPTHFUNC_EQUAL ) - { - if ( stateBits & GLS_DEPTHFUNC_EQUAL ) - { + if ( diff & GLS_DEPTHFUNC_BITS ) { + switch( stateBits & GLS_DEPTHFUNC_BITS ) { + case GLS_DEPTHFUNC_EQUAL: qglDepthFunc( GL_EQUAL ); - } - else - { + break; + case GLS_DEPTHFUNC_ALWAYS: + qglDepthFunc( GL_ALWAYS ); + break; + default: qglDepthFunc( GL_LEQUAL ); + break; } } // // check blend bits // - if ( diff & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) - { + if ( diff & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) { GLenum srcFactor, dstFactor; - if ( stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) - { - switch ( stateBits & GLS_SRCBLEND_BITS ) - { + if ( stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) { + switch ( stateBits & GLS_SRCBLEND_BITS ) { case GLS_SRCBLEND_ZERO: srcFactor = GL_ZERO; break; @@ -262,14 +218,16 @@ void GL_State( unsigned long stateBits ) case GLS_SRCBLEND_ALPHA_SATURATE: srcFactor = GL_SRC_ALPHA_SATURATE; break; + case GLS_DSTBLEND_SRC1_COLOR: + dstFactor = GL_SRC1_COLOR; + break; default: srcFactor = GL_ONE; // to get warning to shut up ri.Error( ERR_DROP, "GL_State: invalid src blend state bits" ); break; } - switch ( stateBits & GLS_DSTBLEND_BITS ) - { + switch ( stateBits & GLS_DSTBLEND_BITS ) { case GLS_DSTBLEND_ZERO: dstFactor = GL_ZERO; break; @@ -300,91 +258,735 @@ void GL_State( unsigned long stateBits ) break; } - qglEnable( GL_BLEND ); - qglBlendFunc( srcFactor, dstFactor ); - } - else - { - qglDisable( GL_BLEND ); - } + qglEnable( GL_BLEND ); + qglBlendFunc( srcFactor, dstFactor ); + } + else + { + qglDisable( GL_BLEND ); + } + } + + // + // check depthmask + // + if ( diff & GLS_DEPTHMASK_TRUE ) { + if ( stateBits & GLS_DEPTHMASK_TRUE ) { + qglDepthMask( GL_TRUE ); + } else { + qglDepthMask( GL_FALSE ); + } + } + + // + // check colormask + // + if ( diff & GLS_COLORMASK_FALSE ) { + if ( stateBits & GLS_COLORMASK_FALSE ) { + qglColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); + } else { + qglColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); + } + } + + // check polygon offset + if ( diff & GLS_POLYGON_OFFSET ) { + if ( stateBits & GLS_POLYGON_OFFSET ) { + qglEnable( GL_POLYGON_OFFSET_FILL ); + qglPolygonOffset( r_offsetFactor->value, r_offsetUnits->value ); + } else { + qglDisable( GL_POLYGON_OFFSET_FILL ); + } + } + // + // fill/line mode + // + if ( diff & GLS_POLYMODE_LINE ) { + if ( stateBits & GLS_POLYMODE_LINE ) { + qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + } else { + qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + } + } + + // + // depthtest + // + if ( diff & GLS_DEPTHTEST_DISABLE ) { + if ( stateBits & GLS_DEPTHTEST_DISABLE ) { + qglDisable( GL_DEPTH_TEST ); + } else { + qglEnable( GL_DEPTH_TEST ); + } + } + + // + // depth range + // + if ( diff & GLS_DEPTHRANGE_BITS ) { + switch ( stateBits & GLS_DEPTHRANGE_BITS ) { + case GLS_DEPTHRANGE_0_TO_1: + qglDepthRange( 0.0f, 1.0f ); + break; + case GLS_DEPTHRANGE_0_TO_0: + qglDepthRange( 0.0f, 0.0f ); + break; + case GLS_DEPTHRANGE_1_TO_1: + qglDepthRange( 1.0f, 1.0f ); + break; + case GLS_DEPTHRANGE_0_TO_03: + qglDepthRange( 0.0f, 0.3f ); + break; + } + } + + // + // alpha test + // + if ( diff & GLS_ATEST_BITS ) { + switch ( stateBits & GLS_ATEST_BITS ) { + case 0: + qglDisable( GL_ALPHA_TEST ); + break; + case GLS_ATEST_GT_0: + qglEnable( GL_ALPHA_TEST ); + qglAlphaFunc( GL_GREATER, 0.0f ); + break; + case GLS_ATEST_LT_80: + qglEnable( GL_ALPHA_TEST ); + qglAlphaFunc( GL_LESS, 0.5f ); + break; + case GLS_ATEST_GE_80: + qglEnable( GL_ALPHA_TEST ); + qglAlphaFunc( GL_GEQUAL, 0.5f ); + break; + default: + assert( 0 ); + break; + } + } + + glState.glStateBits = stateBits; +} +static void GL_Cull( int cullType ) { + if ( glState.faceCulling == cullType ) { + return; + } + + glState.faceCulling = cullType; + + if ( cullType == CT_TWO_SIDED ) + { + qglDisable( GL_CULL_FACE ); + } + else + { + qglEnable( GL_CULL_FACE ); + + if ( cullType == CT_BACK_SIDED ) + { + if ( backEnd.viewParms.isMirror ) + { + qglCullFace( GL_FRONT ); + } + else + { + qglCullFace( GL_BACK ); + } + } + else + { + if ( backEnd.viewParms.isMirror ) + { + qglCullFace( GL_BACK ); + } + else + { + qglCullFace( GL_FRONT ); + } + } + } +} +void GL_Program( GLSLprogram_t *program ) +{ + if ( glState.currentProgram != program ) { + glState.currentProgram = program; + qglUseProgram( program ? program->handle : 0 ); + } +} +static void DisableAttributePointer( int attr ) { + switch( attr ) { + case AL_VERTEX: + qglDisableClientState( GL_VERTEX_ARRAY ); + break; + case AL_NORMAL: + qglDisableClientState( GL_NORMAL_ARRAY ); + break; + case AL_COLOR: + qglDisableClientState( GL_COLOR_ARRAY ); + break; + case AL_TEXCOORD: + case AL_TEXCOORD2: + case AL_TEXCOORD3: + case AL_TEXCOORD4: + GL_ClientActiveTexture( attr - AL_TEXCOORD ); + qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); + break; + default: + qglDisableVertexAttribArrayARB( attr ); + break; + } +} +static void EnableAttributePointer( int attr ) { + switch( attr ) { + case AL_VERTEX: + qglEnableClientState( GL_VERTEX_ARRAY ); + break; + case AL_NORMAL: + qglEnableClientState( GL_NORMAL_ARRAY ); + break; + case AL_COLOR: + qglEnableClientState( GL_COLOR_ARRAY ); + break; + case AL_TEXCOORD: + case AL_TEXCOORD2: + case AL_TEXCOORD3: + case AL_TEXCOORD4: + GL_ClientActiveTexture( attr - AL_TEXCOORD ); + qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); + break; + default: + qglEnableVertexAttribArrayARB( attr ); + break; + } +} +static void SetAttribute4f( int attr, vec_t *values ) { + switch( attr ) { + case AL_VERTEX: + qglVertex4fv( values ); + break; + case AL_NORMAL: + qglNormal3fv( values ); + break; + case AL_COLOR: + qglColor4fv( values ); + break; + case AL_TEXCOORD: + qglTexCoord4fv( values ); + break; + case AL_TEXCOORD2: + case AL_TEXCOORD3: + case AL_TEXCOORD4: + if( qglMultiTexCoord4fvARB ) + qglMultiTexCoord4fvARB( attr - AL_TEXCOORD + GL_TEXTURE0_ARB, values ); + break; + default: + qglVertexAttrib4fvARB( attr, values ); + break; + } + glState.glAttribute[attr].attrType = RA_VEC; + glState.glAttribute[attr].vec[0] = values[0]; + glState.glAttribute[attr].vec[1] = values[1]; + glState.glAttribute[attr].vec[2] = values[2]; + glState.glAttribute[attr].vec[3] = values[3]; +} +static void SetAttributePointer( int attr, GLuint VBO, GLint size, + GLenum type, GLsizei stride, void *ptr ) { + GL_VBO( VBO ); + switch( attr ) { + case AL_VERTEX: + qglVertexPointer( size, type, stride, ptr ); + break; + case AL_NORMAL: + qglNormalPointer( type, stride, ptr ); + break; + case AL_COLOR: + qglColorPointer( size, type, stride, ptr ); + break; + case AL_TEXCOORD: + case AL_TEXCOORD2: + case AL_TEXCOORD3: + case AL_TEXCOORD4: + GL_ClientActiveTexture( attr - AL_TEXCOORD ); + qglTexCoordPointer( size, type, stride, ptr ); + break; + default: + qglVertexAttribPointerARB( attr, size, type, GL_FALSE, stride, ptr ); + break; + } + glState.glAttribute[attr].attrType = RA_POINTER; + glState.glAttribute[attr].VBO = VBO; + glState.glAttribute[attr].ptr = ptr; + glState.glAttribute[attr].size = size; + glState.glAttribute[attr].type = type; + glState.glAttribute[attr].stride = stride; +} +static void SetAttributeDivisor( int attr, GLuint divisor ) { + switch( attr ) { + case AL_VERTEX: + case AL_NORMAL: + case AL_COLOR: + case AL_TEXCOORD: + case AL_TEXCOORD2: + case AL_TEXCOORD3: + case AL_TEXCOORD4: + // ignore non-generic attributes + break; + default: + qglVertexAttribDivisorARB( attr, divisor ); + break; + } + glState.glAttribute[attr].divisor = divisor; +} +static void SetRenderPointers( glRenderState_t *state ) { + int i; + unsigned int attributes; + + if( state->program ) + attributes = state->program->attributes; + else + attributes = (1 << AL_VERTEX) | (1 << AL_NORMAL) | + (1 << AL_COLOR) | (1 << AL_TEXCOORD) | + (1 << AL_TEXCOORD2) | (1 << AL_TEXCOORD3) | + (1 << AL_TEXCOORD4); + + // NVIDIA recommends to set the glVertexPointer last + for( i = AL_NUMATTRIBUTES-1; i >= 0; i-- ) { + if( state->attrib[i].attrType == RA_UNSPEC || + !(attributes & (1 << i)) ) { + if( glState.glAttribute[i].attrType == RA_POINTER ) { + // diable pointer for unspecified attrs, + // otherwise OpenGL may segfault + DisableAttributePointer( i ); + glState.glAttribute[i].attrType = RA_UNSPEC; + } + } else if( state->attrib[i].attrType == RA_VEC ) { + if( glState.glAttribute[i].attrType == RA_POINTER ) { + DisableAttributePointer( i ); + SetAttribute4f( i, state->attrib[i].vec ); + } else if( glState.glAttribute[i].attrType == RA_VEC && + glState.glAttribute[i].vec[0] == state->attrib[i].vec[0] && + glState.glAttribute[i].vec[1] == state->attrib[i].vec[1] && + glState.glAttribute[i].vec[2] == state->attrib[i].vec[2] && + glState.glAttribute[i].vec[3] == state->attrib[i].vec[3] ) { + // do nothing, unchanged attribute + } else { + SetAttribute4f( i, state->attrib[i].vec ); + } + } else { + if( glState.glAttribute[i].attrType == RA_POINTER ) { + if( glState.glAttribute[i].VBO != state->attrib[i].VBO || + glState.glAttribute[i].ptr != state->attrib[i].ptr || + glState.glAttribute[i].size != state->attrib[i].size || + glState.glAttribute[i].type != state->attrib[i].type || + glState.glAttribute[i].stride != state->attrib[i].stride ) { + // pointer or VBO changed + SetAttributePointer( i, state->attrib[i].VBO, + state->attrib[i].size, + state->attrib[i].type, + state->attrib[i].stride, + state->attrib[i].ptr ); + } + } else { + EnableAttributePointer( i ); + SetAttributePointer( i, state->attrib[i].VBO, + state->attrib[i].size, + state->attrib[i].type, + state->attrib[i].stride, + state->attrib[i].ptr ); + } + if( state->attrib[i].divisor != glState.glAttribute[i].divisor ) { + SetAttributeDivisor( i, state->attrib[i].divisor ); + } + } + } +} +static void SetRenderState( glRenderState_t *state ) { + if(backEnd.currentEntity && + (backEnd.currentEntity->e.renderfx & RF_DEPTHHACK) ) { + if( (state->stateBits & GLS_DEPTHRANGE_BITS) == GLS_DEPTHRANGE_0_TO_1 ) + state->stateBits |= GLS_DEPTHRANGE_0_TO_03; + } + + GL_Program( state->program ); + GL_State( state->stateBits ); + GL_Cull( state->faceCulling ); + GL_BindImages( state->numImages, state->image, state->program != NULL ); + SetRenderPointers( state ); +} + +void GL_LockArrays( glRenderState_t *state ) { + SetRenderPointers( state ); + if( tess.numIndexes[2] > 0 ) + qglLockArraysEXT( tess.minIndex[1], tess.maxIndex[2] - tess.minIndex[1] + 1 ); + else + qglLockArraysEXT( tess.minIndex[1], tess.maxIndex[1] - tess.minIndex[1] + 1 ); +} + +void GL_UnlockArrays( void ) { + qglUnlockArraysEXT(); +} + +void GL_StartQuery( GLuint query, GLuint *result ) { + if( !(*result & QUERY_RUNNING_BIT) ) + qglBeginQueryARB( GL_SAMPLES_PASSED_ARB, query ); +} +void GL_EndQuery( GLuint query, GLuint *result ) { + if( !(*result & QUERY_RUNNING_BIT) ) + qglEndQueryARB( GL_SAMPLES_PASSED_ARB ); + *result |= QUERY_RUNNING_BIT; +} +void GL_GetQuery( GLuint query, GLuint *result ) { + GLuint available; + + if( *result & QUERY_RUNNING_BIT ) { + qglGetQueryObjectuivARB( query, + GL_QUERY_RESULT_AVAILABLE_ARB, + &available); + if ( available ) { + qglGetQueryObjectuivARB( query, + GL_QUERY_RESULT_ARB, + result); + + if( *result & QUERY_RUNNING_BIT ) + *result = QUERY_MASK; // overflow + } + } +} + +void GL_DrawElements( glRenderState_t *state, + int numIndexes, GLuint IBO, + const glIndex_t *indexes, + glIndex_t start, glIndex_t end ) { + if( numIndexes <= 0 ) + return; + + SetRenderState( state ); + GL_IBO( IBO ); + + if ( qglDrawRangeElementsEXT ) + qglDrawRangeElementsEXT( GL_TRIANGLES, + start, end, + numIndexes, + GL_INDEX_TYPE, + indexes ); + else + qglDrawElements( GL_TRIANGLES, + numIndexes, + GL_INDEX_TYPE, + indexes ); +} +void GL_DrawInstanced( glRenderState_t *state, + int numIndexes, GLuint IBO, + const glIndex_t *indexes, + glIndex_t start, glIndex_t end, + int instances ) { + if( numIndexes <= 0 ) + return; + + SetRenderState( state ); + GL_IBO( IBO ); + + qglDrawElementsInstancedARB( GL_TRIANGLES, + numIndexes, + GL_INDEX_TYPE, + indexes, + instances ); +} +void GL_DrawArrays( glRenderState_t *state, + GLenum mode, GLint first, GLuint count ) { + SetRenderState( state ); + + qglDrawArrays( mode, first, count ); +} + + +/* +** GL_TexEnv +*/ +void GL_TexEnv( int tmu, int env ) +{ + if ( env == glState.texEnv[tmu] ) + { + return; + } + + GL_ActiveTexture( tmu ); + glState.texEnv[tmu] = env; + + switch ( env ) + { + case GL_MODULATE: + qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + break; + case GL_REPLACE: + qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + break; + case GL_DECAL: + qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL ); + break; + case GL_ADD: + qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD ); + break; + default: + ri.Error( ERR_DROP, "GL_TexEnv: invalid env '%d' passed\n", env ); + break; + } +} + +/* +** GL_State +** +** This routine is responsible for setting the most commonly changed state +** in Q3. +*/ +void GL_VBO( GLuint vbo ) +{ + if ( glState.currentVBO != vbo ) { + glState.currentVBO = vbo; + qglBindBufferARB (GL_ARRAY_BUFFER_ARB, vbo ); + } +} +void GL_IBO( GLuint ibo ) +{ + if ( glState.currentIBO != ibo ) { + glState.currentIBO = ibo; + qglBindBufferARB (GL_ELEMENT_ARRAY_BUFFER_ARB, ibo ); + } +} +void GL_UBO( GLuint ubo ) +{ + if ( glState.currentUBO != ubo ) { + glState.currentUBO = ubo; + qglBindBufferARB (GL_UNIFORM_BUFFER, ubo ); + } +} + +// simple, fast allocator for the backend thread +// memory must be freed in LIFO order +static byte *scratchStart, *scratchPtr; +static size_t freeScratch; +void RB_InitScratchMemory( void ) { + freeScratch = r_scratchmegs->integer; + if( freeScratch <= SMP_SCRATCHMEGS ) + freeScratch = SMP_SCRATCHMEGS; + freeScratch *= 1024 * 1024; + + scratchStart = scratchPtr = ri.Hunk_Alloc( freeScratch, h_low ); +} +void *RB_AllocScratch( size_t amount ) { + byte *mem = scratchPtr; + + amount = (amount + 31) & -32; + if( amount > freeScratch ) { + ri.Error( ERR_DROP, "RB_AllocScratch: out of scratch memory, try to increase /r_scratchmegs\n" ); } + scratchPtr += amount; + freeScratch -= amount; + return mem; +} +void RB_FreeScratch( void *ptr ) { + if( (byte *)ptr < scratchStart || (byte *)ptr > scratchPtr ) { + ri.Error( ERR_DROP, "RB_FreeScratch: bad pointer\n" ); + } + freeScratch += (scratchPtr - (byte *)ptr); + scratchPtr = ptr; +} - // - // check depthmask - // - if ( diff & GLS_DEPTHMASK_TRUE ) - { - if ( stateBits & GLS_DEPTHMASK_TRUE ) - { - qglDepthMask( GL_TRUE ); - } - else - { - qglDepthMask( GL_FALSE ); +GLSLshader_t *RB_CompileShader( const char *name, GLenum type, + const char **code, int parts ) { + GLSLshader_t *shader; + GLint status; + GLint i, j; + unsigned int hash; + const char *ptr; + + // try to reuse existing shader + for( i = 0, hash = 0; i < parts; i++ ) { + for( ptr = code[i]; *ptr; ptr++ ) { + hash = *ptr + hash * 65599; } } + + for( i = 0; i < tr.numGLSLshaders; i++ ) { + if( tr.GLSLshaders[i]->hash == hash ) { + GLint length; + char *source, *sourcePtr; + qboolean same = qtrue; + + qglGetShaderiv( tr.GLSLshaders[i]->handle, + GL_SHADER_SOURCE_LENGTH, + &length ); + sourcePtr = source = ri.Hunk_AllocateTempMemory( length + 1 ); + qglGetShaderSource( tr.GLSLshaders[i]->handle, length + 1, + &length, source ); + + for( j = 0; j < parts; j++ ) { + for( ptr = code[j]; *ptr; ptr++, sourcePtr++ ) { + if( *ptr != *sourcePtr ) + break; + } + } + same = *sourcePtr == '\0'; + ri.Hunk_FreeTempMemory( source ); - // - // fill/line mode - // - if ( diff & GLS_POLYMODE_LINE ) - { - if ( stateBits & GLS_POLYMODE_LINE ) - { - qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); - } - else - { - qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + if( same ) + return tr.GLSLshaders[i]; } } - // - // depthtest - // - if ( diff & GLS_DEPTHTEST_DISABLE ) - { - if ( stateBits & GLS_DEPTHTEST_DISABLE ) - { - qglDisable( GL_DEPTH_TEST ); + shader = ri.Hunk_Alloc( sizeof(GLSLprogram_t), h_low ); + shader->handle = qglCreateShader( type ); + shader->hash = hash; + qglShaderSource( shader->handle, parts, code, NULL ); + qglCompileShader( shader->handle ); + qglGetShaderiv( shader->handle, GL_OBJECT_COMPILE_STATUS_ARB, &status ); + if( !status ) { + char *log; + GLint len; + qglGetShaderiv( shader->handle, GL_OBJECT_INFO_LOG_LENGTH_ARB, &len ); + log = ri.Hunk_AllocateTempMemory( len + 1 ); + qglGetShaderInfoLog( shader->handle, len + 1, &len, log ); + + ri.Printf( PRINT_WARNING, "compile shader (%s) error: %s\n", + name, log ); + while( parts > 0 ) { + ri.Printf( PRINT_WARNING, "%s", *(code++) ); + parts--; } - else - { - qglEnable( GL_DEPTH_TEST ); + + ri.Hunk_FreeTempMemory( log ); + qglDeleteShader( shader->handle ); + return NULL; + } + tr.GLSLshaders[tr.numGLSLshaders++] = shader; + return shader; +} + +GLSLprogram_t *RB_CompileProgram( const char *name, + const char **VScode, int VSparts, + const char **GScode, int GSparts, + int nVerticesOut, int inType, int outType, + const char **FScode, int FSparts, + unsigned int attributes ) { + GLSLshader_t *VertexShader = NULL; + GLSLshader_t *GeometryShader = NULL; + GLSLshader_t *FragmentShader = NULL; + GLint Program; + GLint i; + GLSLprogram_t *newProgram; + + // find shaders + if( VSparts > 0 ) { + VertexShader = RB_CompileShader( name, GL_VERTEX_SHADER_ARB, + VScode, VSparts ); + if( !VertexShader ) + return NULL; // compilation error + } + if( GSparts > 0 ) { + GeometryShader = RB_CompileShader( name, GL_GEOMETRY_SHADER_EXT, + GScode, GSparts ); + if( !GeometryShader ) + return NULL; // compilation error + } + if( FSparts > 0 ) { + FragmentShader = RB_CompileShader( name, GL_FRAGMENT_SHADER_ARB, + FScode, FSparts ); + if( !FragmentShader ) + return NULL; // compilation error + } + + // try to reuse existing program + for( i = 0; i < tr.numGLSLprograms; i++ ) { + if( tr.GLSLprograms[i]->vertex == VertexShader && + tr.GLSLprograms[i]->geometry == GeometryShader && + tr.GLSLprograms[i]->fragment == FragmentShader ) { + return tr.GLSLprograms[i]; } } - // - // alpha test - // - if ( diff & GLS_ATEST_BITS ) - { - switch ( stateBits & GLS_ATEST_BITS ) - { - case 0: - qglDisable( GL_ALPHA_TEST ); - break; - case GLS_ATEST_GT_0: - qglEnable( GL_ALPHA_TEST ); - qglAlphaFunc( GL_GREATER, 0.0f ); - break; - case GLS_ATEST_LT_80: - qglEnable( GL_ALPHA_TEST ); - qglAlphaFunc( GL_LESS, 0.5f ); - break; - case GLS_ATEST_GE_80: - qglEnable( GL_ALPHA_TEST ); - qglAlphaFunc( GL_GEQUAL, 0.5f ); - break; - default: - assert( 0 ); - break; + Program = qglCreateProgram(); + if( VertexShader ) + qglAttachShader( Program, VertexShader->handle ); + if( GeometryShader ) { + qglAttachShader( Program, GeometryShader->handle ); + + qglProgramParameteriEXT( Program, GL_GEOMETRY_VERTICES_OUT_EXT, nVerticesOut ); + qglProgramParameteriEXT( Program, GL_GEOMETRY_INPUT_TYPE_EXT, inType ); + qglProgramParameteriEXT( Program, GL_GEOMETRY_OUTPUT_TYPE_EXT, outType ); + } + if( FragmentShader ) + qglAttachShader( Program, FragmentShader->handle ); + + if( attributes & (1 << AL_COLOR2) ) + qglBindAttribLocationARB( Program, AL_COLOR2, "aEntColor" ); + if( attributes & (1 << AL_CAMERAPOS) ) + qglBindAttribLocationARB( Program, AL_CAMERAPOS, "aCameraPos" ); + if( attributes & (1 << AL_TIMES) ) + qglBindAttribLocationARB( Program, AL_TIMES, "aTimes" ); + if( attributes & (1 << AL_TRANSX) ) + qglBindAttribLocationARB( Program, AL_TRANSX, "aTransX" ); + if( attributes & (1 << AL_TRANSY) ) + qglBindAttribLocationARB( Program, AL_TRANSY, "aTransY" ); + if( attributes & (1 << AL_TRANSZ) ) + qglBindAttribLocationARB( Program, AL_TRANSZ, "aTransZ" ); + if( attributes & (1 << AL_AMBIENTLIGHT) ) + qglBindAttribLocationARB( Program, AL_AMBIENTLIGHT, "aAmbientLight" ); + if( attributes & (1 << AL_DIRECTEDLIGHT) ) + qglBindAttribLocationARB( Program, AL_DIRECTEDLIGHT, "aDirectedLight" ); + if( attributes & (1 << AL_LIGHTDIR) ) + qglBindAttribLocationARB( Program, AL_LIGHTDIR, "aLightDir" ); + + if( qglBindFragDataLocationIndexed ) { + qglBindFragDataLocationIndexed( Program, 0, 0, "dstColorAdd" ); + qglBindFragDataLocationIndexed( Program, 0, 1, "dstColorMult" ); + } + + qglLinkProgram( Program ); + qglGetProgramiv( Program, GL_OBJECT_LINK_STATUS_ARB, &i ); + if ( !i ) { + char *log; + qglGetProgramiv( Program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &i ); + log = ri.Hunk_AllocateTempMemory( i + 1 ); + qglGetProgramInfoLog( Program, i + 1, &i, log ); + + ri.Printf( PRINT_WARNING, "link shader %s error: %s\n", name, log ); + while( VSparts > 0 ) { + ri.Printf( PRINT_WARNING, "%s", *(VScode++) ); + VSparts--; } + while( GSparts > 0 ) { + ri.Printf( PRINT_WARNING, "%s", *(GScode++) ); + GSparts--; + } + while( FSparts > 0 ) { + ri.Printf( PRINT_WARNING, "%s", *(FScode++) ); + FSparts--; + } + + ri.Hunk_FreeTempMemory( log ); + qglDeleteProgram( Program ); + if( FragmentShader ) + qglDeleteShader( FragmentShader->handle ); + if( GeometryShader ) + qglDeleteShader( GeometryShader->handle ); + if( VertexShader ) + qglDeleteShader( VertexShader->handle ); + return NULL; } - glState.glStateBits = stateBits; -} + newProgram = ri.Hunk_Alloc( sizeof(GLSLprogram_t), h_low ); + tr.GLSLprograms[tr.numGLSLprograms++] = newProgram; + + newProgram->handle = Program; + newProgram->vertex = VertexShader; + newProgram->geometry = GeometryShader; + newProgram->fragment = FragmentShader; + newProgram->attributes = attributes; + + return newProgram; +} /* @@ -410,8 +1012,29 @@ static void RB_Hyperspace( void ) { static void SetViewportAndScissor( void ) { + float mat[16], scale; + vec4_t q, c; + + Com_Memcpy( mat, backEnd.viewParms.projectionMatrix, sizeof(mat) ); + if( backEnd.viewParms.portalLevel ) { + c[0] = -DotProduct( backEnd.viewParms.portalPlane.normal, backEnd.viewParms.or.axis[1] ); + c[1] = DotProduct( backEnd.viewParms.portalPlane.normal, backEnd.viewParms.or.axis[2] ); + c[2] = -DotProduct( backEnd.viewParms.portalPlane.normal, backEnd.viewParms.or.axis[0] ); + c[3] = DotProduct( backEnd.viewParms.portalPlane.normal, backEnd.viewParms.or.origin ) - backEnd.viewParms.portalPlane.dist; + + q[0] = (c[0] < 0.0f ? -1.0f : 1.0f) / mat[0]; + q[1] = (c[1] < 0.0f ? -1.0f : 1.0f) / mat[5]; + q[2] = -1.0f; + q[3] = (1.0f + mat[10]) / mat[14]; + + scale = 2.0f / (DotProduct( c, q ) + c[3] * q[3]); + mat[2] = c[0] * scale; + mat[6] = c[1] * scale; + mat[10] = c[2] * scale + 1.0f; + mat[14] = c[3] * scale; + } qglMatrixMode(GL_PROJECTION); - qglLoadMatrixf( backEnd.viewParms.projectionMatrix ); + qglLoadMatrixf( mat ); qglMatrixMode(GL_MODELVIEW); // set the window clipping @@ -445,30 +1068,35 @@ void RB_BeginDrawingView (void) { // 2D images again backEnd.projection2D = qfalse; + // + // set the modelview matrix for the viewer + // + SetViewportAndScissor(); + // ensures that depth writes are enabled for the depth clear GL_State( GLS_DEFAULT ); // clear relevant buffers - clearBits = GL_DEPTH_BUFFER_BIT; - - if ( r_measureOverdraw->integer || r_shadows->integer == 2 ) - { - clearBits |= GL_STENCIL_BUFFER_BIT; - } - if ( r_fastsky->integer && !( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) - { - clearBits |= GL_COLOR_BUFFER_BIT; // FIXME: only if sky shaders have been used + if ( backEnd.viewParms.isFirst ) { + clearBits = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; + + if ( r_clear->integer ) { + clearBits |= GL_COLOR_BUFFER_BIT; + qglClearColor( 1.0f, 0.0f, 0.5f, 1.0f ); + } + if ( r_fastsky->integer && !( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) + { + clearBits |= GL_COLOR_BUFFER_BIT; // FIXME: only if sky shaders have been used #ifdef _DEBUG - qglClearColor( 0.8f, 0.7f, 0.4f, 1.0f ); // FIXME: get color of sky + qglClearColor( 0.8f, 0.7f, 0.4f, 1.0f ); // FIXME: get color of sky #else - qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); // FIXME: get color of sky + qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); // FIXME: get color of sky #endif - } - qglClear( clearBits ); + } + qglStencilMask( glGlobals.portalMask | glGlobals.shadowMask ); + qglClear( clearBits ); - // - // set the modelview matrix for the viewer - // - SetViewportAndScissor(); + RB_PrepareDLights( &backEnd.refdef ); + } if ( ( backEnd.refdef.rdflags & RDF_HYPERSPACE ) ) { @@ -484,32 +1112,103 @@ void RB_BeginDrawingView (void) { // we will only draw a sun if there was sky rendered in this view backEnd.skyRenderedThisView = qfalse; +} + - // clip to the plane of the portal - if ( backEnd.viewParms.isPortal ) { - float plane[4]; - double plane2[4]; +#define MAC_EVENT_PUMP_MSEC 5 - plane[0] = backEnd.viewParms.portalPlane.normal[0]; - plane[1] = backEnd.viewParms.portalPlane.normal[1]; - plane[2] = backEnd.viewParms.portalPlane.normal[2]; - plane[3] = backEnd.viewParms.portalPlane.dist; - plane2[0] = DotProduct (backEnd.viewParms.or.axis[0], plane); - plane2[1] = DotProduct (backEnd.viewParms.or.axis[1], plane); - plane2[2] = DotProduct (backEnd.viewParms.or.axis[2], plane); - plane2[3] = DotProduct (plane, backEnd.viewParms.or.origin) - plane[3]; +/* +================== +RB_InstanceFromEntity - qglLoadMatrixf( s_flipMatrix ); - qglClipPlane (GL_CLIP_PLANE0, plane2); - qglEnable (GL_CLIP_PLANE0); +Fills the current instance vars from an entity +================== +*/ +static void RB_InstanceFromEntity( trRefEntity_t *ent ) { + instanceVars_t *inst = &tess.instances[ tess.numInstances - 1 ]; + + inst->times[0] = tess.shaderTime; + if( tess.dataTexture ) { + inst->times[1] = ent->e.backlerp; + inst->times[2] = (ent->e.frame / tess.framesPerRow) * tess.scaleY + + (ent->e.frame % tess.framesPerRow) * tess.scaleX; + inst->times[3] = (ent->e.oldframe / tess.framesPerRow) * tess.scaleY + + (ent->e.oldframe % tess.framesPerRow) * tess.scaleX; + } else { + inst->times[1] = 0.0f; + inst->times[2] = 0.0f; + inst->times[3] = 0.0f; + } + if( ent->e.reType == RT_MODEL ) { + inst->transX[0] = ent->e.axis[0][0]; + inst->transX[1] = ent->e.axis[1][0]; + inst->transX[2] = ent->e.axis[2][0]; + inst->transX[3] = ent->e.origin[0]; + inst->transY[0] = ent->e.axis[0][1]; + inst->transY[1] = ent->e.axis[1][1]; + inst->transY[2] = ent->e.axis[2][1]; + inst->transY[3] = ent->e.origin[1]; + inst->transZ[0] = ent->e.axis[0][2]; + inst->transZ[1] = ent->e.axis[1][2]; + inst->transZ[2] = ent->e.axis[2][2]; + inst->transZ[3] = ent->e.origin[2]; + } else { + inst->transX[0] = 1.0f; + inst->transX[1] = 0.0f; + inst->transX[2] = 0.0f; + inst->transX[3] = 0.0f; + inst->transY[0] = 0.0f; + inst->transY[1] = 1.0f; + inst->transY[2] = 0.0f; + inst->transY[3] = 0.0f; + inst->transZ[0] = 0.0f; + inst->transZ[1] = 0.0f; + inst->transZ[2] = 1.0f; + inst->transZ[3] = 0.0f; + } + if( ent->lightingCalculated ) { + inst->lightDir[0] = ent->lightDir[0]; + inst->lightDir[1] = ent->lightDir[1]; + inst->lightDir[2] = ent->lightDir[2]; + inst->lightDir[3] = tr.deluxeOffset; + inst->ambLight[0] = ent->ambientLight[0] / 255.0f; + inst->ambLight[1] = ent->ambientLight[1] / 255.0f; + inst->ambLight[2] = ent->ambientLight[2] / 255.0f; + inst->ambLight[3] = 0.0f; + inst->dirLight[0] = ent->directedLight[0] / 255.0f; + inst->dirLight[1] = ent->directedLight[1] / 255.0f; + inst->dirLight[2] = ent->directedLight[2] / 255.0f; } else { - qglDisable (GL_CLIP_PLANE0); + inst->lightDir[0] = tr.sunDirection[0]; + inst->lightDir[1] = tr.sunDirection[1]; + inst->lightDir[2] = tr.sunDirection[2]; + inst->lightDir[3] = tr.deluxeOffset; + inst->ambLight[0] = 0.0f; + inst->ambLight[1] = 0.0f; + inst->ambLight[2] = 0.0f; + inst->ambLight[3] = 0.0f; + inst->dirLight[0] = 0.0f; + inst->dirLight[1] = 0.0f; + inst->dirLight[2] = 0.0f; } + inst->texCoord[0] = ent->e.shaderTexCoord[0]; + inst->texCoord[1] = ent->e.shaderTexCoord[1]; + inst->texCoord[2] = tess.fogNum; + inst->texCoord[3] = 0.0f; + inst->color[0] = ent->e.shaderRGBA[0]; + inst->color[1] = ent->e.shaderRGBA[1]; + inst->color[2] = ent->e.shaderRGBA[2]; + inst->color[3] = ent->e.shaderRGBA[3]; } - -#define MAC_EVENT_PUMP_MSEC 5 +static ID_INLINE trRefEntity_t *getEntity( int entityNum ) { + if ( entityNum != ENTITYNUM_WORLD ) { + return &backEnd.refdef.entities[entityNum]; + } else { + return &tr.worldEntity; + } +} /* ================== @@ -517,15 +1216,17 @@ RB_RenderDrawSurfList ================== */ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { - shader_t *shader, *oldShader; - int fogNum, oldFogNum; - int entityNum, oldEntityNum; - int dlighted, oldDlighted; - qboolean depthRange, oldDepthRange, isCrosshair, wasCrosshair; - int i; - drawSurf_t *drawSurf; - int oldSort; - float originalTime; + shader_t *shader, *oldShader; + int fogNum, oldFogNum; + int entityNum, oldEntityNum; + int dlighted, oldDlighted; + qboolean depthRange, oldDepthRange, isCrosshair, wasCrosshair, worldMatrix; + int i, j; + int startBatch, startMixed, startLight, endBatch; + int oldSort; + float originalTime; + qboolean isGLSL = qfalse; + trRefEntity_t *ent; // save original time for entity shader offsets originalTime = backEnd.refdef.floatTime; @@ -535,7 +1236,7 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { // draw everything oldEntityNum = -1; - backEnd.currentEntity = &tr.worldEntity; + ent = backEnd.currentEntity = &tr.worldEntity; oldShader = NULL; oldFogNum = -1; oldDepthRange = qfalse; @@ -546,28 +1247,100 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { backEnd.pc.c_surfaces += numDrawSurfs; - for (i = 0, drawSurf = drawSurfs ; i < numDrawSurfs ; i++, drawSurf++) { - if ( drawSurf->sort == oldSort ) { - // fast path, same as previous sort - rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); - continue; - } - oldSort = drawSurf->sort; - R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted ); + qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); + worldMatrix = qtrue; - // - // change the tess parameters if needed - // a "entityMergable" shader is a shader that can have surfaces from seperate - // entities merged into a single batch, like smoke and blood puff sprites - if (shader != oldShader || fogNum != oldFogNum || dlighted != oldDlighted - || ( entityNum != oldEntityNum && !shader->entityMergable ) ) { - if (oldShader != NULL) { - RB_EndSurface(); + for( i = 0; i < numDrawSurfs ; ) { +#ifndef NDEBUG + size_t scratchCheck = freeScratch; +#endif + + // build a batch and count vertices + entityNum = QSORT_ENTITYNUM( drawSurfs[i].sort ); + shader = tr.shaders[ drawSurfs[i].shaderIndex ]; + fogNum = QSORT_FOGNUM( drawSurfs[i].sort ); + dlighted = QSORT_DLIGHT( drawSurfs[i].sort ); + + isGLSL = (shader->optimalStageIteratorFunc == RB_StageIteratorGLSL); + backEnd.currentEntity = ent = getEntity( entityNum ); + + RB_BeginSurface( shader ); + + // the first surface is always part of the batch + oldSort = drawSurfs[i].sort; + tesselate( TESS_COUNT, drawSurfs[i].surface ); + + // set up the initial instance data + tess.numInstances = 1; + RB_InstanceFromEntity( ent ); + + startBatch = i; + startMixed = ( QSORT_FOGNUM( drawSurfs[i].sort ) || + QSORT_DLIGHT( drawSurfs[i].sort ) ) + ? i : -1; + startLight = ( QSORT_DLIGHT( drawSurfs[i].sort ) == 2 ) + ? i : -1; + endBatch = -1; + oldSort = -1; + + // find the end of the current batch + for( i++; i < numDrawSurfs; i++ ) { + if( drawSurfs[i].sort == oldSort ) { + tesselate( TESS_COUNT, drawSurfs[i].surface ); + continue; + } + + if( drawSurfs[i].shaderIndex != shader->index ) + break; + + if( QSORT_ENTITYNUM( drawSurfs[i].sort ) != entityNum + && ent->e.reType != RT_SPRITE ) { + // look for instancing opportunities: + // if the same surfaces are rendered again, + // only the entity is different + qboolean useInstance = qtrue; + int nextEnt = QSORT_ENTITYNUM( drawSurfs[i].sort ); + + if( shader->lightmapIndex != LIGHTMAP_MD3 ) { + useInstance = qfalse; + break; + } + + for( j = 0; j < numDrawSurfs - i; j++ ) { + if( QSORT_ENTITYNUM( drawSurfs[i+j].sort ) != nextEnt ) + break; + if( drawSurfs[i+j].surface != drawSurfs[startBatch+j].surface ) { + useInstance = qfalse; + break; + } + } + if( useInstance ) { + entityNum = QSORT_ENTITYNUM( drawSurfs[i].sort ); + backEnd.currentEntity = ent = getEntity( entityNum ); + + if( endBatch < 0 ) + endBatch = i; + tess.numInstances++; + RB_InstanceFromEntity( ent ); + i += j - 1; + continue; + } + break; + } + + if( startMixed < 0 && + ( QSORT_FOGNUM( drawSurfs[i].sort ) || + QSORT_DLIGHT( drawSurfs[i].sort ) ) ) { + startMixed = i; + } + + if( startLight < 0 && + QSORT_DLIGHT( drawSurfs[i].sort ) == 2 ) { + startLight = i; } - RB_BeginSurface( shader, fogNum ); - oldShader = shader; - oldFogNum = fogNum; - oldDlighted = dlighted; + + oldSort = drawSurfs[i].sort; + tesselate( TESS_COUNT, drawSurfs[i].surface ); } // @@ -577,42 +1350,49 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { depthRange = isCrosshair = qfalse; if ( entityNum != ENTITYNUM_WORLD ) { - backEnd.currentEntity = &backEnd.refdef.entities[entityNum]; - backEnd.refdef.floatTime = originalTime - backEnd.currentEntity->e.shaderTime; + backEnd.refdef.floatTime = originalTime - ent->e.shaderTime; // we have to reset the shaderTime as well otherwise image animations start // from the wrong frame tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; // set up the transformation matrix - R_RotateForEntity( backEnd.currentEntity, &backEnd.viewParms, &backEnd.or ); - + R_RotateForEntity( ent, &backEnd.viewParms, &backEnd.or ); + // set up the dynamic lighting if needed - if ( backEnd.currentEntity->needDlights ) { + if ( ent->needDlights ) { R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or ); } + if( !isGLSL ) { + qglLoadMatrixf( backEnd.or.modelMatrix ); + worldMatrix = qfalse; + + } else if( !worldMatrix ) { + qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); + worldMatrix = qtrue; + } - if(backEnd.currentEntity->e.renderfx & RF_DEPTHHACK) - { + if( ent->e.renderfx & RF_DEPTHHACK ) { // hack the depth range to prevent view model from poking into walls depthRange = qtrue; - if(backEnd.currentEntity->e.renderfx & RF_CROSSHAIR) + if( ent->e.renderfx & RF_CROSSHAIR ) isCrosshair = qtrue; } } else { - backEnd.currentEntity = &tr.worldEntity; backEnd.refdef.floatTime = originalTime; backEnd.or = backEnd.viewParms.world; // we have to reset the shaderTime as well otherwise image animations on // the world (like water) continue with the wrong frame tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; - R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or ); + if( !worldMatrix ) { + qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); + worldMatrix = qtrue; + R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or ); + } } - qglLoadMatrixf( backEnd.or.modelMatrix ); - // - // change depthrange. Also change projection matrix so first person weapon does not look like coming + // change projection matrix so first person weapon does not look like coming // out of the screen. // if (oldDepthRange != depthRange || wasCrosshair != isCrosshair) @@ -642,9 +1422,6 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { qglMatrixMode(GL_MODELVIEW); } } - - if(!oldDepthRange) - qglDepthRange (0, 0.3); } else { @@ -654,8 +1431,6 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { qglLoadMatrixf(backEnd.viewParms.projectionMatrix); qglMatrixMode(GL_MODELVIEW); } - - qglDepthRange (0, 1); } oldDepthRange = depthRange; @@ -664,32 +1439,58 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { oldEntityNum = entityNum; } + if( endBatch < 0 ) + endBatch = i; + if( startLight < 0 ) + startLight = endBatch; + if( startMixed < 0 ) + startMixed = startLight; + + RB_AllocateSurface( ); + for( j = startBatch; j < startMixed; j++ ) { + backEnd.currentEntity = getEntity( QSORT_ENTITYNUM( drawSurfs[j].sort ) ); + tesselate( TESS_ALL, drawSurfs[j].surface ); + } + tess.indexRange = 1; + tess.numIndexes[1] = tess.numIndexes[0]; + for( ; j < startLight; j++ ) { + backEnd.currentEntity = getEntity( QSORT_ENTITYNUM( drawSurfs[j].sort ) ); + tess.fogNum = QSORT_FOGNUM( drawSurfs[j].sort ); + tesselate( TESS_ALL, drawSurfs[j].surface ); + } + tess.indexRange = 2; + tess.numIndexes[2] = tess.numIndexes[1]; + for( ; j < endBatch; j++ ) { + backEnd.currentEntity = getEntity( QSORT_ENTITYNUM( drawSurfs[j].sort ) ); + tess.fogNum = QSORT_FOGNUM( drawSurfs[j].sort ); + tesselate( TESS_ALL, drawSurfs[j].surface ); + } - // add the triangles for this surface - rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); - } + RB_EndSurface(); - backEnd.refdef.floatTime = originalTime; + tess.dataTexture = NULL; + tess.numInstances = 0; - // draw the contents of the last shader batch - if (oldShader != NULL) { - RB_EndSurface(); + assert( scratchCheck == freeScratch ); } + backEnd.refdef.floatTime = originalTime; + // go back to the world modelview matrix - qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); - if ( depthRange ) { - qglDepthRange (0, 1); + if( !worldMatrix ) { + qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); } + if( !backEnd.viewParms.noShadows ) { #if 0 - RB_DrawSun(); + RB_DrawSun(); #endif - // darken down any stencil shadows - RB_ShadowFinish(); + // darken down any stencil shadows + RB_ShadowFinish(); - // add light flares on lights that aren't obscured - RB_RenderFlares(); + // add light flares on lights that aren't obscured + RB_RenderFlares(); + } } @@ -707,8 +1508,9 @@ RB_SetGL2D ================ */ -void RB_SetGL2D (void) { +void RB_SetGL2D ( glRenderState_t *state ) { backEnd.projection2D = qtrue; + backEnd.currentEntity = &backEnd.entity2D; // set 2D virtual screen size qglViewport( 0, 0, glConfig.vidWidth, glConfig.vidHeight ); @@ -719,12 +1521,10 @@ void RB_SetGL2D (void) { qglMatrixMode(GL_MODELVIEW); qglLoadIdentity (); - GL_State( GLS_DEPTHTEST_DISABLE | - GLS_SRCBLEND_SRC_ALPHA | - GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); - - qglDisable( GL_CULL_FACE ); - qglDisable( GL_CLIP_PLANE0 ); + state->stateBits = GLS_DEPTHTEST_DISABLE | + GLS_SRCBLEND_SRC_ALPHA | + GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + state->faceCulling = CT_TWO_SIDED; // set time for 2D shaders backEnd.refdef.time = ri.Milliseconds(); @@ -734,85 +1534,138 @@ void RB_SetGL2D (void) { /* ============= -RE_StretchRaw +RB_StretchRaw -FIXME: not exactly backend Stretches a raw 32 bit power of 2 bitmap image over the given screen rectangle. Used for cinematics. ============= */ -void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { - int i, j; - int start, end; +const void *RB_StretchRaw ( const void *data ) { + const stretchRawCommand_t *cmd; + glRenderState_t state; - if ( !tr.registered ) { - return; - } - R_SyncRenderThread(); + cmd = (const stretchRawCommand_t *)data; // we definately want to sync every frame for the cinematics qglFinish(); - start = 0; - if ( r_speeds->integer ) { - start = ri.Milliseconds(); - } - - // make sure rows and cols are powers of 2 - for ( i = 0 ; ( 1 << i ) < cols ; i++ ) { - } - for ( j = 0 ; ( 1 << j ) < rows ; j++ ) { - } - if ( ( 1 << i ) != cols || ( 1 << j ) != rows) { - ri.Error (ERR_DROP, "Draw_StretchRaw: size not a power of 2: %i by %i", cols, rows); - } - - GL_Bind( tr.scratchImage[client] ); - - // if the scratchImage isn't in the format we want, specify it as a new texture - if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { - tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; - tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; - qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - } else { - if (dirty) { - // otherwise, just subimage upload it so that drivers can tell we are going to be changing - // it and don't try and do a texture compression - qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); - } - } - - if ( r_speeds->integer ) { - end = ri.Milliseconds(); - ri.Printf( PRINT_ALL, "qglTexSubImage2D %i, %i: %i msec\n", cols, rows, end - start ); - } - - RB_SetGL2D(); - - qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight ); - - qglBegin (GL_QUADS); - qglTexCoord2f ( 0.5f / cols, 0.5f / rows ); - qglVertex2f (x, y); - qglTexCoord2f ( ( cols - 0.5f ) / cols , 0.5f / rows ); - qglVertex2f (x+w, y); - qglTexCoord2f ( ( cols - 0.5f ) / cols, ( rows - 0.5f ) / rows ); - qglVertex2f (x+w, y+h); - qglTexCoord2f ( 0.5f / cols, ( rows - 0.5f ) / rows ); - qglVertex2f (x, y+h); - qglEnd (); + RB_SetGL2D( &state ); + RB_BeginSurface( tr.defaultShader ); + + tess.numVertexes = 4; + tess.numIndexes[0] = 6; + + RB_AllocateSurface( ); + + tess.numVertexes = 4; + tess.numIndexes[0] = 6; + tess.minIndex[0] = 0; + tess.maxIndex[0] = 3; + + tess.vertexPtr[0].xyz[0] = cmd->x; + tess.vertexPtr[0].xyz[1] = cmd->y; + tess.vertexPtr[0].xyz[2] = 0.0f; + tess.vertexPtr[0].fogNum = 0.0f; + tess.vertexPtr[0].tc1[0] = cmd->s1; + tess.vertexPtr[0].tc1[1] = cmd->t1; + tess.vertexPtr[0].tc2[0] = cmd->s1; + tess.vertexPtr[0].tc2[1] = cmd->t1; + tess.vertexPtr[0].normal[0] = 0.0f; + tess.vertexPtr[0].normal[1] = 0.0f; + tess.vertexPtr[0].normal[2] = 0.0f; + tess.vertexPtr[0].color[0] = tr.identityLightByte; + tess.vertexPtr[0].color[1] = tr.identityLightByte; + tess.vertexPtr[0].color[2] = tr.identityLightByte; + tess.vertexPtr[0].color[3] = 255; + + tess.vertexPtr[1].xyz[0] = cmd->x + cmd->w; + tess.vertexPtr[1].xyz[1] = cmd->y; + tess.vertexPtr[1].xyz[2] = 0.0f; + tess.vertexPtr[1].fogNum = 0.0f; + tess.vertexPtr[1].tc1[0] = cmd->s2; + tess.vertexPtr[1].tc1[1] = cmd->t1; + tess.vertexPtr[1].tc2[0] = cmd->s2; + tess.vertexPtr[1].tc2[1] = cmd->t1; + tess.vertexPtr[1].normal[0] = 0.0f; + tess.vertexPtr[1].normal[1] = 0.0f; + tess.vertexPtr[1].normal[2] = 0.0f; + tess.vertexPtr[1].color[0] = tr.identityLightByte; + tess.vertexPtr[1].color[1] = tr.identityLightByte; + tess.vertexPtr[1].color[2] = tr.identityLightByte; + tess.vertexPtr[1].color[3] = 255; + + tess.vertexPtr[2].xyz[0] = cmd->x + cmd->w; + tess.vertexPtr[2].xyz[1] = cmd->y + cmd->h; + tess.vertexPtr[2].xyz[2] = 0.0f; + tess.vertexPtr[2].fogNum = 0.0f; + tess.vertexPtr[2].tc1[0] = cmd->s2; + tess.vertexPtr[2].tc1[1] = cmd->t2; + tess.vertexPtr[2].tc2[0] = cmd->s2; + tess.vertexPtr[2].tc2[1] = cmd->t2; + tess.vertexPtr[2].normal[0] = 0.0f; + tess.vertexPtr[2].normal[1] = 0.0f; + tess.vertexPtr[2].normal[2] = 0.0f; + tess.vertexPtr[2].color[0] = tr.identityLightByte; + tess.vertexPtr[2].color[1] = tr.identityLightByte; + tess.vertexPtr[2].color[2] = tr.identityLightByte; + tess.vertexPtr[2].color[3] = 255; + + tess.vertexPtr[3].xyz[0] = cmd->x; + tess.vertexPtr[3].xyz[1] = cmd->y + cmd->h; + tess.vertexPtr[3].xyz[2] = 0.0f; + tess.vertexPtr[3].fogNum = 0.0f; + tess.vertexPtr[3].tc1[0] = cmd->s1; + tess.vertexPtr[3].tc1[1] = cmd->t2; + tess.vertexPtr[3].tc2[0] = cmd->s1; + tess.vertexPtr[3].tc2[1] = cmd->t2; + tess.vertexPtr[3].normal[0] = 0.0f; + tess.vertexPtr[3].normal[1] = 0.0f; + tess.vertexPtr[3].normal[2] = 0.0f; + tess.vertexPtr[3].color[0] = tr.identityLightByte; + tess.vertexPtr[3].color[1] = tr.identityLightByte; + tess.vertexPtr[3].color[2] = tr.identityLightByte; + tess.vertexPtr[3].color[3] = 255; + + tess.indexPtr[0] = quadIndexes[0]; + tess.indexPtr[1] = quadIndexes[1]; + tess.indexPtr[2] = quadIndexes[2]; + tess.indexPtr[3] = quadIndexes[3]; + tess.indexPtr[4] = quadIndexes[4]; + tess.indexPtr[5] = quadIndexes[5]; + + tess.numInstances = 1; + tess.instances[0].times[0] = tess.shaderTime; + tess.instances[0].times[1] = 0.0f; + tess.instances[0].times[2] = 0.0f; + tess.instances[0].times[3] = 0.0f; + tess.instances[0].transX[0] = 1.0f; + tess.instances[0].transX[1] = 0.0f; + tess.instances[0].transX[2] = 0.0f; + tess.instances[0].transX[3] = 0.0f; + tess.instances[0].transY[0] = 0.0f; + tess.instances[0].transY[1] = 1.0f; + tess.instances[0].transY[2] = 0.0f; + tess.instances[0].transY[3] = 0.0f; + tess.instances[0].transZ[0] = 0.0f; + tess.instances[0].transZ[1] = 0.0f; + tess.instances[0].transZ[2] = 1.0f; + tess.instances[0].transZ[3] = 0.0f; + + tess.imgOverride = tr.scratchImage[cmd->client]; + RB_EndSurface( ); + tess.imgOverride = NULL; + + return cmd + 1; } void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { + qboolean redefine = cols != tr.scratchImage[client]->width + || rows != tr.scratchImage[client]->height; - GL_Bind( tr.scratchImage[client] ); + GL_BindTexture( tr.scratchImage[client]->texnum ); // if the scratchImage isn't in the format we want, specify it as a new texture - if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { + if( redefine ) { tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); @@ -820,12 +1673,10 @@ void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - } else { - if (dirty) { - // otherwise, just subimage upload it so that drivers can tell we are going to be changing - // it and don't try and do a texture compression - qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); - } + } else if( dirty ) { + // otherwise, just subimage upload it so that drivers can tell we are going to be changing + // it and don't try and do a texture compression + qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); } } @@ -854,74 +1705,179 @@ const void *RB_SetColor( const void *data ) { RB_StretchPic ============= */ +static qboolean overlap(const stretchPicCommand_t *a, + const stretchPicCommand_t *b ) { + if( a->x >= b->x + b->w || b->x >= a->x + a->w ) + return qfalse; + + if( a->y >= b->y + b->h || b->y >= a->y + a->h ) + return qfalse; + + return qtrue; +} const void *RB_StretchPic ( const void *data ) { + struct pic { + const stretchPicCommand_t *cmd; + color4ub_t color; + } *pics; const stretchPicCommand_t *cmd; - shader_t *shader; - int numVerts, numIndexes; + shader_t *shader; + int i, j, k, n; cmd = (const stretchPicCommand_t *)data; if ( !backEnd.projection2D ) { - RB_SetGL2D(); + glRenderState_t dummy; + RB_SetGL2D( &dummy ); } - shader = cmd->shader; - if ( shader != tess.shader ) { - if ( tess.numIndexes ) { - RB_EndSurface(); - } - backEnd.currentEntity = &backEnd.entity2D; - RB_BeginSurface( shader, 0 ); + backEnd.currentEntity = &backEnd.entity2D; + + // read all the following StretchPic commands + n = 1; + data = cmd + 1; + for(;;) { + data = PADP( data, sizeof( void * ) ); + if ( *(int *)data == RC_STRETCH_PIC ) { + n++; + data = (byte *)data + sizeof(stretchPicCommand_t); + } else if ( *(int *)data == RC_SET_COLOR ) { + data = (byte *)data + sizeof(setColorCommand_t); + } else + break; } - RB_CHECKOVERFLOW( 4, 6 ); - numVerts = tess.numVertexes; - numIndexes = tess.numIndexes; - - tess.numVertexes += 4; - tess.numIndexes += 6; - - tess.indexes[ numIndexes ] = numVerts + 3; - tess.indexes[ numIndexes + 1 ] = numVerts + 0; - tess.indexes[ numIndexes + 2 ] = numVerts + 2; - tess.indexes[ numIndexes + 3 ] = numVerts + 2; - tess.indexes[ numIndexes + 4 ] = numVerts + 0; - tess.indexes[ numIndexes + 5 ] = numVerts + 1; - - *(int *)tess.vertexColors[ numVerts ] = - *(int *)tess.vertexColors[ numVerts + 1 ] = - *(int *)tess.vertexColors[ numVerts + 2 ] = - *(int *)tess.vertexColors[ numVerts + 3 ] = *(int *)backEnd.color2D; - - tess.xyz[ numVerts ][0] = cmd->x; - tess.xyz[ numVerts ][1] = cmd->y; - tess.xyz[ numVerts ][2] = 0; - - tess.texCoords[ numVerts ][0][0] = cmd->s1; - tess.texCoords[ numVerts ][0][1] = cmd->t1; - - tess.xyz[ numVerts + 1 ][0] = cmd->x + cmd->w; - tess.xyz[ numVerts + 1 ][1] = cmd->y; - tess.xyz[ numVerts + 1 ][2] = 0; - - tess.texCoords[ numVerts + 1 ][0][0] = cmd->s2; - tess.texCoords[ numVerts + 1 ][0][1] = cmd->t1; - - tess.xyz[ numVerts + 2 ][0] = cmd->x + cmd->w; - tess.xyz[ numVerts + 2 ][1] = cmd->y + cmd->h; - tess.xyz[ numVerts + 2 ][2] = 0; + // add current color to each pic + pics = (struct pic *)RB_AllocScratch(n * sizeof(struct pic)); + for( i = 0; ; ) { + if( cmd->commandId == RC_STRETCH_PIC ) { + pics[i].cmd = cmd; + *(int *)&pics[i].color = *(int *)&backEnd.color2D; + i++; cmd++; + } else if( cmd->commandId == RC_SET_COLOR ) { + cmd = RB_SetColor( cmd ); + } else + break; + cmd = PADP( cmd, sizeof( void * ) ); + } - tess.texCoords[ numVerts + 2 ][0][0] = cmd->s2; - tess.texCoords[ numVerts + 2 ][0][1] = cmd->t2; + // try to reorder by shader + // as there is no Z-order we must be careful to not swap pics that + // overlap + shader = pics[0].cmd->shader; + for( i = 1; i < n ; i++ ) { + for( j = i; j < n; j++ ) { + if( pics[j].cmd->shader == shader ) { + while( j > i && + !overlap( pics[j].cmd, pics[j-1].cmd ) ) { + struct pic tmp = pics[j]; + pics[j] = pics[j-1]; + pics[j-1] = tmp; + j--; + } + break; + } + } + shader = pics[i].cmd->shader; + } - tess.xyz[ numVerts + 3 ][0] = cmd->x; - tess.xyz[ numVerts + 3 ][1] = cmd->y + cmd->h; - tess.xyz[ numVerts + 3 ][2] = 0; + // draw maximal batches + for( i = 0; i < n; ) { + shader = pics[i].cmd->shader; + for( j = i+1; j < n; j++ ) { + if( pics[j].cmd->shader != shader ) + break; + } - tess.texCoords[ numVerts + 3 ][0][0] = cmd->s1; - tess.texCoords[ numVerts + 3 ][0][1] = cmd->t2; + j -= i; + RB_BeginSurface( shader ); + tess.numVertexes = 4 * j; + tess.numIndexes[0] = 6 * j; + + RB_AllocateSurface( ); + + tess.numVertexes = 4 * j; + tess.numIndexes[0] = 6 * j; + tess.minIndex[0] = 0; + tess.maxIndex[0] = tess.numVertexes - 1; + + for( k = 0; k < j; k++, i++ ) { + tess.indexPtr[6*k+0] = quadIndexes[0] + 4*k; + tess.indexPtr[6*k+1] = quadIndexes[1] + 4*k; + tess.indexPtr[6*k+2] = quadIndexes[2] + 4*k; + tess.indexPtr[6*k+3] = quadIndexes[3] + 4*k; + tess.indexPtr[6*k+4] = quadIndexes[4] + 4*k; + tess.indexPtr[6*k+5] = quadIndexes[5] + 4*k; + + *(int *)(&tess.vertexPtr[4*k+0].color) = + *(int *)(&tess.vertexPtr[4*k+1].color) = + *(int *)(&tess.vertexPtr[4*k+2].color) = + *(int *)(&tess.vertexPtr[4*k+3].color) = *(int *)&pics[i].color; + + tess.vertexPtr[4*k+0].xyz[0] = pics[i].cmd->x; + tess.vertexPtr[4*k+0].xyz[1] = pics[i].cmd->y; + tess.vertexPtr[4*k+0].xyz[2] = 0.0f; + tess.vertexPtr[4*k+0].fogNum = 0.0f; + tess.vertexPtr[4*k+0].tc1[0] = pics[i].cmd->s1; + tess.vertexPtr[4*k+0].tc1[1] = pics[i].cmd->t1; + tess.vertexPtr[4*k+0].tc2[0] = pics[i].cmd->s1; + tess.vertexPtr[4*k+0].tc2[1] = pics[i].cmd->t1; + + tess.vertexPtr[4*k+1].xyz[0] = pics[i].cmd->x + pics[i].cmd->w; + tess.vertexPtr[4*k+1].xyz[1] = pics[i].cmd->y; + tess.vertexPtr[4*k+1].xyz[2] = 0.0f; + tess.vertexPtr[4*k+1].fogNum = 0.0f; + tess.vertexPtr[4*k+1].tc1[0] = pics[i].cmd->s2; + tess.vertexPtr[4*k+1].tc1[1] = pics[i].cmd->t1; + tess.vertexPtr[4*k+1].tc2[0] = pics[i].cmd->s2; + tess.vertexPtr[4*k+1].tc2[1] = pics[i].cmd->t1; + + tess.vertexPtr[4*k+2].xyz[0] = pics[i].cmd->x + pics[i].cmd->w; + tess.vertexPtr[4*k+2].xyz[1] = pics[i].cmd->y + pics[i].cmd->h; + tess.vertexPtr[4*k+2].xyz[2] = 0.0f; + tess.vertexPtr[4*k+2].fogNum = 0.0f; + tess.vertexPtr[4*k+2].tc1[0] = pics[i].cmd->s2; + tess.vertexPtr[4*k+2].tc1[1] = pics[i].cmd->t2; + tess.vertexPtr[4*k+2].tc2[0] = pics[i].cmd->s2; + tess.vertexPtr[4*k+2].tc2[1] = pics[i].cmd->t2; + + tess.vertexPtr[4*k+3].xyz[0] = pics[i].cmd->x; + tess.vertexPtr[4*k+3].xyz[1] = pics[i].cmd->y + pics[i].cmd->h; + tess.vertexPtr[4*k+3].xyz[2] = 0.0f; + tess.vertexPtr[4*k+3].fogNum = 0.0f; + tess.vertexPtr[4*k+3].tc1[0] = pics[i].cmd->s1; + tess.vertexPtr[4*k+3].tc1[1] = pics[i].cmd->t2; + tess.vertexPtr[4*k+3].tc2[0] = pics[i].cmd->s1; + tess.vertexPtr[4*k+3].tc2[1] = pics[i].cmd->t2; + } + tess.numInstances = 1; + tess.instances[0].times[0] = tess.shaderTime; + tess.instances[0].times[1] = 0.0f; + tess.instances[0].times[2] = 0.0f; + tess.instances[0].times[3] = 0.0f; + tess.instances[0].transX[0] = 1.0f; + tess.instances[0].transX[1] = 0.0f; + tess.instances[0].transX[2] = 0.0f; + tess.instances[0].transX[3] = 0.0f; + tess.instances[0].transY[0] = 0.0f; + tess.instances[0].transY[1] = 1.0f; + tess.instances[0].transY[2] = 0.0f; + tess.instances[0].transY[3] = 0.0f; + tess.instances[0].transZ[0] = 0.0f; + tess.instances[0].transZ[1] = 0.0f; + tess.instances[0].transZ[2] = 1.0f; + tess.instances[0].transZ[3] = 0.0f; + + tess.instances[0].color[0] = 0; + tess.instances[0].color[1] = 0; + tess.instances[0].color[2] = 0; + tess.instances[0].color[3] = 0; + + RB_EndSurface (); + } - return (const void *)(cmd + 1); + RB_FreeScratch( pics ); + return (const void *)cmd; } @@ -934,11 +1890,6 @@ RB_DrawSurfs const void *RB_DrawSurfs( const void *data ) { const drawSurfsCommand_t *cmd; - // finish any 2D drawing if needed - if ( tess.numIndexes ) { - RB_EndSurface(); - } - cmd = (const drawSurfsCommand_t *)data; backEnd.refdef = cmd->refdef; @@ -963,12 +1914,6 @@ const void *RB_DrawBuffer( const void *data ) { qglDrawBuffer( cmd->buffer ); - // clear screen for debugging - if ( r_clear->integer ) { - qglClearColor( 1, 0, 0.5, 1 ); - qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - } - return (const void *)(cmd + 1); } @@ -987,9 +1932,11 @@ void RB_ShowImages( void ) { image_t *image; float x, y, w, h; int start, end; + glRenderState_t state; + InitState( &state ); if ( !backEnd.projection2D ) { - RB_SetGL2D(); + RB_SetGL2D( &state ); } qglClear( GL_COLOR_BUFFER_BIT ); @@ -1001,30 +1948,98 @@ void RB_ShowImages( void ) { for ( i=0 ; iinteger == 2 ) { - w *= image->uploadWidth / 512.0f; - h *= image->uploadHeight / 512.0f; - } - - GL_Bind( image ); - qglBegin (GL_QUADS); - qglTexCoord2f( 0, 0 ); - qglVertex2f( x, y ); - qglTexCoord2f( 1, 0 ); - qglVertex2f( x + w, y ); - qglTexCoord2f( 1, 1 ); - qglVertex2f( x + w, y + h ); - qglTexCoord2f( 0, 1 ); - qglVertex2f( x, y + h ); - qglEnd(); + RB_BeginSurface( tr.defaultShader ); + + tess.numVertexes = 4; + tess.numIndexes[0] = 6; + + RB_AllocateSurface( ); + + tess.numVertexes = 4; + tess.numIndexes[0] = 6; + tess.minIndex[0] = 0; + tess.maxIndex[0] = 3; + + w = glConfig.vidWidth / 40; + h = glConfig.vidHeight / 30; + x = i % 40 * w; + y = i / 40 * h; + + tess.vertexPtr[0].xyz[0] = x; + tess.vertexPtr[0].xyz[1] = y; + tess.vertexPtr[0].xyz[2] = 0.0f; + tess.vertexPtr[0].fogNum = 0.0f; + tess.vertexPtr[0].tc1[0] = 0.0f; + tess.vertexPtr[0].tc1[1] = 0.0f; + tess.vertexPtr[0].tc2[0] = 0.0f; + tess.vertexPtr[0].tc2[1] = 0.0f; + tess.vertexPtr[0].normal[0] = 0.0f; + tess.vertexPtr[0].normal[1] = 0.0f; + tess.vertexPtr[0].normal[2] = 0.0f; + tess.vertexPtr[0].color[0] = tr.identityLightByte; + tess.vertexPtr[0].color[1] = tr.identityLightByte; + tess.vertexPtr[0].color[2] = tr.identityLightByte; + tess.vertexPtr[0].color[3] = 255; + + tess.vertexPtr[1].xyz[0] = x + w; + tess.vertexPtr[1].xyz[1] = y; + tess.vertexPtr[1].xyz[2] = 0.0f; + tess.vertexPtr[1].fogNum = 0.0f; + tess.vertexPtr[1].tc1[0] = 1.0f; + tess.vertexPtr[1].tc1[1] = 0.0f; + tess.vertexPtr[1].tc2[0] = 1.0f; + tess.vertexPtr[1].tc2[1] = 0.0f; + tess.vertexPtr[1].normal[0] = 0.0f; + tess.vertexPtr[1].normal[1] = 0.0f; + tess.vertexPtr[1].normal[2] = 0.0f; + tess.vertexPtr[1].color[0] = tr.identityLightByte; + tess.vertexPtr[1].color[1] = tr.identityLightByte; + tess.vertexPtr[1].color[2] = tr.identityLightByte; + tess.vertexPtr[1].color[3] = 255; + + tess.vertexPtr[2].xyz[0] = x + w; + tess.vertexPtr[2].xyz[1] = y + h; + tess.vertexPtr[2].xyz[2] = 0.0f; + tess.vertexPtr[2].fogNum = 0.0f; + tess.vertexPtr[2].tc1[0] = 1.0f; + tess.vertexPtr[2].tc1[1] = 1.0f; + tess.vertexPtr[2].tc2[0] = 1.0f; + tess.vertexPtr[2].tc2[1] = 1.0f; + tess.vertexPtr[2].normal[0] = 0.0f; + tess.vertexPtr[2].normal[1] = 0.0f; + tess.vertexPtr[2].normal[2] = 0.0f; + tess.vertexPtr[2].color[0] = tr.identityLightByte; + tess.vertexPtr[2].color[1] = tr.identityLightByte; + tess.vertexPtr[2].color[2] = tr.identityLightByte; + tess.vertexPtr[2].color[3] = 255; + + tess.vertexPtr[3].xyz[0] = x; + tess.vertexPtr[3].xyz[1] = y + h; + tess.vertexPtr[3].xyz[2] = 0.0f; + tess.vertexPtr[3].fogNum = 0.0f; + tess.vertexPtr[3].tc1[0] = 0.0f; + tess.vertexPtr[3].tc1[1] = 1.0f; + tess.vertexPtr[3].tc2[0] = 0.0f; + tess.vertexPtr[3].tc2[1] = 1.0f; + tess.vertexPtr[3].normal[0] = 0.0f; + tess.vertexPtr[3].normal[1] = 0.0f; + tess.vertexPtr[3].normal[2] = 0.0f; + tess.vertexPtr[3].color[0] = tr.identityLightByte; + tess.vertexPtr[3].color[1] = tr.identityLightByte; + tess.vertexPtr[3].color[2] = tr.identityLightByte; + tess.vertexPtr[3].color[3] = 255; + + tess.indexPtr[0] = quadIndexes[0]; + tess.indexPtr[1] = quadIndexes[1]; + tess.indexPtr[2] = quadIndexes[2]; + tess.indexPtr[3] = quadIndexes[3]; + tess.indexPtr[4] = quadIndexes[4]; + tess.indexPtr[5] = quadIndexes[5]; + + tess.imgOverride = image; + RB_EndSurface( ); + tess.imgOverride = NULL; } - qglFinish(); end = ri.Milliseconds(); @@ -1057,9 +2072,6 @@ const void *RB_ClearDepth(const void *data) { const clearDepthCommand_t *cmd = data; - if(tess.numIndexes) - RB_EndSurface(); - // texture swapping test if (r_showImages->integer) RB_ShowImages(); @@ -1078,11 +2090,6 @@ RB_SwapBuffers const void *RB_SwapBuffers( const void *data ) { const swapBuffersCommand_t *cmd; - // finish any 2D drawing if needed - if ( tess.numIndexes ) { - RB_EndSurface(); - } - // texture swapping test if ( r_showImages->integer ) { RB_ShowImages(); @@ -1097,15 +2104,15 @@ const void *RB_SwapBuffers( const void *data ) { long sum = 0; unsigned char *stencilReadback; - stencilReadback = ri.Hunk_AllocateTempMemory( glConfig.vidWidth * glConfig.vidHeight ); + stencilReadback = RB_AllocScratch( glConfig.vidWidth * glConfig.vidHeight ); qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback ); for ( i = 0; i < glConfig.vidWidth * glConfig.vidHeight; i++ ) { - sum += stencilReadback[i]; + sum += stencilReadback[i] & glGlobals.shadowMask; } backEnd.pc.c_overDraw += sum; - ri.Hunk_FreeTempMemory( stencilReadback ); + RB_FreeScratch( stencilReadback ); } @@ -1117,8 +2124,28 @@ const void *RB_SwapBuffers( const void *data ) { GLimp_EndFrame(); - backEnd.projection2D = qfalse; + if ( qglGenQueriesARB && r_ext_occlusion_query->integer ) { + int shader; + + for( shader = 0; shader < tr.numGLSLprograms; shader++ ) { + tr.GLSLprograms[shader]->QuerySum = 0; + } + + for( shader = 0; shader < tr.numShaders; shader++ ) { + if ( tr.shaders[shader]->QueryID ) { + GL_GetQuery( tr.shaders[shader]->QueryID, + &tr.shaders[shader]->QueryResult ); + } + if( tr.shaders[shader]->GLSLprogram ) { + tr.shaders[shader]->GLSLprogram->QuerySum += QUERY_RESULT(&tr.shaders[shader]->QueryResult); + if( tr.shaders[shader]->GLSLprogram->QuerySum > QUERY_MASK ) + tr.shaders[shader]->GLSLprogram->QuerySum = QUERY_MASK; + } + } + } + backEnd.projection2D = qfalse; + return (const void *)(cmd + 1); } @@ -1151,6 +2178,10 @@ void RB_ExecuteRenderCommands( const void *data ) { case RC_STRETCH_PIC: data = RB_StretchPic( data ); break; + case RC_STRETCH_RAW: + //Check if it's time for BLOOM! + data = RB_StretchRaw( data ); + break; case RC_DRAW_SURFS: data = RB_DrawSurfs( data ); break; @@ -1192,6 +2223,9 @@ RB_RenderThread void RB_RenderThread( void ) { const void *data; + // set default state + GL_SetDefaultState(); + // wait for either a rendering command or a quit command while ( 1 ) { // sleep until we have work to do diff --git a/code/renderer/tr_bsp.c b/code/renderer/tr_bsp.c index 17afd7e..cce2c27 100644 --- a/code/renderer/tr_bsp.c +++ b/code/renderer/tr_bsp.c @@ -132,11 +132,13 @@ R_LoadLightmaps =============== */ #define LIGHTMAP_SIZE 128 -static void R_LoadLightmaps( lump_t *l ) { +static void R_LoadLightmaps( lump_t *l, lump_t *surfs ) { byte *buf, *buf_p; + dsurface_t *surf; int len; - byte image[LIGHTMAP_SIZE*LIGHTMAP_SIZE*4]; - int i, j; + int offs; + byte *image, *image_p; + int i, j, k, x, y; float maxIntensity = 0; double sumIntensity = 0; @@ -151,60 +153,133 @@ static void R_LoadLightmaps( lump_t *l ) { // create all the lightmaps tr.numLightmaps = len / (LIGHTMAP_SIZE * LIGHTMAP_SIZE * 3); - if ( tr.numLightmaps == 1 ) { - //FIXME: HACK: maps with only one lightmap turn up fullbright for some reason. - //this avoids this, but isn't the correct solution. - tr.numLightmaps++; + // if all odd indexes are unused, we probably have deluxemaps + tr.hasDeluxemaps = (tr.numLightmaps > 1); + for( i = 0, surf = (dsurface_t *)(fileBase + surfs->fileofs); + i < surfs->filelen / sizeof(dsurface_t); i++, surf++ ) { + int lightmapNum = LittleLong( surf->lightmapNum ); + if ( lightmapNum >= 0 && (lightmapNum & 1) != 0 ) { + tr.hasDeluxemaps = qfalse; + break; + } } // if we are in r_vertexLight mode, we don't need the lightmaps at all if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) { + tr.lightmapWidth = tr.lightmapHeight = 1; return; } + + // see how many lightmaps we can stuff into one texture + tr.lightmapWidth = tr.lightmapHeight = 1; + while ( tr.lightmapWidth * LIGHTMAP_SIZE < glConfig.maxTextureSize && + tr.lightmapWidth * tr.lightmapHeight < tr.numLightmaps ) { + tr.lightmapWidth *= 2; + if ( tr.lightmapWidth * tr.lightmapHeight >= tr.numLightmaps ) + break; + tr.lightmapHeight *= 2; + } - tr.lightmaps = ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low ); - for ( i = 0 ; i < tr.numLightmaps ; i++ ) { - // expand the 24 bit on-disk to 32 bit - buf_p = buf + i * LIGHTMAP_SIZE*LIGHTMAP_SIZE * 3; - - if ( r_lightmap->integer == 2 ) - { // color code by intensity as development tool (FIXME: check range) - for ( j = 0; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) - { - float r = buf_p[j*3+0]; - float g = buf_p[j*3+1]; - float b = buf_p[j*3+2]; - float intensity; - float out[3] = {0.0, 0.0, 0.0}; - - intensity = 0.33f * r + 0.685f * g + 0.063f * b; - - if ( intensity > 255 ) - intensity = 1.0f; - else - intensity /= 255.0f; - - if ( intensity > maxIntensity ) - maxIntensity = intensity; - - HSVtoRGB( intensity, 1.00, 0.50, out ); - - image[j*4+0] = out[0] * 255; - image[j*4+1] = out[1] * 255; - image[j*4+2] = out[2] * 255; - image[j*4+3] = 255; - - sumIntensity += intensity; - } - } else { - for ( j = 0 ; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) { - R_ColorShiftLightingBytes( &buf_p[j*3], &image[j*4] ); - image[j*4+3] = 255; + // calculate number of resulting lightmap textures + tr.numLightmaps = (tr.numLightmaps + tr.lightmapWidth * tr.lightmapHeight - 1) + / (tr.lightmapWidth * tr.lightmapHeight); + + tr.lightmaps = ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), + h_low ); + image = ri.Hunk_AllocateTempMemory( tr.lightmapWidth * tr.lightmapHeight * + LIGHTMAP_SIZE * LIGHTMAP_SIZE * 4 ); + Com_Memset( image, 0, tr.lightmapWidth * tr.lightmapHeight * + LIGHTMAP_SIZE * LIGHTMAP_SIZE * 4); + + offs = 0; + for ( i = 0 ; i < tr.numLightmaps; i++ ) { + for ( y = 0; y < tr.lightmapHeight; y++ ) { + if ( offs >= len) + break; + + for ( x = 0; x < tr.lightmapWidth; x++ ) { + if ( offs >= len ) + break; + + // expand the 24 bit on-disk to 32 bit + buf_p = buf + offs; + image_p = image + (y * LIGHTMAP_SIZE * tr.lightmapWidth + x) * LIGHTMAP_SIZE * 4; + + if ( r_lightmap->integer == 2 ) + { // color code by intensity as development tool (FIXME: check range) + for ( j = 0; j < LIGHTMAP_SIZE ; j++ ) { + for ( k = 0; k < LIGHTMAP_SIZE; k++ ) { + float r = buf_p[(j * LIGHTMAP_SIZE + k)*3+0]; + float g = buf_p[(j * LIGHTMAP_SIZE + k)*3+1]; + float b = buf_p[(j * LIGHTMAP_SIZE + k)*3+2]; + float intensity; + float out[3] = {0.0, 0.0, 0.0}; + + intensity = 0.33f * r + 0.685f * g + 0.063f * b; + + if ( intensity > 255 ) + intensity = 1.0f; + else + intensity /= 255.0f; + + if ( intensity > maxIntensity ) + maxIntensity = intensity; + + HSVtoRGB( intensity, 1.00, 0.50, out ); + + image_p[k*4+0] = out[0] * 255; + image_p[k*4+1] = out[1] * 255; + image_p[k*4+2] = out[2] * 255; + image_p[k*4+3] = 255; + + sumIntensity += intensity; + } + // go to next line + image_p += tr.lightmapWidth*LIGHTMAP_SIZE*4; + } + } else { + for ( j = 0; j < LIGHTMAP_SIZE ; j++ ) { + for ( k = 0; k < LIGHTMAP_SIZE; k++ ) { + R_ColorShiftLightingBytes( &buf_p[(j * LIGHTMAP_SIZE + k)*3], &image_p[k*4+0] ); + image_p[k*4+3] = 255; + } + // go to next line + image_p += tr.lightmapWidth*LIGHTMAP_SIZE*4; + } + } + if ( tr.hasDeluxemaps ) { + offs += LIGHTMAP_SIZE * LIGHTMAP_SIZE * 3; + buf_p = buf + offs; + image_p = image + (y * LIGHTMAP_SIZE * tr.lightmapWidth + ++x) * LIGHTMAP_SIZE * 4; + + for ( j = 0; j < LIGHTMAP_SIZE ; j++ ) { + for ( k = 0; k < LIGHTMAP_SIZE; k++ ) { + // copy deluxemap data + image_p[k*4+0] = buf_p[(j * LIGHTMAP_SIZE + k)*3+0]; + image_p[k*4+1] = buf_p[(j * LIGHTMAP_SIZE + k)*3+1]; + image_p[k*4+2] = buf_p[(j * LIGHTMAP_SIZE + k)*3+2]; + image_p[k*4+3] = 255; + } + // go to next line + image_p += tr.lightmapWidth*LIGHTMAP_SIZE*4; + } + } + offs += LIGHTMAP_SIZE * LIGHTMAP_SIZE * 3; } } + tr.lightmaps[i] = R_CreateImage( va("*lightmap%d",i), image, - LIGHTMAP_SIZE, LIGHTMAP_SIZE, qfalse, qfalse, GL_CLAMP_TO_EDGE ); + LIGHTMAP_SIZE * tr.lightmapWidth, + LIGHTMAP_SIZE * tr.lightmapHeight, + qfalse, qfalse, GL_CLAMP_TO_EDGE ); + ri.Printf( PRINT_DEVELOPER, "lightmaps[%i]=%i\n", i, tr.lightmaps[i]->texnum); } + // dummy lightmap + tr.lightmaps[i] = R_CreateImage( va("*lightmap%d",i), image, + LIGHTMAP_SIZE, LIGHTMAP_SIZE, + qfalse, qfalse, GL_CLAMP_TO_EDGE ); + tr.deluxeOffset = 1.0f / tr.lightmapWidth; + ri.Hunk_FreeTempMemory( image ); if ( r_lightmap->integer == 2 ) { ri.Printf( PRINT_ALL, "Brightest lightmap value: %d\n", ( int ) ( maxIntensity * 255 ) ); @@ -305,13 +380,22 @@ static void ParseFace( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int int i, j; srfSurfaceFace_t *cv; int numPoints, numIndexes; - int lightmapNum; + int lightmapNum, lightmapX, lightmapY; int sfaceSize, ofsIndexes; lightmapNum = LittleLong( ds->lightmapNum ); + if (lightmapNum >= 0) { + lightmapX = lightmapNum & (tr.lightmapWidth - 1); + lightmapNum /= tr.lightmapWidth; + lightmapY = lightmapNum & (tr.lightmapHeight - 1); + lightmapNum /= tr.lightmapHeight; + } else { + lightmapX = lightmapY = 0; + } // get fog volume - surf->fogIndex = LittleLong( ds->fogNum ) + 1; + surf->fogIndex = (short)LittleLong( ds->fogNum ) + 1; + surf->type = (short)SF_FACE; // get shader value surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum ); @@ -322,8 +406,8 @@ static void ParseFace( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int numPoints = LittleLong( ds->numVerts ); if (numPoints > MAX_FACE_POINTS) { ri.Printf( PRINT_WARNING, "WARNING: MAX_FACE_POINTS exceeded: %i\n", numPoints); - numPoints = MAX_FACE_POINTS; - surf->shader = tr.defaultShader; + numPoints = MAX_FACE_POINTS; + surf->shader = tr.defaultShader; } numIndexes = LittleLong( ds->numIndexes ); @@ -349,6 +433,12 @@ static void ParseFace( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int cv->points[i][5+j] = LittleFloat( verts[i].lightmap[j] ); } R_ColorShiftLightingBytes( verts[i].color, (byte *)&cv->points[i][7] ); + + if (lightmapNum >= 0) { + // adjust lightmap coords + cv->points[i][5] = (cv->points[i][5] + lightmapX) / tr.lightmapWidth; + cv->points[i][6] = (cv->points[i][6] + lightmapY) / tr.lightmapHeight; + } } indexes += LittleLong( ds->firstIndex ); @@ -378,15 +468,23 @@ static void ParseMesh ( dsurface_t *ds, drawVert_t *verts, msurface_t *surf ) { int i, j; int width, height, numPoints; drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE]; - int lightmapNum; + int lightmapNum, lightmapX, lightmapY; vec3_t bounds[2]; vec3_t tmpVec; static surfaceType_t skipData = SF_SKIP; lightmapNum = LittleLong( ds->lightmapNum ); + if (lightmapNum >= 0) { + lightmapX = lightmapNum & (tr.lightmapWidth - 1); + lightmapNum /= tr.lightmapWidth; + lightmapY = lightmapNum & (tr.lightmapHeight - 1); + lightmapNum /= tr.lightmapHeight; + } else { + lightmapX = lightmapY = 0; + } // get fog volume - surf->fogIndex = LittleLong( ds->fogNum ) + 1; + surf->fogIndex = (short)LittleLong( ds->fogNum ) + 1; // get shader value surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum ); @@ -397,6 +495,7 @@ static void ParseMesh ( dsurface_t *ds, drawVert_t *verts, msurface_t *surf ) { // we may have a nodraw surface, because they might still need to // be around for movement clipping if ( s_worldData.shaders[ LittleLong( ds->shaderNum ) ].surfaceFlags & SURF_NODRAW ) { + surf->type = (short)SF_SKIP; surf->data = &skipData; return; } @@ -416,10 +515,17 @@ static void ParseMesh ( dsurface_t *ds, drawVert_t *verts, msurface_t *surf ) { points[i].lightmap[j] = LittleFloat( verts[i].lightmap[j] ); } R_ColorShiftLightingBytes( verts[i].color, points[i].color ); + + if (lightmapNum >= 0) { + // adjust lightmap coords + points[i].lightmap[0] = (points[i].lightmap[0] + lightmapX) / tr.lightmapWidth; + points[i].lightmap[1] = (points[i].lightmap[1] + lightmapY) / tr.lightmapHeight; + } } // pre-tesseleate grid = R_SubdividePatchToGrid( width, height, points ); + surf->type = (short)(grid->surfaceType); surf->data = (surfaceType_t *)grid; // copy the level of detail origin, which is the center @@ -446,7 +552,8 @@ static void ParseTriSurf( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, i int numVerts, numIndexes; // get fog volume - surf->fogIndex = LittleLong( ds->fogNum ) + 1; + surf->fogIndex = (short)LittleLong( ds->fogNum ) + 1; + surf->type = (short)SF_TRIANGLES; // get shader surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX ); @@ -504,7 +611,8 @@ static void ParseFlare( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int int i; // get fog volume - surf->fogIndex = LittleLong( ds->fogNum ) + 1; + surf->fogIndex = (short)LittleLong( ds->fogNum ) + 1; + surf->type = (short)SF_FLARE; // get shader surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX ); @@ -1225,6 +1333,9 @@ void R_MovePatchSurfacesToHunk(void) { R_LoadSurfaces =============== */ +static int sortByShader(const void *a, const void *b) { + return ((msurface_t *)a)->shader - ((msurface_t *)b)->shader; +} static void R_LoadSurfaces( lump_t *surfs, lump_t *verts, lump_t *indexLump ) { dsurface_t *in; msurface_t *out; @@ -1292,6 +1403,61 @@ static void R_LoadSurfaces( lump_t *surfs, lump_t *verts, lump_t *indexLump ) { ri.Printf( PRINT_ALL, "...loaded %d faces, %i meshes, %i trisurfs, %i flares\n", numFaces, numMeshes, numTriSurfs, numFlares ); + + if ( qglGenBuffersARB ) { + // sort surfaces by shader for improved locality + msurface_t *sortBuffer = ri.Hunk_AllocateTempMemory( count * sizeof(msurface_t) ); + GLuint vbo; + + Com_Memcpy( sortBuffer, s_worldData.surfaces, count * sizeof(msurface_t) ); + qsort( sortBuffer, count, sizeof(msurface_t), + sortByShader ); + + // render all vertexes into a VBO + RB_BeginSurface( tr.buildVBOShader ); + + for ( i = 0, out = sortBuffer; i < count; i++, out++ ) { + switch( out->type ) { + case SF_FACE: + tesselate( TESS_COUNT, out->data ); + break; + case SF_GRID: + tesselate( TESS_COUNT, out->data ); + break; + case SF_TRIANGLES: + tesselate( TESS_COUNT, out->data ); + break; + default: + break; + } + } + + qglGenBuffersARB( 1, &vbo ); + GL_VBO( vbo ); + RB_AllocateSurface(); + for ( i = 0, out = sortBuffer; i < count; i++, out++ ) { + tess.fogNum = out->fogIndex; + switch( out->type ) { + case SF_FACE: + ((srfSurfaceFace_t *)(out->data))->vboStart = tess.numVertexes; + tesselate( TESS_VERTEX, out->data ); + break; + case SF_GRID: + ((srfGridMesh_t *)(out->data))->vboStart = tess.numVertexes; + tesselate( TESS_VERTEX, out->data ); + break; + case SF_TRIANGLES: + ((srfTriangles_t *)(out->data))->vboStart = tess.numVertexes; + tesselate( TESS_VERTEX, out->data ); + break; + default: + break; + } + } + RB_EndSurface(); + + ri.Hunk_FreeTempMemory( sortBuffer ); + } } @@ -1365,6 +1531,7 @@ static void R_LoadNodesAndLeafs (lump_t *nodeLump, lump_t *leafLump) { dnode_t *in; dleaf_t *inLeaf; mnode_t *out; + mcluster_t *clusters; int numNodes, numLeafs; in = (void *)(fileBase + nodeLump->fileofs); @@ -1429,6 +1596,20 @@ static void R_LoadNodesAndLeafs (lump_t *nodeLump, lump_t *leafLump) { // chain decendants R_SetParent (s_worldData.nodes, NULL); + + // calculate cluster bounds + clusters = ri.Hunk_Alloc ( s_worldData.numClusters * sizeof(mcluster_t), h_low); + tr.clusters = s_worldData.clusters = clusters; + for ( i = 0; i < s_worldData.numClusters; i++ ) { + ClearBounds( clusters[i].mins, clusters[i].maxs ); + } + out = s_worldData.nodes + numNodes; + for ( i = 0; i < numLeafs; i++ ) { + if ( ( j = out[i].cluster ) >= 0 ) { + AddPointToBounds( out[i].mins, clusters[j].mins, clusters[j].maxs ); + AddPointToBounds( out[i].maxs, clusters[j].mins, clusters[j].maxs ); + } + } } //============================================================================= @@ -1554,10 +1735,6 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) { s_worldData.fogs = ri.Hunk_Alloc ( s_worldData.numfogs*sizeof(*out), h_low); out = s_worldData.fogs + 1; - if ( !count ) { - return; - } - brushes = (void *)(fileBase + brushesLump->fileofs); if (brushesLump->filelen % sizeof(*brushes)) { ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); @@ -1635,7 +1812,6 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) { out++; } - } @@ -1645,6 +1821,7 @@ R_LoadLightGrid ================ */ +#define sign(x) (((x) >= 0.0f) - ((x) < 0.0f)) void R_LoadLightGrid( lump_t *l ) { int i; vec3_t maxs; @@ -1683,6 +1860,146 @@ void R_LoadLightGrid( lump_t *l ) { R_ColorShiftLightingBytes( &w->lightGridData[i*8], &w->lightGridData[i*8] ); R_ColorShiftLightingBytes( &w->lightGridData[i*8+3], &w->lightGridData[i*8+3] ); } + + // upload light grid to 3D texture + if( backEnd.fogBuffer && qglTexImage3DEXT ) { + int z, maxz, planeSize; + byte *buffer, *in, *plane1, *plane2; + + qglTexImage3DEXT( GL_PROXY_TEXTURE_3D, 0, GL_RGBA8, + (int)w->lightGridBounds[0], + (int)w->lightGridBounds[1], + 2 * (int)w->lightGridBounds[2], + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ); + qglGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D, 0, + GL_TEXTURE_WIDTH, &i); + if( i == 0 ) + return; + + buffer = ri.Hunk_AllocateTempMemory( numGridPoints * 8 ); + + planeSize = w->lightGridBounds[0] * w->lightGridBounds[1]; + maxz = 2 * w->lightGridBounds[2] - 1; + for( z = 0; z < w->lightGridBounds[2]; z++ ) { + in = w->lightGridData + 8 * z * planeSize; + plane1 = buffer + 4 * z * planeSize; + plane2 = buffer + 4 * (maxz - z) * planeSize; + for( i = 0; i < planeSize; i++ ) { + int lat, lng; + vec3_t normal; + float scale; + + lat = in[i * 8 + 7] * (FUNCTABLE_SIZE/256); + lng = in[i * 8 + 6] * (FUNCTABLE_SIZE/256); + normal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + normal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + normal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + + scale = 1.0f / (fabs(normal[0]) + fabs(normal[1]) + fabs(normal[2])); + normal[0] *= scale; + normal[1] *= scale; + normal[2] *= scale; + + if( normal[2] < 0.0f ) { + normal[0] = sign(normal[0]) - normal[0]; + normal[1] = sign(normal[1]) - normal[1]; + } + + plane1[i * 4 + 0] = in[i * 8 + 0]; + plane1[i * 4 + 1] = in[i * 8 + 1]; + plane1[i * 4 + 2] = in[i * 8 + 2]; + plane1[i * 4 + 3] = (byte)(normal[0] * 127.5f + 127.5f); + plane2[i * 4 + 0] = in[i * 8 + 3]; + plane2[i * 4 + 1] = in[i * 8 + 4]; + plane2[i * 4 + 2] = in[i * 8 + 5]; + plane2[i * 4 + 3] = (byte)(normal[1] * 127.5f + 127.5f); + } + } + + qglGenTextures( 1, &tr.lightGridTexture ); + GL_ActiveTexture( TMU_LIGHTGRID ); + qglBindTexture( GL_TEXTURE_3D, tr.lightGridTexture ); + qglTexImage3DEXT( GL_TEXTURE_3D, 0, GL_RGBA8, + w->lightGridBounds[0], + w->lightGridBounds[1], + 2 * w->lightGridBounds[2], + 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer ); + qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, + GL_LINEAR ); + qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, + GL_LINEAR ); + qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE ); + qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE ); + qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, + GL_CLAMP_TO_EDGE ); + + ri.Hunk_FreeTempMemory( buffer ); + } +} + +/* + */ +static void BuildFogBuffer( void ) { + int i; + byte *buffer; + float *ptrLightGridScale, *ptrLightGridOffset, *ptrColor, *ptrPlane; + + if( backEnd.fogBuffer ) { + GL_UBO( backEnd.fogBuffer ); + buffer = qglMapBufferARB( GL_UNIFORM_BUFFER, GL_WRITE_ONLY_ARB ); + ptrLightGridScale = (float *)(buffer + backEnd.uLightGridScale.offset); + ptrLightGridOffset = (float *)(buffer + backEnd.uLightGridOffset.offset); + ptrColor = (float *)(buffer + backEnd.uFogColors.offset); + ptrPlane = (float *)(buffer + backEnd.uFogPlanes.offset); + + if( !tr.lightGridTexture ) { + ptrLightGridScale[0] = 0.0f; + ptrLightGridScale[1] = 0.0f; + ptrLightGridScale[2] = 0.0f; + ptrLightGridOffset[0] = 0.0f; + ptrLightGridOffset[1] = 0.0f; + ptrLightGridOffset[2] = 0.0f; + } else { + ptrLightGridScale[0] = s_worldData.lightGridInverseSize[0] / s_worldData.lightGridBounds[0]; + ptrLightGridScale[1] = s_worldData.lightGridInverseSize[1] / s_worldData.lightGridBounds[1]; + ptrLightGridScale[2] = 0.5f * s_worldData.lightGridInverseSize[2] / s_worldData.lightGridBounds[2]; + ptrLightGridOffset[0] = (s_worldData.lightGridOrigin[0] - 0.5 * s_worldData.lightGridSize[0]) + * ptrLightGridScale[0]; + ptrLightGridOffset[1] = (s_worldData.lightGridOrigin[1] - 0.5 * s_worldData.lightGridSize[1]) + * ptrLightGridScale[1]; + ptrLightGridOffset[2] = (s_worldData.lightGridOrigin[2] - 0.5 * s_worldData.lightGridSize[2]) + * ptrLightGridScale[2]; + } + + for(i = 1; i < s_worldData.numfogs; i++ ) { + ptrColor[0] = s_worldData.fogs[i].parms.color[0]; + ptrColor[1] = s_worldData.fogs[i].parms.color[1]; + ptrColor[2] = s_worldData.fogs[i].parms.color[2]; + ptrColor[3] = 8.0f * s_worldData.fogs[i].tcScale; + + if( s_worldData.fogs[i].hasSurface ) { + ptrPlane[0] = s_worldData.fogs[i].surface[0]; + ptrPlane[1] = s_worldData.fogs[i].surface[1]; + ptrPlane[2] = s_worldData.fogs[i].surface[2]; + ptrPlane[3] = s_worldData.fogs[i].surface[3]; + } else { + ptrPlane[0] = 0.0f; + ptrPlane[1] = 0.0f; + ptrPlane[2] = 0.0f; + ptrPlane[3] = 1.0f; + } + + ptrColor = (float *)((byte *)ptrColor + backEnd.uFogColors.stride); + ptrPlane = (float *)((byte *)ptrPlane + backEnd.uFogPlanes.stride); + } + + qglUnmapBufferARB( GL_UNIFORM_BUFFER ); + GL_UBO( 0 ); + + qglBindBufferBase( GL_UNIFORM_BUFFER, 2, backEnd.fogBuffer ); + } } /* @@ -1848,7 +2165,7 @@ void RE_LoadWorldMap( const char *name ) { // load into heap R_LoadShaders( &header->lumps[LUMP_SHADERS] ); - R_LoadLightmaps( &header->lumps[LUMP_LIGHTMAPS] ); + R_LoadLightmaps( &header->lumps[LUMP_LIGHTMAPS], &header->lumps[LUMP_SURFACES] ); R_LoadPlanes (&header->lumps[LUMP_PLANES]); R_LoadFogs( &header->lumps[LUMP_FOGS], &header->lumps[LUMP_BRUSHES], &header->lumps[LUMP_BRUSHSIDES] ); R_LoadSurfaces( &header->lumps[LUMP_SURFACES], &header->lumps[LUMP_DRAWVERTS], &header->lumps[LUMP_DRAWINDEXES] ); @@ -1864,6 +2181,8 @@ void RE_LoadWorldMap( const char *name ) { // only set tr.world now that we know the entire level has loaded properly tr.world = &s_worldData; - ri.FS_FreeFile( buffer.v ); + ri.FS_FreeFile( buffer.v ); + + BuildFogBuffer( ); } diff --git a/code/renderer/tr_cmds.c b/code/renderer/tr_cmds.c index fe3fd13..53b30d1 100644 --- a/code/renderer/tr_cmds.c +++ b/code/renderer/tr_cmds.c @@ -52,7 +52,7 @@ void R_PerformanceCounters( void ) { tr.pc.c_sphere_cull_md3_in, tr.pc.c_sphere_cull_md3_clip, tr.pc.c_sphere_cull_md3_out, tr.pc.c_box_cull_md3_in, tr.pc.c_box_cull_md3_clip, tr.pc.c_box_cull_md3_out ); } else if (r_speeds->integer == 3) { - ri.Printf (PRINT_ALL, "viewcluster: %i\n", tr.viewCluster ); + ri.Printf (PRINT_ALL, "viewcluster: %i\n", tr.viewParms.viewCluster ); } else if (r_speeds->integer == 4) { if ( backEnd.pc.c_dlightVertexes ) { ri.Printf (PRINT_ALL, "dlight srf:%i culled:%i verts:%i tris:%i\n", @@ -245,9 +245,9 @@ Passing NULL will set the color to white void RE_SetColor( const float *rgba ) { setColorCommand_t *cmd; - if ( !tr.registered ) { - return; - } + if ( !tr.registered ) { + return; + } cmd = R_GetCommandBuffer( sizeof( *cmd ) ); if ( !cmd ) { return; @@ -268,6 +268,80 @@ void RE_SetColor( const float *rgba ) { /* ============= +RE_StretchRaw + +Stretches a raw 32 bit power of 2 bitmap image over the given screen rectangle. +Used for cinematics. +============= +*/ +void RE_StretchRaw (int x, int y, int w, int h, + int cols, int rows, const byte *data, + int client, qboolean dirty) { + int i, j; + int start, end; + stretchRawCommand_t *cmd; + + if ( !tr.registered ) { + return; + } + R_SyncRenderThread(); + + start = end = 0; + if ( r_speeds->integer ) { + start = ri.Milliseconds(); + } + + // make sure rows and cols are powers of 2 + for ( i = 0 ; ( 1 << i ) < cols ; i++ ) { + } + for ( j = 0 ; ( 1 << j ) < rows ; j++ ) { + } + if ( ( 1 << i ) != cols || ( 1 << j ) != rows) { + ri.Error (ERR_DROP, "Draw_StretchRaw: size not a power of 2: %i by %i", cols, rows); + } + + // if the scratchImage isn't in the format we want, specify it as a new texture + GL_BindTexture( tr.scratchImage[client]->texnum ); + + if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { + tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; + tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; + qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + } else { + if (dirty) { + // otherwise, just subimage upload it so that drivers can tell we are going to be changing + // it and don't try and do a texture compression + qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); + } + } + + if ( r_speeds->integer ) { + end = ri.Milliseconds(); + ri.Printf( PRINT_ALL, "qglTexSubImage2D %i, %i: %i msec\n", cols, rows, end - start ); + } + + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_STRETCH_RAW; + cmd->client = client; + cmd->x = x; + cmd->y = y; + cmd->w = w; + cmd->h = h; + cmd->s1 = 0.5f / cols; + cmd->t1 = 0.5f / rows; + cmd->s2 = 1.0f - cmd->s1; + cmd->t2 = 1.0f - cmd->t1; +} + +/* +============= RE_StretchPic ============= */ @@ -275,9 +349,9 @@ void RE_StretchPic ( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ) { stretchPicCommand_t *cmd; - if (!tr.registered) { - return; - } + if (!tr.registered) { + return; + } cmd = R_GetCommandBuffer( sizeof( *cmd ) ); if ( !cmd ) { return; @@ -363,7 +437,7 @@ void RE_BeginFrame( stereoFrame_t stereoFrame ) { // if ( r_measureOverdraw->integer ) { - if ( glConfig.stencilBits < 4 ) + if ( glGlobals.shadowBits < 4 ) { ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits ); ri.Cvar_Set( "r_measureOverdraw", "0" ); @@ -379,7 +453,7 @@ void RE_BeginFrame( stereoFrame_t stereoFrame ) { { R_SyncRenderThread(); qglEnable( GL_STENCIL_TEST ); - qglStencilMask( ~0U ); + qglStencilMask( glGlobals.shadowMask ); qglClearStencil( 0U ); qglStencilFunc( GL_ALWAYS, 0U, ~0U ); qglStencilOp( GL_KEEP, GL_INCR, GL_INCR ); @@ -510,6 +584,11 @@ void RE_BeginFrame( stereoFrame_t stereoFrame ) { } tr.refdef.stereoFrame = stereoFrame; + + // sort shaders by size of occluded area + if ( qglGenQueriesARB && stereoFrame != STEREO_RIGHT ) { + R_SortShaders( ); + } } @@ -520,9 +599,15 @@ RE_EndFrame Returns the number of msec spent in the back end ============= */ +#ifdef RETURN_GLTIME +void RE_EndFrame( int *frontEndMsec, int *backEndMsec, int *GLMsec ) { +#else void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) { +#endif swapBuffersCommand_t *cmd; + GL_CheckDebugLog(); + if ( !tr.registered ) { return; } @@ -532,7 +617,28 @@ void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) { } cmd->commandId = RC_SWAP_BUFFERS; - R_IssueRenderCommands( qtrue ); + if( glGlobals.timerQuery && + qglIsQueryARB( glGlobals.timerQuery ) ) { + GLuint available; + qglGetQueryObjectuivARB( glGlobals.timerQuery, + GL_QUERY_RESULT_AVAILABLE_ARB, + &available ); + if( available ) { + qglGetQueryObjectui64vEXT( glGlobals.timerQuery, + GL_QUERY_RESULT_ARB, + &glGlobals.timerResult ); + glGlobals.timerRunning = qfalse; + } + } + + if( glGlobals.timerQuery && !glGlobals.timerRunning ) { + qglBeginQueryARB( GL_TIME_ELAPSED_EXT, glGlobals.timerQuery ); + R_IssueRenderCommands( qtrue ); + qglEndQueryARB( GL_TIME_ELAPSED_EXT ); + glGlobals.timerRunning = qtrue; + } else { + R_IssueRenderCommands( qtrue ); + } // use the other buffers next frame, because another CPU // may still be rendering into the current ones @@ -546,6 +652,11 @@ void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) { *backEndMsec = backEnd.pc.msec; } backEnd.pc.msec = 0; +#ifdef RETURN_GLTIME + if ( GLMsec ) { + *GLMsec = glGlobals.timerResult / 1000000; + } +#endif } /* diff --git a/code/renderer/tr_flares.c b/code/renderer/tr_flares.c index 05b1835..e38ae39 100644 --- a/code/renderer/tr_flares.c +++ b/code/renderer/tr_flares.c @@ -62,7 +62,7 @@ typedef struct flare_s { int addedFrame; - qboolean inPortal; // true if in a portal view of the scene + int portalLevel; // portalLevel if in a portal view of the scene int frameSceneNum; void *surface; int fogNum; @@ -154,7 +154,7 @@ void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t // see if a flare with a matching surface, scene, and view exists for ( f = r_activeFlares ; f ; f = f->next ) { if ( f->surface == surface && f->frameSceneNum == backEnd.viewParms.frameSceneNum - && f->inPortal == backEnd.viewParms.isPortal ) { + && f->portalLevel == backEnd.viewParms.portalLevel ) { break; } } @@ -172,7 +172,7 @@ void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t f->surface = surface; f->frameSceneNum = backEnd.viewParms.frameSceneNum; - f->inPortal = backEnd.viewParms.isPortal; + f->portalLevel = backEnd.viewParms.portalLevel; f->addedFrame = -1; } @@ -312,7 +312,8 @@ void RB_RenderFlare( flare_t *f ) { vec3_t color; int iColor[3]; float distance, intensity, factor; - byte fogFactors[3] = {255, 255, 255}; + color4ub_t fogFactors = {255, 255, 255, 255}; + static glIndex_t indexes[6] = { 0, 1, 2, 0, 2, 3 }; backEnd.pc.c_flareRenders++; @@ -352,73 +353,92 @@ void RB_RenderFlare( flare_t *f ) { VectorScale(f->color, f->drawIntensity * intensity, color); -// Calculations for fogging + RB_BeginSurface( tr.flareShader ); + + tess.numVertexes = 4; + tess.numIndexes[0] = 6; + + RB_AllocateSurface( ); + + // Calculations for fogging if(tr.world && f->fogNum < tr.world->numfogs) { - tess.numVertexes = 1; - VectorCopy(f->origin, tess.xyz[0]); + tess.numVertexes = 1; + tess.numIndexes[0] = 0; + + VectorCopy(f->origin, tess.vertexPtr->xyz); tess.fogNum = f->fogNum; - RB_CalcModulateColorsByFog(fogFactors); + RB_CalcModulateColorsByFog( &fogFactors, 1); // We don't need to render the flare if colors are 0 anyways. - if(!(fogFactors[0] || fogFactors[1] || fogFactors[2])) + if(!(fogFactors[0] || fogFactors[1] || fogFactors[2])) { return; + } + tess.numVertexes = 4; + tess.numIndexes[0] = 6; } iColor[0] = color[0] * fogFactors[0]; iColor[1] = color[1] * fogFactors[1]; iColor[2] = color[2] * fogFactors[2]; - RB_BeginSurface( tr.flareShader, f->fogNum ); + tess.fogNum = f->fogNum; // FIXME: use quadstamp? - tess.xyz[tess.numVertexes][0] = f->windowX - size; - tess.xyz[tess.numVertexes][1] = f->windowY - size; - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = iColor[0]; - tess.vertexColors[tess.numVertexes][1] = iColor[1]; - tess.vertexColors[tess.numVertexes][2] = iColor[2]; - tess.vertexColors[tess.numVertexes][3] = 255; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = f->windowX - size; - tess.xyz[tess.numVertexes][1] = f->windowY + size; - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = iColor[0]; - tess.vertexColors[tess.numVertexes][1] = iColor[1]; - tess.vertexColors[tess.numVertexes][2] = iColor[2]; - tess.vertexColors[tess.numVertexes][3] = 255; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = f->windowX + size; - tess.xyz[tess.numVertexes][1] = f->windowY + size; - tess.texCoords[tess.numVertexes][0][0] = 1; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = iColor[0]; - tess.vertexColors[tess.numVertexes][1] = iColor[1]; - tess.vertexColors[tess.numVertexes][2] = iColor[2]; - tess.vertexColors[tess.numVertexes][3] = 255; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = f->windowX + size; - tess.xyz[tess.numVertexes][1] = f->windowY - size; - tess.texCoords[tess.numVertexes][0][0] = 1; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = iColor[0]; - tess.vertexColors[tess.numVertexes][1] = iColor[1]; - tess.vertexColors[tess.numVertexes][2] = iColor[2]; - tess.vertexColors[tess.numVertexes][3] = 255; - tess.numVertexes++; - - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 1; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 3; + tess.vertexPtr[0].xyz[0] = f->windowX - size; + tess.vertexPtr[0].xyz[1] = f->windowY - size; + tess.vertexPtr[0].xyz[2] = 0.0f; + tess.vertexPtr[0].fogNum = 0.0f; + tess.vertexPtr[0].tc1[0] = 0.0f; + tess.vertexPtr[0].tc1[1] = 0.0f; + tess.vertexPtr[0].tc2[0] = 0.0f; + tess.vertexPtr[0].tc2[1] = 0.0f; + tess.vertexPtr[0].color[0] = iColor[0]; + tess.vertexPtr[0].color[1] = iColor[1]; + tess.vertexPtr[0].color[2] = iColor[2]; + tess.vertexPtr[0].color[3] = 255; + + tess.vertexPtr[1].xyz[0] = f->windowX - size; + tess.vertexPtr[1].xyz[1] = f->windowY + size; + tess.vertexPtr[1].xyz[2] = 0.0f; + tess.vertexPtr[1].fogNum = 0.0f; + tess.vertexPtr[1].tc1[0] = 0.0f; + tess.vertexPtr[1].tc1[1] = 1.0f; + tess.vertexPtr[1].tc2[0] = 0.0f; + tess.vertexPtr[1].tc2[1] = 1.0f; + tess.vertexPtr[1].color[0] = iColor[0]; + tess.vertexPtr[1].color[1] = iColor[1]; + tess.vertexPtr[1].color[2] = iColor[2]; + tess.vertexPtr[1].color[3] = 255; + + tess.vertexPtr[2].xyz[0] = f->windowX + size; + tess.vertexPtr[2].xyz[1] = f->windowY + size; + tess.vertexPtr[2].xyz[2] = 0.0f; + tess.vertexPtr[2].fogNum = 0.0f; + tess.vertexPtr[2].tc1[0] = 1.0f; + tess.vertexPtr[2].tc1[1] = 1.0f; + tess.vertexPtr[2].tc2[0] = 1.0f; + tess.vertexPtr[2].tc2[1] = 1.0f; + tess.vertexPtr[2].color[0] = iColor[0]; + tess.vertexPtr[2].color[1] = iColor[1]; + tess.vertexPtr[2].color[2] = iColor[2]; + tess.vertexPtr[2].color[3] = 255; + + tess.vertexPtr[3].xyz[0] = f->windowX + size; + tess.vertexPtr[3].xyz[1] = f->windowY - size; + tess.vertexPtr[3].xyz[2] = 0.0f; + tess.vertexPtr[3].fogNum = 0.0f; + tess.vertexPtr[3].tc1[0] = 1.0f; + tess.vertexPtr[3].tc1[1] = 0.0f; + tess.vertexPtr[3].tc2[0] = 1.0f; + tess.vertexPtr[3].tc2[1] = 0.0f; + tess.vertexPtr[3].color[0] = iColor[0]; + tess.vertexPtr[3].color[1] = iColor[1]; + tess.vertexPtr[3].color[2] = iColor[2]; + tess.vertexPtr[3].color[3] = 255; + + Com_Memcpy( tess.indexPtr, indexes, 6 * sizeof( glIndex_t ) ); RB_EndSurface(); } @@ -480,7 +500,7 @@ void RB_RenderFlares (void) { // don't draw any here that aren't from this scene / portal f->drawIntensity = 0; if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum - && f->inPortal == backEnd.viewParms.isPortal ) { + && f->portalLevel == backEnd.viewParms.portalLevel ) { RB_TestFlare( f ); if ( f->drawIntensity ) { draw = qtrue; @@ -500,10 +520,6 @@ void RB_RenderFlares (void) { return; // none visible } - if ( backEnd.viewParms.isPortal ) { - qglDisable (GL_CLIP_PLANE0); - } - qglPushMatrix(); qglLoadIdentity(); qglMatrixMode( GL_PROJECTION ); @@ -515,7 +531,7 @@ void RB_RenderFlares (void) { for ( f = r_activeFlares ; f ; f = f->next ) { if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum - && f->inPortal == backEnd.viewParms.isPortal + && f->portalLevel == backEnd.viewParms.portalLevel && f->drawIntensity ) { RB_RenderFlare( f ); } diff --git a/code/renderer/tr_font.c b/code/renderer/tr_font.c index 87465e5..39b9bda 100644 --- a/code/renderer/tr_font.c +++ b/code/renderer/tr_font.c @@ -112,14 +112,14 @@ FT_Bitmap *R_RenderGlyph(FT_GlyphSlot glyph, glyphInfo_t* glyphOut) { if ( glyph->format == ft_glyph_format_outline ) { size = pitch*height; - bit2 = ri.Malloc(sizeof(FT_Bitmap)); + bit2 = ri.Hunk_AllocateTempMemory( sizeof(FT_Bitmap) ); bit2->width = width; bit2->rows = height; bit2->pitch = pitch; bit2->pixel_mode = ft_pixel_mode_grays; //bit2->pixel_mode = ft_pixel_mode_mono; - bit2->buffer = ri.Malloc(pitch*height); + bit2->buffer = ri.Hunk_AllocTempMemory( pitch * height ); bit2->num_grays = 256; Com_Memset( bit2->buffer, 0, size ); @@ -147,7 +147,7 @@ void WriteTGA (char *filename, byte *data, int width, int height) { unsigned char *flip; unsigned char *src, *dst; - buffer = ri.Malloc(width*height*4 + 18); + buffer = ri.Hunk_AllocateTempMemory( width*height*4 + 18 ); Com_Memset (buffer, 0, 18); buffer[2] = 2; // uncompressed type buffer[12] = width&255; @@ -177,15 +177,13 @@ void WriteTGA (char *filename, byte *data, int width, int height) { Com_Memcpy(src, dst, width*4); Com_Memcpy(dst, flip, width*4); } - ri.Free(flip); + ri.Hunk_FreeTempMemory( flip ); ri.FS_WriteFile(filename, buffer, c); - //f = fopen (filename, "wb"); - //fwrite (buffer, 1, c, f); - //fclose (f); + ri.FS_WriteFile(filename, buffer, c); - ri.Free (buffer); + ri.Hunk_FreeTempMemory( buffer ); } static glyphInfo_t *RE_ConstructGlyphInfo(unsigned char *imageOut, int *xOut, int *yOut, int *maxHeight, FT_Face face, const unsigned char c, qboolean calcHeight) { @@ -237,8 +235,8 @@ static glyphInfo_t *RE_ConstructGlyphInfo(unsigned char *imageOut, int *xOut, in if (*yOut + *maxHeight + 1 >= 255) { *yOut = -1; *xOut = -1; - ri.Free(bitmap->buffer); - ri.Free(bitmap); + ri.Hunk_FreeTempMemory( bitmap->buffer ); + ri.Hunk_FreeTempMemory( bitmap ); return &glyph; } @@ -291,8 +289,8 @@ static glyphInfo_t *RE_ConstructGlyphInfo(unsigned char *imageOut, int *xOut, in *xOut += scaled_width + 1; - ri.Free(bitmap->buffer); - ri.Free(bitmap); + ri.Hunk_FreeTempMemory( bitmap->buffer ); + ri.Hunk_FreeTempMemory( bitmap ); } return &glyph; diff --git a/code/renderer/tr_image.c b/code/renderer/tr_image.c index f511201..7d86654 100644 --- a/code/renderer/tr_image.c +++ b/code/renderer/tr_image.c @@ -110,13 +110,31 @@ void GL_TextureMode( const char *string ) { gl_filter_min = modes[i].minimize; gl_filter_max = modes[i].maximize; + // bound texture anisotropy + + if(glGlobals.textureFilterAnisotropic) + { + if(r_ext_texture_filter_anisotropic->value > glGlobals.maxAnisotropy) + { + ri.Cvar_Set("r_ext_texture_filter_anisotropic", va("%d", glGlobals.maxAnisotropy)); + } + else if(r_ext_texture_filter_anisotropic->value < 1.0) + { + ri.Cvar_Set("r_ext_texture_filter_anisotropic", "1.0"); + } + } + // change all the existing mipmap texture objects for ( i = 0 ; i < tr.numImages ; i++ ) { glt = tr.images[ i ]; if ( glt->mipmap ) { - GL_Bind (glt); + GL_BindTexture ( glt->texnum ); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + + // set texture anisotropy + if(glGlobals.textureFilterAnisotropic) + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, r_ext_texture_filter_anisotropic->value); } } } @@ -229,13 +247,13 @@ If a larger shrinking is needed, use the mipmap function before or after. ================ */ -static void ResampleTexture( unsigned *in, int inwidth, int inheight, unsigned *out, - int outwidth, int outheight ) { +static void ResampleTexture( const byte *in, int inwidth, int inheight, + byte *out, int outwidth, int outheight ) { int i, j; - unsigned *inrow, *inrow2; + const byte *inrow, *inrow2; unsigned frac, fracstep; unsigned p1[2048], p2[2048]; - byte *pix1, *pix2, *pix3, *pix4; + const byte *pix1, *pix2, *pix3, *pix4; if (outwidth>2048) ri.Error(ERR_DROP, "ResampleTexture: max width"); @@ -253,19 +271,19 @@ static void ResampleTexture( unsigned *in, int inwidth, int inheight, unsigned * frac += fracstep; } - for (i=0 ; i> 1; for (j=0 ; j>2; - ((byte *)(out+j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2; - ((byte *)(out+j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2; - ((byte *)(out+j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2; + pix1 = inrow + p1[j]; + pix2 = inrow + p2[j]; + pix3 = inrow2 + p1[j]; + pix4 = inrow2 + p2[j]; + out[4*j+0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2; + out[4*j+1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2; + out[4*j+2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2; + out[4*j+3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2; } } } @@ -278,16 +296,16 @@ Scale up the pixel values in a texture to increase the lighting range ================ */ -void R_LightScaleTexture (unsigned *in, int inwidth, int inheight, qboolean only_gamma ) +void R_LightScaleTexture (byte *in, int inwidth, int inheight, qboolean only_gamma ) { if ( only_gamma ) { if ( !glConfig.deviceSupportsGamma ) { - int i, c; + int i, c; byte *p; - p = (byte *)in; + p = in; c = inwidth*inheight; for (i=0 ; iinteger ) { R_MipMap2( (unsigned *)in, width, height ); @@ -408,28 +427,201 @@ static void R_MipMap (byte *in, int width, int height) { row = width * 4; out = in; + + if( width == 1 ) { + if( height == 1 ) { + return; + } else if( height & 1 ) { + height >>= 1; + for( i = 0; i < height; i++, out+=4, in+=8 ) { + w0 = (height - i) * 0x10000 / (2*height+1); + w1 = height * 0x10000 / (2*height+1); + w2 = 0x10000 - w0 - w1; + out[0] = (w0 * in[0] + w1 * in[4] + w2 * in[ 8]) >> 16; + out[1] = (w0 * in[1] + w1 * in[5] + w2 * in[ 9]) >> 16; + out[2] = (w0 * in[2] + w1 * in[6] + w2 * in[10]) >> 16; + out[3] = (w0 * in[3] + w1 * in[7] + w2 * in[11]) >> 16; + } + } else { + height >>= 1; + for( i = 0; i < height; i++, out+=4, in+=8 ) { + out[0] = (in[0] + in[4]) >> 1; + out[1] = (in[1] + in[5]) >> 1; + out[2] = (in[2] + in[6]) >> 1; + out[3] = (in[3] + in[7]) >> 1; + } + } + } else if( width & 1 ) { + width >>= 1; + if( height == 1 ) { + for( j = 0; j < width; j++, out+=4, in+=8 ) { + w0 = (width - j) * 0x10000 / (2*width+1); + w1 = width * 0x10000 / (2*width+1); + w2 = 0x10000 - w0 - w1; + out[0] = (w0 * in[0] + w1 * in[4] + w2 * in[ 8]) >> 16; + out[1] = (w0 * in[1] + w1 * in[5] + w2 * in[ 9]) >> 16; + out[2] = (w0 * in[2] + w1 * in[6] + w2 * in[10]) >> 16; + out[3] = (w0 * in[3] + w1 * in[7] + w2 * in[11]) >> 16; + } + } else if( height & 1 ) { + height >>= 1; + for (i=0 ; i> 16; + out[1] = (w0 * w3 * in[ 1] + w0 * w4 * in[ 5] + w0 * w5 * in[ 9] + + w1 * w3 * in[ row+1] + w1 * w4 * in[ row+5] + w1 * w5 * in[ row+ 9] + + w2 * w3 * in[2*row+1] + w2 * w4 * in[2*row+5] + w2 * w5 * in[2*row+ 9]) >> 16; + out[2] = (w0 * w3 * in[ 2] + w0 * w4 * in[ 6] + w0 * w5 * in[ 10] + + w1 * w3 * in[ row+2] + w1 * w4 * in[ row+6] + w1 * w5 * in[ row+10] + + w2 * w3 * in[2*row+2] + w2 * w4 * in[2*row+6] + w2 * w5 * in[2*row+10]) >> 16; + out[3] = (w0 * w3 * in[ 3] + w0 * w4 * in[ 7] + w0 * w5 * in[ 11] + + w1 * w3 * in[ row+3] + w1 * w4 * in[ row+7] + w1 * w5 * in[ row+11] + + w2 * w3 * in[2*row+3] + w2 * w4 * in[2*row+7] + w2 * w5 * in[2*row+11]) >> 16; + } + } + } else { + height >>= 1; + for (i=0 ; i>17; + out[1] = (w0 * (in[1] + in[row+1]) + w1 * (in[5] + in[row+5]) + w2 * (in[ 9] + in[row+ 9]))>>17; + out[2] = (w0 * (in[2] + in[row+2]) + w1 * (in[6] + in[row+6]) + w2 * (in[10] + in[row+10]))>>17; + out[3] = (w0 * (in[3] + in[row+3]) + w1 * (in[7] + in[row+7]) + w2 * (in[11] + in[row+11]))>>17; + } + } + } + } else { + width >>= 1; + if( height == 1 ) { + for( j = 0; j < width; j++, out+=4, in+=8 ) { + out[0] = (in[0] + in[4]) >> 1; + out[1] = (in[1] + in[5]) >> 1; + out[2] = (in[2] + in[6]) >> 1; + out[3] = (in[3] + in[7]) >> 1; + } + } else if( height & 1 ) { + height >>= 1; + for (i=0 ; i>17; + out[1] = (w0 * (in[1] + in[5]) + w1 * (in[row+1] + in[row+5]) + w2 * (in[2*row+1] + in[2*row+5]))>>17; + out[2] = (w0 * (in[2] + in[6]) + w1 * (in[row+2] + in[row+6]) + w2 * (in[2*row+2] + in[2*row+6]))>>17; + out[3] = (w0 * (in[3] + in[7]) + w1 * (in[row+3] + in[row+7]) + w2 * (in[2*row+3] + in[2*row+7]))>>17; + } + } + } else { + height >>= 1; + for (i=0 ; i>2; + out[1] = (in[1] + in[5] + in[row+1] + in[row+5])>>2; + out[2] = (in[2] + in[6] + in[row+2] + in[row+6])>>2; + out[3] = (in[3] + in[7] + in[row+3] + in[row+7])>>2; + } + } + } + } +} + +/* +================ +R_MipMapHeightMap + +Operates in place, quartering the size of the texture +The red & green and blue channels are set the the x, y and z of the +average normal and the alpha channel is the max or average height. +================ +*/ +static ID_INLINE byte max2(byte a, byte b) { return (a >= b) ? a : b; } +static ID_INLINE byte max4(byte a, byte b, byte c, byte d) { return max2(max2(a, b), max2(c, d)); } +static void R_MipMapHeightMap (byte *in, int width, int height) { + int i, j; + byte *out; + int row; + vec3_t normal, normalSum; + + if ( width == 1 && height == 1 ) { + return; + } + + row = width * 4; + out = in; width >>= 1; height >>= 1; if ( width == 0 || height == 0 ) { width += height; // get largest for (i=0 ; i>1; - out[1] = ( in[1] + in[5] )>>1; - out[2] = ( in[2] + in[6] )>>1; - out[3] = ( in[3] + in[7] )>>1; + VectorClear( normalSum ); + + normal[0] = in[0] * 1.0f/127.5f - 1.0f; + normal[1] = in[1] * 1.0f/127.5f - 1.0f; + normal[2] = in[2] * 1.0f/127.5f - 1.0f; + VectorAdd( normal, normalSum, normalSum ); + + normal[0] = in[4] * 1.0f/127.5f - 1.0f; + normal[1] = in[5] * 1.0f/127.5f - 1.0f; + normal[2] = in[6] * 1.0f/127.5f - 1.0f; + VectorAdd( normal, normalSum, normalSum ); + + VectorNormalizeFast( normalSum ); + out[0] = (normalSum[0] + 1.0f) * 127.5f; + out[1] = (normalSum[1] + 1.0f) * 127.5f; + out[2] = (normalSum[2] + 1.0f) * 127.5f; + + out[3] = max2( in[3], in[7] ); } return; } for (i=0 ; i>2; - out[1] = (in[1] + in[5] + in[row+1] + in[row+5])>>2; - out[2] = (in[2] + in[6] + in[row+2] + in[row+6])>>2; - out[3] = (in[3] + in[7] + in[row+3] + in[row+7])>>2; + VectorClear( normalSum ); + + normal[0] = in[0] * 1.0f/127.5f - 1.0f; + normal[1] = in[1] * 1.0f/127.5f - 1.0f; + normal[2] = in[2] * 1.0f/127.5f - 1.0f; + VectorAdd( normal, normalSum, normalSum ); + + normal[0] = in[4] * 1.0f/127.5f - 1.0f; + normal[1] = in[5] * 1.0f/127.5f - 1.0f; + normal[2] = in[6] * 1.0f/127.5f - 1.0f; + VectorAdd( normal, normalSum, normalSum ); + + normal[0] = in[row+0] * 1.0f/127.5f - 1.0f; + normal[1] = in[row+1] * 1.0f/127.5f - 1.0f; + normal[2] = in[row+2] * 1.0f/127.5f - 1.0f; + VectorAdd( normal, normalSum, normalSum ); + + normal[0] = in[row+4] * 1.0f/127.5f - 1.0f; + normal[1] = in[row+5] * 1.0f/127.5f - 1.0f; + normal[2] = in[row+6] * 1.0f/127.5f - 1.0f; + VectorAdd( normal, normalSum, normalSum ); + + VectorNormalizeFast( normalSum ); + out[0] = (normalSum[0] + 1.0f) * 127.5f; + out[1] = (normalSum[1] + 1.0f) * 127.5f; + out[2] = (normalSum[2] + 1.0f) * 127.5f; + + out[3] = max4( in[3], in[7], + in[row+3], in[row+7] ); } } + return; } @@ -476,281 +668,211 @@ byte mipBlendColors[16][4] = { {0,0,255,128}, }; +static qboolean TexFormatSupported( GLenum internalFormat, int width, int height ) { + qglTexImage2D (GL_PROXY_TEXTURE_2D, 0, internalFormat, + width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + qglGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, + GL_TEXTURE_WIDTH, &width); + return (width != 0); +} + /* =============== Upload32 +Upload a color texture with all required mipmaps =============== */ -extern qboolean charSet; -static void Upload32( unsigned *data, - int width, int height, - qboolean mipmap, - qboolean picmip, - qboolean lightMap, - int *format, - int *pUploadWidth, int *pUploadHeight ) +static void Upload32( byte *data, + int width, int height, + qboolean mipmap, + qboolean picmip, + qboolean lightMap, + int *format, + int *pUploadWidth, int *pUploadHeight, + qboolean *hasAlpha, int *maxMipLevel ) { - int samples; - unsigned *scaledBuffer = NULL; - unsigned *resampledBuffer = NULL; - int scaled_width, scaled_height; - int i, c; + int base_level, max_level; + int pot_width, pot_height, w, h, w2, h2; + qboolean rescale = qfalse; + qboolean hasColor = qfalse; + int samples; + byte *resampledBuffer = NULL; + int i, c; byte *scan; GLenum internalFormat = GL_RGB; - float rMax = 0, gMax = 0, bMax = 0; // // convert to exact power of 2 sizes // - for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) + for( pot_width = 1 ; pot_width < width ; pot_width<<=1) ; - for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) + for( pot_height = 1 ; pot_height < height ; pot_height<<=1) ; - if ( r_roundImagesDown->integer && scaled_width > width ) - scaled_width >>= 1; - if ( r_roundImagesDown->integer && scaled_height > height ) - scaled_height >>= 1; - if ( scaled_width != width || scaled_height != height ) { - resampledBuffer = ri.Hunk_AllocateTempMemory( scaled_width * scaled_height * 4 ); - ResampleTexture (data, width, height, resampledBuffer, scaled_width, scaled_height); - data = resampledBuffer; - width = scaled_width; - height = scaled_height; - } + if ( r_roundImagesDown->integer && pot_width > width ) + pot_width >>= 1; + if ( r_roundImagesDown->integer && pot_height > height ) + pot_height >>= 1; // - // perform optional picmip operation + // compute mip levels // - if ( picmip ) { - scaled_width >>= r_picmip->integer; - scaled_height >>= r_picmip->integer; - } - - // - // clamp to minimum size - // - if (scaled_width < 1) { - scaled_width = 1; + base_level = 0; + if( picmip && r_picmip->integer >= 0 ) + base_level = r_picmip->integer; + + w = width; + h = height; + max_level = 0; + while( w > 1 || h > 1 ) { + // OpenGL rounds down non-pot sizes + w >>= 1; + h >>= 1; + max_level++; + } + + // find best texture format supported + w = width; h = height; w2 = pot_width; h2 = pot_height; + for( i = 0; i <= max_level; i++ ) { + if( i < base_level ) + continue; + if( TexFormatSupported( GL_RGBA8, w, h ) ) { + break; + } + if( TexFormatSupported( GL_RGBA8, w2, h2 ) ) { + rescale = qtrue; + break; + } } - if (scaled_height < 1) { - scaled_height = 1; + base_level = i; + if( !mipmap ) { + max_level = base_level; } - // - // clamp to the current upper OpenGL limit - // scale both axis down equally so we don't have to - // deal with a half mip resampling - // - while ( scaled_width > glConfig.maxTextureSize - || scaled_height > glConfig.maxTextureSize ) { - scaled_width >>= 1; - scaled_height >>= 1; - } + if ( rescale ) { + resampledBuffer = ri.Hunk_AllocateTempMemory( pot_width * pot_height * 4 ); + ResampleTexture (data, width, height, resampledBuffer, pot_width, pot_height); + data = resampledBuffer; - scaledBuffer = ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); + width = pot_width; + height = pot_height; + } // // scan the texture for each channel's max values // and verify if the alpha channel is being used or not // c = width*height; - scan = ((byte *)data); + scan = data; samples = 3; - if( r_greyscale->integer ) - { - for ( i = 0; i < c; i++ ) - { - byte luma = LUMA(scan[i*4], scan[i*4 + 1], scan[i*4 + 2]); - scan[i*4] = luma; - scan[i*4 + 1] = luma; - scan[i*4 + 2] = luma; - } - } - else if( r_greyscale->value ) - { - for ( i = 0; i < c; i++ ) - { - float luma = LUMA(scan[i*4], scan[i*4 + 1], scan[i*4 + 2]); - scan[i*4] = LERP(scan[i*4], luma, r_greyscale->value); - scan[i*4 + 1] = LERP(scan[i*4 + 1], luma, r_greyscale->value); - scan[i*4 + 2] = LERP(scan[i*4 + 2], luma, r_greyscale->value); - } - } - - if(lightMap) - { + if(lightMap) { if(r_greyscale->integer) internalFormat = GL_LUMINANCE; else internalFormat = GL_RGB; - } - else - { - for ( i = 0; i < c; i++ ) - { - if ( scan[i*4+0] > rMax ) - { - rMax = scan[i*4+0]; - } - if ( scan[i*4+1] > gMax ) - { - gMax = scan[i*4+1]; - } - if ( scan[i*4+2] > bMax ) - { - bMax = scan[i*4+2]; + } else { + for ( i = 0; i < c; i++ ) { + if( scan[i*4 + 0] != scan[i*4 + 1] || scan[i*4 + 0] != scan[i*4 + 2] ) { + hasColor = qtrue; + if( samples == 4 ) + break; } - if ( scan[i*4 + 3] != 255 ) - { + if( scan[i*4 + 3] != 255 ) { samples = 4; - break; + if( hasColor ) + break; } } // select proper internal format - if ( samples == 3 ) - { - if(r_greyscale->integer) - { - if(r_texturebits->integer == 16) - internalFormat = GL_LUMINANCE8; - else if(r_texturebits->integer == 32) - internalFormat = GL_LUMINANCE16; - else - internalFormat = GL_LUMINANCE; - } - else - { - if ( glConfig.textureCompression == TC_S3TC_ARB ) - { + if ( samples == 3 ) { + *hasAlpha = qfalse; + if( r_greyscale->integer || !hasColor ) { + internalFormat = GL_LUMINANCE8; + } else { + if ( glConfig.textureCompression == TC_S3TC_ARB ) { internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - } - else if ( glConfig.textureCompression == TC_S3TC ) - { + } else if ( glConfig.textureCompression == TC_S3TC ) { internalFormat = GL_RGB4_S3TC; - } - else if ( r_texturebits->integer == 16 ) - { + } else if ( r_texturebits->integer == 16 ) { internalFormat = GL_RGB5; - } - else if ( r_texturebits->integer == 32 ) - { + } else if ( r_texturebits->integer == 32 ) { internalFormat = GL_RGB8; - } - else - { + } else { internalFormat = GL_RGB; } } - } - else if ( samples == 4 ) - { - if(r_greyscale->integer) - { - if(r_texturebits->integer == 16) - internalFormat = GL_LUMINANCE8_ALPHA8; - else if(r_texturebits->integer == 32) - internalFormat = GL_LUMINANCE16_ALPHA16; - else - internalFormat = GL_LUMINANCE_ALPHA; - } - else - { - if ( r_texturebits->integer == 16 ) - { + } else if ( samples == 4 ) { + *hasAlpha = qtrue; + if( r_greyscale->integer || !hasColor ) { + internalFormat = GL_LUMINANCE8_ALPHA8; + } else { + if ( r_texturebits->integer == 16 ) { internalFormat = GL_RGBA4; - } - else if ( r_texturebits->integer == 32 ) - { + } else if ( r_texturebits->integer == 32 ) { internalFormat = GL_RGBA8; - } - else - { + } else { internalFormat = GL_RGBA; } } } } - // copy or resample data as appropriate for first MIP level - if ( ( scaled_width == width ) && - ( scaled_height == height ) ) { - if (!mipmap) - { - qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - *pUploadWidth = scaled_width; - *pUploadHeight = scaled_height; - *format = internalFormat; + w = width; h = height; - goto done; + // there may be base_level unused levels, but we need the + // data for mipmapping + for( i = 0; i <= max_level; i++ ) { + if( i == 0 ) { + // data pointer is already set up + } else { + // compute mipmaps inplace + R_MipMap( data, w, h ); + + if( w > 1 ) w >>= 1; + if( h > 1 ) h >>= 1; } - Com_Memcpy (scaledBuffer, data, width*height*4); - } - else - { - // use the normal mip-mapping function to go down from here - while ( width > scaled_width || height > scaled_height ) { - R_MipMap( (byte *)data, width, height ); - width >>= 1; - height >>= 1; - if ( width < 1 ) { - width = 1; - } - if ( height < 1 ) { - height = 1; + + if( i >= base_level ) { + R_LightScaleTexture (data, w, h, !mipmap ); + if ( r_colorMipLevels->integer ) { + R_BlendOverTexture( data, w * h, + mipBlendColors[i - base_level] ); } + qglTexImage2D (GL_TEXTURE_2D, i - base_level, + internalFormat, w, h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, data); } - Com_Memcpy( scaledBuffer, data, width * height * 4 ); - } - - R_LightScaleTexture (scaledBuffer, scaled_width, scaled_height, !mipmap ); - *pUploadWidth = scaled_width; - *pUploadHeight = scaled_height; - *format = internalFormat; - - qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer ); - - if (mipmap) - { - int miplevel; - - miplevel = 0; - while (scaled_width > 1 || scaled_height > 1) - { - R_MipMap( (byte *)scaledBuffer, scaled_width, scaled_height ); - scaled_width >>= 1; - scaled_height >>= 1; - if (scaled_width < 1) - scaled_width = 1; - if (scaled_height < 1) - scaled_height = 1; - miplevel++; + if( i == base_level ) { + *pUploadWidth = w; + *pUploadHeight = h; + *format = internalFormat; + *maxMipLevel = max_level - base_level; - if ( r_colorMipLevels->integer ) { - R_BlendOverTexture( (byte *)scaledBuffer, scaled_width * scaled_height, mipBlendColors[miplevel] ); + if( qglGenerateMipmap && mipmap && + !r_colorMipLevels->integer ) { + // use automatic mipmap generation + qglGenerateMipmap( GL_TEXTURE_2D ); + break; } - - qglTexImage2D (GL_TEXTURE_2D, miplevel, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer ); } } -done: if (mipmap) { - if ( textureFilterAnisotropic ) + if ( glGlobals.textureFilterAnisotropic ) qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, - (GLint)Com_Clamp( 1, maxAnisotropy, r_ext_max_anisotropy->integer ) ); + (GLint)Com_Clamp( 1, glGlobals.maxAnisotropy, r_ext_max_anisotropy->integer ) ); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } else { - if ( textureFilterAnisotropic ) + if ( glGlobals.textureFilterAnisotropic ) qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 ); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); @@ -759,6 +881,151 @@ done: GL_CheckErrors(); + if ( resampledBuffer ) + ri.Hunk_FreeTempMemory( resampledBuffer ); +} + + +/* +=============== +UploadHeightMap + +Upload a normal/height texture with all required mipmaps +=============== +*/ +static void UploadHeightMap( const byte *data, + int width, int height, + qboolean picmip, + int *format, + int *pUploadWidth, int *pUploadHeight, + int *maxMipLevel ) +{ + byte *scaledBuffer = NULL; + byte *resampledBuffer = NULL; + int scaled_width, scaled_height; + int i, c; + byte *scan; + GLenum internalFormat = GL_RGB; + int miplevel; + float hMax = 0; + qboolean skip; + + // + // convert to exact power of 2 sizes + // + for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) + ; + for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) + ; + if ( r_roundImagesDown->integer && scaled_width > width ) + scaled_width >>= 1; + if ( r_roundImagesDown->integer && scaled_height > height ) + scaled_height >>= 1; + + if ( scaled_width != width || scaled_height != height ) { + resampledBuffer = ri.Hunk_AllocateTempMemory( scaled_width * scaled_height * 4 ); + ResampleTexture (data, width, height, resampledBuffer, scaled_width, scaled_height); + data = resampledBuffer; + + width = scaled_width; + height = scaled_height; + } + + // + // perform optional picmip operation + // + if ( picmip ) { + scaled_width >>= r_picmip->integer; + scaled_height >>= r_picmip->integer; + } + + // + // clamp to minimum size + // + if (scaled_width < 1) { + scaled_width = 1; + } + if (scaled_height < 1) { + scaled_height = 1; + } + + // + // clamp to the current upper OpenGL limit + // scale both axis down equally so we don't have to + // deal with a half mip resampling + // + while ( scaled_width > glConfig.maxTextureSize + || scaled_height > glConfig.maxTextureSize ) { + scaled_width >>= 1; + scaled_height >>= 1; + } + // calculate maxMipLevel for this texture + for( i = 1, *maxMipLevel = 0; i < scaled_width || i < scaled_height; + i <<= 1, (*maxMipLevel)++); + + scaledBuffer = ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); + + // select proper internal format + internalFormat = GL_RGBA8; + skip = qfalse; + + // copy or resample data as appropriate for first MIP level + // use the normal mip-mapping function to go down from here + while ( width > scaled_width || height > scaled_height ) { + R_MipMapHeightMap( (byte *)data, width, height ); + width >>= 1; + height >>= 1; + if ( width < 1 ) { + width = 1; + } + if ( height < 1 ) { + height = 1; + } + } + Com_Memcpy( scaledBuffer, data, width * height * 4 ); + + // + // scan the texture for maximum height values and increase + // height values so that the maximum is 255. + // + c = scaled_width * scaled_height; + scan = ((byte *)scaledBuffer); + + for ( i = 0; i < c; i++ ) + { + if ( scan[i*4+3] > hMax ) + { + hMax = scan[i*4+3]; + } + } + if( hMax < 255 ) { + for ( i = 0; i < c; i++ ) + { + scan[i*4+3] += 255 - hMax; + } + } + + *pUploadWidth = scaled_width; + *pUploadHeight = scaled_height; + *format = internalFormat; + + for( miplevel = 0; miplevel <= *maxMipLevel; miplevel++ ) { + qglTexImage2D (GL_TEXTURE_2D, miplevel, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer ); + + R_MipMapHeightMap( (byte *)scaledBuffer, scaled_width, scaled_height ); + scaled_width >>= 1; + scaled_height >>= 1; + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + } + + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + GL_CheckErrors(); + if ( scaledBuffer != 0 ) ri.Hunk_FreeTempMemory( scaledBuffer ); if ( resampledBuffer != 0 ) @@ -773,11 +1040,12 @@ R_CreateImage This is the only way any image_t are created ================ */ -image_t *R_CreateImage( const char *name, const byte *pic, int width, int height, - qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) { +image_t *R_CreateImage( const char *name, byte *pic, int width, int height, + qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) { image_t *image; qboolean isLightmap = qfalse; long hash; + GLenum target = GL_TEXTURE_2D; if (strlen(name) >= MAX_QPATH ) { ri.Error (ERR_DROP, "R_CreateImage: \"%s\" is too long", name); @@ -791,9 +1059,16 @@ image_t *R_CreateImage( const char *name, const byte *pic, int width, int height } image = tr.images[tr.numImages] = ri.Hunk_Alloc( sizeof( image_t ), h_low ); - image->texnum = 1024 + tr.numImages; + qglGenTextures(1, &image->texnum); + //image->texnum = 1024 + tr.numImages; tr.numImages++; + if( !height ) { + target = GL_TEXTURE_BUFFER_EXT; + qglGenBuffersARB( 1, &image->buffer ); + } + + image->target = target; image->mipmap = mipmap; image->allowPicmip = allowPicmip; @@ -810,27 +1085,38 @@ image_t *R_CreateImage( const char *name, const byte *pic, int width, int height image->TMU = 0; } - if ( qglActiveTextureARB ) { - GL_SelectTexture( image->TMU ); - } - - GL_Bind(image); - - Upload32( (unsigned *)pic, image->width, image->height, - image->mipmap, - allowPicmip, - isLightmap, - &image->internalFormat, - &image->uploadWidth, - &image->uploadHeight ); - - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode ); - - qglBindTexture( GL_TEXTURE_2D, 0 ); + if( target == GL_TEXTURE_2D ) { + GL_BindTexture( image->texnum ); + + if( pic ) { + if( *name == '^' ) { + // height map + UploadHeightMap( pic, + image->width, image->height, + allowPicmip, + &image->internalFormat, + &image->uploadWidth, + &image->uploadHeight, + &image->maxMipLevel + ); + image->hasAlpha = qtrue; + } else { + Upload32( pic, + image->width, image->height, + image->mipmap, + allowPicmip, + isLightmap, + &image->internalFormat, + &image->uploadWidth, + &image->uploadHeight, + &image->hasAlpha, + &image->maxMipLevel + ); + } + } - if ( image->TMU == 1 ) { - GL_SelectTexture( 0 ); + qglTexParameterf( target, GL_TEXTURE_WRAP_S, glWrapClampMode ); + qglTexParameterf( target, GL_TEXTURE_WRAP_T, glWrapClampMode ); } hash = generateHashValue(name); @@ -999,6 +1285,163 @@ image_t *R_FindImageFile( const char *name, qboolean mipmap, qboolean allowPicmi return image; } +/* +=============== +R_FindHeightMapFile + +Finds or loads the given height map file. +Returns NULL if it fails, not a default image. +============== +*/ +image_t *R_FindHeightMapFile( const char *name, qboolean mipmap, int glWrapClampMode ) { + image_t *image; + int width, height; + byte *pic; + long hash; + char localName[ MAX_QPATH ]; + + if (!name) { + return NULL; + } + + localName[0] = '^'; + strcpy( localName+1, name ); + + hash = generateHashValue( localName ); + + // + // see if the image is already loaded + // + for (image=hashTable[hash]; image; image=image->next) { + if ( !strcmp( name, image->imgName ) ) { + // the white image can be used with any set of parms, but other mismatches are errors + if ( strcmp( name, "*white" ) ) { + if ( image->mipmap != mipmap ) { + ri.Printf( PRINT_DEVELOPER, "WARNING: reused image %s with mixed mipmap parm\n", name ); + } + if ( image->allowPicmip ) { + ri.Printf( PRINT_DEVELOPER, "WARNING: reused image %s with mixed allowPicmip parm\n", name ); + } + if ( image->wrapClampMode != glWrapClampMode ) { + ri.Printf( PRINT_ALL, "WARNING: reused image %s with mixed glWrapClampMode parm\n", name ); + } + } + return image; + } + } + + // + // load the pic from disk + // + R_LoadImage( localName+1, &pic, &width, &height ); + if ( pic == NULL ) { + return NULL; + } + + image = R_CreateImage( localName, pic, width, height, mipmap, qfalse, glWrapClampMode ); + ri.Free( pic ); + return image; +} + + +image_t *R_CombineImages( int num, image_t **images ) { + int i, cols, width, height, lod, maxLod, xoffs; + byte *data; + image_t *result; + + if( num <= 1 ) + return NULL; + + /* check that all images are compatible */ + for( i = 1; i < num; i++ ) { + if( images[i]->uploadWidth != images[0]->uploadWidth || + images[i]->uploadHeight != images[0]->uploadHeight || + images[i]->internalFormat != images[0]->internalFormat ) + return NULL; + } + + width = images[0]->uploadWidth; + height = images[0]->uploadHeight; + + /* Check that they fit into one texture */ + for( cols = 1; cols < num; cols *= 2 ) { + if( cols * width >= glConfig.maxTextureSize ) + break; + } + + /* TODO: avoid the GPU->CPU->GPU roundtrip with some render-to-texture + * magic */ + data = ri.Hunk_AllocateTempMemory( width * height < 16 ? + 16 * sizeof(color4ub_t) : + width * height * sizeof(color4ub_t) ); + + result = R_CreateImage( "*combined", NULL, cols * width, + height, images[0]->mipmap, + images[0]->allowPicmip, GL_REPEAT ); + result->uploadWidth = cols * width; + result->uploadHeight = height; + result->internalFormat = images[0]->internalFormat; + + if( !images[0]->mipmap ) { + maxLod = 0; + } else { + for( maxLod = 0; (1<internalFormat, + cols * width, height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ); + xoffs = 0; + if( width < 4 ) + qglPixelStorei( GL_PACK_ROW_LENGTH, 4 ); + for( i = 0; i < num; i++ ) { + GL_BindTexture( images[i]->texnum ); + qglGetTexImage( GL_TEXTURE_2D, lod, GL_RGBA, GL_UNSIGNED_BYTE, + data + (xoffs & 3) * sizeof(color4ub_t) ); + if( ((xoffs + width) & 3) == 0 ) { + GL_BindTexture( result->texnum ); + qglTexSubImage2D( GL_TEXTURE_2D, lod, + xoffs & -4, 0, + width < 4 ? 4 : width, height, + GL_RGBA, GL_UNSIGNED_BYTE, data ); + GL_CheckErrors(); + } + + xoffs += width; + } + for( ; i < cols; i++ ) { + if( ((xoffs + width) & 3) == 0 ) { + GL_BindTexture( result->texnum ); + qglTexSubImage2D( GL_TEXTURE_2D, lod, + xoffs & -4, 0, + width < 4 ? 4 : width, height, + GL_RGBA, GL_UNSIGNED_BYTE, data ); + GL_CheckErrors(); + xoffs += width; + break; + } + + xoffs += width; + } + if( xoffs & 3 ) { + GL_BindTexture( result->texnum ); + qglTexSubImage2D( GL_TEXTURE_2D, lod, + xoffs & -4, 0, + xoffs & 3, height, + GL_RGBA, GL_UNSIGNED_BYTE, data ); + GL_CheckErrors(); + } + if( width < 4 ) + qglPixelStorei( GL_PACK_ROW_LENGTH, 0 ); + width >>= 1; height >>= 1; + } + qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, maxLod ); + + ri.Hunk_FreeTempMemory( data ); + + return result; +} /* ================ @@ -1200,6 +1643,21 @@ void R_CreateBuiltinImages( void ) { R_CreateDlightImage(); R_CreateFogImage(); + + if( r_depthPass->integer && qglCreateShader ) { + tr.depthImage = R_CreateImage("*depth", NULL, + glConfig.vidWidth, + glConfig.vidHeight, + qfalse, qfalse, + GL_CLAMP_TO_EDGE ); + qglTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, + glConfig.vidWidth, glConfig.vidHeight, + 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + } else { + tr.depthImage = NULL; + } } @@ -1312,20 +1770,15 @@ void R_DeleteTextures( void ) { for ( i=0; itexnum ); + if( tr.images[i]->target == GL_TEXTURE_BUFFER_EXT ) + qglDeleteBuffersARB( 1, &tr.images[i]->buffer ); } Com_Memset( tr.images, 0, sizeof( tr.images ) ); tr.numImages = 0; Com_Memset( glState.currenttextures, 0, sizeof( glState.currenttextures ) ); - if ( qglActiveTextureARB ) { - GL_SelectTexture( 1 ); - qglBindTexture( GL_TEXTURE_2D, 0 ); - GL_SelectTexture( 0 ); - qglBindTexture( GL_TEXTURE_2D, 0 ); - } else { - qglBindTexture( GL_TEXTURE_2D, 0 ); - } + GL_UnbindAllTextures( ); } /* @@ -1502,12 +1955,12 @@ qhandle_t RE_RegisterSkin( const char *name ) { if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) { skin->numSurfaces = 1; skin->surfaces[0] = ri.Hunk_Alloc( sizeof(skin->surfaces[0]), h_low ); - skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); + skin->surfaces[0]->shader = R_FindShader( name, tr.defaultMD3Shader->lightmapIndex, qtrue ); return hSkin; } // load and parse the skin file - ri.FS_ReadFile( name, &text.v ); + ri.FS_ReadFile( name, &text.v ); if ( !text.c ) { return 0; } @@ -1537,7 +1990,7 @@ qhandle_t RE_RegisterSkin( const char *name ) { surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); - surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); + surf->shader = R_FindShader( token, tr.defaultMD3Shader->lightmapIndex, qtrue ); skin->numSurfaces++; } @@ -1567,7 +2020,7 @@ void R_InitSkins( void ) { skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); Q_strncpyz( skin->name, "", sizeof( skin->name ) ); skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces ), h_low ); + skin->surfaces[0] = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); skin->surfaces[0]->shader = tr.defaultShader; } diff --git a/code/renderer/tr_init.c b/code/renderer/tr_init.c index 7e89b83..ca664b2 100644 --- a/code/renderer/tr_init.c +++ b/code/renderer/tr_init.c @@ -24,10 +24,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "tr_local.h" glconfig_t glConfig; -qboolean textureFilterAnisotropic = qfalse; -int maxAnisotropy = 0; -float displayAspect = 0.0f; - glstate_t glState; static void GfxInfo_f( void ); @@ -36,6 +32,8 @@ static void GfxInfo_f( void ); cvar_t *com_altivec; #endif +cvar_t *r_scratchmegs; + cvar_t *r_flareSize; cvar_t *r_flareFade; cvar_t *r_flareCoeff; @@ -95,6 +93,22 @@ cvar_t *r_ext_compiled_vertex_array; cvar_t *r_ext_texture_env_add; cvar_t *r_ext_texture_filter_anisotropic; cvar_t *r_ext_max_anisotropy; +cvar_t *r_ext_vertex_buffer_object; +cvar_t *r_ext_map_buffer_range; +cvar_t *r_ext_vertex_shader; +cvar_t *r_ext_geometry_shader; +cvar_t *r_ext_framebuffer_object; +cvar_t *r_ext_occlusion_query; +cvar_t *r_ext_timer_query; +cvar_t *r_ext_instanced_arrays; +cvar_t *r_ext_texture_float; +cvar_t *r_ext_texture_buffer_object; +cvar_t *r_ext_uniform_buffer_object; +cvar_t *r_ext_texture3D; +cvar_t *r_ext_separate_stencil; +cvar_t *r_ext_debug_output; +cvar_t *r_ext_gpu_shader4; +cvar_t *r_ext_blend_func_extended; cvar_t *r_ignoreGLErrors; cvar_t *r_logFile; @@ -102,7 +116,6 @@ cvar_t *r_logFile; cvar_t *r_stencilbits; cvar_t *r_depthbits; cvar_t *r_colorbits; -cvar_t *r_primitives; cvar_t *r_texturebits; cvar_t *r_ext_multisample; @@ -163,11 +176,24 @@ cvar_t *r_marksOnTriangleMeshes; cvar_t *r_aviMotionJpegQuality; cvar_t *r_screenshotJpegQuality; +cvar_t *r_flush; +cvar_t *r_minIBOSize; +cvar_t *r_depthPass; +cvar_t *r_perPixelLighting; +cvar_t *r_parallax; + cvar_t *r_maxpolys; int max_polys; cvar_t *r_maxpolyverts; int max_polyverts; +void APIENTRY R_DebugProc(GLenum source, GLenum type, + GLuint id, GLenum severity, + GLsizei length, const GLchar *message, + GLvoid *userParam) { + // dummy function for placing breakpoints +} + /* ** InitOpenGL ** @@ -209,8 +235,29 @@ static void InitOpenGL( void ) { glConfig.maxTextureSize = 0; } + + // reserve some stencil bits for portal/mirror rendering + if ( glConfig.stencilBits > 8 ) { + temp = 4; + } else if ( glConfig.stencilBits > 6 ) { + temp = 3; + } else if ( glConfig.stencilBits > 4 ) { + temp = 2; + } else if ( glConfig.stencilBits > 2 ) { + temp = 1; + } else { + temp = 0; + } + + glGlobals.portalBits = temp; + glGlobals.shadowBits = glConfig.stencilBits - temp; + glGlobals.portalLevels = (1 << temp) - 1; + glGlobals.portalMask = glGlobals.portalLevels << glGlobals.shadowBits; + glGlobals.shadowMask = (1 << glGlobals.shadowBits) - 1; } + RB_InitScratchMemory( ); + // init command buffers and SMP R_InitCommandBuffers(); @@ -219,8 +266,83 @@ static void InitOpenGL( void ) // set default state GL_SetDefaultState(); + + // enable debug_output extension + if( qglDebugMessageControlARB ) { + if( r_ext_debug_output->integer & 0x4 ) + qglDebugMessageControlARB( GL_DONT_CARE, GL_DONT_CARE, + GL_DEBUG_SEVERITY_HIGH_ARB, + 0, NULL, GL_TRUE ); + if( r_ext_debug_output->integer & 0x2 ) + qglDebugMessageControlARB( GL_DONT_CARE, GL_DONT_CARE, + GL_DEBUG_SEVERITY_MEDIUM_ARB, + 0, NULL, GL_TRUE ); + if( r_ext_debug_output->integer & 0x1 ) + qglDebugMessageControlARB( GL_DONT_CARE, GL_DONT_CARE, + GL_DEBUG_SEVERITY_LOW_ARB, + 0, NULL, GL_TRUE ); + qglDebugMessageInsertARB( GL_DEBUG_SOURCE_APPLICATION_ARB, + GL_DEBUG_TYPE_OTHER_ARB, 1, + GL_DEBUG_SEVERITY_LOW_ARB, + -1, "debug log started" ); + if( r_ext_debug_output->integer & 0x8 ) { + qglDebugMessageCallbackARB(&R_DebugProc, NULL); + qglEnable( GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB ); + } + } else if ( qglDebugMessageEnableAMD ) { + if( r_ext_debug_output->integer & 0x4 ) + qglDebugMessageEnableAMD( 0, GL_DEBUG_SEVERITY_HIGH_AMD, + 0, NULL, GL_TRUE ); + if( r_ext_debug_output->integer & 0x2 ) + qglDebugMessageEnableAMD( 0, GL_DEBUG_SEVERITY_MEDIUM_AMD, + 0, NULL, GL_TRUE ); + if( r_ext_debug_output->integer & 0x1 ) + qglDebugMessageEnableAMD( 0, GL_DEBUG_SEVERITY_LOW_AMD, + 0, NULL, GL_TRUE ); + qglDebugMessageInsertAMD( GL_DEBUG_CATEGORY_APPLICATION_AMD, + GL_DEBUG_SEVERITY_LOW_AMD, 1, + 0, "debug log started" ); + } + +#if GL_SGIS_generate_mipmap + qglHint( GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST ); +#endif } + +/* +================== +GL_CheckDebugLog +================== +*/ +void GL_CheckDebugLog( void ) { + GLint messagesAvailable; + char message[1024]; + GLenum source, type, id, severity; + GLsizei length; + + if( qglDebugMessageControlARB ) { + qglGetIntegerv( GL_DEBUG_LOGGED_MESSAGES_ARB, &messagesAvailable ); + while( messagesAvailable-- ) { + if( qglGetDebugMessageLogARB( 1, sizeof(message), + &source, &type, &id, + &severity, &length, message) ) { + ri.Printf( PRINT_ALL, "GL debug: %s\n", message ); + } + } + } else if( qglDebugMessageEnableAMD ) { + qglGetIntegerv( GL_DEBUG_LOGGED_MESSAGES_AMD, &messagesAvailable ); + while( messagesAvailable-- ) { + if( qglGetDebugMessageLogAMD( 1, sizeof(message), + &type, &id, + &severity, &length, message) ) { + ri.Printf( PRINT_ALL, "GL debug: %s\n", message ); + } + } + } +} + + /* ================== GL_CheckErrors @@ -231,6 +353,9 @@ void GL_CheckErrors( void ) { char s[64]; err = qglGetError(); + + GL_CheckDebugLog(); + if ( err == GL_NO_ERROR ) { return; } @@ -842,33 +967,29 @@ const void *RB_TakeVideoFrameCmd( const void *data ) */ void GL_SetDefaultState( void ) { + if( !GLimp_InBackend() ) + return; + qglClearDepth( 1.0f ); qglCullFace(GL_FRONT); - qglColor4f (1,1,1,1); - // initialize downstream texture unit if we're running // in a multitexture environment if ( qglActiveTextureARB ) { - GL_SelectTexture( 1 ); - GL_TextureMode( r_textureMode->string ); - GL_TexEnv( GL_MODULATE ); - qglDisable( GL_TEXTURE_2D ); - GL_SelectTexture( 0 ); + int i; + + for( i = 1; i < glConfig.numTextureUnits; i++ ) { + GL_TexEnv( i, GL_MODULATE ); + } } - qglEnable(GL_TEXTURE_2D); GL_TextureMode( r_textureMode->string ); - GL_TexEnv( GL_MODULATE ); + GL_TexEnv( 0, GL_MODULATE ); qglShadeModel( GL_SMOOTH ); qglDepthFunc( GL_LEQUAL ); - // the vertex array is always enabled, but the color and texture - // arrays are enabled and disabled around the compiled vertex array call - qglEnableClientState (GL_VERTEX_ARRAY); - // // make sure our GL state vector is set correctly // @@ -908,6 +1029,7 @@ void GfxInfo_f( void ) ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_SIZE: %d\n", glConfig.maxTextureSize ); ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_UNITS_ARB: %d\n", glConfig.numTextureUnits ); ri.Printf( PRINT_ALL, "\nPIXELFORMAT: color(%d-bits) Z(%d-bit) stencil(%d-bits)\n", glConfig.colorBits, glConfig.depthBits, glConfig.stencilBits ); + ri.Printf( PRINT_ALL, "\nSTENCILFORMAT: portals(%d-bits) shadows(%d-bit)\n", glGlobals.portalBits, glGlobals.shadowBits ); ri.Printf( PRINT_ALL, "MODE: %d, %d x %d %s hz:", r_mode->integer, glConfig.vidWidth, glConfig.vidHeight, fsstrings[r_fullscreen->integer == 1] ); if ( glConfig.displayFrequency ) { @@ -926,31 +1048,6 @@ void GfxInfo_f( void ) ri.Printf( PRINT_ALL, "GAMMA: software w/ %d overbright bits\n", tr.overbrightBits ); } - // rendering primitives - { - int primitives; - - // default is to use triangles if compiled vertex arrays are present - ri.Printf( PRINT_ALL, "rendering primitives: " ); - primitives = r_primitives->integer; - if ( primitives == 0 ) { - if ( qglLockArraysEXT ) { - primitives = 2; - } else { - primitives = 1; - } - } - if ( primitives == -1 ) { - ri.Printf( PRINT_ALL, "none\n" ); - } else if ( primitives == 2 ) { - ri.Printf( PRINT_ALL, "single glDrawElements\n" ); - } else if ( primitives == 1 ) { - ri.Printf( PRINT_ALL, "multiple glArrayElement\n" ); - } else if ( primitives == 3 ) { - ri.Printf( PRINT_ALL, "multiple glColor4ubv + glTexCoord2fv + glVertex3fv\n" ); - } - } - ri.Printf( PRINT_ALL, "texturemode: %s\n", r_textureMode->string ); ri.Printf( PRINT_ALL, "picmip: %d\n", r_picmip->integer ); ri.Printf( PRINT_ALL, "texture bits: %d\n", r_texturebits->integer ); @@ -985,18 +1082,36 @@ R_Register */ void R_Register( void ) { - #ifdef USE_RENDERER_DLOPEN +#ifdef USE_RENDERER_DLOPEN com_altivec = ri.Cvar_Get("com_altivec", "1", CVAR_ARCHIVE); - #endif +#endif // // latched and archived variables // + r_scratchmegs = ri.Cvar_Get( "r_scratchmegs", va( "%d", SMP_SCRATCHMEGS ), CVAR_ARCHIVE | CVAR_LATCH ); + r_allowExtensions = ri.Cvar_Get( "r_allowExtensions", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_ext_compressed_textures = ri.Cvar_Get( "r_ext_compressed_textures", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_ext_multitexture = ri.Cvar_Get( "r_ext_multitexture", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_ext_compiled_vertex_array = ri.Cvar_Get( "r_ext_compiled_vertex_array", "1", CVAR_ARCHIVE | CVAR_LATCH); r_ext_texture_env_add = ri.Cvar_Get( "r_ext_texture_env_add", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_vertex_buffer_object = ri.Cvar_Get( "r_ext_vertex_buffer_object", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_map_buffer_range = ri.Cvar_Get( "r_ext_map_buffer_range", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_vertex_shader = ri.Cvar_Get( "r_ext_vertex_shader", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_geometry_shader = ri.Cvar_Get( "r_ext_geometry_shader", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_framebuffer_object = ri.Cvar_Get( "r_ext_framebuffer_object", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_occlusion_query = ri.Cvar_Get( "r_ext_occlusion_query", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_timer_query = ri.Cvar_Get( "r_ext_timer_query", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_instanced_arrays = ri.Cvar_Get( "r_ext_instanced_arrays", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_texture_float = ri.Cvar_Get( "r_ext_texture_float", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_texture_buffer_object = ri.Cvar_Get( "r_ext_texture_buffer_object", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_uniform_buffer_object = ri.Cvar_Get( "r_ext_uniform_buffer_object", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_texture3D = ri.Cvar_Get( "r_ext_texture3D", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_separate_stencil = ri.Cvar_Get( "r_ext_separate_stencil", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_debug_output = ri.Cvar_Get( "r_ext_debug_output", "0", CVAR_LATCH); + r_ext_gpu_shader4 = ri.Cvar_Get( "r_ext_gpu_shader4", "1", CVAR_LATCH ); + r_ext_blend_func_extended = ri.Cvar_Get( "r_ext_blend_func_extended", "1", CVAR_LATCH ); r_ext_texture_filter_anisotropic = ri.Cvar_Get( "r_ext_texture_filter_anisotropic", "0", CVAR_ARCHIVE | CVAR_LATCH ); @@ -1066,8 +1181,6 @@ void R_Register( void ) r_railCoreWidth = ri.Cvar_Get( "r_railCoreWidth", "6", CVAR_ARCHIVE ); r_railSegmentLength = ri.Cvar_Get( "r_railSegmentLength", "32", CVAR_ARCHIVE ); - r_primitives = ri.Cvar_Get( "r_primitives", "0", CVAR_ARCHIVE ); - r_ambientScale = ri.Cvar_Get( "r_ambientScale", "0.6", CVAR_CHEAT ); r_directedScale = ri.Cvar_Get( "r_directedScale", "1", CVAR_CHEAT ); @@ -1127,6 +1240,12 @@ void R_Register( void ) r_maxpolys = ri.Cvar_Get( "r_maxpolys", va("%d", MAX_POLYS), 0); r_maxpolyverts = ri.Cvar_Get( "r_maxpolyverts", va("%d", MAX_POLYVERTS), 0); + r_flush = ri.Cvar_Get("r_flush","0", 0); + r_minIBOSize = ri.Cvar_Get("r_minIBOSize","1024", 0); + r_depthPass = ri.Cvar_Get("r_depthPass","0", CVAR_LATCH | CVAR_ARCHIVE); + r_perPixelLighting = ri.Cvar_Get("r_perPixelLighting","0", CVAR_LATCH | CVAR_ARCHIVE); + r_parallax = ri.Cvar_Get("r_parallax","0",CVAR_LATCH | CVAR_ARCHIVE); + // make sure all the commands added here are also // removed in R_Shutdown ri.Cmd_AddCommand( "imagelist", R_ImageList_f ); @@ -1152,20 +1271,25 @@ void R_Init( void ) { ri.Printf( PRINT_ALL, "----- R_Init -----\n" ); + if( sizeof( glVertex_t ) != 48 ) + ri.Printf( PRINT_ERROR, "Incorrect glVertex_t size\n" ); + // clear all our internal state Com_Memset( &tr, 0, sizeof( tr ) ); + tr.worldEntity.e.axis[0][0] = 1.0f; + tr.worldEntity.e.axis[1][1] = 1.0f; + tr.worldEntity.e.axis[2][2] = 1.0f; Com_Memset( &backEnd, 0, sizeof( backEnd ) ); Com_Memset( &tess, 0, sizeof( tess ) ); if(sizeof(glconfig_t) != 11332) ri.Error( ERR_FATAL, "Mod ABI incompatible: sizeof(glconfig_t) == %u != 11332", (unsigned int) sizeof(glconfig_t)); -// Swap_Init(); + backEnd.entity2D.e.axis[0][0] = 1.0f; + backEnd.entity2D.e.axis[1][1] = 1.0f; + backEnd.entity2D.e.axis[2][2] = 1.0f; - if ( (intptr_t)tess.xyz & 15 ) { - ri.Printf( PRINT_WARNING, "tess.xyz not 16 byte aligned\n" ); - } - Com_Memset( tess.constantColor255, 255, sizeof( tess.constantColor255 ) ); +// Swap_Init(); // // init function tables @@ -1234,6 +1358,10 @@ void R_Init( void ) { R_InitFreeType(); + if( qglGenBuffersARB ) { + qglGenBuffersARB( 1, &tess.VtxBuf.id ); + qglGenBuffersARB( 1, &tess.IdxBuf.id ); + } err = qglGetError(); if ( err != GL_NO_ERROR ) @@ -1267,6 +1395,123 @@ void RE_Shutdown( qboolean destroyWindow ) { R_SyncRenderThread(); R_ShutdownCommandBuffers(); R_DeleteTextures(); + + if ( qglDeleteQueriesARB && tr.numShaders ) { + int shader; + + for ( shader = 0; shader < tr.numShaders; shader++ ) { + if ( tr.shaders[shader]->QueryID ) { + qglDeleteQueriesARB( 1, &tr.shaders[shader]->QueryID ); + tr.shaders[shader]->QueryID = 0; + } + } + } + + GL_IBO( 0 ); + GL_VBO( 0 ); + GL_UBO( 0 ); + if ( qglIsBufferARB && tr.clusters ) { + int cluster, dir; + for( cluster = 0; cluster < tr.world->numClusters; + cluster++ ) { + for( dir = 0; dir < 7; dir++ ) { + if( tr.clusters[cluster].numIBOSurfaces[dir] > 0 ) { + qglDeleteBuffersARB( 1, &tr.clusters[cluster].iboSurfaces[dir][0].ibo.ibo ); + } + } + } + } + if ( qglIsBufferARB && backEnd.worldVBO ) { + if ( qglIsBufferARB( backEnd.worldVBO->vbo ) ) { + qglDeleteBuffersARB( 1, &backEnd.worldVBO->vbo ); + ri.Free(backEnd.worldVBO); + backEnd.worldVBO = NULL; + } + } + if ( qglIsBufferARB && tr.numModels ) { + int mod, lod; + model_t *model; + + for ( mod = 0; mod < tr.numModels; mod++ ) { + srfMD3Texture_t *(*data)[MD3_MAX_LODS]; + model = tr.models[mod]; + if ( model->type != MOD_MESH ) + continue; + + data = model->modelData; + for ( lod = 0; lod < MD3_MAX_LODS; lod++ ) { + srfMD3Texture_t *surf; + if( data && (*data)[lod] != NULL ) { + int srf; + for( srf = 0; srf < model->md3[lod]->numSurfaces; srf++ ) { + surf = &(*data)[lod][srf]; + if( qglIsBufferARB( surf->IBO->ibo ) ) + qglDeleteBuffersARB( 1, &surf->IBO->ibo ); + if( surf->VBO && + qglIsBufferARB( surf->VBO->vbo ) ) + qglDeleteBuffersARB( 1, &surf->VBO->vbo ); + } + } + } + } + } + if( qglIsBufferARB && backEnd.dlightBuffer ) { + if( qglIsBufferARB( backEnd.dlightBuffer ) ) + qglDeleteBuffersARB( 1, &backEnd.dlightBuffer ); + backEnd.dlightBuffer = 0; + } + if( qglIsBufferARB && backEnd.fogBuffer ) { + if( qglIsBufferARB( backEnd.fogBuffer ) ) + qglDeleteBuffersARB( 1, &backEnd.fogBuffer ); + backEnd.fogBuffer = 0; + } + if( qglIsBufferARB && tess.VtxBuf.id ) { + if( qglIsBufferARB( tess.VtxBuf.id ) ) + qglDeleteBuffersARB( 1, &tess.VtxBuf.id ); + tess.VtxBuf.id = 0; + } + if( qglIsBufferARB && tess.IdxBuf.id ) { + if( qglIsBufferARB( tess.IdxBuf.id ) ) + qglDeleteBuffersARB( 1, &tess.IdxBuf.id ); + tess.IdxBuf.id = 0; + } + + RB_DiscardSurface( ); + + if ( qglCreateProgram ) { + int i; + + for( i = 1; i < AL_NUMATTRIBUTES; i++ ) { + qglDisableVertexAttribArrayARB( i ); + } + + GL_Program( NULL ); + for ( i = 0; i < tr.numGLSLprograms; i++ ) { + qglDeleteProgram( tr.GLSLprograms[i]->handle ); + } + for ( i = 0; i < tr.numGLSLshaders; i++ ) { + qglDeleteShader( tr.GLSLshaders[i]->handle ); + } + } + + Com_Memset( &glState, 0, sizeof(glstate_t) ); + } + + // disable debugging + if( qglDebugMessageControlARB ) { + qglDebugMessageInsertARB( GL_DEBUG_SOURCE_APPLICATION_ARB, + GL_DEBUG_TYPE_OTHER_ARB, 1, + GL_DEBUG_SEVERITY_LOW_ARB, + -1, "debug log stopped" ); + GL_CheckDebugLog(); + qglDebugMessageControlARB( GL_DONT_CARE, GL_DONT_CARE, + GL_DONT_CARE, 0, NULL, GL_FALSE ); + } else if ( qglDebugMessageEnableAMD ) { + qglDebugMessageInsertAMD( GL_DEBUG_CATEGORY_APPLICATION_AMD, + GL_DEBUG_SEVERITY_LOW_AMD, 1, + 0, "debug log stopped" ); + GL_CheckDebugLog(); + qglDebugMessageEnableAMD( 0, 0, 0, NULL, GL_FALSE ); } R_DoneFreeType(); @@ -1289,7 +1534,7 @@ Touch all images to make sure they are resident */ void RE_EndRegistration( void ) { R_SyncRenderThread(); - if (!ri.Sys_LowPhysicalMemory()) { + if (!ri.Sys_LowPhysicalMemory() && GLimp_InBackend() ) { RB_ShowImages(); } } diff --git a/code/renderer/tr_light.c b/code/renderer/tr_light.c index 8915385..77666ec 100644 --- a/code/renderer/tr_light.c +++ b/code/renderer/tr_light.c @@ -94,11 +94,11 @@ void R_DlightBmodel( bmodel_t *bmodel ) { for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) { surf = bmodel->firstSurface + i; - if ( *surf->data == SF_FACE ) { + if ( surf->type == SF_FACE ) { ((srfSurfaceFace_t *)surf->data)->dlightBits[ tr.smpFrame ] = mask; - } else if ( *surf->data == SF_GRID ) { + } else if ( surf->type == SF_GRID ) { ((srfGridMesh_t *)surf->data)->dlightBits[ tr.smpFrame ] = mask; - } else if ( *surf->data == SF_TRIANGLES ) { + } else if ( surf->type == SF_TRIANGLES ) { ((srfTriangles_t *)surf->data)->dlightBits[ tr.smpFrame ] = mask; } } @@ -332,19 +332,21 @@ void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ) { d = VectorLength( ent->directedLight ); VectorScale( ent->lightDir, d, lightDir ); - for ( i = 0 ; i < refdef->num_dlights ; i++ ) { - dl = &refdef->dlights[i]; - VectorSubtract( dl->origin, lightOrigin, dir ); - d = VectorNormalize( dir ); + if( !backEnd.dlightBuffer ) { + for ( i = 0 ; i < refdef->num_dlights ; i++ ) { + dl = &refdef->dlights[i]; + VectorSubtract( dl->origin, lightOrigin, dir ); + d = VectorNormalize( dir ); - power = DLIGHT_AT_RADIUS * ( dl->radius * dl->radius ); - if ( d < DLIGHT_MINIMUM_RADIUS ) { - d = DLIGHT_MINIMUM_RADIUS; - } - d = power / ( d * d ); + power = DLIGHT_AT_RADIUS * ( dl->radius * dl->radius ); + if ( d < DLIGHT_MINIMUM_RADIUS ) { + d = DLIGHT_MINIMUM_RADIUS; + } + d = power / ( d * d ); - VectorMA( ent->directedLight, d, dl->color, ent->directedLight ); - VectorMA( lightDir, d, dir, lightDir ); + VectorMA( ent->directedLight, d, dl->color, ent->directedLight ); + VectorMA( lightDir, d, dir, lightDir ); + } } // clamp ambient @@ -392,3 +394,178 @@ int R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, ve return qtrue; } + +/* +================= +BoundingSphere + +helper function to generate bounding sphere of two spheres +returns the radius +================= +*/ +static float +BoundingSphere(vec3_t o1, float r1, vec3_t o2, float r2, vec3_t oOut) { + float d = Distance( o1, o2 ); + float factor; + + if( r1 >= r2 + d ) { + VectorCopy( o1, oOut ); + return r1; + } + if( r2 >= r1 + d ) { + VectorCopy( o2, oOut ); + return r2; + } + factor = 0.5f * (r2 + d - r1) / d; + oOut[0] = o1[0] + factor * (o2[0] - o1[0]); + oOut[1] = o1[1] + factor * (o2[1] - o1[1]); + oOut[2] = o1[2] + factor * (o2[2] - o1[2]); + return 0.5f * (r1 + d + r2); +} + +// node of dlight bounding volume tree +struct node { + vec3_t origin; + float radius; + int leaves, left, right; +}; + +static void +UploadDLights( const trRefdef_t *refdef, struct node *nodes, int root, byte *buffer, + int *ofsSpheres, int *ofsColors, int *ofsLinks ) { + if( root < refdef->num_dlights ) { + VectorCopy( refdef->dlights[root].origin, (float *)(buffer + *ofsSpheres) ); + ((float *)(buffer + *ofsSpheres))[3] = refdef->dlights[root].radius; + *ofsSpheres += backEnd.uDLSpheres.stride; + + VectorCopy( refdef->dlights[root].color, (float *)(buffer + *ofsColors) ); + *ofsColors += backEnd.uDLColors.stride; + + *(GLint *)(buffer + *ofsLinks) = 1; + *ofsLinks += backEnd.uDLLinks.stride; + } else { + root -= refdef->num_dlights; + + VectorCopy( nodes[root].origin, (float *)(buffer + *ofsSpheres) ); + ((float *)(buffer + *ofsSpheres))[3] = nodes[root].radius; + *ofsSpheres += backEnd.uDLSpheres.stride; + + *(GLint *)(buffer + *ofsLinks) = nodes[root].leaves; + *ofsLinks += backEnd.uDLLinks.stride; + + UploadDLights( refdef, nodes, nodes[root].left, buffer, + ofsSpheres, ofsColors, ofsLinks ); + UploadDLights( refdef, nodes, nodes[root].right, buffer, + ofsSpheres, ofsColors, ofsLinks ); + } +} +/* +================= +RB_PrepareDLights +================= +*/ +void RB_PrepareDLights( const trRefdef_t *refdef ) +{ + struct node nodes[ MAX_SHADER_DLIGHTS - 1 ]; + int numNodes = 0; + int activeNodes[ MAX_SHADER_DLIGHTS ]; + int numActiveNodes = refdef->num_dlights; + int i, j; + byte *buffer; + int ofsSpheres, ofsColors, ofsLinks; + float *ptrDebug; + + if( !backEnd.dlightBuffer ) + return; + + // bottom-up tree generator + for( i = 0; i < numActiveNodes; i++ ) { + activeNodes[i] = i; + } + + while( numActiveNodes > 1 ) { + float best = 999999.f; + int besti = 0, bestj = 0; + + for( i = 0; i < numActiveNodes; i++) { + for( j = i + 1; j < numActiveNodes; j++ ) { + float r, r1, r2; + vec3_t o; + vec_t *o1, *o2; + int leaves1, leaves2; + + if( activeNodes[i] < refdef->num_dlights ) { + r1 = 2.0f * refdef->dlights[activeNodes[i]].radius; + o1 = refdef->dlights[activeNodes[i]].origin; + leaves1 = 1; + } else { + int idx = activeNodes[i] - refdef->num_dlights; + r1 = nodes[idx].radius; + o1 = nodes[idx].origin; + leaves1 = nodes[idx].leaves; + } + if( activeNodes[j] < refdef->num_dlights ) { + r2 = 2.0f * refdef->dlights[activeNodes[j]].radius; + o2 = refdef->dlights[activeNodes[j]].origin; + leaves2 = 1; + } else { + int idx = activeNodes[j] - refdef->num_dlights; + r2 = nodes[idx].radius; + o2 = nodes[idx].origin; + leaves2 = nodes[idx].leaves; + } + + r = BoundingSphere(o1, r1, o2, r2, o); + if( r < best ) { + besti = i; + bestj = j; + VectorCopy(o, nodes[numNodes].origin); + nodes[numNodes].radius = r; + nodes[numNodes].left = activeNodes[i]; + nodes[numNodes].right = activeNodes[j]; + nodes[numNodes].leaves = leaves1 + leaves2; + best = r; + } + } + } + + activeNodes[besti] = refdef->num_dlights + numNodes++; + for( j = bestj + 1; j < numActiveNodes; j++ ) { + activeNodes[j - 1] = activeNodes[j]; + } + numActiveNodes--; + } + + buffer = RB_AllocScratch( backEnd.dlightBufferSize ); + + ofsSpheres = backEnd.uDLSpheres.offset; + ofsColors = backEnd.uDLColors.offset; + ofsLinks = backEnd.uDLLinks.offset; + ptrDebug = (float *)(buffer + backEnd.uDLDebug.offset); + if( refdef->num_dlights > 0 ) { + *(int *)(buffer + backEnd.uDLNum.offset) = 2 * refdef->num_dlights - 1; + UploadDLights( refdef, nodes, activeNodes[0], buffer, + &ofsSpheres, &ofsColors, &ofsLinks ); + } else { + *(int *)(buffer + backEnd.uDLNum.offset) = 0; + } + switch( r_lightmap->integer ) { + case 1: + ptrDebug[0] = 1.0f / backEnd.viewParms.viewportWidth; + ptrDebug[1] = 1.0f / backEnd.viewParms.viewportHeight; + break; + case 2: + ptrDebug[0] = 4.0f / backEnd.viewParms.viewportWidth; + ptrDebug[1] = 3.0f / backEnd.viewParms.viewportHeight; + break; + default: + ptrDebug[0] = 0.0f; + ptrDebug[1] = 0.0f; + break; + } + GL_UBO( backEnd.dlightBuffer ); + qglBufferDataARB( GL_UNIFORM_BUFFER, backEnd.dlightBufferSize, + buffer, GL_DYNAMIC_DRAW_ARB ); + GL_UBO( 0 ); + RB_FreeScratch( buffer ); +} diff --git a/code/renderer/tr_local.h b/code/renderer/tr_local.h index 58c3e2a..db3a89c 100644 --- a/code/renderer/tr_local.h +++ b/code/renderer/tr_local.h @@ -32,13 +32,20 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "iqm.h" #define GL_INDEX_TYPE GL_UNSIGNED_INT -typedef unsigned int glIndex_t; +typedef GLuint glIndex_t; // everything that is needed by the backend needs // to be double buffered to allow it to run in // parallel on a dual cpu machine #define SMP_FRAMES 2 +// To avoid allocating memory in the backend thread +// I preallocate a fixed size block and just allocate +// free temp memory within this block. The size can +// be set with the cVar r_scratchmegs, the default is +// defined here. +#define SMP_SCRATCHMEGS 4 + // 14 bits // can't be increased without changing bit packing for drawsurfs // see QSORT_SHADERNUM_SHIFT @@ -86,9 +93,11 @@ typedef struct { typedef struct image_s { char imgName[MAX_QPATH]; // game path, including extension + GLenum target; // GL_TEXTURE_2D, etc. int width, height; // source image int uploadWidth, uploadHeight; // after power of two and picmip but not including clamp to MAX_TEXTURE_SIZE GLuint texnum; // gl texture binding + GLuint buffer; // VBO for buffer textures int frameUsed; // for texture usage in frame statistics @@ -96,8 +105,10 @@ typedef struct image_s { int TMU; // only needed for voodoo2 qboolean mipmap; + int maxMipLevel; qboolean allowPicmip; - int wrapClampMode; // GL_CLAMP_TO_EDGE or GL_REPEAT + qboolean hasAlpha; + GLint wrapClampMode; // GL_CLAMP_TO_EDGE or GL_REPEAT struct image_s* next; } image_t; @@ -107,6 +118,8 @@ typedef struct image_s { typedef enum { SS_BAD, SS_PORTAL, // mirrors, portals, viewscreens + SS_DEPTH, // Z-only pass + SS_COPYDEPTH, // copy depth to texture SS_ENVIRONMENT, // sky box SS_OPAQUE, // opaque @@ -134,6 +147,9 @@ typedef enum { #define MAX_SHADER_STAGES 8 +// TMUs 0-7 may be used by shader stages, 8 and higher can be allocated globally +#define TMU_LIGHTGRID 8 + typedef enum { GF_NONE, @@ -277,6 +293,7 @@ typedef struct { typedef struct { image_t *image[MAX_IMAGE_ANIMATIONS]; + image_t *combinedImage; int numImageAnimations; float imageAnimationSpeed; @@ -290,6 +307,7 @@ typedef struct { qboolean isLightmap; qboolean vertexLightmap; qboolean isVideoMap; + int multitextureEnv; // 0, GL_MODULATE, GL_ADD } textureBundle_t; #define NUM_TEXTURE_BUNDLES 2 @@ -318,7 +336,8 @@ struct shaderCommands_s; // any change in the LIGHTMAP_* defines here MUST be reflected in // R_FindShader() in tr_bsp.c -#define LIGHTMAP_2D -4 // shader is for 2D rendering +#define LIGHTMAP_2D -5 // shader is for 2D rendering +#define LIGHTMAP_MD3 -4 // shader for MD3 rendering (interpolation) #define LIGHTMAP_BY_VERTEX -3 // pre-lit triangle models #define LIGHTMAP_WHITEIMAGE -2 #define LIGHTMAP_NONE -1 @@ -346,6 +365,109 @@ typedef struct { } fogParms_t; +typedef byte color4ub_t[4]; + +/* This is the interleaved layout of the vertex data in RAM and VBO. + * The struct simply stores all known data per vertex, even if + * it isn't needed because the shaders doesn't use e.g. normals. + * But keeping track of all shaders that may affect a vertex is + * simply too much hassle and building a new VBO for each shader + * is probably wasting more graphics memory than the unused data + * here. + */ +typedef struct glVertex_s { + vec3_t xyz; + float fogNum; + vec2_t tc1; + vec2_t tc2; + vec3_t normal; + color4ub_t color; +} glVertex_t; + +typedef struct vboInfo_s { + GLuint vbo; + glVertex_t *offset; +} vboInfo_t; +typedef struct iboInfo_s { + struct iboInfo_s *next; + vboInfo_t *vbo; + GLuint ibo; + int numIndexes; + int minIndex; + int maxIndex; + glIndex_t *offset; +} iboInfo_t; + +// NVIDIA uses special indexes for built-in Variables, +// these values are chosen to avoid conflicts with the +// built-in attributes we use: +typedef enum { + AL_VERTEX = 0, + AL_TIMES = 1, + AL_NORMAL = 2, + AL_COLOR = 3, + AL_COLOR2 = 4, + AL_TRANSX = 5, + AL_TRANSY = 6, + AL_TRANSZ = 7, + AL_TEXCOORD = 8, + AL_TEXCOORD2 = 9, + AL_TEXCOORD3 = 10, + AL_TEXCOORD4 = 11, + AL_AMBIENTLIGHT = 12, + AL_DIRECTEDLIGHT = 13, + AL_LIGHTDIR = 14, + AL_CAMERAPOS = 15, + AL_NUMATTRIBUTES = 16 +} attribLocation_t; + +// information about a uniform in an uniform buffer object +typedef struct BufUniform_s { + GLuint offset; + GLuint stride; +} BufUniform_t; + +// flags describing how the GL attributes (vertex, normal, tc1, tc2 and color) +// are bound in a shader +typedef enum { + GLA_COLOR_dynamic = 0x0001, + GLA_COLOR_vtxcolor = 0x0002, + GLA_COLOR_uniform = 0x0004, + GLA_COLOR_mask = 0x0007, + + GLA_TC1_dynamic = 0x0008, + GLA_TC1_texcoord = 0x0010, + GLA_TC1_lmcoord = 0x0020, + GLA_TC1_uniform = 0x0040, + GLA_TC1_mask = 0x0078, + + GLA_TC2_dynamic = 0x0080, + GLA_TC2_texcoord = 0x0100, + GLA_TC2_lmcoord = 0x0200, + GLA_TC2_uniform = 0x0400, + GLA_TC2_mask = 0x0780, + + GLA_NORMAL_dynamic = 0x1000, + GLA_VERTEX_dynamic = 0x2000, + GLA_FULL_dynamic = 0x4000 +} GLAttrFlag; +// shift GLA_TC_shift bits to get the next TC bundle +#define GLA_TC_shift 4 + +typedef struct GLSLshader_s { + GLhandleARB handle; + unsigned int hash; +} GLSLshader_t; + +typedef struct GLSLprogram_s { + GLhandleARB handle; + GLint unifFogPlanes; + GLint unifFogColors; + GLSLshader_t *vertex, *geometry, *fragment; + unsigned int attributes; // bit mask + GLuint QuerySum; +} GLSLprogram_t; + typedef struct shader_s { char name[MAX_QPATH]; // game path, including extension int lightmapIndex; // for a shader to match, both name and lightmapIndex must match @@ -369,24 +491,19 @@ typedef struct shader_s { qboolean entityMergable; // merge across entites optimizable (smoke, blood) qboolean isSky; + qboolean isDepth; skyParms_t sky; fogParms_t fogParms; float portalRange; // distance to fog out at - int multitextureEnv; // 0, GL_MODULATE, GL_ADD (FIXME: put in stage) - cullType_t cullType; // CT_FRONT_SIDED, CT_BACK_SIDED, or CT_TWO_SIDED - qboolean polygonOffset; // set for decals and other items that must be offset qboolean noMipMaps; // for console fonts, 2D elements, etc. qboolean noPicMip; // for images that must always be full resolution fogPass_t fogPass; // draw a blended pass, possibly with depth test equals - qboolean needsNormal; // not all shaders will need all data to be gathered - qboolean needsST1; - qboolean needsST2; - qboolean needsColor; + short allGLAttr, anyGLAttr; int numDeforms; deformStage_t deforms[MAX_SHADER_DEFORMS]; @@ -402,6 +519,7 @@ typedef struct shader_s { int numStates; // if non-zero this is a state shader struct shader_s *currentShader; // current state if this is a state shader struct shader_s *parentShader; // current state if this is a state shader + struct shader_s *depthShader; // associated shader for the Z-pass int currentState; // current state index for cycle purposes long expireTime; // time in milliseconds this expires @@ -409,6 +527,10 @@ typedef struct shader_s { int shaderStates[MAX_STATES_PER_SHADER]; // index to valid shader states + GLuint QueryID; + GLuint QueryResult; + GLSLprogram_t *GLSLprogram; + struct shader_s *next; } shader_t; @@ -491,18 +613,22 @@ typedef struct { orientationr_t or; orientationr_t world; vec3_t pvsOrigin; // may be different than or.origin for portals - qboolean isPortal; // true if this view is through a portal + int portalLevel; // number of portals we're looking through qboolean isMirror; // the portal is a mirror, invert the face culling + qboolean isFirst; // this is the first view of the scene, so glClear has to be called + qboolean noShadows; // don't compute shadows, used for portal surfaces int frameSceneNum; // copied from tr.frameSceneNum int frameCount; // copied from tr.frameCount cplane_t portalPlane; // clip anything behind this if mirroring int viewportX, viewportY, viewportWidth, viewportHeight; float fovX, fovY; float projectionMatrix[16]; - cplane_t frustum[4]; + cplane_t frustum[5]; vec3_t visBounds[2]; float zFar; stereoFrame_t stereoFrame; + int viewCluster; + int frustType; } viewParms_t; @@ -531,14 +657,18 @@ typedef enum { SF_FLARE, SF_ENTITY, // beams, rails, lightning, etc that can be determined by entity SF_DISPLAY_LIST, + SF_IBO, + SF_MD3_TEXTURE, + SF_FAR_PLANE, SF_NUM_SURFACE_TYPES, SF_MAX = 0x7fffffff // ensures that sizeof( surfaceType_t ) == sizeof( int ) } surfaceType_t; typedef struct drawSurf_s { - unsigned sort; // bit combination for fast compares - surfaceType_t *surface; // any of surface*_t + unsigned sort; // bit combination for fast compares + int shaderIndex; + surfaceType_t *surface; // any of surface*_t } drawSurf_t; #define MAX_FACE_POINTS 64 @@ -590,6 +720,7 @@ typedef struct srfGridMesh_s { // vertexes int width, height; + int vboStart; float *widthLodError; float *heightLodError; drawVert_t verts[1]; // variable sized @@ -609,6 +740,7 @@ typedef struct { int numPoints; int numIndices; int ofsIndices; + int vboStart; float points[1][VERTEXSIZE]; // variable sized // there is a variable length list of indices here also } srfSurfaceFace_t; @@ -631,9 +763,22 @@ typedef struct { int *indexes; int numVerts; + int vboStart; + drawVert_t *verts; } srfTriangles_t; + +// MD3 surface stored in a texture on the GPU +typedef struct { + surfaceType_t surfaceType; + image_t *image; + vboInfo_t *VBO; + iboInfo_t *IBO; + int framesPerRow; + float scaleX, scaleY; +} srfMD3Texture_t; + // inter-quake-model typedef struct { int num_vertexes; @@ -668,8 +813,23 @@ typedef struct srfIQModel_s { int first_triangle, num_triangles; } srfIQModel_t; +// data loaded into an IBO/VBO +typedef struct srfIBO_s { + surfaceType_t surfaceType; + shader_t *shader; + iboInfo_t ibo; +} srfIBO_t; + +typedef enum tessMode_e { + TESS_COUNT = 0, + TESS_VERTEX = 1, + TESS_INDEX = 2, + TESS_ALL = 3 +} tessMode_t; -extern void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])(void *); +#define tesselate( mode, surf ) rb_surfaceTable[ *(surf) ] ( (mode), (surf) ) +extern void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])(tessMode_t, + surfaceType_t *); /* ============================================================================== @@ -691,7 +851,8 @@ BRUSH MODELS typedef struct msurface_s { int viewCount; // if == tr.viewCount, already added struct shader_s *shader; - int fogIndex; + short fogIndex; + short type; // copy of data->surfaceType surfaceType_t *data; // any of srf*_t } msurface_t; @@ -721,10 +882,18 @@ typedef struct mnode_s { typedef struct { vec3_t bounds[2]; // for culling msurface_t *firstSurface; - int numSurfaces; + int numSurfaces; + int numIBOSurfaces; + srfIBO_t *iboSurfaces; } bmodel_t; typedef struct { + vec3_t mins, maxs; // for bounding box culling + int numIBOSurfaces[7]; + srfIBO_t *iboSurfaces[7]; +} mcluster_t; + +typedef struct { char name[MAX_QPATH]; // ie: maps/tim_dm2.bsp char baseName[MAX_QPATH]; // ie: tim_dm2 @@ -761,6 +930,7 @@ typedef struct { int numClusters; int clusterBytes; const byte *vis; // may be passed in by CM_LoadMap to save space + mcluster_t *clusters; byte *novis; // clusterBytes of 0xff @@ -784,12 +954,13 @@ typedef enum { typedef struct model_s { char name[MAX_QPATH]; modtype_t type; - int index; // model = tr.models[model->index] + int index; // model = tr.models[model->index] - int dataSize; // just for listing purposes + int dataSize; // just for listing purposes bmodel_t *bmodel; // only if type == MOD_BRUSH md3Header_t *md3[MD3_MAX_LODS]; // only if type == MOD_MESH - void *modelData; // only if type == (MOD_MD4 | MOD_MDR | MOD_IQM) + void *modelData; // only if type == (MOD_MD4 | MOD_MDR | MOD_IQM ) + // also MOD_MESH, if using precalculated VBOs int numLods; } model_t; @@ -829,18 +1000,37 @@ the bits are allocated as follows: 21 - 31 : sorted shader index TTimo - 1.32 -0-1 : dlightmap index -2-6 : fog index +17-31 : sorted shader index 7-16 : entity index -17-30 : sorted shader index +5-6 : dlights value: 0 = no dlights, 1 = dlights and surface, 2 = dlight only +0-4 : fog index */ -#define QSORT_FOGNUM_SHIFT 2 -#define QSORT_ENTITYNUM_SHIFT 7 -#define QSORT_SHADERNUM_SHIFT (QSORT_ENTITYNUM_SHIFT+ENTITYNUM_BITS) +// sizes of the sort fields +#define QSORT_SHADERNUM_BITS 15 +#define QSORT_ENTITYNUM_BITS 10 +#define QSORT_DLIGHT_BITS 2 +#define QSORT_FOGNUM_BITS 5 + #if (QSORT_SHADERNUM_SHIFT+SHADERNUM_BITS) > 32 #error "Need to update sorting, too many bits." #endif +// derived shifts and masks +#define QSORT_FOGNUM_SHIFT 0 +#define QSORT_DLIGHT_SHIFT (QSORT_FOGNUM_SHIFT + QSORT_FOGNUM_BITS) +#define QSORT_ENTITYNUM_SHIFT (QSORT_DLIGHT_SHIFT + QSORT_DLIGHT_BITS) +#define QSORT_SHADERNUM_SHIFT (QSORT_ENTITYNUM_SHIFT + QSORT_ENTITYNUM_BITS) + +#define QSORT_FOGNUM_MASK ((1 << QSORT_DLIGHT_SHIFT) - (1 << QSORT_FOGNUM_SHIFT)) +#define QSORT_DLIGHT_MASK ((1 << QSORT_ENTITYNUM_SHIFT) - (1 << QSORT_DLIGHT_SHIFT)) +#define QSORT_ENTITYNUM_MASK ((1 << QSORT_SHADERNUM_SHIFT) - (1 << QSORT_ENTITYNUM_SHIFT)) +#define QSORT_SHADERNUM_MASK (0xffffffff - (1 << QSORT_SHADERNUM_SHIFT) + 1) + +#define QSORT_FOGNUM(sort) ((sort & QSORT_FOGNUM_MASK) >> QSORT_FOGNUM_SHIFT) +#define QSORT_DLIGHT(sort) ((sort & QSORT_DLIGHT_MASK) >> QSORT_DLIGHT_SHIFT) +#define QSORT_ENTITYNUM(sort) ((sort & QSORT_ENTITYNUM_MASK) >> QSORT_ENTITYNUM_SHIFT) +#define QSORT_SHADERNUM(sort) ((sort & QSORT_SHADERNUM_MASK) >> QSORT_SHADERNUM_SHIFT) + extern int gl_filter_min, gl_filter_max; /* @@ -862,17 +1052,66 @@ typedef struct { #define FUNCTABLE_SIZE2 10 #define FUNCTABLE_MASK (FUNCTABLE_SIZE-1) +// binding state of a single attribute +typedef struct { + enum { + RA_UNSPEC, + RA_POINTER, + RA_VEC + } attrType; + GLuint VBO; + GLint size; + GLenum type; + GLsizei stride; + void *ptr; + GLuint divisor; + vec4_t vec; +} glAttribState_t; // the renderer front end should never modify glstate_t typedef struct { - int currenttextures[2]; - int currenttmu; + int currenttextures[MAX_SHADER_STAGES]; + GLenum texTargets[MAX_SHADER_STAGES]; qboolean finishCalled; - int texEnv[2]; - int faceCulling; + qboolean texEnabled[NUM_TEXTURE_BUNDLES]; + int texEnv[NUM_TEXTURE_BUNDLES]; + int faceCulling; unsigned long glStateBits; + GLint activeTexture, clientActiveTexture; + GLuint currentVBO, currentIBO, currentUBO; + GLSLprogram_t *currentProgram; + + glAttribState_t glAttribute[ AL_NUMATTRIBUTES ]; } glstate_t; +// this struct holds all GL state data for a single render call (glDraw*) +typedef struct { + unsigned long stateBits; + int faceCulling; + GLSLprogram_t *program; + int numImages; + image_t *image[MAX_SHADER_STAGES]; + glAttribState_t attrib[AL_NUMATTRIBUTES]; +} glRenderState_t; +#define InitState( st ) Com_Memset( (st), 0, sizeof(glRenderState_t) ) +#define SetAttrPointerInst( st, ind, vbo, s, t, str, p, d ) \ + (st)->attrib[ind].attrType = RA_POINTER; \ + (st)->attrib[ind].VBO = (vbo); \ + (st)->attrib[ind].size = (s); \ + (st)->attrib[ind].type = (t); \ + (st)->attrib[ind].stride = (str); \ + (st)->attrib[ind].ptr = (p); \ + (st)->attrib[ind].divisor = (d) +#define SetAttrPointer( st, ind, vbo, s, t, str, p ) \ + SetAttrPointerInst( st, ind, vbo, s, t, str, p, 0 ) +#define SetAttrVec4f( st, ind, x, y, z, w ) \ + (st)->attrib[ind].attrType = RA_VEC; \ + (st)->attrib[ind].vec[0] = (x); \ + (st)->attrib[ind].vec[1] = (y); \ + (st)->attrib[ind].vec[2] = (z); \ + (st)->attrib[ind].vec[3] = (w) +#define SetAttrUnspec( st, ind ) \ + (st)->attrib[ind].attrType = RA_UNSPEC typedef struct { int c_surfaces, c_shaders, c_vertexes, c_indexes, c_totalIndexes; @@ -888,6 +1127,14 @@ typedef struct { int msec; // total msec for backend run } backEndCounters_t; +typedef struct glStreamBuffer_s { + GLuint id; + qboolean mapped; + GLsizei size; + GLsizei nextFree; + GLsizei current; +} glStreamBuffer_t; + // all state modified by the back end is seperated // from the front end state typedef struct { @@ -899,11 +1146,32 @@ typedef struct { qboolean isHyperspace; trRefEntity_t *currentEntity; qboolean skyRenderedThisView; // flag for drawing sun + int currentInstance; qboolean projection2D; // if qtrue, drawstretchpic doesn't need to change modes byte color2D[4]; qboolean vertexes2D; // shader needs to be finished trRefEntity_t entity2D; // currentEntity will point at this when doing 2D rendering + + vboInfo_t *worldVBO; // VBO containing all world vertexes + + GLSLprogram_t *normalProgram; // GLSL program to render normals + GLSLprogram_t *normalProgramMD3; // GLSL program to render normals + + GLuint dlightBuffer; + GLsizei dlightBufferSize; + BufUniform_t uDLSpheres; + BufUniform_t uDLColors; + BufUniform_t uDLLinks; + BufUniform_t uDLNum; + BufUniform_t uDLDebug; + + GLuint fogBuffer; + GLsizei fogBufferSize; + BufUniform_t uLightGridScale; + BufUniform_t uLightGridOffset; + BufUniform_t uFogPlanes; + BufUniform_t uFogColors; // density in .w } backEndState_t; /* @@ -931,6 +1199,8 @@ typedef struct { world_t *world; const byte *externalVisData; // from RE_SetWorldVisData, shared with CM_Load + mcluster_t *clusters; + int visCluster; image_t *defaultImage; image_t *scratchImage[32]; @@ -939,16 +1209,28 @@ typedef struct { image_t *flareImage; image_t *whiteImage; // full of 0xff image_t *identityLightImage; // full of tr.identityLightByte + image_t *depthImage; shader_t *defaultShader; shader_t *shadowShader; + shader_t *buildVBOShader; + shader_t *buildIBOShader; + shader_t *preparePortalShader; + shader_t *finalisePortalShader; shader_t *projectionShadowShader; shader_t *flareShader; shader_t *sunShader; + shader_t *fogShader; + shader_t *defaultMD3Shader; + shader_t *copyDepthShader; int numLightmaps; + qboolean hasDeluxemaps; + float deluxeOffset; image_t **lightmaps; + GLuint lightGridTexture; + int lightmapWidth, lightmapHeight; trRefEntity_t *currentEntity; trRefEntity_t worldEntity; // point currentEntity at this when rendering world @@ -966,8 +1248,6 @@ typedef struct { trRefdef_t refdef; - int viewCluster; - vec3_t sunLight; // from the sky shader for this level vec3_t sunDirection; @@ -990,6 +1270,10 @@ typedef struct { int numShaders; shader_t *shaders[MAX_SHADERS]; shader_t *sortedShaders[MAX_SHADERS]; + int numGLSLshaders; + GLSLshader_t *GLSLshaders[2 * MAX_SHADERS]; + int numGLSLprograms; + GLSLprogram_t *GLSLprograms[MAX_SHADERS]; int numSkins; skin_t *skins[MAX_SKINS]; @@ -1002,22 +1286,36 @@ typedef struct { float fogTable[FOG_TABLE_SIZE]; } trGlobals_t; +// globals that are filled in InitOpenGL and should not be cleared in R_Init +typedef struct { + qboolean floatTextures; + qboolean pixelBufferObjects; + qboolean gpuShader4; + GLint maxTextureUnits; + GLint maxTextureImageUnits; + GLint maxVertexTextureImageUnits; + GLuint timerQuery; + GLuint64EXT timerResult; + qboolean timerRunning; + int portalBits, shadowBits; + GLuint portalMask, shadowMask, portalLevels; + qboolean textureFilterAnisotropic; + int maxAnisotropy; + float displayAspect; +} glGlobals_t; + + extern backEndState_t backEnd; extern trGlobals_t tr; +extern glGlobals_t glGlobals; extern glconfig_t glConfig; // outside of TR since it shouldn't be cleared during ref re-init extern glstate_t glState; // outside of TR since it shouldn't be cleared during ref re-init -// These two variables should live inside glConfig but can't because of compatibility issues to the original ID vms. -// If you release a stand-alone game and your mod uses tr_types.h from this build you can safely move them to -// the glconfig_t struct. -extern qboolean textureFilterAnisotropic; -extern int maxAnisotropy; -extern float displayAspect; - // // cvars // +extern cvar_t *r_scratchmegs; extern cvar_t *r_flareSize; extern cvar_t *r_flareFade; // coefficient for the flare intensity falloff function. @@ -1040,22 +1338,17 @@ extern cvar_t *r_stencilbits; // number of desired stencil bits extern cvar_t *r_depthbits; // number of desired depth bits extern cvar_t *r_colorbits; // number of desired color bits, only relevant for fullscreen extern cvar_t *r_texturebits; // number of desired texture bits + // 0 = use framebuffer depth + // 16 = use 16-bit textures + // 32 = use 32-bit textures + // all else = error extern cvar_t *r_ext_multisample; - // 0 = use framebuffer depth - // 16 = use 16-bit textures - // 32 = use 32-bit textures - // all else = error extern cvar_t *r_measureOverdraw; // enables stencil buffer overdraw measurement extern cvar_t *r_lodbias; // push/pull LOD transitions extern cvar_t *r_lodscale; -extern cvar_t *r_primitives; // "0" = based on compiled vertex array existance - // "1" = glDrawElemet tristrips - // "2" = glDrawElements triangles - // "-1" = no drawing - extern cvar_t *r_inGameVideo; // controls whether in game video should be draw extern cvar_t *r_fastsky; // controls whether sky should be cleared or drawn extern cvar_t *r_drawSun; // controls drawing of sun quad @@ -1087,6 +1380,22 @@ extern cvar_t *r_ext_texture_env_add; extern cvar_t *r_ext_texture_filter_anisotropic; extern cvar_t *r_ext_max_anisotropy; +extern cvar_t *r_ext_vertex_buffer_object; +extern cvar_t *r_ext_map_buffer_range; +extern cvar_t *r_ext_vertex_shader; +extern cvar_t *r_ext_geometry_shader; +extern cvar_t *r_ext_framebuffer_object; +extern cvar_t *r_ext_occlusion_query; +extern cvar_t *r_ext_timer_query; +extern cvar_t *r_ext_instanced_arrays; +extern cvar_t *r_ext_texture_float; +extern cvar_t *r_ext_texture_buffer_object; +extern cvar_t *r_ext_uniform_buffer_object; +extern cvar_t *r_ext_texture3D; +extern cvar_t *r_ext_separate_stencil; +extern cvar_t *r_ext_debug_output; +extern cvar_t *r_ext_gpu_shader4; +extern cvar_t *r_ext_blend_func_extended; extern cvar_t *r_nobind; // turns off binding to appropriate textures extern cvar_t *r_singleShader; // make most world faces use default shader @@ -1147,6 +1456,12 @@ extern cvar_t *r_saveFontData; extern cvar_t *r_marksOnTriangleMeshes; +extern cvar_t *r_flush; +extern cvar_t *r_minIBOSize; +extern cvar_t *r_depthPass; +extern cvar_t *r_perPixelLighting; +extern cvar_t *r_parallax; + //==================================================================== float R_NoiseGet4f( float x, float y, float z, float t ); @@ -1164,10 +1479,8 @@ void R_AddLightningBoltSurfaces( trRefEntity_t *e ); void R_AddPolygonSurfaces( void ); -void R_DecomposeSort( unsigned sort, int *entityNum, shader_t **shader, - int *fogNum, int *dlightMap ); - void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int fogIndex, int dlightMap ); +void R_SortSurfaces( drawSurf_t *source, int size ); #define CULL_IN 0 // completely unclipped @@ -1185,14 +1498,53 @@ void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms, /* ** GL wrapper/helper functions */ -void GL_Bind( image_t *image ); +void GL_ActiveTexture( int unit ); +void GL_BindTexture( GLint texnum ); +void GL_UnbindAllTextures( void ); void GL_SetDefaultState (void); -void GL_SelectTexture( int unit ); void GL_TextureMode( const char *string ); void GL_CheckErrors( void ); -void GL_State( unsigned long stateVector ); -void GL_TexEnv( int env ); -void GL_Cull( int cullType ); +void GL_CheckDebugLog( void ); +void GL_VBO( GLuint vbo ); +void GL_IBO( GLuint ibo ); +void GL_UBO( GLuint ubo ); + +void RB_InitScratchMemory( void ); +void *RB_AllocScratch( size_t amount ); +void RB_FreeScratch( void *ptr ); + +GLSLshader_t *RB_CompileShader( const char *name, GLenum type, + const char **code, int parts ); +GLSLprogram_t *RB_CompileProgram( const char *name, + const char **VScode, int VSparts, + const char **GScode, int GSparts, + int nVerticesOut, int inType, int outType, + const char **FScode, int FSparts, + unsigned int attributes ); +void GL_Program( GLSLprogram_t *program ); +void GL_TexEnv( int tmu, int env ); +void GL_LockArrays( glRenderState_t * ); +void GL_UnlockArrays( void ); + +#define QUERY_RUNNING_BIT 0x80000000 +#define QUERY_MASK 0x7fffffff +#define QUERY_RESULT(q) (*(q) & QUERY_MASK) + +void GL_StartQuery( GLuint query, GLuint *result ); +void GL_EndQuery( GLuint query, GLuint *result ); +void GL_GetQuery( GLuint query, GLuint *result ); + +void GL_DrawElements( glRenderState_t *state, + int numIndexes, GLuint IBO, + const glIndex_t *indexes, + glIndex_t start, glIndex_t end ); +void GL_DrawInstanced( glRenderState_t *state, + int numIndexes, GLuint IBO, + const glIndex_t *indexes, + glIndex_t start, glIndex_t end, + int instances ); +void GL_DrawArrays( glRenderState_t *state, + GLenum mode, GLint first, GLuint count ); #define GLS_SRCBLEND_ZERO 0x00000001 #define GLS_SRCBLEND_ONE 0x00000002 @@ -1213,14 +1565,25 @@ void GL_Cull( int cullType ); #define GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA 0x00000060 #define GLS_DSTBLEND_DST_ALPHA 0x00000070 #define GLS_DSTBLEND_ONE_MINUS_DST_ALPHA 0x00000080 +#define GLS_DSTBLEND_SRC1_COLOR 0x00000090 #define GLS_DSTBLEND_BITS 0x000000f0 #define GLS_DEPTHMASK_TRUE 0x00000100 +#define GLS_COLORMASK_FALSE 0x00000200 +#define GLS_POLYGON_OFFSET 0x00000400 #define GLS_POLYMODE_LINE 0x00001000 -#define GLS_DEPTHTEST_DISABLE 0x00010000 +#define GLS_DEPTHTEST_DISABLE 0x00010000 #define GLS_DEPTHFUNC_EQUAL 0x00020000 +#define GLS_DEPTHFUNC_ALWAYS 0x00040000 +#define GLS_DEPTHFUNC_BITS 0x00060000 + +#define GLS_DEPTHRANGE_0_TO_1 0x00000000 +#define GLS_DEPTHRANGE_0_TO_0 0x00100000 +#define GLS_DEPTHRANGE_1_TO_1 0x00200000 +#define GLS_DEPTHRANGE_0_TO_03 0x00300000 +#define GLS_DEPTHRANGE_BITS 0x00300000 #define GLS_ATEST_GT_0 0x10000000 #define GLS_ATEST_LT_80 0x20000000 @@ -1247,9 +1610,11 @@ model_t *R_AllocModel( void ); void R_Init( void ); image_t *R_FindImageFile( const char *name, qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ); -image_t *R_CreateImage( const char *name, const byte *pic, int width, int height, qboolean mipmap +image_t *R_CreateImage( const char *name, byte *pic, int width, int height, qboolean mipmap , qboolean allowPicmip, int wrapClampMode ); qboolean R_GetModeInfo( int *width, int *height, float *windowAspect, int mode ); +image_t *R_FindHeightMapFile( const char *name, qboolean mipmap, int glWrapClampMode ); +image_t *R_CombineImages( int num, image_t **images ); void R_SetColorMappings( void ); void R_GammaCorrect( byte *buffer, int bufSize ); @@ -1284,6 +1649,7 @@ shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImag shader_t *R_GetShaderByHandle( qhandle_t hShader ); shader_t *R_GetShaderByState( int index, long *cycleTime ); shader_t *R_FindShaderByName( const char *name ); +void R_SortShaders( void ); void R_InitShaders( void ); void R_ShaderList_f( void ); void R_RemapShader(const char *oldShader, const char *newShader, const char *timeOffset); @@ -1304,6 +1670,7 @@ qboolean GLimp_SpawnRenderThread( void (*function)( void ) ); void *GLimp_RendererSleep( void ); void GLimp_FrontEndSleep( void ); void GLimp_WakeRenderer( void *data ); +qboolean GLimp_InBackend( void ); void GLimp_LogComment( char *comment ); void GLimp_Minimize(void); @@ -1322,36 +1689,56 @@ TESSELATOR/SHADER DECLARATIONS ==================================================================== */ -typedef byte color4ub_t[4]; - typedef struct stageVars { - color4ub_t colors[SHADER_MAX_VERTEXES]; - vec2_t texcoords[NUM_TEXTURE_BUNDLES][SHADER_MAX_VERTEXES]; + color4ub_t *colors; + vec2_t *texcoords[NUM_TEXTURE_BUNDLES]; } stageVars_t; +typedef struct instanceVars_s { + vec4_t times; + vec4_t transX; + vec4_t transY; + vec4_t transZ; + vec4_t texCoord; + vec4_t lightDir; + vec4_t ambLight; + vec3_t dirLight; + color4ub_t color; +} instanceVars_t; typedef struct shaderCommands_s { - glIndex_t indexes[SHADER_MAX_INDEXES] QALIGN(16); - vec4_t xyz[SHADER_MAX_VERTEXES] QALIGN(16); - vec4_t normal[SHADER_MAX_VERTEXES] QALIGN(16); - vec2_t texCoords[SHADER_MAX_VERTEXES][2] QALIGN(16); - color4ub_t vertexColors[SHADER_MAX_VERTEXES] QALIGN(16); - int vertexDlightBits[SHADER_MAX_VERTEXES] QALIGN(16); - - stageVars_t svars QALIGN(16); - - color4ub_t constantColor255[SHADER_MAX_VERTEXES] QALIGN(16); + glIndex_t *indexPtr; + glVertex_t *vertexPtr; + int numVertexes; + int indexRange; + int numIndexes[3]; + int minIndex[3], maxIndex[3]; + + // vertex and index data is tesselated into these buffers + void *vertexBuffer; + glStreamBuffer_t VtxBuf; + glStreamBuffer_t IdxBuf; + + int dlightBits; // or together of all vertexDlightBits + stageVars_t svars; + + iboInfo_t *firstIBO; + + // data for vertex shader animation + image_t *dataTexture; + image_t *imgOverride; // use this image for shader img + int framesPerRow; + float scaleX, scaleY; shader_t *shader; - float shaderTime; + float shaderTime; int fogNum; - int dlightBits; // or together of all vertexDlightBits - - int numIndexes; - int numVertexes; + // instancing + int numInstances; + instanceVars_t instances[ MAX_ENTITIES ]; // info extracted from current shader int numPasses; @@ -1361,15 +1748,23 @@ typedef struct shaderCommands_s extern shaderCommands_t tess; -void RB_BeginSurface(shader_t *shader, int fogNum ); +void RB_SetGL2D( glRenderState_t *state ); +void RB_BeginSurface(shader_t *shader ); +void RB_AllocateSurface(void); +void RB_DiscardSurface(void); void RB_EndSurface(void); -void RB_CheckOverflow( int verts, int indexes ); -#define RB_CHECKOVERFLOW(v,i) if (tess.numVertexes + (v) >= SHADER_MAX_VERTEXES || tess.numIndexes + (i) >= SHADER_MAX_INDEXES ) {RB_CheckOverflow(v,i);} void RB_StageIteratorGeneric( void ); void RB_StageIteratorSky( void ); void RB_StageIteratorVertexLitTexture( void ); void RB_StageIteratorLightmappedMultitexture( void ); +void RB_StageIteratorGLSL( void ); +void RB_StageIteratorPreparePortal( void ); +void RB_StageIteratorFinalisePortal( void ); +void RB_StageIteratorCopyDepth( void ); +void RB_StageIteratorBuildWorldVBO( void ); +void RB_StageIteratorBuildIBO( void ); +void RB_LightSurface( void ); void RB_AddQuadStamp( vec3_t origin, vec3_t left, vec3_t up, byte *color ); void RB_AddQuadStampExt( vec3_t origin, vec3_t left, vec3_t up, byte *color, float s1, float t1, float s2, float t2 ); @@ -1416,7 +1811,7 @@ void R_DlightBmodel( bmodel_t *bmodel ); void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ); void R_TransformDlights( int count, dlight_t *dl, orientationr_t *or ); int R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ); - +void RB_PrepareDLights( const trRefdef_t *refdef ); /* ============================================================ @@ -1438,7 +1833,7 @@ SKIES ============================================================ */ -void R_BuildCloudData( shaderCommands_t *shader ); +void R_BuildCloudData( shaderCommands_t *input, shader_t *shader ); void R_InitSkyTexCoords( float cloudLayerHeight ); void R_DrawSkyBox( shaderCommands_t *shader ); void RB_DrawSun( void ); @@ -1520,17 +1915,17 @@ ANIMATED MODELS // void R_MakeAnimModel( model_t *model ); haven't seen this one really, so not needed I guess. void R_AddAnimSurfaces( trRefEntity_t *ent ); -void RB_SurfaceAnim( md4Surface_t *surfType ); +void RB_SurfaceAnim( tessMode_t mode, surfaceType_t *surfType ); #ifdef RAVENMD4 void R_MDRAddAnimSurfaces( trRefEntity_t *ent ); -void RB_MDRSurfaceAnim( md4Surface_t *surface ); +void RB_MDRSurfaceAnim( tessMode_t mode, surfaceType_t *surface ); #endif qboolean R_LoadIQM (model_t *mod, void *buffer, int filesize, const char *name ); void R_AddIQMSurfaces( trRefEntity_t *ent ); -void RB_IQMSurfaceAnim( surfaceType_t *surface ); +void RB_IQMSurfaceAnim( tessMode_t mode, surfaceType_t *surface ); int R_IQMLerpTag( orientation_t *tag, iqmData_t *data, - int startFrame, int endFrame, - float frac, const char *tagName ); + int startFrame, int endFrame, + float frac, const char *tagName ); /* ============================================================= @@ -1556,26 +1951,25 @@ void R_TransformClipToWindow( const vec4_t clip, const viewParms_t *view, vec4_t void RB_DeformTessGeometry( void ); -void RB_CalcEnvironmentTexCoords( float *dstTexCoords ); -void RB_CalcFogTexCoords( float *dstTexCoords ); -void RB_CalcScrollTexCoords( const float scroll[2], float *dstTexCoords ); -void RB_CalcRotateTexCoords( float rotSpeed, float *dstTexCoords ); -void RB_CalcScaleTexCoords( const float scale[2], float *dstTexCoords ); -void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *dstTexCoords ); -void RB_CalcTransformTexCoords( const texModInfo_t *tmi, float *dstTexCoords ); -void RB_CalcModulateColorsByFog( unsigned char *dstColors ); -void RB_CalcModulateAlphasByFog( unsigned char *dstColors ); -void RB_CalcModulateRGBAsByFog( unsigned char *dstColors ); -void RB_CalcWaveAlpha( const waveForm_t *wf, unsigned char *dstColors ); -void RB_CalcWaveColor( const waveForm_t *wf, unsigned char *dstColors ); -void RB_CalcAlphaFromEntity( unsigned char *dstColors ); -void RB_CalcAlphaFromOneMinusEntity( unsigned char *dstColors ); -void RB_CalcStretchTexCoords( const waveForm_t *wf, float *texCoords ); -void RB_CalcColorFromEntity( unsigned char *dstColors ); -void RB_CalcColorFromOneMinusEntity( unsigned char *dstColors ); -void RB_CalcSpecularAlpha( unsigned char *alphas ); -void RB_CalcDiffuseColor( unsigned char *colors ); - +void RB_CalcEnvironmentTexCoords( vec2_t *dstTexCoords, int numVertexes ); +void RB_CalcFogTexCoords( vec2_t *dstTexCoords, int skip, int numVertexes ); +void RB_CalcScrollTexCoords( const float scroll[2], vec2_t *dstTexCoords, int numVertexes ); +void RB_CalcRotateTexCoords( float rotSpeed, vec2_t *dstTexCoords, int numVertexes ); +void RB_CalcScaleTexCoords( const float scale[2], vec2_t *dstTexCoords, int numVertexes ); +void RB_CalcTurbulentTexCoords( const waveForm_t *wf, vec2_t *dstTexCoords, int numVertexes ); +void RB_CalcTransformTexCoords( const texModInfo_t *tmi, vec2_t *dstTexCoords, int numVertexes ); +void RB_CalcModulateColorsByFog( color4ub_t *dstColors, int numVertexes ); +void RB_CalcModulateAlphasByFog( color4ub_t *dstColors, int numVertexes ); +void RB_CalcModulateRGBAsByFog( color4ub_t *dstColors, int numVertexes ); +void RB_CalcWaveAlpha( const waveForm_t *wf, color4ub_t *dstColors, int numVertexes ); +void RB_CalcWaveColor( const waveForm_t *wf, color4ub_t *dstColors, int numVertexes ); +void RB_CalcAlphaFromEntity( color4ub_t *dstColors, int numVertexes ); +void RB_CalcAlphaFromOneMinusEntity( color4ub_t *dstColors, int numVertexes ); +void RB_CalcStretchTexCoords( const waveForm_t *wf, vec2_t *texCoords, int numVertexes ); +void RB_CalcColorFromEntity( color4ub_t *dstColors, int numVertexes ); +void RB_CalcColorFromOneMinusEntity( color4ub_t *dstColors, int numVertexes ); +void RB_CalcSpecularAlpha( color4ub_t *alphas, int numVertexes ); +void RB_CalcDiffuseColor( color4ub_t *colors, int numVertexes ); /* ============================================================= @@ -1640,6 +2034,15 @@ typedef struct { typedef struct { int commandId; + int client; + float x, y; + float w, h; + float s1, t1; + float s2, t2; +} stretchRawCommand_t; + +typedef struct { + int commandId; trRefdef_t refdef; viewParms_t viewParms; drawSurf_t *drawSurfs; @@ -1681,6 +2084,7 @@ typedef enum { RC_END_OF_LIST, RC_SET_COLOR, RC_STRETCH_PIC, + RC_STRETCH_RAW, RC_DRAW_SURFS, RC_DRAW_BUFFER, RC_SWAP_BUFFERS, @@ -1734,7 +2138,11 @@ void RE_SetColor( const float *rgba ); void RE_StretchPic ( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ); void RE_BeginFrame( stereoFrame_t stereoFrame ); +#ifdef RETURN_GLTIME +void RE_EndFrame( int *frontEndMsec, int *backEndMsec, int *GLMsec ); +#else void RE_EndFrame( int *frontEndMsec, int *backEndMsec ); +#endif void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, unsigned char *image_buffer, int padding); size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality, diff --git a/code/renderer/tr_main.c b/code/renderer/tr_main.c index e6ab253..4e2038a 100644 --- a/code/renderer/tr_main.c +++ b/code/renderer/tr_main.c @@ -26,6 +26,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include // memcpy trGlobals_t tr; +glGlobals_t glGlobals; static float s_flipMatrix[16] = { // convert from our coordinate system (looking down X) @@ -42,6 +43,7 @@ refimport_t ri; // entities that will have procedurally generated surfaces will just // point at this for their sorting surface surfaceType_t entitySurface = SF_ENTITY; +surfaceType_t farPlaneSurface = SF_FAR_PLANE; /* ================= @@ -453,6 +455,7 @@ the projection matrix. void R_SetupFrustum (viewParms_t *dest, float xmin, float xmax, float ymax, float zProj, float stereoSep) { vec3_t ofsorigin; + vec3_t corner[4]; float oppleg, adjleg, length; int i; @@ -470,6 +473,13 @@ void R_SetupFrustum (viewParms_t *dest, float xmin, float xmax, float ymax, floa VectorScale(dest->or.axis[0], oppleg, dest->frustum[1].normal); VectorMA(dest->frustum[1].normal, -adjleg, dest->or.axis[1], dest->frustum[1].normal); + + VectorScale(dest->or.axis[0], zProj, corner[0]); + VectorCopy(corner[0], corner[2]); + VectorMA(corner[0], xmax, dest->or.axis[1], corner[0]); + VectorCopy(corner[0], corner[1]); + VectorMA(corner[2], xmin, dest->or.axis[1], corner[2]); + VectorCopy(corner[2], corner[3]); } else { @@ -482,10 +492,18 @@ void R_SetupFrustum (viewParms_t *dest, float xmin, float xmax, float ymax, floa VectorScale(dest->or.axis[0], oppleg / length, dest->frustum[0].normal); VectorMA(dest->frustum[0].normal, zProj / length, dest->or.axis[1], dest->frustum[0].normal); + VectorScale(dest->or.axis[0], zProj, corner[0]); + VectorCopy(corner[0], corner[2]); + VectorMA(corner[0], oppleg, dest->or.axis[1], corner[0]); + VectorCopy(corner[0], corner[1]); + oppleg = xmin + stereoSep; length = sqrt(oppleg * oppleg + zProj * zProj); VectorScale(dest->or.axis[0], -oppleg / length, dest->frustum[1].normal); VectorMA(dest->frustum[1].normal, -zProj / length, dest->or.axis[1], dest->frustum[1].normal); + + VectorMA(corner[2], oppleg, dest->or.axis[1], corner[2]); + VectorCopy(corner[2], corner[3]); } length = sqrt(ymax * ymax + zProj * zProj); @@ -497,12 +515,91 @@ void R_SetupFrustum (viewParms_t *dest, float xmin, float xmax, float ymax, floa VectorScale(dest->or.axis[0], oppleg, dest->frustum[3].normal); VectorMA(dest->frustum[3].normal, -adjleg, dest->or.axis[2], dest->frustum[3].normal); + + VectorMA(corner[0], ymax, dest->or.axis[2], corner[0]); + VectorMA(corner[1], -ymax, dest->or.axis[2], corner[1]); + VectorMA(corner[2], ymax, dest->or.axis[2], corner[2]); + VectorMA(corner[3], -ymax, dest->or.axis[2], corner[3]); for (i=0 ; i<4 ; i++) { dest->frustum[i].type = PLANE_NON_AXIAL; dest->frustum[i].dist = DotProduct (ofsorigin, dest->frustum[i].normal); SetPlaneSignbits( &dest->frustum[i] ); } + + if ( r_nocull->integer ) { + dest->frustType = 0; + } else if ( qglBindBufferARB && tr.clusters ) { + // Find main view direction and try to clip the opposite + // side. The distance is not known, it is added in + // R_AddWorldSurfaces. + + if ( corner[0][0] >= 0 && corner[1][0] >= 0 && + corner[2][0] >= 0 && corner[3][0] >= 0 ) { + dest->frustum[4].type = PLANE_NON_AXIAL; + dest->frustum[4].normal[0] = 1.0f; + dest->frustum[4].normal[1] = 0.0f; + dest->frustum[4].normal[2] = 0.0f; + + SetPlaneSignbits( &dest->frustum[4] ); + + dest->frustType = 1; + } else if ( corner[0][0] <= 0 && corner[1][0] <= 0 && + corner[2][0] <= 0 && corner[3][0] <= 0 ) { + dest->frustum[4].type = PLANE_NON_AXIAL; + dest->frustum[4].normal[0] = -1.0f; + dest->frustum[4].normal[1] = 0.0f; + dest->frustum[4].normal[2] = 0.0f; + + SetPlaneSignbits( &dest->frustum[4] ); + + dest->frustType = 2; + } else if ( corner[0][1] >= 0 && corner[1][1] >= 0 && + corner[2][1] >= 0 && corner[3][1] >= 0 ) { + dest->frustum[4].type = PLANE_NON_AXIAL; + dest->frustum[4].normal[0] = 0.0f; + dest->frustum[4].normal[1] = 1.0f; + dest->frustum[4].normal[2] = 0.0f; + + SetPlaneSignbits( &dest->frustum[4] ); + + dest->frustType = 3; + } else if ( corner[0][1] <= 0 && corner[1][1] <= 0 && + corner[2][1] <= 0 && corner[3][1] <= 0 ) { + dest->frustum[4].type = PLANE_NON_AXIAL; + dest->frustum[4].normal[0] = 0.0f; + dest->frustum[4].normal[1] = -1.0f; + dest->frustum[4].normal[2] = 0.0f; + + SetPlaneSignbits( &dest->frustum[4] ); + + dest->frustType = 4; + } else if ( corner[0][2] >= 0 && corner[1][2] >= 0 && + corner[2][2] >= 0 && corner[3][2] >= 0 ) { + dest->frustum[4].type = PLANE_NON_AXIAL; + dest->frustum[4].normal[0] = 0.0f; + dest->frustum[4].normal[1] = 0.0f; + dest->frustum[4].normal[2] = 1.0f; + + SetPlaneSignbits( &dest->frustum[4] ); + + dest->frustType = 5; + } else if ( corner[0][2] <= 0 && corner[1][2] <= 0 && + corner[2][2] <= 0 && corner[3][2] <= 0 ) { + dest->frustum[4].type = PLANE_NON_AXIAL; + dest->frustum[4].normal[0] = 0.0f; + dest->frustum[4].normal[1] = 0.0f; + dest->frustum[4].normal[2] = -1.0f; + + SetPlaneSignbits( &dest->frustum[4] ); + + dest->frustType = 6; + } else { + dest->frustType = 0; + } + } else { + dest->frustType = 0; + } } /* @@ -868,18 +965,26 @@ static qboolean SurfIsOffscreen( const drawSurf_t *drawSurf, vec4_t clipDest[128 R_RotateForViewer(); - R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted ); - RB_BeginSurface( shader, fogNum ); - rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); + entityNum = QSORT_ENTITYNUM( drawSurf->sort ); + shader = tr.shaders[ drawSurf->shaderIndex ]; + fogNum = QSORT_FOGNUM( drawSurf->sort ); + dlighted = QSORT_DLIGHT( drawSurf->sort ); + + RB_BeginSurface( shader ); + + tesselate( TESS_COUNT, drawSurf->surface ); assert( tess.numVertexes < 128 ); + RB_AllocateSurface( ); + tesselate( TESS_ALL, drawSurf->surface ); + for ( i = 0; i < tess.numVertexes; i++ ) { int j; unsigned int pointFlags = 0; - R_TransformModelToClip( tess.xyz[i], tr.or.modelMatrix, tr.viewParms.projectionMatrix, eye, clip ); + R_TransformModelToClip( tess.vertexPtr[i].xyz, tr.or.modelMatrix, tr.viewParms.projectionMatrix, eye, clip ); for ( j = 0; j < 3; j++ ) { @@ -899,6 +1004,7 @@ static qboolean SurfIsOffscreen( const drawSurf_t *drawSurf, vec4_t clipDest[128 // trivially reject if ( pointAnd ) { + RB_DiscardSurface(); return qtrue; } @@ -907,14 +1013,16 @@ static qboolean SurfIsOffscreen( const drawSurf_t *drawSurf, vec4_t clipDest[128 // based on vertex distance isn't 100% correct (we should be checking for // range to the surface), but it's good enough for the types of portals // we have in the game right now. - numTriangles = tess.numIndexes / 3; + numTriangles = tess.numIndexes[0] / 3; - for ( i = 0; i < tess.numIndexes; i += 3 ) + for ( i = 0; i < tess.numIndexes[0]; i += 3 ) { - vec3_t normal; + vec3_t normal, vertexNormal; + float dot; float len; - VectorSubtract( tess.xyz[tess.indexes[i]], tr.viewParms.or.origin, normal ); + VectorSubtract( tess.vertexPtr[tess.indexPtr[i]].xyz, + tr.viewParms.or.origin, normal ); len = VectorLengthSquared( normal ); // lose the sqrt if ( len < shortest ) @@ -922,13 +1030,16 @@ static qboolean SurfIsOffscreen( const drawSurf_t *drawSurf, vec4_t clipDest[128 shortest = len; } - if ( DotProduct( normal, tess.normal[tess.indexes[i]] ) >= 0 ) + VectorCopy( tess.vertexPtr[tess.indexPtr[i]].normal, + vertexNormal ); + if ( ( dot = DotProduct( normal, vertexNormal ) ) >= 0 ) { numTriangles--; } } if ( !numTriangles ) { + RB_DiscardSurface(); return qtrue; } @@ -936,14 +1047,17 @@ static qboolean SurfIsOffscreen( const drawSurf_t *drawSurf, vec4_t clipDest[128 // with them (although we could) if ( IsMirror( drawSurf, entityNum ) ) { + RB_DiscardSurface(); return qfalse; } if ( shortest > (tess.shader->portalRange*tess.shader->portalRange) ) { + RB_DiscardSurface(); return qtrue; } + RB_DiscardSurface(); return qfalse; } @@ -955,13 +1069,14 @@ Returns qtrue if another view has been rendered ======================== */ qboolean R_MirrorViewBySurface (drawSurf_t *drawSurf, int entityNum) { - vec4_t clipDest[128]; - viewParms_t newParms; - viewParms_t oldParms; + vec4_t clipDest[128]; + viewParms_t newParms; + viewParms_t oldParms; orientation_t surface, camera; - // don't recursively mirror - if (tr.viewParms.isPortal) { + // don't recursively mirror too much + if ( tr.viewParms.portalLevel >= glGlobals.portalLevels && + tr.viewParms.portalLevel > 0 ) { ri.Printf( PRINT_DEVELOPER, "WARNING: recursive mirror/portal found\n" ); return qfalse; } @@ -976,10 +1091,23 @@ qboolean R_MirrorViewBySurface (drawSurf_t *drawSurf, int entityNum) { } // save old viewParms so we can return to it after the mirror view + tr.viewParms.noShadows = qtrue; oldParms = tr.viewParms; + // draw stencil mask if stencil bits are available + if( glGlobals.portalLevels > 0 ) { + oldParms.isFirst = qfalse; + tr.currentEntityNum = entityNum; + tr.shiftedEntityNum = entityNum << QSORT_ENTITYNUM_SHIFT; + R_AddDrawSurf( drawSurf->surface, tr.preparePortalShader, 0, 0 ); + R_AddDrawSurfCmd( tr.refdef.drawSurfs + tr.refdef.numDrawSurfs - 1, 1 ); + } + + tr.viewParms.portalLevel++; + newParms = tr.viewParms; - newParms.isPortal = qtrue; + newParms.isFirst = qfalse; + newParms.noShadows = qfalse; if ( !R_GetPortalOrientations( drawSurf, entityNum, &surface, &camera, newParms.pvsOrigin, &newParms.isMirror ) ) { return qfalse; // bad portal, no portalentity @@ -1000,6 +1128,14 @@ qboolean R_MirrorViewBySurface (drawSurf_t *drawSurf, int entityNum) { R_RenderView (&newParms); tr.viewParms = oldParms; + if( glGlobals.portalLevels > 0 ) { + tr.currentEntityNum = entityNum; + tr.shiftedEntityNum = entityNum << QSORT_ENTITYNUM_SHIFT; + R_AddDrawSurf( drawSurf->surface, tr.finalisePortalShader, 0, 0 ); + R_AddDrawSurfCmd( tr.refdef.drawSurfs + tr.refdef.numDrawSurfs - 1, 1 ); + } + + tr.viewParms.noShadows = qfalse; return qtrue; } @@ -1047,53 +1183,26 @@ DRAWSURF SORTING /* =============== -R_Radix +R_SortSurfaces + +Sort the drawSurfs by shader, surface, entity etc. =============== */ -static ID_INLINE void R_Radix( int byte, int size, drawSurf_t *source, drawSurf_t *dest ) +static int cmpSurfaces( const void *aa, const void *bb ) { - int count[ 256 ] = { 0 }; - int index[ 256 ]; - int i; - unsigned char *sortKey = NULL; - unsigned char *end = NULL; - - sortKey = ( (unsigned char *)&source[ 0 ].sort ) + byte; - end = sortKey + ( size * sizeof( drawSurf_t ) ); - for( ; sortKey < end; sortKey += sizeof( drawSurf_t ) ) - ++count[ *sortKey ]; - - index[ 0 ] = 0; - - for( i = 1; i < 256; ++i ) - index[ i ] = index[ i - 1 ] + count[ i - 1 ]; - - sortKey = ( (unsigned char *)&source[ 0 ].sort ) + byte; - for( i = 0; i < size; ++i, sortKey += sizeof( drawSurf_t ) ) - dest[ index[ *sortKey ]++ ] = source[ i ]; + const drawSurf_t *a = aa; + const drawSurf_t *b = bb; + int diff; + + diff = a->sort - b->sort; + if( !diff ) + diff = (intptr_t)a->surface - (intptr_t)b->surface; + return diff; } -/* -=============== -R_RadixSort - -Radix sort with 4 byte size buckets -=============== -*/ -static void R_RadixSort( drawSurf_t *source, int size ) +void R_SortSurfaces( drawSurf_t *source, int size ) { - static drawSurf_t scratch[ MAX_DRAWSURFS ]; -#ifdef Q3_LITTLE_ENDIAN - R_Radix( 0, size, source, scratch ); - R_Radix( 1, size, scratch, source ); - R_Radix( 2, size, source, scratch ); - R_Radix( 3, size, scratch, source ); -#else - R_Radix( 3, size, source, scratch ); - R_Radix( 2, size, scratch, source ); - R_Radix( 1, size, source, scratch ); - R_Radix( 0, size, scratch, source ); -#endif //Q3_LITTLE_ENDIAN + qsort( source, size, sizeof(drawSurf_t), cmpSurfaces ); } //========================================================================================== @@ -1104,7 +1213,7 @@ R_AddDrawSurf ================= */ void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, - int fogIndex, int dlightMap ) { + int fogIndex, int dlightMap ) { int index; // instead of checking for overflow, we just mask the index @@ -1113,35 +1222,22 @@ void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, // the sort data is packed into a single 32 bit value so it can be // compared quickly during the qsorting process tr.refdef.drawSurfs[index].sort = (shader->sortedIndex << QSORT_SHADERNUM_SHIFT) - | tr.shiftedEntityNum | ( fogIndex << QSORT_FOGNUM_SHIFT ) | (int)dlightMap; + | tr.shiftedEntityNum | ( fogIndex << QSORT_FOGNUM_SHIFT ) + | (dlightMap << QSORT_DLIGHT_SHIFT); + tr.refdef.drawSurfs[index].shaderIndex = shader->index; tr.refdef.drawSurfs[index].surface = surface; tr.refdef.numDrawSurfs++; } /* ================= -R_DecomposeSort -================= -*/ -void R_DecomposeSort( unsigned sort, int *entityNum, shader_t **shader, - int *fogNum, int *dlightMap ) { - *fogNum = ( sort >> QSORT_FOGNUM_SHIFT ) & 31; - *shader = tr.sortedShaders[ ( sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1) ]; - *entityNum = ( sort >> QSORT_ENTITYNUM_SHIFT ) & MAX_ENTITIES; - *dlightMap = sort & 3; -} - -/* -================= R_SortDrawSurfs ================= */ void R_SortDrawSurfs( drawSurf_t *drawSurfs, int numDrawSurfs ) { shader_t *shader; - int fogNum; - int entityNum; - int dlighted; - int i; + int entityNum; + int i; // it is possible for some views to not have any surfaces if ( numDrawSurfs < 1 ) { @@ -1158,12 +1254,12 @@ void R_SortDrawSurfs( drawSurf_t *drawSurfs, int numDrawSurfs ) { } // sort the drawsurfs by sort type, then orientation, then shader - R_RadixSort( drawSurfs, numDrawSurfs ); + R_SortSurfaces( drawSurfs, numDrawSurfs ); // check for any pass through drawing, which // may cause another view to be rendered first for ( i = 0 ; i < numDrawSurfs ; i++ ) { - R_DecomposeSort( (drawSurfs+i)->sort, &entityNum, &shader, &fogNum, &dlighted ); + shader = tr.shaders[ (drawSurfs+i)->shaderIndex ]; if ( shader->sort > SS_PORTAL ) { break; @@ -1174,15 +1270,17 @@ void R_SortDrawSurfs( drawSurf_t *drawSurfs, int numDrawSurfs ) { ri.Error (ERR_DROP, "Shader '%s'with sort == SS_BAD", shader->name ); } + entityNum = QSORT_ENTITYNUM( (drawSurfs+i)->sort ); + // if the mirror was completely clipped away, we may need to check another surface if ( R_MirrorViewBySurface( (drawSurfs+i), entityNum) ) { - // this is a debug option to see exactly what is being mirrored - if ( r_portalOnly->integer ) { - return; - } - break; // only one mirror view at a time + //break; // only one mirror view at a time } } + // this is a debug option to see exactly what is being mirrored + if ( r_portalOnly->integer && !tr.viewParms.portalLevel ) { + return; + } R_AddDrawSurfCmd( drawSurfs, numDrawSurfs ); } @@ -1215,7 +1313,7 @@ void R_AddEntitySurfaces (void) { // we don't want the hacked weapon position showing in // mirrors, because the true body position will already be drawn // - if ( (ent->e.renderfx & RF_FIRST_PERSON) && tr.viewParms.isPortal) { + if ( (ent->e.renderfx & RF_FIRST_PERSON) && tr.viewParms.portalLevel ) { continue; } @@ -1231,7 +1329,7 @@ void R_AddEntitySurfaces (void) { // self blood sprites, talk balloons, etc should not be drawn in the primary // view. We can't just do this check for all entities, because md3 // entities may still want to cast shadows from them - if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { + if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.portalLevel ) { continue; } shader = R_GetShaderByHandle( ent->e.customShader ); @@ -1265,7 +1363,7 @@ void R_AddEntitySurfaces (void) { R_AddBrushModelSurfaces( ent ); break; case MOD_BAD: // null model axis - if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { + if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.portalLevel ) { break; } R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0 ); @@ -1290,6 +1388,8 @@ R_GenerateDrawSurfs ==================== */ void R_GenerateDrawSurfs( void ) { + // World surfaces have to be added first, IBO construction + // depends on an empty drawSurf list R_AddWorldSurfaces (); R_AddPolygonSurfaces(); @@ -1307,6 +1407,10 @@ void R_GenerateDrawSurfs( void ) { R_SetupProjectionZ (&tr.viewParms); R_AddEntitySurfaces (); + + if( tr.copyDepthShader ) { + R_AddDrawSurf( &farPlaneSurface, tr.copyDepthShader, 0, 0 ); + } } /* @@ -1315,29 +1419,29 @@ R_DebugPolygon ================ */ void R_DebugPolygon( int color, int numPoints, float *points ) { - int i; + glRenderState_t state; - GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + InitState( &state ); // draw solid shade + state.stateBits = GLS_DEPTHMASK_TRUE | + GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE; + state.faceCulling = CT_FRONT_SIDED; - qglColor3f( color&1, (color>>1)&1, (color>>2)&1 ); - qglBegin( GL_POLYGON ); - for ( i = 0 ; i < numPoints ; i++ ) { - qglVertex3fv( points + i * 3 ); - } - qglEnd(); + state.numImages = 1; + state.image[0] = tr.whiteImage; + + SetAttrVec4f( &state, AL_COLOR, color&1, (color>>1)&1, + (color>>2)&1, 1.0f ); + SetAttrPointer( &state, AL_VERTEX, 0, 3, GL_FLOAT, 0, points ); + GL_DrawArrays( &state, GL_POLYGON, 0, numPoints ); // draw wireframe outline - GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - qglDepthRange( 0, 0 ); - qglColor3f( 1, 1, 1 ); - qglBegin( GL_POLYGON ); - for ( i = 0 ; i < numPoints ; i++ ) { - qglVertex3fv( points + i * 3 ); - } - qglEnd(); - qglDepthRange( 0, 1 ); + state.stateBits = GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE | + GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | + GLS_DEPTHRANGE_0_TO_0; + SetAttrVec4f( &state, AL_COLOR, 1.0f, 1.0f, 1.0f, 1.0f ); + GL_DrawArrays( &state, GL_POLYGON, 0, numPoints ); } /* @@ -1355,8 +1459,6 @@ void R_DebugGraphics( void ) { // the render thread can't make callbacks to the main thread R_SyncRenderThread(); - GL_Bind( tr.whiteImage); - GL_Cull( CT_FRONT_SIDED ); ri.CM_DrawDebugSurface( R_DebugPolygon ); } diff --git a/code/renderer/tr_marks.c b/code/renderer/tr_marks.c index 0ff3e30..8c6c765 100644 --- a/code/renderer/tr_marks.c +++ b/code/renderer/tr_marks.c @@ -163,7 +163,7 @@ void R_BoxSurfaces_r(mnode_t *node, vec3_t mins, vec3_t maxs, surfaceType_t **li surf->viewCount = tr.viewCount; } // extra check for surfaces to avoid list overflows - else if (*(surf->data) == SF_FACE) { + else if (surf->type == SF_FACE) { // the face plane should go through the box s = BoxOnPlaneSide( mins, maxs, &(( srfSurfaceFace_t * ) surf->data)->plane ); if (s == 1 || s == 2) { @@ -173,8 +173,8 @@ void R_BoxSurfaces_r(mnode_t *node, vec3_t mins, vec3_t maxs, surfaceType_t **li surf->viewCount = tr.viewCount; } } - else if (*(surfaceType_t *) (surf->data) != SF_GRID && - *(surfaceType_t *) (surf->data) != SF_TRIANGLES) + else if (surf->type != SF_GRID && + surf->type != SF_TRIANGLES ) surf->viewCount = tr.viewCount; // check the viewCount because the surface may have // already been added if it spans multiple leafs diff --git a/code/renderer/tr_mesh.c b/code/renderer/tr_mesh.c index 0883088..2c8013f 100644 --- a/code/renderer/tr_mesh.c +++ b/code/renderer/tr_mesh.c @@ -295,7 +295,7 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) { qboolean personalModel; // don't add third_person objects if not in a portal - personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; + personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.portalLevel; if ( ent->e.renderfx & RF_WRAP_FRAMES ) { ent->e.frame %= tr.currentModel->md3[0]->numFrames; @@ -393,7 +393,7 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) { && fogNum == 0 && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse ); + R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, 0 ); } // projection shadows work fine with personal models @@ -401,12 +401,28 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) { && fogNum == 0 && (ent->e.renderfx & RF_SHADOW_PLANE ) && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse ); + R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, 0 ); } // don't add third_person objects if not viewing through a portal if ( !personalModel ) { - R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse ); + srfMD3Texture_t *(*data)[MD3_MAX_LODS]; + + data = tr.currentModel->modelData; + if( data && (*data)[lod] && + shader->lightmapIndex == LIGHTMAP_MD3 && + shader->GLSLprogram ) { + srfMD3Texture_t *surf = &(*data)[lod][i]; + if( shader->depthShader ) { + R_AddDrawSurf( (void *)surf, shader->depthShader, 0, 0 ); + } + R_AddDrawSurf( (void *)surf, shader, fogNum, 0 ); + } else { + if( shader->depthShader ) { + R_AddDrawSurf( (void *)surface, shader->depthShader, 0, 0 ); + } + R_AddDrawSurf( (void *)surface, shader, fogNum, 0 ); + } } surface = (md3Surface_t *)( (byte *)surface + surface->ofsEnd ); diff --git a/code/renderer/tr_model.c b/code/renderer/tr_model.c index 51e0427..131b98f 100644 --- a/code/renderer/tr_model.c +++ b/code/renderer/tr_model.c @@ -384,7 +384,7 @@ R_LoadMD3 ================= */ static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_name ) { - int i, j; + int i, j, k; md3Header_t *pinmodel; md3Frame_t *frame; md3Surface_t *surf; @@ -395,6 +395,10 @@ static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_ md3Tag_t *tag; int version; int size; + int numFrames; + trRefEntity_t thisEntity, *oldEntity; + image_t *image; + int shadertype = LIGHTMAP_NONE; pinmodel = (md3Header_t *)buffer; @@ -422,7 +426,8 @@ static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_ LL(mod->md3[lod]->ofsSurfaces); LL(mod->md3[lod]->ofsEnd); - if ( mod->md3[lod]->numFrames < 1 ) { + numFrames = mod->md3[lod]->numFrames; + if ( numFrames < 1 ) { ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has no frames\n", mod_name ); return qfalse; } @@ -465,6 +470,7 @@ static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_ LL(surf->ofsXyzNormals); LL(surf->ofsEnd); +#ifdef SHADER_MAX_VERTEXES if ( surf->numVerts > SHADER_MAX_VERTEXES ) { ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has more than %i verts on a surface (%i).\n", mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); @@ -475,6 +481,7 @@ static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_ mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); return qfalse; } +#endif // change to surface identifier surf->ident = SF_MD3; @@ -489,19 +496,6 @@ static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_ surf->name[j-2] = 0; } - // register the shaders - shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders ); - for ( j = 0 ; j < surf->numShaders ; j++, shader++ ) { - shader_t *sh; - - sh = R_FindShader( shader->name, LIGHTMAP_NONE, qtrue ); - if ( sh->defaultShader ) { - shader->shaderIndex = 0; - } else { - shader->shaderIndex = sh->index; - } - } - // swap all the triangles tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { @@ -533,6 +527,262 @@ static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_ surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd ); } + // VBO interpolation needs shaders, buffer objects, float textures + // and multitexture + if( qglCreateShader && + qglGenBuffersARB && + glGlobals.floatTextures && + qglActiveTextureARB && + glGlobals.maxVertexTextureImageUnits > 0 && + mod->md3[lod]->numSurfaces > 0 ) { + vboInfo_t *VBO; + GLuint IBO; + int frameW = 1; + int frameH = 1; + int texW = 1; + int texH = 1; + float vrtScaleX, vrtScaleY, frmScaleX, frmScaleY; + float coordOffs; + vec4_t *texData; + srfMD3Texture_t *(*data)[ MD3_MAX_LODS ] = mod->modelData; + srfMD3Texture_t *md3srf; + + // count vertices + surf = (md3Surface_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsSurfaces ); + + RB_BeginSurface( tr.buildIBOShader ); + for ( i = 0 ; i < mod->md3[lod]->numSurfaces ; i++ ) { + tesselate( TESS_COUNT, (surfaceType_t *)surf ); + surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd ); + } + + if( !data ) { + data = ri.Hunk_Alloc( sizeof( *data ) + + mod->md3[lod]->numSurfaces * sizeof( srfMD3Texture_t ), h_low ); + md3srf = (srfMD3Texture_t *)(data + 1); + (*data)[lod] = md3srf; + mod->modelData = data; + } else { + md3srf = ri.Hunk_Alloc( mod->md3[lod]->numSurfaces * sizeof(srfMD3Texture_t), h_low ); + (*data)[lod] = md3srf; + } + + // the vertex coords and normals for all frames are + // put into a texture, and one VBO/IBO is + // created for the indexes and texture coords for + // the first frame. + if( qglTexBufferEXT ) { + frameW = tess.numVertexes; + frameH = 1; + texW = numFrames; + texH = 0; + vrtScaleX = 1.0f; + vrtScaleY = 0.0f; + frmScaleX = (float)tess.numVertexes; + frmScaleY = 0.0f; + coordOffs = 0.0f; + } else { + while( frameW * frameH < tess.numVertexes ) { + frameH *= 2; + if( frameW * frameH >= tess.numVertexes ) + break; + frameW *= 2; + } + while( texW * texH < numFrames ) { + texW *= 2; + if( texW * texH >= numFrames ) + break; + texH *= 2; + } + frmScaleX = 1.0f / texW; + frmScaleY = 1.0 / texH; + vrtScaleX = frmScaleX / frameW; + vrtScaleY = frmScaleY / frameH; + coordOffs = 0.5f; + } + + oldEntity = backEnd.currentEntity; + backEnd.currentEntity = &thisEntity; + + // render first frame + thisEntity.e.frame = 0; + thisEntity.e.oldframe = 0; + thisEntity.e.backlerp = 0.0f; + tess.fogNum = 0; + + VBO = ri.Hunk_Alloc( sizeof( vboInfo_t ), h_dontcare ); + qglGenBuffersARB( 1, &VBO->vbo ); + VBO->offset = NULL; + md3srf[0].VBO = VBO; + + qglGenBuffersARB( 1, &IBO ); + GL_IBO( IBO ); + RB_AllocateSurface( ); + + surf = (md3Surface_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsSurfaces ); + for ( i = 0 ; i < mod->md3[lod]->numSurfaces ; i++) { + int firstIndex = tess.numIndexes[0]; + + md3srf[i].surfaceType = SF_MD3_TEXTURE; + md3srf[i].framesPerRow = texW; + md3srf[i].scaleX = frmScaleX; + md3srf[i].scaleY = 1024.0f * frmScaleY; + + md3srf[i].IBO = ri.Hunk_Alloc( sizeof( iboInfo_t ), h_dontcare ); + md3srf[i].IBO->ibo = IBO; + md3srf[i].IBO->offset = tess.numIndexes[0] + (glIndex_t *)NULL; + + md3srf[i].IBO->vbo = VBO; + + tesselate( TESS_INDEX, (surfaceType_t *)surf ); + + md3srf[i].IBO->numIndexes = tess.numIndexes[0] - firstIndex; + md3srf[i].IBO->minIndex = tess.minIndex[0]; + md3srf[i].IBO->maxIndex = tess.maxIndex[0]; + + tess.maxIndex[0] = 0; + tess.minIndex[0] = 0x7fffffff; + + surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd ); + } + + // allocate empty texture for vertex positions + image = R_CreateImage( surf->name, NULL, + frameW * texW, frameH * texH, + qfalse, qfalse, GL_REPEAT ); + + // a float has 24 significant bits, and we have 4 floats + // per texel, so a total 96 bits are available. These + // store 3x16 bits for the vertex position and 3x8 + // bits for the three normal vectors. + + if( qglTexBufferEXT ) { + image->uploadWidth = texW * frameW; + image->uploadHeight = texH * frameH; + image->internalFormat = GL_RGBA32F_ARB; + GL_VBO( image->buffer ); + qglBufferDataARB( GL_ARRAY_BUFFER_ARB, + frameW * texW * sizeof(vec4_t), + NULL, GL_STATIC_DRAW_ARB ); + texData = qglMapBufferARB( GL_ARRAY_BUFFER_ARB, + GL_WRITE_ONLY_ARB ); + } else { + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + image->uploadWidth = texW * frameW; + image->uploadHeight = texH * frameH; + image->internalFormat = GL_RGB32F_ARB; + qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB32F_ARB, + texW * frameW, texH * frameH, + 0, GL_RGBA, GL_FLOAT, NULL ); + texData = ri.Hunk_AllocateTempMemory(frameW * frameH * sizeof(vec4_t)); + } + + GL_VBO( md3srf[0].VBO->vbo ); + GL_IBO( md3srf[0].IBO->ibo ); + + // render all frames + for( k = 0; k < numFrames; k++ ) { + thisEntity.e.frame = k; + thisEntity.e.oldframe = k; + thisEntity.e.backlerp = 0.0f; + + surf = (md3Surface_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsSurfaces ); + tess.numIndexes[0] = 0; + tess.numVertexes = 0; + for ( i = 0 ; i < mod->md3[lod]->numSurfaces ; i++) { + md3srf[i].image = image; + tesselate( TESS_VERTEX, (surfaceType_t *)surf ); + surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd ); + } + + if( k == 0 ) { + vec4_t *ptr; + qglBufferDataARB( GL_ARRAY_BUFFER_ARB, + sizeof(vec4_t) * tess.numVertexes, + NULL, GL_STATIC_DRAW_ARB ); + + ptr = qglMapBufferARB( GL_ARRAY_BUFFER_ARB, + GL_WRITE_ONLY_ARB ); + // build VBO, set 2nd texcoord (normally lightmap) + // to position of vertex in the data texture + for( j = 0; j < tess.numVertexes; j++ ) { + ptr[j][0] = tess.vertexPtr[j].tc1[0]; + ptr[j][1] = tess.vertexPtr[j].tc1[1]; + ptr[j][2] = (coordOffs + (j % frameW)) * vrtScaleX; + ptr[j][3] = (coordOffs + (j / frameW)) * vrtScaleY ; + } + qglUnmapBufferARB( GL_ARRAY_BUFFER_ARB ); + } + + for( j = 0; j < tess.numVertexes; j++ ) { + glVertex_t *ptr = &tess.vertexPtr[j]; + unsigned short tmpX = (unsigned short)(ptr->xyz[0] / MD3_XYZ_SCALE + 32768.0f); + unsigned short tmpY = (unsigned short)(ptr->xyz[1] / MD3_XYZ_SCALE + 32768.0f); + unsigned short tmpZ = (unsigned short)(ptr->xyz[2] / MD3_XYZ_SCALE + 32768.0f); + unsigned char nrmX = (unsigned char)(ptr->normal[0] * 127 + 128); + unsigned char nrmY = (unsigned char)(ptr->normal[1] * 127 + 128); + unsigned char nrmZ = (unsigned char)(ptr->normal[2] * 127 + 128); + + unsigned int int1 = (nrmX << 16) | tmpX; + unsigned int int2 = (nrmY << 16) | tmpY; + unsigned int int3 = (nrmZ << 16) | tmpZ; + + texData[j][0] = ((float)int1) * (1.0f / 65536.0f); + texData[j][1] = ((float)int2) * (1.0f / 65536.0f); + texData[j][2] = ((float)int3) * (1.0f / 65536.0f); + texData[j][3] = 1.0f; // unused + } + + if( qglTexBufferEXT ) { + texData += frameW; + } else { + qglTexSubImage2D( GL_TEXTURE_2D, 0, + (k % texW) * frameW, + (k / texW) * frameH, + frameW, frameH, + GL_RGBA, GL_FLOAT, + texData ); + } + } + if( qglTexBufferEXT ) { + GL_VBO( image->buffer ); + qglUnmapBufferARB( GL_ARRAY_BUFFER_ARB ); + + qglBindTexture( GL_TEXTURE_BUFFER_EXT, image->texnum ); + qglTexBufferEXT( GL_TEXTURE_BUFFER_EXT, + GL_RGBA32F_ARB, image->buffer ); + qglBindTexture( GL_TEXTURE_BUFFER_EXT, 0 ); + } else { + ri.Hunk_FreeTempMemory( texData ); + } + + RB_EndSurface( ); + + shadertype = LIGHTMAP_MD3; + backEnd.currentEntity = oldEntity; + } + + // register the shaders + surf = (md3Surface_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsSurfaces ); + for ( i = 0 ; i < mod->md3[lod]->numSurfaces ; i++) { + shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders ); + + for ( j = 0 ; j < surf->numShaders ; j++, shader++ ) { + shader_t *sh; + + sh = R_FindShader( shader->name, shadertype, qtrue ); + if ( sh->defaultShader ) { + shader->shaderIndex = 0; + } else { + shader->shaderIndex = sh->index; + } + } + + // find the next surface + surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd ); + } + return qtrue; } @@ -742,6 +992,7 @@ static qboolean R_LoadMDR( model_t *mod, void *buffer, int filesize, const char // numBoneReferences and BoneReferences generally seem to be unused // now do the checks that may fail. +#ifdef SHADER_MAX_VERTEXES if ( surf->numVerts > SHADER_MAX_VERTEXES ) { ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has more than %i verts on a surface (%i).\n", @@ -754,6 +1005,7 @@ static qboolean R_LoadMDR( model_t *mod, void *buffer, int filesize, const char mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); return qfalse; } +#endif // lowercase the surface name so skin compares are faster Q_strlwr( surf->name ); @@ -958,6 +1210,7 @@ static qboolean R_LoadMD4( model_t *mod, void *buffer, const char *mod_name ) { LL(surf->ofsVerts); LL(surf->ofsEnd); +#ifdef SHADER_MAX_VERTEXES if ( surf->numVerts > SHADER_MAX_VERTEXES ) { ri.Printf(PRINT_WARNING, "R_LoadMD4: %s has more than %i verts on a surface (%i).\n", mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); @@ -968,6 +1221,7 @@ static qboolean R_LoadMD4( model_t *mod, void *buffer, const char *mod_name ) { mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); return qfalse; } +#endif // change to surface identifier surf->ident = SF_MD4; @@ -1047,7 +1301,7 @@ void RE_BeginRegistration( glconfig_t *glconfigOut ) { R_SyncRenderThread(); - tr.viewCluster = -1; // force markleafs to regenerate + tr.visCluster = -1; // force markleafs to regenerate R_ClearFlares(); RE_ClearScene(); diff --git a/code/renderer/tr_model_iqm.c b/code/renderer/tr_model_iqm.c index 98517d5..25a7f26 100644 --- a/code/renderer/tr_model_iqm.c +++ b/code/renderer/tr_model_iqm.c @@ -92,26 +92,36 @@ static void JointToMatrix( vec4_t rot, vec3_t scale, vec3_t trans, mat[10] = scale[2] * (1.0f - (xx + yy)); mat[11] = trans[2]; } -static void Matrix34Invert( float *inMat, float *outMat ) -{ - vec3_t trans; - float invSqrLen, *v; - - outMat[ 0] = inMat[ 0]; outMat[ 1] = inMat[ 4]; outMat[ 2] = inMat[ 8]; - outMat[ 4] = inMat[ 1]; outMat[ 5] = inMat[ 5]; outMat[ 6] = inMat[ 9]; - outMat[ 8] = inMat[ 2]; outMat[ 9] = inMat[ 6]; outMat[10] = inMat[10]; - - v = outMat + 0; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); - v = outMat + 4; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); - v = outMat + 8; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); - - trans[0] = inMat[ 3]; - trans[1] = inMat[ 7]; - trans[2] = inMat[11]; - - outMat[ 3] = -DotProduct(outMat + 0, trans); - outMat[ 7] = -DotProduct(outMat + 4, trans); - outMat[11] = -DotProduct(outMat + 8, trans); +static void JointToMatrixInverse( vec4_t rot, vec3_t scale, vec3_t trans, + float *mat ) { + float xx = 2.0f * rot[0] * rot[0]; + float yy = 2.0f * rot[1] * rot[1]; + float zz = 2.0f * rot[2] * rot[2]; + float xy = 2.0f * rot[0] * rot[1]; + float xz = 2.0f * rot[0] * rot[2]; + float yz = 2.0f * rot[1] * rot[2]; + float wx = 2.0f * rot[3] * rot[0]; + float wy = 2.0f * rot[3] * rot[1]; + float wz = 2.0f * rot[3] * rot[2]; + + vec3_t inv_scale; + + inv_scale[0] = 1.0f / scale[0]; + inv_scale[1] = 1.0f / scale[1]; + inv_scale[2] = 1.0f / scale[2]; + + mat[ 0] = inv_scale[0] * (1.0f - (yy + zz)); + mat[ 1] = inv_scale[1] * (xy + wz); + mat[ 2] = inv_scale[2] * (xz - wy); + mat[ 3] = -DotProduct((mat + 0), trans); + mat[ 4] = inv_scale[0] * (xy - wz); + mat[ 5] = inv_scale[1] * (1.0f - (xx + zz)); + mat[ 6] = inv_scale[2] * (yz + wx); + mat[ 7] = -DotProduct((mat + 4), trans); + mat[ 8] = inv_scale[0] * (xz + wy); + mat[ 9] = inv_scale[1] * (yz - wx); + mat[10] = inv_scale[2] * (1.0f - (xx + yy)); + mat[11] = -DotProduct((mat + 8), trans); } /* @@ -132,8 +142,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na unsigned short *framedata; char *str; int i, j; - float jointMats[IQM_MAX_JOINTS * 2 * 12]; - float *mat; + float *jointMats, *mat; size_t size, joint_names; iqmData_t *iqmData; srfIQModel_t *surface; @@ -185,13 +194,6 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na LL( header->num_extensions ); LL( header->ofs_extensions ); - // check ioq3 joint limit - if ( header->num_joints > IQM_MAX_JOINTS ) { - ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %d joints (%d).\n", - mod_name, IQM_MAX_JOINTS, header->num_joints); - return qfalse; - } - // check and swap vertex arrays if( IQM_CheckRange( header, header->ofs_vertexarrays, header->num_vertexarrays, @@ -305,20 +307,6 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na LL( mesh->first_triangle ); LL( mesh->num_triangles ); - // check ioq3 limits - if ( mesh->num_vertexes > SHADER_MAX_VERTEXES ) - { - ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i verts on a surface (%i).\n", - mod_name, SHADER_MAX_VERTEXES, mesh->num_vertexes ); - return qfalse; - } - if ( mesh->num_triangles*3 > SHADER_MAX_INDEXES ) - { - ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i triangles on a surface (%i).\n", - mod_name, SHADER_MAX_INDEXES / 3, mesh->num_triangles ); - return qfalse; - } - if( mesh->first_vertex >= header->num_vertexes || mesh->first_vertex + mesh->num_vertexes > header->num_vertexes || mesh->first_triangle >= header->num_triangles || @@ -463,13 +451,14 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na // calculate joint matrices and their inverses // they are needed only until the pose matrices are calculated + jointMats = (float *)ri.Hunk_AllocateTempMemory( header->num_joints * 2 * 3 * 4 * sizeof(float) ); mat = jointMats; joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); for( i = 0; i < header->num_joints; i++, joint++ ) { float baseFrame[12], invBaseFrame[12]; JointToMatrix( joint->rotate, joint->scale, joint->translate, baseFrame ); - Matrix34Invert( baseFrame, invBaseFrame ); + JointToMatrixInverse( joint->rotate, joint->scale, joint->translate, invBaseFrame ); if ( joint->parent >= 0 ) { @@ -545,6 +534,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na mat += 12; } } + ri.Hunk_FreeTempMemory( jointMats ); // register shaders // overwrite the material offset with the shader index @@ -768,7 +758,7 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { surface = data->surfaces; // don't add third_person objects if not in a portal - personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; + personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && tr.viewParms.portalLevel == 0; if ( ent->e.renderfx & RF_WRAP_FRAMES ) { ent->e.frame %= data->num_frames; @@ -905,127 +895,129 @@ RB_AddIQMSurfaces Compute vertices for this model surface ================= */ -void RB_IQMSurfaceAnim( surfaceType_t *surface ) { +void RB_IQMSurfaceAnim( tessMode_t mode, surfaceType_t *surface ) { srfIQModel_t *surf = (srfIQModel_t *)surface; iqmData_t *data = surf->data; - float jointMats[IQM_MAX_JOINTS * 12]; int i; - vec4_t *outXYZ = &tess.xyz[tess.numVertexes]; - vec4_t *outNormal = &tess.normal[tess.numVertexes]; - vec2_t (*outTexCoord)[2] = &tess.texCoords[tess.numVertexes]; - color4ub_t *outColor = &tess.vertexColors[tess.numVertexes]; - - int frame = backEnd.currentEntity->e.frame % data->num_frames; - int oldframe = backEnd.currentEntity->e.oldframe % data->num_frames; - float backlerp = backEnd.currentEntity->e.backlerp; - - int *tri; - glIndex_t *ptr; - glIndex_t base; - - RB_CHECKOVERFLOW( surf->num_vertexes, surf->num_triangles * 3 ); - - // compute interpolated joint matrices - ComputeJointMats( data, frame, oldframe, backlerp, jointMats ); - - // transform vertexes and fill other data - for( i = 0; i < surf->num_vertexes; - i++, outXYZ++, outNormal++, outTexCoord++, outColor++ ) { - int j, k; - float vtxMat[12]; - float nrmMat[9]; - int vtx = i + surf->first_vertex; - - // compute the vertex matrix by blending the up to - // four blend weights - for( k = 0; k < 12; k++ ) - vtxMat[k] = data->blendWeights[4*vtx] - * jointMats[12*data->blendIndexes[4*vtx] + k]; - for( j = 1; j < 4; j++ ) { - if( data->blendWeights[4*vtx + j] <= 0 ) - break; + if( mode & TESS_VERTEX ) { + glVertex_t *ptr = tess.vertexPtr + tess.numVertexes; + + float *mat; + int frame = backEnd.currentEntity->e.frame % data->num_frames; + int oldframe = backEnd.currentEntity->e.oldframe % data->num_frames; + float backlerp = backEnd.currentEntity->e.backlerp; + + // compute interpolated joint matrices + mat = (float *)RB_AllocScratch( data->num_joints * 12 * sizeof( float ) ); + ComputeJointMats( data, frame, oldframe, backlerp, mat ); + + // transform vertexes and fill other data + for( i = 0; i < surf->num_vertexes; i++, ptr++ ) { + int j, k; + float vtxMat[12]; + float nrmMat[9]; + int vtx = i + surf->first_vertex; + + // compute the vertex matrix by blending the up to + // four blend weights + for( k = 0; k < 12; k++ ) + vtxMat[k] = data->blendWeights[4*vtx] + * mat[12*data->blendIndexes[4*vtx] + k]; + for( j = 1; j < 4; j++ ) { + if( data->blendWeights[4*vtx + j] <= 0 ) + break; + for( k = 0; k < 12; k++ ) + vtxMat[k] += data->blendWeights[4*vtx + j] + * mat[12*data->blendIndexes[4*vtx + j] + k]; + } for( k = 0; k < 12; k++ ) - vtxMat[k] += data->blendWeights[4*vtx + j] - * jointMats[12*data->blendIndexes[4*vtx + j] + k]; + vtxMat[k] *= 1.0f / 255.0f; + + // compute the normal matrix as transpose of the adjoint + // of the vertex matrix + nrmMat[ 0] = vtxMat[ 5]*vtxMat[10] - vtxMat[ 6]*vtxMat[ 9]; + nrmMat[ 1] = vtxMat[ 6]*vtxMat[ 8] - vtxMat[ 4]*vtxMat[10]; + nrmMat[ 2] = vtxMat[ 4]*vtxMat[ 9] - vtxMat[ 5]*vtxMat[ 8]; + nrmMat[ 3] = vtxMat[ 2]*vtxMat[ 9] - vtxMat[ 1]*vtxMat[10]; + nrmMat[ 4] = vtxMat[ 0]*vtxMat[10] - vtxMat[ 2]*vtxMat[ 8]; + nrmMat[ 5] = vtxMat[ 1]*vtxMat[ 8] - vtxMat[ 0]*vtxMat[ 9]; + nrmMat[ 6] = vtxMat[ 1]*vtxMat[ 6] - vtxMat[ 2]*vtxMat[ 5]; + nrmMat[ 7] = vtxMat[ 2]*vtxMat[ 4] - vtxMat[ 0]*vtxMat[ 6]; + nrmMat[ 8] = vtxMat[ 0]*vtxMat[ 5] - vtxMat[ 1]*vtxMat[ 4]; + + ptr->xyz[0] = + vtxMat[ 0] * data->positions[3*vtx+0] + + vtxMat[ 1] * data->positions[3*vtx+1] + + vtxMat[ 2] * data->positions[3*vtx+2] + + vtxMat[ 3]; + ptr->xyz[1] = + vtxMat[ 4] * data->positions[3*vtx+0] + + vtxMat[ 5] * data->positions[3*vtx+1] + + vtxMat[ 6] * data->positions[3*vtx+2] + + vtxMat[ 7]; + ptr->xyz[2] = + vtxMat[ 8] * data->positions[3*vtx+0] + + vtxMat[ 9] * data->positions[3*vtx+1] + + vtxMat[10] * data->positions[3*vtx+2] + + vtxMat[11]; + ptr->fogNum = 0.0f; + + ptr->tc1[0] = data->texcoords[2*vtx + 0]; + ptr->tc1[1] = data->texcoords[2*vtx + 1]; + ptr->tc2[0] = ptr->tc1[0]; + ptr->tc2[1] = ptr->tc1[1]; + + ptr->normal[0] = + nrmMat[ 0] * data->normals[3*vtx+0] + + nrmMat[ 1] * data->normals[3*vtx+1] + + nrmMat[ 2] * data->normals[3*vtx+2]; + ptr->normal[1] = + nrmMat[ 3] * data->normals[3*vtx+0] + + nrmMat[ 4] * data->normals[3*vtx+1] + + nrmMat[ 5] * data->normals[3*vtx+2]; + ptr->normal[2] = + nrmMat[ 6] * data->normals[3*vtx+0] + + nrmMat[ 7] * data->normals[3*vtx+1] + + nrmMat[ 8] * data->normals[3*vtx+2]; + + ptr->color[0] = data->colors[4*vtx+0]; + ptr->color[1] = data->colors[4*vtx+1]; + ptr->color[2] = data->colors[4*vtx+2]; + ptr->color[3] = data->colors[4*vtx+3]; } - for( k = 0; k < 12; k++ ) - vtxMat[k] *= 1.0f / 255.0f; - - // compute the normal matrix as transpose of the adjoint - // of the vertex matrix - nrmMat[ 0] = vtxMat[ 5]*vtxMat[10] - vtxMat[ 6]*vtxMat[ 9]; - nrmMat[ 1] = vtxMat[ 6]*vtxMat[ 8] - vtxMat[ 4]*vtxMat[10]; - nrmMat[ 2] = vtxMat[ 4]*vtxMat[ 9] - vtxMat[ 5]*vtxMat[ 8]; - nrmMat[ 3] = vtxMat[ 2]*vtxMat[ 9] - vtxMat[ 1]*vtxMat[10]; - nrmMat[ 4] = vtxMat[ 0]*vtxMat[10] - vtxMat[ 2]*vtxMat[ 8]; - nrmMat[ 5] = vtxMat[ 1]*vtxMat[ 8] - vtxMat[ 0]*vtxMat[ 9]; - nrmMat[ 6] = vtxMat[ 1]*vtxMat[ 6] - vtxMat[ 2]*vtxMat[ 5]; - nrmMat[ 7] = vtxMat[ 2]*vtxMat[ 4] - vtxMat[ 0]*vtxMat[ 6]; - nrmMat[ 8] = vtxMat[ 0]*vtxMat[ 5] - vtxMat[ 1]*vtxMat[ 4]; - - (*outTexCoord)[0][0] = data->texcoords[2*vtx + 0]; - (*outTexCoord)[0][1] = data->texcoords[2*vtx + 1]; - (*outTexCoord)[1][0] = (*outTexCoord)[0][0]; - (*outTexCoord)[1][1] = (*outTexCoord)[0][1]; - - (*outXYZ)[0] = - vtxMat[ 0] * data->positions[3*vtx+0] + - vtxMat[ 1] * data->positions[3*vtx+1] + - vtxMat[ 2] * data->positions[3*vtx+2] + - vtxMat[ 3]; - (*outXYZ)[1] = - vtxMat[ 4] * data->positions[3*vtx+0] + - vtxMat[ 5] * data->positions[3*vtx+1] + - vtxMat[ 6] * data->positions[3*vtx+2] + - vtxMat[ 7]; - (*outXYZ)[2] = - vtxMat[ 8] * data->positions[3*vtx+0] + - vtxMat[ 9] * data->positions[3*vtx+1] + - vtxMat[10] * data->positions[3*vtx+2] + - vtxMat[11]; - (*outXYZ)[3] = 1.0f; - - (*outNormal)[0] = - nrmMat[ 0] * data->normals[3*vtx+0] + - nrmMat[ 1] * data->normals[3*vtx+1] + - nrmMat[ 2] * data->normals[3*vtx+2]; - (*outNormal)[1] = - nrmMat[ 3] * data->normals[3*vtx+0] + - nrmMat[ 4] * data->normals[3*vtx+1] + - nrmMat[ 5] * data->normals[3*vtx+2]; - (*outNormal)[2] = - nrmMat[ 6] * data->normals[3*vtx+0] + - nrmMat[ 7] * data->normals[3*vtx+1] + - nrmMat[ 8] * data->normals[3*vtx+2]; - (*outNormal)[3] = 0.0f; - - (*outColor)[0] = data->colors[4*vtx+0]; - (*outColor)[1] = data->colors[4*vtx+1]; - (*outColor)[2] = data->colors[4*vtx+2]; - (*outColor)[3] = data->colors[4*vtx+3]; + + RB_FreeScratch( mat ); } - tri = data->triangles + 3 * surf->first_triangle; - ptr = &tess.indexes[tess.numIndexes]; - base = tess.numVertexes; + if( mode & TESS_INDEX ) { + glIndex_t *ptr = tess.indexPtr + tess.numIndexes[tess.indexRange]; + glIndex_t base = tess.numVertexes; + + int *tri = data->triangles; + tri += 3 * surf->first_triangle; - for( i = 0; i < surf->num_triangles; i++ ) { - *ptr++ = base + (*tri++ - surf->first_vertex); - *ptr++ = base + (*tri++ - surf->first_vertex); - *ptr++ = base + (*tri++ - surf->first_vertex); + for( i = 0; i < surf->num_triangles; i++ ) { + *ptr++ = base + (*tri++ - surf->first_vertex); + *ptr++ = base + (*tri++ - surf->first_vertex); + *ptr++ = base + (*tri++ - surf->first_vertex); + } } - tess.numIndexes += 3 * surf->num_triangles; + tess.numIndexes[tess.indexRange] += 3 * surf->num_triangles; + if( tess.minIndex[tess.indexRange] > tess.numVertexes ) + tess.minIndex[tess.indexRange] = tess.numVertexes; tess.numVertexes += surf->num_vertexes; + if( tess.maxIndex[tess.indexRange] > tess.numVertexes - 1 ) + tess.maxIndex[tess.indexRange] = tess.numVertexes - 1; } int R_IQMLerpTag( orientation_t *tag, iqmData_t *data, int startFrame, int endFrame, float frac, const char *tagName ) { - float jointMats[IQM_MAX_JOINTS * 12]; int joint; char *names = data->names; + float *mat; // get joint number by reading the joint names for( joint = 0; joint < data->num_joints; joint++ ) { @@ -1039,20 +1031,21 @@ int R_IQMLerpTag( orientation_t *tag, iqmData_t *data, return qfalse; } - ComputeJointMats( data, startFrame, endFrame, frac, jointMats ); - - tag->axis[0][0] = jointMats[12 * joint + 0]; - tag->axis[1][0] = jointMats[12 * joint + 1]; - tag->axis[2][0] = jointMats[12 * joint + 2]; - tag->origin[0] = jointMats[12 * joint + 3]; - tag->axis[0][1] = jointMats[12 * joint + 4]; - tag->axis[1][1] = jointMats[12 * joint + 5]; - tag->axis[2][1] = jointMats[12 * joint + 6]; - tag->origin[1] = jointMats[12 * joint + 7]; - tag->axis[0][2] = jointMats[12 * joint + 8]; - tag->axis[1][2] = jointMats[12 * joint + 9]; - tag->axis[2][2] = jointMats[12 * joint + 10]; - tag->origin[2] = jointMats[12 * joint + 11]; + mat = (float *)ri.Hunk_AllocateTempMemory( data->num_joints * 12 * sizeof( float ) ); + ComputeJointMats( data, startFrame, endFrame, frac, mat ); + tag->axis[0][0] = mat[12 * joint + 0]; + tag->axis[1][0] = mat[12 * joint + 1]; + tag->axis[2][0] = mat[12 * joint + 2]; + tag->origin[0] = mat[12 * joint + 3]; + tag->axis[0][1] = mat[12 * joint + 4]; + tag->axis[1][1] = mat[12 * joint + 5]; + tag->axis[2][1] = mat[12 * joint + 6]; + tag->origin[1] = mat[12 * joint + 7]; + tag->axis[0][2] = mat[12 * joint + 8]; + tag->axis[1][2] = mat[12 * joint + 9]; + tag->axis[2][2] = mat[12 * joint + 10]; + tag->origin[0] = mat[12 * joint + 11]; + ri.Hunk_FreeTempMemory( mat ); return qtrue; } diff --git a/code/renderer/tr_public.h b/code/renderer/tr_public.h index adb6c0e..837ddea 100644 --- a/code/renderer/tr_public.h +++ b/code/renderer/tr_public.h @@ -80,8 +80,11 @@ typedef struct { void (*BeginFrame)( stereoFrame_t stereoFrame ); // if the pointers are not NULL, timing info will be returned +#ifdef RETURN_GLTIME + void (*EndFrame)( int *frontEndMsec, int *backEndMsec, int *GLMsec ); +#else void (*EndFrame)( int *frontEndMsec, int *backEndMsec ); - +#endif int (*MarkFragments)( int numPoints, const vec3_t *points, const vec3_t projection, int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ); diff --git a/code/renderer/tr_scene.c b/code/renderer/tr_scene.c index d1d34e3..2a94e35 100644 --- a/code/renderer/tr_scene.c +++ b/code/renderer/tr_scene.c @@ -105,7 +105,7 @@ void R_AddPolygonSurfaces( void ) { for ( i = 0, poly = tr.refdef.polys; i < tr.refdef.numPolys ; i++, poly++ ) { sh = R_GetShaderByHandle( poly->hShader ); - R_AddDrawSurf( ( void * )poly, sh, poly->fogIndex, qfalse ); + R_AddDrawSurf( ( void * )poly, sh, poly->fogIndex, 0 ); } } @@ -205,6 +205,8 @@ RE_AddRefEntityToScene ===================== */ void RE_AddRefEntityToScene( const refEntity_t *ent ) { + trRefEntity_t *trEnt; + if ( !tr.registered ) { return; } @@ -224,8 +226,9 @@ void RE_AddRefEntityToScene( const refEntity_t *ent ) { ri.Error( ERR_DROP, "RE_AddRefEntityToScene: bad reType %i", ent->reType ); } - backEndData[tr.smpFrame]->entities[r_numentities].e = *ent; - backEndData[tr.smpFrame]->entities[r_numentities].lightingCalculated = qfalse; + trEnt = &backEndData[tr.smpFrame]->entities[r_numentities]; + trEnt->e = *ent; + trEnt->lightingCalculated = qfalse; r_numentities++; } @@ -249,6 +252,9 @@ void RE_AddDynamicLightToScene( const vec3_t org, float intensity, float r, floa if ( intensity <= 0 ) { return; } + if ( r > 1.0f ) r = 1.0f; + if ( g > 1.0f ) g = 1.0f; + if ( b > 1.0f ) b = 1.0f; // these cards don't have the correct blend mode if ( glConfig.hardwareType == GLHW_RIVA128 || glConfig.hardwareType == GLHW_PERMEDIA2 ) { return; @@ -393,7 +399,8 @@ void RE_RenderScene( const refdef_t *fd ) { parms.viewportY = glConfig.vidHeight - ( tr.refdef.y + tr.refdef.height ); parms.viewportWidth = tr.refdef.width; parms.viewportHeight = tr.refdef.height; - parms.isPortal = qfalse; + parms.portalLevel = 0; + parms.isFirst = qtrue; parms.fovX = tr.refdef.fov_x; parms.fovY = tr.refdef.fov_y; diff --git a/code/renderer/tr_shade.c b/code/renderer/tr_shade.c index 04cef08..f199e1e 100644 --- a/code/renderer/tr_shade.c +++ b/code/renderer/tr_shade.c @@ -33,173 +33,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA This file deals with applying shaders to surface data in the tess struct. */ -/* -================ -R_ArrayElementDiscrete - -This is just for OpenGL conformance testing, it should never be the fastest -================ -*/ -static void APIENTRY R_ArrayElementDiscrete( GLint index ) { - qglColor4ubv( tess.svars.colors[ index ] ); - if ( glState.currenttmu ) { - qglMultiTexCoord2fARB( 0, tess.svars.texcoords[ 0 ][ index ][0], tess.svars.texcoords[ 0 ][ index ][1] ); - qglMultiTexCoord2fARB( 1, tess.svars.texcoords[ 1 ][ index ][0], tess.svars.texcoords[ 1 ][ index ][1] ); - } else { - qglTexCoord2fv( tess.svars.texcoords[ 0 ][ index ] ); - } - qglVertex3fv( tess.xyz[ index ] ); -} - -/* -=================== -R_DrawStripElements - -=================== -*/ -static int c_vertexes; // for seeing how long our average strips are -static int c_begins; -static void R_DrawStripElements( int numIndexes, const glIndex_t *indexes, void ( APIENTRY *element )(GLint) ) { - int i; - int last[3] = { -1, -1, -1 }; - qboolean even; - - c_begins++; - - if ( numIndexes <= 0 ) { - return; - } - - qglBegin( GL_TRIANGLE_STRIP ); - - // prime the strip - element( indexes[0] ); - element( indexes[1] ); - element( indexes[2] ); - c_vertexes += 3; - - last[0] = indexes[0]; - last[1] = indexes[1]; - last[2] = indexes[2]; - - even = qfalse; - - for ( i = 3; i < numIndexes; i += 3 ) - { - // odd numbered triangle in potential strip - if ( !even ) - { - // check previous triangle to see if we're continuing a strip - if ( ( indexes[i+0] == last[2] ) && ( indexes[i+1] == last[1] ) ) - { - element( indexes[i+2] ); - c_vertexes++; - assert( indexes[i+2] < tess.numVertexes ); - even = qtrue; - } - // otherwise we're done with this strip so finish it and start - // a new one - else - { - qglEnd(); - - qglBegin( GL_TRIANGLE_STRIP ); - c_begins++; - - element( indexes[i+0] ); - element( indexes[i+1] ); - element( indexes[i+2] ); - - c_vertexes += 3; - - even = qfalse; - } - } - else - { - // check previous triangle to see if we're continuing a strip - if ( ( last[2] == indexes[i+1] ) && ( last[0] == indexes[i+0] ) ) - { - element( indexes[i+2] ); - c_vertexes++; - - even = qfalse; - } - // otherwise we're done with this strip so finish it and start - // a new one - else - { - qglEnd(); - - qglBegin( GL_TRIANGLE_STRIP ); - c_begins++; - - element( indexes[i+0] ); - element( indexes[i+1] ); - element( indexes[i+2] ); - c_vertexes += 3; - - even = qfalse; - } - } - - // cache the last three vertices - last[0] = indexes[i+0]; - last[1] = indexes[i+1]; - last[2] = indexes[i+2]; - } - - qglEnd(); -} - - - -/* -================== -R_DrawElements - -Optionally performs our own glDrawElements that looks for strip conditions -instead of using the single glDrawElements call that may be inefficient -without compiled vertex arrays. -================== -*/ -static void R_DrawElements( int numIndexes, const glIndex_t *indexes ) { - int primitives; - - primitives = r_primitives->integer; - - // default is to use triangles if compiled vertex arrays are present - if ( primitives == 0 ) { - if ( qglLockArraysEXT ) { - primitives = 2; - } else { - primitives = 1; - } - } - - - if ( primitives == 2 ) { - qglDrawElements( GL_TRIANGLES, - numIndexes, - GL_INDEX_TYPE, - indexes ); - return; - } - - if ( primitives == 1 ) { - R_DrawStripElements( numIndexes, indexes, qglArrayElement ); - return; - } - - if ( primitives == 3 ) { - R_DrawStripElements( numIndexes, indexes, R_ArrayElementDiscrete ); - return; - } - - // anything else will cause no drawing +static const size_t vertexSize = sizeof(glVertex_t); +static ID_INLINE void *BufferOffset(GLsizei offs) { + return offs + (byte *)NULL; } - /* ============================================================= @@ -209,7 +47,6 @@ SURFACE SHADERS */ shaderCommands_t tess; -static qboolean setArraysOnce; /* ================= @@ -217,17 +54,24 @@ R_BindAnimatedImage ================= */ -static void R_BindAnimatedImage( textureBundle_t *bundle ) { +static void R_GetAnimatedImage( textureBundle_t *bundle, qboolean combined, + image_t **pImage ) { int index; if ( bundle->isVideoMap ) { ri.CIN_RunCinematic(bundle->videoMapHandle); ri.CIN_UploadCinematic(bundle->videoMapHandle); + *pImage = bundle->image[0]; return; } if ( bundle->numImageAnimations <= 1 ) { - GL_Bind( bundle->image[0] ); + *pImage = bundle->image[0]; + return; + } + + if ( combined && bundle->combinedImage ) { + *pImage = bundle->combinedImage; return; } @@ -241,7 +85,7 @@ static void R_BindAnimatedImage( textureBundle_t *bundle ) { } index %= bundle->numImageAnimations; - GL_Bind( bundle->image[ index ] ); + *pImage = bundle->image[ index ]; } /* @@ -249,32 +93,31 @@ static void R_BindAnimatedImage( textureBundle_t *bundle ) { DrawTris Draws triangle outlines for debugging +This requires that all vertex pointers etc. are still bound from the StageIterator ================ */ -static void DrawTris (shaderCommands_t *input) { - GL_Bind( tr.whiteImage ); - qglColor3f (1,1,1); - - GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE ); - qglDepthRange( 0, 0 ); - - qglDisableClientState (GL_COLOR_ARRAY); - qglDisableClientState (GL_TEXTURE_COORD_ARRAY); - - qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD - - if (qglLockArraysEXT) { - qglLockArraysEXT(0, input->numVertexes); - GLimp_LogComment( "glLockArraysEXT\n" ); - } - - R_DrawElements( input->numIndexes, input->indexes ); - - if (qglUnlockArraysEXT) { - qglUnlockArraysEXT(); - GLimp_LogComment( "glUnlockArraysEXT\n" ); +static void DrawTris ( glRenderState_t *state, + int numIndexes, GLuint IBO, const glIndex_t *indexes, + glIndex_t start, glIndex_t end, + float r, float g, float b ) { + state->stateBits = GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE | + GLS_DEPTHRANGE_0_TO_0; + + state->numImages = 1; + state->image[0] = tr.whiteImage; + + if ( tess.currentStageIteratorFunc == RB_StageIteratorGLSL ) { + shader_t *shader = tr.defaultShader; + if( tess.dataTexture ) { + shader = tr.defaultMD3Shader; + state->numImages = 2; + state->image[1] = tess.dataTexture; + } + state->program = shader->GLSLprogram; } - qglDepthRange( 0, 1 ); + SetAttrVec4f( state, AL_COLOR, r, g, b, 1.0f ); + + GL_DrawElements( state, numIndexes, IBO, indexes, start, end ); } @@ -285,24 +128,142 @@ DrawNormals Draws vertex normals for debugging ================ */ -static void DrawNormals (shaderCommands_t *input) { +static void DrawNormals ( glRenderState_t *state ) { int i; - vec3_t temp; + vec3_t *temp; + iboInfo_t *ibo; - GL_Bind( tr.whiteImage ); - qglColor3f (1,1,1); - qglDepthRange( 0, 0 ); // never occluded - GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE ); + if( backEnd.projection2D ) + return; - qglBegin (GL_LINES); - for (i = 0 ; i < input->numVertexes ; i++) { - qglVertex3fv (input->xyz[i]); - VectorMA (input->xyz[i], 2, input->normal[i], temp); - qglVertex3fv (temp); - } - qglEnd (); + if( backEnd.normalProgram && tess.currentStageIteratorFunc == RB_StageIteratorGLSL ) { + for( ibo = tess.firstIBO; ibo; ibo = ibo->next ) { + vboInfo_t *vbo = ibo->vbo; + + if( backEnd.currentEntity->e.reType != RT_MODEL ) { + SetAttrVec4f( state, AL_TRANSX, + 1.0f, 0.0f, 0.0f, 0.0f ); + SetAttrVec4f( state, AL_TRANSY, + 0.0f, 1.0f, 0.0f, 0.0f ); + SetAttrVec4f( state, AL_TRANSZ, + 0.0f, 0.0f, 1.0f, 0.0f ); + } else { + SetAttrVec4f( state, AL_TRANSX, + backEnd.currentEntity->e.axis[0][0], + backEnd.currentEntity->e.axis[1][0], + backEnd.currentEntity->e.axis[2][0], + backEnd.currentEntity->e.origin[0] ); + SetAttrVec4f( state, AL_TRANSY, + backEnd.currentEntity->e.axis[0][1], + backEnd.currentEntity->e.axis[1][1], + backEnd.currentEntity->e.axis[2][1], + backEnd.currentEntity->e.origin[1] ); + SetAttrVec4f( state, AL_TRANSZ, + backEnd.currentEntity->e.axis[0][2], + backEnd.currentEntity->e.axis[1][2], + backEnd.currentEntity->e.axis[2][2], + backEnd.currentEntity->e.origin[2] ); + } + + if( !tess.dataTexture ) { + state->program = backEnd.normalProgram; + SetAttrPointer( state, AL_NORMAL, vbo->vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->normal ); + } else { + float frameOffs = (backEnd.currentEntity->e.frame / tess.framesPerRow) * tess.scaleY + + (backEnd.currentEntity->e.frame % tess.framesPerRow) * tess.scaleX; + float oldFrameOffs = (backEnd.currentEntity->e.oldframe / tess.framesPerRow) * tess.scaleY + + (backEnd.currentEntity->e.oldframe % tess.framesPerRow) * tess.scaleX; + state->program = backEnd.normalProgramMD3; + state->numImages = 1; + state->image[0] = tess.dataTexture; + SetAttrVec4f( state, AL_TIMES, + tess.shaderTime, + backEnd.currentEntity->e.backlerp, + frameOffs, + oldFrameOffs ); + } + state->stateBits = GLS_POLYMODE_LINE | + GLS_DEPTHMASK_TRUE | + GLS_DEPTHRANGE_0_TO_0; + SetAttrVec4f( state, AL_COLOR, 1.0f, 1.0f, 1.0f, 1.0f ); + + SetAttrPointer( state, AL_VERTEX, vbo->vbo, + 4, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->xyz ); + GL_DrawArrays( state, GL_POINTS, ibo->minIndex, + ibo->maxIndex - ibo->minIndex + 1 ); + } + + if( tess.numVertexes > 0 ) { + state->program = backEnd.normalProgram; + state->numImages = 0; + SetAttrVec4f( state, AL_COLOR, 1.0f, 1.0f, 1.0f, 1.0f ); + state->stateBits = GLS_POLYMODE_LINE | + GLS_DEPTHMASK_TRUE | + GLS_DEPTHRANGE_0_TO_0; + + if( backEnd.currentEntity->e.reType != RT_MODEL ) { + SetAttrVec4f( state, AL_TRANSX, + 1.0f, 0.0f, 0.0f, 0.0f ); + SetAttrVec4f( state, AL_TRANSY, + 0.0f, 1.0f, 0.0f, 0.0f ); + SetAttrVec4f( state, AL_TRANSZ, + 0.0f, 0.0f, 1.0f, 0.0f ); + } else { + SetAttrVec4f( state, AL_TRANSX, + backEnd.currentEntity->e.axis[0][0], + backEnd.currentEntity->e.axis[1][0], + backEnd.currentEntity->e.axis[2][0], + backEnd.currentEntity->e.origin[0] ); + SetAttrVec4f( state, AL_TRANSY, + backEnd.currentEntity->e.axis[0][1], + backEnd.currentEntity->e.axis[1][1], + backEnd.currentEntity->e.axis[2][1], + backEnd.currentEntity->e.origin[1] ); + SetAttrVec4f( state, AL_TRANSZ, + backEnd.currentEntity->e.axis[0][2], + backEnd.currentEntity->e.axis[1][2], + backEnd.currentEntity->e.axis[2][2], + backEnd.currentEntity->e.origin[2] ); + } - qglDepthRange( 0, 1 ); + SetAttrPointer( state, AL_NORMAL, 0, + 3, GL_FLOAT, sizeof(glVertex_t), + &tess.vertexPtr->normal ); + SetAttrPointer( state, AL_VERTEX, 0, + 4, GL_FLOAT, sizeof(glVertex_t), + &tess.vertexPtr->xyz ); + GL_DrawArrays( state, GL_POINTS, 0, tess.numVertexes ); + } + } else { + if( tess.numVertexes > 0 ) { + temp = RB_AllocScratch( tess.numVertexes * 2 * sizeof(vec3_t) ); + state->program = NULL; + state->numImages = 0; + SetAttrVec4f( state, AL_COLOR, 1.0f, 1.0f, 1.0f, 1.0f ); + state->stateBits = GLS_POLYMODE_LINE | + GLS_DEPTHMASK_TRUE | + GLS_DEPTHRANGE_0_TO_0; + + if( tess.currentStageIteratorFunc == RB_StageIteratorGLSL ) + qglLoadMatrixf( backEnd.or.modelMatrix ); + + for (i = 0 ; i < tess.numVertexes ; i++) { + VectorCopy( tess.vertexPtr[i].xyz, temp[2*i] ); + VectorMA( tess.vertexPtr[i].xyz, 2, tess.vertexPtr[i].normal, temp[2*i+1] ); + } + SetAttrPointer( state, AL_VERTEX, 0, + 3, GL_FLOAT, sizeof(vec3_t), + temp ); + GL_DrawArrays( state, GL_LINES, 0, 2 * tess.numVertexes ); + RB_FreeScratch( temp ); + + if( tess.currentStageIteratorFunc == RB_StageIteratorGLSL ) + qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); + } + } } /* @@ -314,24 +275,158 @@ because a surface may be forced to perform a RB_End due to overflow. ============== */ -void RB_BeginSurface( shader_t *shader, int fogNum ) { +void RB_BeginSurface( shader_t *shader ) { + + shader_t *state = shader; - shader_t *state = (shader->remappedShader) ? shader->remappedShader : shader; + tess.indexPtr = NULL; + tess.vertexPtr = NULL; + tess.firstIBO = NULL; + + tess.numVertexes = tess.indexRange = 0; + tess.numIndexes[0] = tess.numIndexes[1] = tess.numIndexes[2] = 0; + tess.minIndex[0] = tess.minIndex[1] = tess.minIndex[2] = 0x7fffffff; + tess.maxIndex[0] = tess.maxIndex[1] = tess.maxIndex[2] = 0; + + if( shader->remappedShader ) + shader = shader->remappedShader; - tess.numIndexes = 0; - tess.numVertexes = 0; tess.shader = state; - tess.fogNum = fogNum; tess.dlightBits = 0; // will be OR'd in by surface functions + tess.fogNum = 0; tess.xstages = state->stages; tess.numPasses = state->numUnfoggedPasses; tess.currentStageIteratorFunc = state->optimalStageIteratorFunc; + tess.numInstances = 0; tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; if (tess.shader->clampTime && tess.shaderTime >= tess.shader->clampTime) { tess.shaderTime = tess.shader->clampTime; } +} + +static void *RB_AllocateStreamBuffer(GLenum target, glStreamBuffer_t *buf, GLsizei amount) +{ + void *ptr; + + if( qglMapBufferRange ) { + // use unsynchronized map_buffer_range to append to the end + // or orphan the buffer and create a new one if not enough room + if( amount > buf->size ) { + for(buf->size = 0x10000; amount > buf->size; ) + buf->size *= 2; + qglBufferDataARB( target, buf->size, NULL, + GL_STREAM_DRAW_ARB ); + buf->nextFree = 0; + } + + if( amount + buf->nextFree > buf->size ) { + buf->nextFree = 0; + ptr = qglMapBufferRange( target, buf->nextFree, amount, + GL_MAP_WRITE_BIT | + GL_MAP_INVALIDATE_BUFFER_BIT ); + } else { + ptr = qglMapBufferRange( target, buf->nextFree, amount, + GL_MAP_WRITE_BIT | + GL_MAP_INVALIDATE_RANGE_BIT | + GL_MAP_UNSYNCHRONIZED_BIT ); + } + buf->mapped = qtrue; + buf->current = buf->nextFree; + buf->nextFree += amount; + } else { + // always orphan and create a new buffer + qglBufferDataARB( target, amount, NULL, GL_STREAM_DRAW_ARB ); + ptr = qglMapBufferARB( target, GL_WRITE_ONLY_ARB ); + buf->size = amount; + buf->mapped = qtrue; + } + + return ptr; +} + +/* +============== +RB_AllocateSurface + +Allocate Buffers (in RAM or VBO) to store the vertex data. +============== +*/ +void RB_AllocateSurface( void ) { + int numIndexes; + int size; + + numIndexes = tess.numIndexes[0] + + tess.numIndexes[1] + + tess.numIndexes[2]; + if ( tess.shader == tr.shadowShader ) { + // need more room for stencil shadows + tess.numVertexes *= 2; + numIndexes *= 6; + } + + if( tess.numVertexes == 0 && numIndexes == 0 ) { + return; + } + + // round to next multiple of 4 vertexes for alignment + tess.numVertexes = (tess.numVertexes + 3) & -4; + + if( tess.shader == tr.buildVBOShader ) { + qglBufferDataARB( GL_ARRAY_BUFFER_ARB, + tess.numVertexes * vertexSize, NULL, + GL_STATIC_DRAW_ARB ); + tess.vertexPtr = (glVertex_t *)qglMapBufferARB( GL_ARRAY_BUFFER_ARB, + GL_WRITE_ONLY_ARB ); + tess.indexPtr = NULL; + } else if( tess.shader == tr.buildIBOShader ) { + qglBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, + numIndexes * sizeof(glIndex_t), NULL, + GL_STATIC_DRAW_ARB ); + tess.indexPtr = (glIndex_t *)qglMapBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, + GL_WRITE_ONLY_ARB ); + size = tess.numVertexes * vertexSize; + + tess.vertexBuffer = RB_AllocScratch( size ); + tess.vertexPtr = tess.vertexBuffer; + } else if( tess.VtxBuf.id ) { + size_t size = tess.numVertexes * vertexSize; + + if( tess.shader->anyGLAttr & GLA_FULL_dynamic ) { + tess.vertexBuffer = RB_AllocScratch( size + numIndexes * sizeof(glIndex_t) ); + tess.vertexPtr = tess.vertexBuffer; + tess.indexPtr = (glIndex_t *)(tess.vertexPtr + tess.numVertexes); + } else { + if( tess.shader->anyGLAttr & (GLA_VERTEX_dynamic | + GLA_NORMAL_dynamic | + GLA_TC2_dynamic | + GLA_TC1_dynamic | + GLA_COLOR_dynamic ) ) { + tess.vertexBuffer = RB_AllocScratch( size ); + tess.vertexPtr = tess.vertexBuffer; + } else { + GL_VBO( tess.VtxBuf.id ); + tess.vertexPtr = RB_AllocateStreamBuffer( GL_ARRAY_BUFFER_ARB, + &tess.VtxBuf, size ); + } + + GL_IBO( tess.IdxBuf.id ); + tess.indexPtr = RB_AllocateStreamBuffer( GL_ELEMENT_ARRAY_BUFFER, + &tess.IdxBuf, numIndexes * sizeof(glIndex_t) ); + } + } else { + size = tess.numVertexes * vertexSize + + numIndexes * sizeof(glIndex_t); + + tess.vertexBuffer = RB_AllocScratch( size ); + tess.vertexPtr = tess.vertexBuffer; + tess.indexPtr = (glIndex_t *)(tess.vertexPtr + tess.numVertexes); + } + tess.numVertexes = tess.indexRange = 0; + tess.numIndexes[0] = tess.numIndexes[1] = tess.numIndexes[2] = 0; + tess.minIndex[0] = tess.minIndex[1] = tess.minIndex[2] = 0x7fffffff; + tess.maxIndex[0] = tess.maxIndex[1] = tess.maxIndex[2] = 0; } @@ -345,52 +440,54 @@ t0 = most upstream according to spec t1 = most downstream according to spec =================== */ -static void DrawMultitextured( shaderCommands_t *input, int stage ) { +static void DrawMultitextured( glRenderState_t *state, + shaderCommands_t *input, + int stage ) { shaderStage_t *pStage; + int bundle; pStage = tess.xstages[stage]; - GL_State( pStage->stateBits ); + state->program = NULL; + state->stateBits = pStage->stateBits; // this is an ugly hack to work around a GeForce driver // bug with multitexture and clip planes - if ( backEnd.viewParms.isPortal ) { + if ( backEnd.viewParms.portalLevel ) { qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); } // // base // - GL_SelectTexture( 0 ); - qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[0] ); - R_BindAnimatedImage( &pStage->bundle[0] ); + state->numImages = 1; + R_GetAnimatedImage( &pStage->bundle[0], qfalse, &state->image[0] ); // - // lightmap/secondary pass + // lightmap/secondary passes // - GL_SelectTexture( 1 ); - qglEnable( GL_TEXTURE_2D ); - qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); + for ( bundle = 1; bundle < glConfig.numTextureUnits; bundle++ ) { + if ( !pStage->bundle[bundle].multitextureEnv ) + break; + + if ( r_lightmap->integer ) { + GL_TexEnv( bundle, GL_REPLACE ); + } else { + GL_TexEnv( bundle, pStage->bundle[bundle].multitextureEnv ); + } - if ( r_lightmap->integer ) { - GL_TexEnv( GL_REPLACE ); - } else { - GL_TexEnv( tess.shader->multitextureEnv ); + R_GetAnimatedImage( &pStage->bundle[bundle], qfalse, &state->image[bundle] ); } - qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[1] ); - - R_BindAnimatedImage( &pStage->bundle[1] ); - - R_DrawElements( input->numIndexes, input->indexes ); - - // - // disable texturing on TEXTURE1, then select TEXTURE0 - // - //qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); - qglDisable( GL_TEXTURE_2D ); - - GL_SelectTexture( 0 ); + state->numImages = bundle; + if( tess.IdxBuf.mapped ) { + GL_DrawElements( state, input->numIndexes[1], tess.IdxBuf.id, + BufferOffset(tess.IdxBuf.current), + input->minIndex[1], input->maxIndex[1] ); + } else { + GL_DrawElements( state, input->numIndexes[1], 0, input->indexPtr, + input->minIndex[1], input->maxIndex[1] ); + } } @@ -403,7 +500,7 @@ Perform dynamic lighting with another rendering pass =================== */ #if idppc_altivec -static void ProjectDlightTexture_altivec( void ) { +static void ProjectDlightTexture_altivec( glRenderState_t *state ) { int i, l; vec_t origin0, origin1, origin2; float texCoords0, texCoords1; @@ -416,12 +513,12 @@ static void ProjectDlightTexture_altivec( void ) { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff); - float *texCoords; - byte *colors; - byte clipBits[SHADER_MAX_VERTEXES]; - float texCoordsArray[SHADER_MAX_VERTEXES][2]; - byte colorArray[SHADER_MAX_VERTEXES][4]; - unsigned hitIndexes[SHADER_MAX_INDEXES]; + vec2_t *texCoords; + color4ub_t *colors; + byte *clipBits; + vec2_t *texCoordsArray; + color4ub_t *colorArray; + glIndex_t *hitIndexes; int numIndexes; float scale; float radius; @@ -432,6 +529,11 @@ static void ProjectDlightTexture_altivec( void ) { return; } + clipBits = RB_AllocScratch( sizeof( byte ) * tess.numVertexes ); + texCoordsArray = RB_AllocScratch( sizeof( vec2_t ) * tess.numVertexes ); + colorArray = RB_AllocScratch( sizeof( color4ub_t ) * tess.numVertexes ); + hitIndexes = RB_AllocScratch( sizeof( glIndex_t ) * tess.numIndexes[2] ); + // There has to be a better way to do this so that floatColor // and/or modulate are already 16-byte aligned. floatColorVecPerm = vec_lvsl(0,(float *)floatColor); @@ -445,8 +547,8 @@ static void ProjectDlightTexture_altivec( void ) { if ( !( tess.dlightBits & ( 1 << l ) ) ) { continue; // this surface definately doesn't have any of this light } - texCoords = texCoordsArray[0]; - colors = colorArray[0]; + texCoords = texCoordsArray; + colors = colorArray; dl = &backEnd.refdef.dlights[l]; origin0 = dl->transformed[0]; @@ -480,13 +582,14 @@ static void ProjectDlightTexture_altivec( void ) { floatColorVec0 = vec_ld(0, floatColor); floatColorVec1 = vec_ld(11, floatColor); floatColorVec0 = vec_perm(floatColorVec0,floatColorVec0,floatColorVecPerm); - for ( i = 0 ; i < tess.numVertexes ; i++, texCoords += 2, colors += 4 ) { + for ( i = tess.minIndex[2] ; i < tess.numVertexes ; + i++, texCoords++, colors++ ) { int clip = 0; vec_t dist0, dist1, dist2; - dist0 = origin0 - tess.xyz[i][0]; - dist1 = origin1 - tess.xyz[i][1]; - dist2 = origin2 - tess.xyz[i][2]; + dist0 = origin0 - tess.vertexPtr2[i].xyz[0]; + dist1 = origin1 - tess.vertexPtr2[i].xyz[1]; + dist2 = origin2 - tess.vertexPtr2[i].xyz[2]; backEnd.pc.c_dlightVertexes++; @@ -494,10 +597,10 @@ static void ProjectDlightTexture_altivec( void ) { texCoords1 = 0.5f + dist1 * scale; if( !r_dlightBacks->integer && - // dist . tess.normal[i] - ( dist0 * tess.normal[i][0] + - dist1 * tess.normal[i][1] + - dist2 * tess.normal[i][2] ) < 0.0f ) { + // dist . tess.normal[i] + ( dist0 * tess.vertexPtr3[i].normal[0] + + dist1 * tess.vertexPtr3[i].normal[1] + + dist2 * tess.vertexPtr3[i].normal[2] ) < 0.0f ) { clip = 63; } else { if ( texCoords0 < 0.0f ) { @@ -510,8 +613,8 @@ static void ProjectDlightTexture_altivec( void ) { } else if ( texCoords1 > 1.0f ) { clip |= 8; } - texCoords[0] = texCoords0; - texCoords[1] = texCoords1; + (*texCoords)[0] = texCoords0; + (*texCoords)[1] = texCoords1; // modulate the strength based on the height and color if ( dist2 > radius ) { @@ -543,12 +646,13 @@ static void ProjectDlightTexture_altivec( void ) { // build a list of triangles that need light numIndexes = 0; - for ( i = 0 ; i < tess.numIndexes ; i += 3 ) { - int a, b, c; + for ( i = tess.numIndexes[0]; + i < tess.numIndexes[0] + tess.numIndexes[2] ; i += 3 ) { + glIndex_t a, b, c; - a = tess.indexes[i]; - b = tess.indexes[i+1]; - c = tess.indexes[i+2]; + a = tess.indexPtr[i]; + b = tess.indexPtr[i+1]; + c = tess.indexPtr[i+2]; if ( clipBits[a] & clipBits[b] & clipBits[c] ) { continue; // not lighted } @@ -562,38 +666,58 @@ static void ProjectDlightTexture_altivec( void ) { continue; } - qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); - qglTexCoordPointer( 2, GL_FLOAT, 0, texCoordsArray[0] ); - - qglEnableClientState( GL_COLOR_ARRAY ); - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, colorArray ); - - GL_Bind( tr.dlightImage ); + state->program = NULL; + GL_VBO( 0, 0 ); + + SetAttrPointer( state, AL_TEXCOORD, 0, + 2, GL_FLOAT, sizeof(vec2_t), + texCoordsArray ); + SetAttrPointer( state, AL_COLOR, 0, + 4, GL_UNSIGNED_BYTE, sizeof(color4ub_t), + colorArray ); + + state->numImages = 1; + state->image[0] = tr.dlightImage; // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light // where they aren't rendered if ( dl->additive ) { - GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + state->stateBits = GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | + GLS_DEPTHFUNC_EQUAL; + } else { + state->stateBits = GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | + GLS_DEPTHFUNC_EQUAL; } - else { - GL_State( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + GL_DrawElements( state, numIndexes, 0, hitIndexes, + 0, tess.numVertexes-1 ); + + if( r_showtris->integer ) { + DrawTris( state, numIndexes, + 0, hitIndexes, + 0, tess.numVertexes - 1, + 1.0f, 1.0f, 1.0f ); } - R_DrawElements( numIndexes, hitIndexes ); + backEnd.pc.c_totalIndexes += numIndexes; backEnd.pc.c_dlightIndexes += numIndexes; } + + RB_FreeScratch( hitIndexes ); + RB_FreeScratch( colorArray ); + RB_FreeScratch( texCoordsArray ); + RB_FreeScratch( clipBits ); } #endif -static void ProjectDlightTexture_scalar( void ) { +static void ProjectDlightTexture_scalar( glRenderState_t *state ) { int i, l; vec3_t origin; - float *texCoords; - byte *colors; - byte clipBits[SHADER_MAX_VERTEXES]; - float texCoordsArray[SHADER_MAX_VERTEXES][2]; - byte colorArray[SHADER_MAX_VERTEXES][4]; - unsigned hitIndexes[SHADER_MAX_INDEXES]; + vec2_t *texCoords; + color4ub_t *colors; + byte *clipBits; + vec2_t *texCoordsArray; + color4ub_t *colorArray; + glIndex_t *hitIndexes; int numIndexes; float scale; float radius; @@ -604,14 +728,19 @@ static void ProjectDlightTexture_scalar( void ) { return; } + clipBits = RB_AllocScratch( sizeof( byte ) * tess.numVertexes ); + texCoordsArray = RB_AllocScratch( sizeof( vec2_t ) * tess.numVertexes ); + colorArray = RB_AllocScratch( sizeof( color4ub_t ) * tess.numVertexes ); + hitIndexes = RB_AllocScratch( sizeof( glIndex_t ) * tess.numIndexes[2] ); + for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) { dlight_t *dl; if ( !( tess.dlightBits & ( 1 << l ) ) ) { continue; // this surface definately doesn't have any of this light } - texCoords = texCoordsArray[0]; - colors = colorArray[0]; + texCoords = texCoordsArray; + colors = colorArray; dl = &backEnd.refdef.dlights[l]; VectorCopy( dl->transformed, origin ); @@ -641,36 +770,35 @@ static void ProjectDlightTexture_scalar( void ) { floatColor[2] = dl->color[2] * 255.0f; } - for ( i = 0 ; i < tess.numVertexes ; i++, texCoords += 2, colors += 4 ) { - int clip = 0; + for ( i = tess.minIndex[2] ; i < tess.numVertexes ; + i++, texCoords++, colors++ ) { + int clip = 0; vec3_t dist; - VectorSubtract( origin, tess.xyz[i], dist ); + VectorSubtract( origin, tess.vertexPtr[i].xyz, dist ); backEnd.pc.c_dlightVertexes++; - texCoords[0] = 0.5f + dist[0] * scale; - texCoords[1] = 0.5f + dist[1] * scale; + (*texCoords)[0] = 0.5f + dist[0] * scale; + (*texCoords)[1] = 0.5f + dist[1] * scale; if( !r_dlightBacks->integer && - // dist . tess.normal[i] - ( dist[0] * tess.normal[i][0] + - dist[1] * tess.normal[i][1] + - dist[2] * tess.normal[i][2] ) < 0.0f ) { + // dist . tess.normal[i] + ( dist[0] * tess.vertexPtr[i].normal[0] + + dist[1] * tess.vertexPtr[i].normal[1] + + dist[2] * tess.vertexPtr[i].normal[2] ) < 0.0f ) { clip = 63; } else { - if ( texCoords[0] < 0.0f ) { + if ( (*texCoords)[0] < 0.0f ) { clip |= 1; - } else if ( texCoords[0] > 1.0f ) { + } else if ( (*texCoords)[0] > 1.0f ) { clip |= 2; } - if ( texCoords[1] < 0.0f ) { + if ( (*texCoords)[1] < 0.0f ) { clip |= 4; - } else if ( texCoords[1] > 1.0f ) { + } else if ( (*texCoords)[1] > 1.0f ) { clip |= 8; } - texCoords[0] = texCoords[0]; - texCoords[1] = texCoords[1]; // modulate the strength based on the height and color if ( dist[2] > radius ) { @@ -689,20 +817,21 @@ static void ProjectDlightTexture_scalar( void ) { } } clipBits[i] = clip; - colors[0] = ri.ftol(floatColor[0] * modulate); - colors[1] = ri.ftol(floatColor[1] * modulate); - colors[2] = ri.ftol(floatColor[2] * modulate); - colors[3] = 255; + (*colors)[0] = ri.ftol(floatColor[0] * modulate); + (*colors)[1] = ri.ftol(floatColor[1] * modulate); + (*colors)[2] = ri.ftol(floatColor[2] * modulate); + (*colors)[3] = 255; } // build a list of triangles that need light numIndexes = 0; - for ( i = 0 ; i < tess.numIndexes ; i += 3 ) { - int a, b, c; + for ( i = tess.numIndexes[0] ; + i < tess.numIndexes[0] + tess.numIndexes[2] ; i += 3 ) { + glIndex_t a, b, c; - a = tess.indexes[i]; - b = tess.indexes[i+1]; - c = tess.indexes[i+2]; + a = tess.indexPtr[i]; + b = tess.indexPtr[i+1]; + c = tess.indexPtr[i+2]; if ( clipBits[a] & clipBits[b] & clipBits[c] ) { continue; // not lighted } @@ -715,37 +844,58 @@ static void ProjectDlightTexture_scalar( void ) { if ( !numIndexes ) { continue; } - - qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); - qglTexCoordPointer( 2, GL_FLOAT, 0, texCoordsArray[0] ); - - qglEnableClientState( GL_COLOR_ARRAY ); - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, colorArray ); - - GL_Bind( tr.dlightImage ); + + state->program = tr.defaultShader->GLSLprogram; + + SetAttrPointer( state, AL_TEXCOORD, 0, + 2, GL_FLOAT, sizeof(vec2_t), + texCoordsArray ); + SetAttrPointer( state, AL_COLOR, 0, + 4, GL_UNSIGNED_BYTE, sizeof(color4ub_t), + colorArray ); + + state->numImages = 1; + state->image[0] = tr.dlightImage; + state->stateBits = 0; + if ( tess.xstages[0] ) + state->stateBits |= tess.xstages[0]->stateBits & GLS_POLYGON_OFFSET; // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light // where they aren't rendered if ( dl->additive ) { - GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + state->stateBits |= GLS_DEPTHFUNC_EQUAL | + GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE; + } else { + state->stateBits |= GLS_DEPTHFUNC_EQUAL | + GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE; } - else { - GL_State( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + + GL_DrawElements( state, numIndexes, 0, hitIndexes, + 0, tess.numVertexes-1 ); + if( r_showtris->integer ) { + DrawTris( state, numIndexes, + 0, hitIndexes, + 0, tess.numVertexes - 1, + 1.0f, 1.0f, 1.0f ); } - R_DrawElements( numIndexes, hitIndexes ); backEnd.pc.c_totalIndexes += numIndexes; backEnd.pc.c_dlightIndexes += numIndexes; } + + RB_FreeScratch( hitIndexes ); + RB_FreeScratch( colorArray ); + RB_FreeScratch( texCoordsArray ); + RB_FreeScratch( clipBits ); } -static void ProjectDlightTexture( void ) { +static void ProjectDlightTexture( glRenderState_t *state ) { #if idppc_altivec if (com_altivec->integer) { // must be in a seperate function or G3 systems will crash. - ProjectDlightTexture_altivec(); + ProjectDlightTexture_altivec( state ); return; } #endif - ProjectDlightTexture_scalar(); + ProjectDlightTexture_scalar( state ); } @@ -756,33 +906,47 @@ RB_FogPass Blends a fog texture on top of everything else =================== */ -static void RB_FogPass( void ) { +static void RB_FogPass( glRenderState_t *state ) { fog_t *fog; - int i; - qglEnableClientState( GL_COLOR_ARRAY ); - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors ); + tess.svars.texcoords[0] = RB_AllocScratch( tess.numVertexes * sizeof(vec2_t) ); + + state->stateBits = GLS_DEFAULT; + if ( tess.xstages[0] ) + state->stateBits |= tess.xstages[0]->stateBits & GLS_POLYGON_OFFSET; + if ( tess.shader->fogPass == FP_EQUAL ) + state->stateBits |= GLS_DEPTHFUNC_EQUAL; - qglEnableClientState( GL_TEXTURE_COORD_ARRAY); - qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoords[0] ); + if ( tr.fogShader->GLSLprogram ) { + state->program = tr.fogShader->GLSLprogram; + state->stateBits |= GLS_SRCBLEND_ONE | GLS_DSTBLEND_SRC_ALPHA; + } else { + state->program = NULL; + state->stateBits |= GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + } fog = tr.world->fogs + tess.fogNum; - for ( i = 0; i < tess.numVertexes; i++ ) { - * ( int * )&tess.svars.colors[i] = fog->colorInt; - } + RB_CalcFogTexCoords( tess.svars.texcoords[0], tess.minIndex[2], + tess.numVertexes ); - RB_CalcFogTexCoords( ( float * ) tess.svars.texcoords[0] ); + SetAttrPointer( state, AL_TEXCOORD, 0, + 2, GL_FLOAT, sizeof(vec2_t), + tess.svars.texcoords[0][0] ); + SetAttrVec4f( state, AL_COLOR, + fog->parms.color[0], + fog->parms.color[1], + fog->parms.color[2], + 1.0f ); - GL_Bind( tr.fogImage ); + state->numImages = 1; + state->image[0] = tr.fogImage; - if ( tess.shader->fogPass == FP_EQUAL ) { - GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL ); - } else { - GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); - } + GL_DrawElements( state, tess.numIndexes[2], + 0, tess.indexPtr + tess.numIndexes[0], + 0, tess.numVertexes-1 ); - R_DrawElements( tess.numIndexes, tess.indexes ); + RB_FreeScratch( tess.svars.texcoords[0] ); } /* @@ -790,141 +954,226 @@ static void RB_FogPass( void ) { ComputeColors =============== */ -static void ComputeColors( shaderStage_t *pStage ) +static void ComputeColors( glRenderState_t *state, + vboInfo_t *VBO, + shaderStage_t *pStage, + qboolean skipFog ) { int i; - - // - // rgbGen - // - switch ( pStage->rgbGen ) - { - case CGEN_IDENTITY: - Com_Memset( tess.svars.colors, 0xff, tess.numVertexes * 4 ); - break; - default: - case CGEN_IDENTITY_LIGHTING: - Com_Memset( tess.svars.colors, tr.identityLightByte, tess.numVertexes * 4 ); - break; - case CGEN_LIGHTING_DIFFUSE: - RB_CalcDiffuseColor( ( unsigned char * ) tess.svars.colors ); - break; - case CGEN_EXACT_VERTEX: - Com_Memcpy( tess.svars.colors, tess.vertexColors, tess.numVertexes * sizeof( tess.vertexColors[0] ) ); - break; - case CGEN_CONST: - for ( i = 0; i < tess.numVertexes; i++ ) { - *(int *)tess.svars.colors[i] = *(int *)pStage->constantColor; - } - break; - case CGEN_VERTEX: - if ( tr.identityLight == 1 ) - { - Com_Memcpy( tess.svars.colors, tess.vertexColors, tess.numVertexes * sizeof( tess.vertexColors[0] ) ); - } - else - { - for ( i = 0; i < tess.numVertexes; i++ ) - { - tess.svars.colors[i][0] = tess.vertexColors[i][0] * tr.identityLight; - tess.svars.colors[i][1] = tess.vertexColors[i][1] * tr.identityLight; - tess.svars.colors[i][2] = tess.vertexColors[i][2] * tr.identityLight; - tess.svars.colors[i][3] = tess.vertexColors[i][3]; - } - } - break; - case CGEN_ONE_MINUS_VERTEX: - if ( tr.identityLight == 1 ) - { - for ( i = 0; i < tess.numVertexes; i++ ) - { - tess.svars.colors[i][0] = 255 - tess.vertexColors[i][0]; - tess.svars.colors[i][1] = 255 - tess.vertexColors[i][1]; - tess.svars.colors[i][2] = 255 - tess.vertexColors[i][2]; - } - } - else - { - for ( i = 0; i < tess.numVertexes; i++ ) - { - tess.svars.colors[i][0] = ( 255 - tess.vertexColors[i][0] ) * tr.identityLight; - tess.svars.colors[i][1] = ( 255 - tess.vertexColors[i][1] ) * tr.identityLight; - tess.svars.colors[i][2] = ( 255 - tess.vertexColors[i][2] ) * tr.identityLight; - } - } - break; - case CGEN_FOG: - { - fog_t *fog; - - fog = tr.world->fogs + tess.fogNum; - - for ( i = 0; i < tess.numVertexes; i++ ) { - * ( int * )&tess.svars.colors[i] = fog->colorInt; - } - } - break; - case CGEN_WAVEFORM: - RB_CalcWaveColor( &pStage->rgbWave, ( unsigned char * ) tess.svars.colors ); - break; - case CGEN_ENTITY: - RB_CalcColorFromEntity( ( unsigned char * ) tess.svars.colors ); - break; - case CGEN_ONE_MINUS_ENTITY: - RB_CalcColorFromOneMinusEntity( ( unsigned char * ) tess.svars.colors ); - break; + alphaGen_t aGen = pStage->alphaGen; + colorGen_t rgbGen = pStage->rgbGen; + color4ub_t color; + fog_t *fog; + qboolean constRGB, constA; + + // get rid of AGEN_SKIP + if ( aGen == AGEN_SKIP ) { + if ( rgbGen == CGEN_EXACT_VERTEX || + rgbGen == CGEN_VERTEX ) { + aGen = AGEN_VERTEX; + } else { + aGen = AGEN_IDENTITY; + } + } + + // no need to multiply by 1 + if ( tr.identityLight == 1 && rgbGen == CGEN_VERTEX ) { + rgbGen = CGEN_EXACT_VERTEX; } - // - // alphaGen - // - switch ( pStage->alphaGen ) - { - case AGEN_SKIP: + // check for constant RGB + switch( rgbGen ) { + case CGEN_IDENTITY_LIGHTING: + color[0] = color[1] = color[2] = tr.identityLightByte; + constRGB = qtrue; break; - case AGEN_IDENTITY: - if ( pStage->rgbGen != CGEN_IDENTITY ) { - if ( ( pStage->rgbGen == CGEN_VERTEX && tr.identityLight != 1 ) || - pStage->rgbGen != CGEN_VERTEX ) { - for ( i = 0; i < tess.numVertexes; i++ ) { - tess.svars.colors[i][3] = 0xff; - } - } - } + case CGEN_IDENTITY: + color[0] = color[1] = color[2] = 255; + constRGB = qtrue; break; - case AGEN_CONST: - if ( pStage->rgbGen != CGEN_CONST ) { - for ( i = 0; i < tess.numVertexes; i++ ) { - tess.svars.colors[i][3] = pStage->constantColor[3]; - } - } + case CGEN_ENTITY: + RB_CalcColorFromEntity( &color, 1 ); + constRGB = qtrue; break; - case AGEN_WAVEFORM: - RB_CalcWaveAlpha( &pStage->alphaWave, ( unsigned char * ) tess.svars.colors ); + case CGEN_ONE_MINUS_ENTITY: + RB_CalcColorFromOneMinusEntity( &color, 1 ); + constRGB = qtrue; break; - case AGEN_LIGHTING_SPECULAR: - RB_CalcSpecularAlpha( ( unsigned char * ) tess.svars.colors ); + case CGEN_WAVEFORM: + RB_CalcWaveColor( &pStage->rgbWave, &color, 1 ); + constRGB = qtrue; + break; + case CGEN_FOG: + fog = tr.world->fogs + tess.fogNum; + *(int *)(&color) = fog->colorInt; + constRGB = qtrue; + break; + case CGEN_CONST: + *(int *)(&color) = *(int *)pStage->constantColor; + constRGB = qtrue; + break; + default: + constRGB = qfalse; + break; + } + + // check for constant ALPHA + switch( aGen ) { + case AGEN_IDENTITY: + color[3] = 255; + constA = qtrue; break; case AGEN_ENTITY: - RB_CalcAlphaFromEntity( ( unsigned char * ) tess.svars.colors ); + RB_CalcAlphaFromEntity( &color, 1 ); + constA = qtrue; break; case AGEN_ONE_MINUS_ENTITY: - RB_CalcAlphaFromOneMinusEntity( ( unsigned char * ) tess.svars.colors ); + RB_CalcAlphaFromOneMinusEntity( &color, 1 ); + constA = qtrue; break; - case AGEN_VERTEX: - if ( pStage->rgbGen != CGEN_VERTEX ) { - for ( i = 0; i < tess.numVertexes; i++ ) { - tess.svars.colors[i][3] = tess.vertexColors[i][3]; - } + case AGEN_WAVEFORM: + RB_CalcWaveAlpha( &pStage->alphaWave, &color, 1 ); + constA = qtrue; + break; + case AGEN_CONST: + color[3] = pStage->constantColor[3]; + constA = qtrue; + break; + default: + constA = qfalse; + break; + } + + if ( !r_greyscale->integer && + (skipFog || !tess.fogNum || pStage->adjustColorsForFog == ACFF_NONE) ) { + // if RGB and ALPHA are constant, just set the GL color + if ( constRGB && constA ) { + SetAttrVec4f( state, AL_COLOR, + color[0] / 255.0f, + color[1] / 255.0f, + color[2] / 255.0f, + color[3] / 255.0f ); + tess.svars.colors = NULL; + return; } - break; - case AGEN_ONE_MINUS_VERTEX: - for ( i = 0; i < tess.numVertexes; i++ ) - { - tess.svars.colors[i][3] = 255 - tess.vertexColors[i][3]; - } - break; - case AGEN_PORTAL: + + // if RGB and ALPHA are identical to vertex data, bind that + if ( aGen == AGEN_VERTEX && rgbGen == CGEN_EXACT_VERTEX ) { + if( VBO && VBO->vbo ) { + SetAttrPointer( state, AL_COLOR, VBO->vbo, + 4, GL_UNSIGNED_BYTE, + sizeof(glVertex_t), + &VBO->offset->color ); + } else if( tess.VtxBuf.mapped ) { + SetAttrPointer( state, AL_COLOR, tess.VtxBuf.id, + 4, GL_UNSIGNED_BYTE, sizeof(glVertex_t), + &((glVertex_t *)BufferOffset(tess.VtxBuf.current))->color ); + } else { + SetAttrPointer( state, AL_COLOR, 0, + 4, GL_UNSIGNED_BYTE, + sizeof(glVertex_t), + &tess.vertexPtr->color ); + } + tess.svars.colors = NULL; + return; + } + } + + // we have to allocate a per-Vertex color value + tess.svars.colors = RB_AllocScratch( tess.numVertexes * sizeof(color4ub_t) ); + + // + // rgbGen + // + switch ( rgbGen ) + { + case CGEN_BAD: + case CGEN_IDENTITY_LIGHTING: + case CGEN_IDENTITY: + case CGEN_ENTITY: + case CGEN_ONE_MINUS_ENTITY: + case CGEN_WAVEFORM: + case CGEN_FOG: + case CGEN_CONST: + for ( i = 0; i < tess.numVertexes; i++ ) { + *(int *)tess.svars.colors[i] = *(int *)(&color); + } + break; + case CGEN_LIGHTING_DIFFUSE: + RB_CalcDiffuseColor( tess.svars.colors, tess.numVertexes ); + break; + case CGEN_EXACT_VERTEX: + for ( i = 0; i < tess.numVertexes; i++ ) + { + tess.svars.colors[i][0] = tess.vertexPtr[i].color[0]; + tess.svars.colors[i][1] = tess.vertexPtr[i].color[1]; + tess.svars.colors[i][2] = tess.vertexPtr[i].color[2]; + tess.svars.colors[i][3] = tess.vertexPtr[i].color[3]; + } + break; + case CGEN_VERTEX: + for ( i = 0; i < tess.numVertexes; i++ ) + { + tess.svars.colors[i][0] = tess.vertexPtr[i].color[0] * tr.identityLight; + tess.svars.colors[i][1] = tess.vertexPtr[i].color[1] * tr.identityLight; + tess.svars.colors[i][2] = tess.vertexPtr[i].color[2] * tr.identityLight; + tess.svars.colors[i][3] = tess.vertexPtr[i].color[3]; + } + break; + case CGEN_ONE_MINUS_VERTEX: + if ( tr.identityLight == 1 ) + { + for ( i = 0; i < tess.numVertexes; i++ ) + { + tess.svars.colors[i][0] = 255 - tess.vertexPtr[i].color[0]; + tess.svars.colors[i][1] = 255 - tess.vertexPtr[i].color[1]; + tess.svars.colors[i][2] = 255 - tess.vertexPtr[i].color[2]; + } + } + else + { + for ( i = 0; i < tess.numVertexes; i++ ) + { + tess.svars.colors[i][0] = ( 255 - tess.vertexPtr[i].color[0] ) * tr.identityLight; + tess.svars.colors[i][1] = ( 255 - tess.vertexPtr[i].color[1] ) * tr.identityLight; + tess.svars.colors[i][2] = ( 255 - tess.vertexPtr[i].color[2] ) * tr.identityLight; + } + } + break; + } + + // + // alphaGen + // + switch ( aGen ) + { + case AGEN_SKIP: + case AGEN_IDENTITY: + case AGEN_ENTITY: + case AGEN_ONE_MINUS_ENTITY: + case AGEN_WAVEFORM: + case AGEN_CONST: + for ( i = 0; i < tess.numVertexes; i++ ) { + tess.svars.colors[i][3] = color[3]; + } + break; + case AGEN_LIGHTING_SPECULAR: + RB_CalcSpecularAlpha( tess.svars.colors, tess.numVertexes ); + break; + case AGEN_VERTEX: + if ( pStage->rgbGen != CGEN_VERTEX ) { + for ( i = 0; i < tess.numVertexes; i++ ) { + tess.svars.colors[i][3] = tess.vertexPtr[i].color[3]; + } + } + break; + case AGEN_ONE_MINUS_VERTEX: + for ( i = 0; i < tess.numVertexes; i++ ) + { + tess.svars.colors[i][3] = 255 - tess.vertexPtr[i].color[3]; + } + break; + case AGEN_PORTAL: { unsigned char alpha; @@ -933,7 +1182,7 @@ static void ComputeColors( shaderStage_t *pStage ) float len; vec3_t v; - VectorSubtract( tess.xyz[i], backEnd.viewParms.or.origin, v ); + VectorSubtract( tess.vertexPtr[i].xyz, backEnd.viewParms.or.origin, v ); len = VectorLength( v ); len /= tess.shader->portalRange; @@ -965,13 +1214,13 @@ static void ComputeColors( shaderStage_t *pStage ) switch ( pStage->adjustColorsForFog ) { case ACFF_MODULATE_RGB: - RB_CalcModulateColorsByFog( ( unsigned char * ) tess.svars.colors ); + RB_CalcModulateColorsByFog( tess.svars.colors, tess.numVertexes ); break; case ACFF_MODULATE_ALPHA: - RB_CalcModulateAlphasByFog( ( unsigned char * ) tess.svars.colors ); + RB_CalcModulateAlphasByFog( tess.svars.colors, tess.numVertexes ); break; case ACFF_MODULATE_RGBA: - RB_CalcModulateRGBAsByFog( ( unsigned char * ) tess.svars.colors ); + RB_CalcModulateRGBAsByFog( tess.svars.colors, tess.numVertexes ); break; case ACFF_NONE: break; @@ -1000,6 +1249,10 @@ static void ComputeColors( shaderStage_t *pStage ) tess.svars.colors[i][2] = LERP(tess.svars.colors[i][2], scale, r_greyscale->value); } } + + SetAttrPointer( state, AL_COLOR, 0, + 4, GL_UNSIGNED_BYTE, sizeof(color4ub_t), + tess.svars.colors ); } /* @@ -1007,44 +1260,106 @@ static void ComputeColors( shaderStage_t *pStage ) ComputeTexCoords =============== */ -static void ComputeTexCoords( shaderStage_t *pStage ) { +static void ComputeTexCoords( glRenderState_t *state, + vboInfo_t *VBO, + shaderStage_t *pStage ) { int i; int b; - + qboolean noTexMods; + for ( b = 0; b < NUM_TEXTURE_BUNDLES; b++ ) { int tm; + noTexMods = (pStage->bundle[b].numTexMods == 0) || + (pStage->bundle[b].texMods[0].type == TMOD_NONE); + + if ( noTexMods && pStage->bundle[b].tcGen == TCGEN_BAD ) { + SetAttrUnspec( state, AL_TEXCOORD + b ); + continue; + } + + if ( noTexMods && pStage->bundle[b].tcGen == TCGEN_TEXTURE ) { + if( VBO && VBO->vbo ) { + SetAttrPointer( state, AL_TEXCOORD + b, VBO->vbo, + 2, GL_FLOAT, sizeof(glVertex_t), + &VBO->offset->tc1); + } else if( tess.VtxBuf.mapped ) { + SetAttrPointer( state, AL_TEXCOORD + b, tess.VtxBuf.id, + 2, GL_FLOAT, sizeof(glVertex_t), + &((glVertex_t *)BufferOffset(tess.VtxBuf.current))->tc1 ); + } else if( tess.vertexPtr == NULL ) { + SetAttrPointer( state, AL_TEXCOORD + b, backEnd.worldVBO->vbo, + 2, GL_FLOAT, sizeof(glVertex_t), + &backEnd.worldVBO->offset->tc1); + } else { + SetAttrPointer( state, AL_TEXCOORD + b, 0, + 2, GL_FLOAT, sizeof(glVertex_t), + &tess.vertexPtr->tc1 ); + } + tess.svars.texcoords[b] = NULL; + continue; + } + + if ( noTexMods && pStage->bundle[b].tcGen == TCGEN_LIGHTMAP ) { + if( VBO && VBO->vbo ) { + SetAttrPointer( state, AL_TEXCOORD + b, VBO->vbo, + 2, GL_FLOAT, sizeof(glVertex_t), + &VBO->offset->tc2); + } else if( tess.VtxBuf.mapped ) { + SetAttrPointer( state, AL_TEXCOORD + b, tess.VtxBuf.id, + 2, GL_FLOAT, sizeof(glVertex_t), + &((glVertex_t *)BufferOffset(tess.VtxBuf.current))->tc2 ); + } else if( tess.vertexPtr == NULL ) { + SetAttrPointer( state, AL_TEXCOORD + b, backEnd.worldVBO->vbo, + 2, GL_FLOAT, sizeof(glVertex_t), + &backEnd.worldVBO->offset->tc2); + } else { + SetAttrPointer( state, AL_TEXCOORD + b, 0, + 2, GL_FLOAT, sizeof(glVertex_t), + &tess.vertexPtr->tc2 ); + } + tess.svars.texcoords[b] = NULL; + continue; + } + + tess.svars.texcoords[b] = RB_AllocScratch( tess.numVertexes * sizeof(vec2_t) ); // // generate the texture coordinates // switch ( pStage->bundle[b].tcGen ) { case TCGEN_IDENTITY: - Com_Memset( tess.svars.texcoords[b], 0, sizeof( float ) * 2 * tess.numVertexes ); + Com_Memset( tess.svars.texcoords[b], 0, sizeof( vec2_t ) * tess.numVertexes ); break; case TCGEN_TEXTURE: + if( VBO ) ri.Error( ERR_DROP, "tcgen in VBO\n" ); for ( i = 0 ; i < tess.numVertexes ; i++ ) { - tess.svars.texcoords[b][i][0] = tess.texCoords[i][0][0]; - tess.svars.texcoords[b][i][1] = tess.texCoords[i][0][1]; + tess.svars.texcoords[b][i][0] = tess.vertexPtr[i].tc1[0]; + tess.svars.texcoords[b][i][1] = tess.vertexPtr[i].tc1[1]; } break; case TCGEN_LIGHTMAP: + if( VBO ) ri.Error( ERR_DROP, "tcgen in VBO\n" ); for ( i = 0 ; i < tess.numVertexes ; i++ ) { - tess.svars.texcoords[b][i][0] = tess.texCoords[i][1][0]; - tess.svars.texcoords[b][i][1] = tess.texCoords[i][1][1]; + tess.svars.texcoords[b][i][0] = tess.vertexPtr[i].tc2[0]; + tess.svars.texcoords[b][i][1] = tess.vertexPtr[i].tc2[1]; } break; case TCGEN_VECTOR: + if( VBO ) ri.Error( ERR_DROP, "tcgen in VBO\n" ); for ( i = 0 ; i < tess.numVertexes ; i++ ) { - tess.svars.texcoords[b][i][0] = DotProduct( tess.xyz[i], pStage->bundle[b].tcGenVectors[0] ); - tess.svars.texcoords[b][i][1] = DotProduct( tess.xyz[i], pStage->bundle[b].tcGenVectors[1] ); + tess.svars.texcoords[b][i][0] = DotProduct( tess.vertexPtr[i].xyz, pStage->bundle[b].tcGenVectors[0] ); + tess.svars.texcoords[b][i][1] = DotProduct( tess.vertexPtr[i].xyz, pStage->bundle[b].tcGenVectors[1] ); } break; case TCGEN_FOG: - RB_CalcFogTexCoords( ( float * ) tess.svars.texcoords[b] ); + if( VBO ) ri.Error( ERR_DROP, "tcgen in VBO\n" ); + RB_CalcFogTexCoords( tess.svars.texcoords[b], + 0, tess.numVertexes ); break; case TCGEN_ENVIRONMENT_MAPPED: - RB_CalcEnvironmentTexCoords( ( float * ) tess.svars.texcoords[b] ); + if( VBO ) ri.Error( ERR_DROP, "tcgen in VBO\n" ); + RB_CalcEnvironmentTexCoords( tess.svars.texcoords[b], tess.numVertexes ); break; case TCGEN_BAD: return; @@ -1062,37 +1377,37 @@ static void ComputeTexCoords( shaderStage_t *pStage ) { case TMOD_TURBULENT: RB_CalcTurbulentTexCoords( &pStage->bundle[b].texMods[tm].wave, - ( float * ) tess.svars.texcoords[b] ); + tess.svars.texcoords[b], tess.numVertexes ); break; case TMOD_ENTITY_TRANSLATE: RB_CalcScrollTexCoords( backEnd.currentEntity->e.shaderTexCoord, - ( float * ) tess.svars.texcoords[b] ); + tess.svars.texcoords[b], tess.numVertexes ); break; case TMOD_SCROLL: RB_CalcScrollTexCoords( pStage->bundle[b].texMods[tm].scroll, - ( float * ) tess.svars.texcoords[b] ); + tess.svars.texcoords[b], tess.numVertexes ); break; case TMOD_SCALE: RB_CalcScaleTexCoords( pStage->bundle[b].texMods[tm].scale, - ( float * ) tess.svars.texcoords[b] ); + tess.svars.texcoords[b], tess.numVertexes ); break; case TMOD_STRETCH: RB_CalcStretchTexCoords( &pStage->bundle[b].texMods[tm].wave, - ( float * ) tess.svars.texcoords[b] ); + tess.svars.texcoords[b], tess.numVertexes ); break; case TMOD_TRANSFORM: RB_CalcTransformTexCoords( &pStage->bundle[b].texMods[tm], - ( float * ) tess.svars.texcoords[b] ); + tess.svars.texcoords[b], tess.numVertexes ); break; case TMOD_ROTATE: RB_CalcRotateTexCoords( pStage->bundle[b].texMods[tm].rotateSpeed, - ( float * ) tess.svars.texcoords[b] ); + tess.svars.texcoords[b], tess.numVertexes ); break; default: @@ -1100,32 +1415,103 @@ static void ComputeTexCoords( shaderStage_t *pStage ) { break; } } + SetAttrPointer( state, AL_TEXCOORD + b, 0, + 2, GL_FLOAT, sizeof(vec2_t), + tess.svars.texcoords[b] ); } } /* ** RB_IterateStagesGeneric */ -static void RB_IterateStagesGeneric( shaderCommands_t *input ) +static void RB_IterateStagesGenericVBO( glRenderState_t *state, + iboInfo_t *ibo, + qboolean skipDetail ) { int stage; + int bundle; for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) { - shaderStage_t *pStage = tess.xstages[stage]; + shaderStage_t *pStage = tess.xstages[stage]; + vboInfo_t *vbo = ibo->vbo; if ( !pStage ) { break; } - ComputeColors( pStage ); - ComputeTexCoords( pStage ); + // if the surface was invisible in the last frame, skip + // all detail stages + if ( skipDetail && pStage->isDetail ) + continue; + + state->stateBits = pStage->stateBits; + + ComputeColors( state, vbo, pStage, qtrue ); + ComputeTexCoords( state, vbo, pStage ); + + // + // set state + // + state->numImages = 1; + if ( pStage->bundle[0].vertexLightmap && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) && r_lightmap->integer ) + { + state->image[0] = tr.whiteImage; + } + else + R_GetAnimatedImage( &pStage->bundle[0], qfalse, &state->image[0] ); + + // + // do multitexture + // + for ( bundle = 1; bundle < glConfig.numTextureUnits; bundle++ ) { + if ( !pStage->bundle[bundle].multitextureEnv ) + break; + + if ( r_lightmap->integer ) { + GL_TexEnv( bundle, GL_REPLACE ); + } else { + GL_TexEnv( bundle, pStage->bundle[bundle].multitextureEnv ); + } + R_GetAnimatedImage( &pStage->bundle[bundle], qfalse, &state->image[bundle] ); + } + state->numImages = bundle; + + GL_DrawElements( state, ibo->numIndexes, ibo->ibo, ibo->offset, + ibo->minIndex, ibo->maxIndex ); + + // allow skipping out to show just lightmaps during development + if ( r_lightmap->integer && ( pStage->bundle[0].isLightmap || pStage->bundle[1].isLightmap || pStage->bundle[0].vertexLightmap ) ) + { + break; + } + } +} +static void RB_IterateStagesGeneric( glRenderState_t *state, + shaderCommands_t *input, + qboolean skipDetail ) +{ + int stage, b; + + for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) + { + shaderStage_t *pStage = tess.xstages[stage]; - if ( !setArraysOnce ) + if ( !pStage ) { - qglEnableClientState( GL_COLOR_ARRAY ); - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, input->svars.colors ); + break; + } + + if ( skipDetail && pStage->isDetail ) + continue; + + if( !tess.vertexPtr ) { + ComputeColors( state, backEnd.worldVBO, pStage, qtrue ); + ComputeTexCoords( state, backEnd.worldVBO, pStage ); + } else { + ComputeColors( state, NULL, pStage, qfalse ); + ComputeTexCoords( state, NULL, pStage ); } // @@ -1133,32 +1519,53 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input ) // if ( pStage->bundle[1].image[0] != 0 ) { - DrawMultitextured( input, stage ); + DrawMultitextured( state, input, stage ); } else { - if ( !setArraysOnce ) - { - qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[0] ); - } - // // set state // + state->stateBits = pStage->stateBits; + state->numImages = 1; if ( pStage->bundle[0].vertexLightmap && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) && r_lightmap->integer ) { - GL_Bind( tr.whiteImage ); + state->image[0] = tr.whiteImage; } - else - R_BindAnimatedImage( &pStage->bundle[0] ); - - GL_State( pStage->stateBits ); + else if( tess.imgOverride ) + state->image[0] = tess.imgOverride; + else + R_GetAnimatedImage( &pStage->bundle[0], qfalse, + &state->image[0] ); // // draw // - R_DrawElements( input->numIndexes, input->indexes ); + if( tess.IdxBuf.mapped ) { + GL_DrawElements( state, input->numIndexes[1], + tess.IdxBuf.id, + BufferOffset(tess.IdxBuf.current), + input->minIndex[1], + input->maxIndex[1] ); + } else { + GL_DrawElements( state, input->numIndexes[1], + 0, input->indexPtr, + input->minIndex[1], + input->maxIndex[1] ); + } } + + for ( b = NUM_TEXTURE_BUNDLES - 1; b >= 0; b-- ) { + if ( input->svars.texcoords[b] != NULL ) { + RB_FreeScratch( input->svars.texcoords[b] ); + input->svars.texcoords[b] = NULL; + } + } + if ( input->svars.colors != NULL ) { + RB_FreeScratch( input->svars.colors ); + input->svars.colors = NULL; + } + // allow skipping out to show just lightmaps during development if ( r_lightmap->integer && ( pStage->bundle[0].isLightmap || pStage->bundle[1].isLightmap || pStage->bundle[0].vertexLightmap ) ) { @@ -1175,6 +1582,10 @@ void RB_StageIteratorGeneric( void ) { shaderCommands_t *input; shader_t *shader; + iboInfo_t *ibo; + glRenderState_t state; + GLuint glQueryID = 0, *glQueryResult = NULL; + qboolean skipDetails = qfalse; input = &tess; shader = input->shader; @@ -1188,98 +1599,118 @@ void RB_StageIteratorGeneric( void ) { // don't just call LogComment, or we will get // a call to va() every frame! - GLimp_LogComment( va("--- RB_StageIteratorGeneric( %s ) ---\n", tess.shader->name) ); + GLimp_LogComment( va("--- RB_StageIteratorGeneric( %s ) ---\n", shader->name) ); } - // - // set face culling appropriately - // - GL_Cull( shader->cullType ); - - // set polygon offset if necessary - if ( shader->polygonOffset ) - { - qglEnable( GL_POLYGON_OFFSET_FILL ); - qglPolygonOffset( r_offsetFactor->value, r_offsetUnits->value ); - } + InitState( &state ); // - // if there is only a single pass then we can enable color - // and texture arrays before we compile, otherwise we need - // to avoid compiling those arrays since they will change - // during multipass rendering + // set face culling appropriately // - if ( tess.numPasses > 1 || shader->multitextureEnv ) - { - setArraysOnce = qfalse; - qglDisableClientState (GL_COLOR_ARRAY); - qglDisableClientState (GL_TEXTURE_COORD_ARRAY); - } - else - { - setArraysOnce = qtrue; - - qglEnableClientState( GL_COLOR_ARRAY); - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors ); - - qglEnableClientState( GL_TEXTURE_COORD_ARRAY); - qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoords[0] ); - } + state.faceCulling = shader->cullType; - // - // lock XYZ - // - qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD - if (qglLockArraysEXT) - { - qglLockArraysEXT(0, input->numVertexes); - GLimp_LogComment( "glLockArraysEXT\n" ); - } - - // - // enable color and texcoord arrays after the lock if necessary - // - if ( !setArraysOnce ) - { - qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); - qglEnableClientState( GL_COLOR_ARRAY ); + if( qglBeginQueryARB && + !backEnd.viewParms.portalLevel ) { + if ( backEnd.currentEntity == &tr.worldEntity ) { + glQueryID = shader->QueryID; + glQueryResult = &shader->QueryResult; + } } - - // - // call shader function - // - RB_IterateStagesGeneric( input ); - - // - // now do any dynamic lighting needed - // - if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE - && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) { - ProjectDlightTexture(); + + if( glQueryID ) { + GL_GetQuery( glQueryID, glQueryResult ); + GL_StartQuery( glQueryID, glQueryResult ); + skipDetails = (QUERY_RESULT(glQueryResult) == 0); } - // - // now do fog - // - if ( tess.fogNum && tess.shader->fogPass ) { - RB_FogPass(); + for( ibo = tess.firstIBO; ibo; ibo = ibo->next ) { + vboInfo_t *vbo = ibo->vbo; + + SetAttrPointer( &state, AL_VERTEX, vbo->vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->xyz ); + + RB_IterateStagesGenericVBO( &state, ibo, skipDetails ); + + if ( r_showtris->integer && !shader->isDepth ) { + DrawTris( &state, ibo->numIndexes, + ibo->ibo, ibo->offset, + ibo->minIndex, + ibo->maxIndex, + 1.0f, 1.0f, 0.0f ); + } + if ( r_shownormals->integer ) { + DrawNormals( &state ); + } } - // - // unlock arrays - // - if (qglUnlockArraysEXT) - { - qglUnlockArraysEXT(); - GLimp_LogComment( "glUnlockArraysEXT\n" ); + if ( tess.numIndexes[1] > 0 ) { + glVertex_t *vertexes; + GLuint vbo; + + if ( input->vertexPtr ) { + if( tess.VtxBuf.mapped ) { + vbo = tess.VtxBuf.id; + vertexes = BufferOffset( tess.VtxBuf.current ); + } else { + vbo = 0; + vertexes = input->vertexPtr; + } + } else { + vbo = backEnd.worldVBO->vbo; + vertexes = backEnd.worldVBO->offset; + } + // + // lock XYZ + // + SetAttrPointer( &state, AL_VERTEX, vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + &vertexes->xyz ); + if( qglLockArraysEXT && !vbo ) { + if( tess.shader->allGLAttr & GLA_COLOR_vtxcolor ) + SetAttrPointer( &state, AL_COLOR, vbo, + 4, GL_UNSIGNED_BYTE, + sizeof(glVertex_t), + &vertexes->color ); + if( tess.shader->allGLAttr & GLA_TC1_texcoord ) + SetAttrPointer( &state, AL_TEXCOORD, vbo, + 2, GL_FLOAT, sizeof(glVertex_t), + &vertexes->tc1 ); + if( tess.shader->allGLAttr & GLA_TC1_lmcoord ) + SetAttrPointer( &state, AL_TEXCOORD, vbo, + 2, GL_FLOAT, sizeof(glVertex_t), + &vertexes->tc2 ); + if( tess.shader->allGLAttr & GLA_TC2_texcoord ) + SetAttrPointer( &state, AL_TEXCOORD2, vbo, + 2, GL_FLOAT, sizeof(glVertex_t), + &vertexes->tc1 ); + if( tess.shader->allGLAttr & GLA_TC2_lmcoord ) + SetAttrPointer( &state, AL_TEXCOORD2, vbo, + 2, GL_FLOAT, sizeof(glVertex_t), + &vertexes->tc2 ); + GL_LockArrays( &state ); + } + // + // call shader function + // + RB_IterateStagesGeneric( &state, input, skipDetails ); + + if ( r_showtris->integer && !shader->isDepth ) { + DrawTris( &state, tess.numIndexes[1], + 0, tess.indexPtr, + tess.minIndex[1], tess.maxIndex[1], + 1.0f, 0.0f, 0.0f ); + } + if( qglLockArraysEXT && !vbo ) { + GL_UnlockArrays( ); + } + if ( r_shownormals->integer ) { + DrawNormals( &state ); + } } - // - // reset polygon offset - // - if ( shader->polygonOffset ) - { - qglDisable( GL_POLYGON_OFFSET_FILL ); + if( glQueryID ) { + GL_EndQuery( glQueryID, glQueryResult ); } } @@ -1291,181 +1722,899 @@ void RB_StageIteratorVertexLitTexture( void ) { shaderCommands_t *input; shader_t *shader; + iboInfo_t *ibo; + glRenderState_t state; + GLuint glQueryID = 0, *glQueryResult = NULL; input = &tess; shader = input->shader; // - // compute colors - // - RB_CalcDiffuseColor( ( unsigned char * ) tess.svars.colors ); - - // // log this call // if ( r_logFile->integer ) { // don't just call LogComment, or we will get // a call to va() every frame! - GLimp_LogComment( va("--- RB_StageIteratorVertexLitTexturedUnfogged( %s ) ---\n", tess.shader->name) ); + GLimp_LogComment( va("--- RB_StageIteratorVertexLitTexturedUnfogged( %s ) ---\n", shader->name) ); } - // - // set face culling appropriately - // - GL_Cull( shader->cullType ); + InitState( &state ); // - // set arrays and lock + // set face culling appropriately // - qglEnableClientState( GL_COLOR_ARRAY); - qglEnableClientState( GL_TEXTURE_COORD_ARRAY); + state.stateBits = tess.xstages[0]->stateBits; + state.faceCulling = shader->cullType; - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors ); - qglTexCoordPointer( 2, GL_FLOAT, 16, tess.texCoords[0][0] ); - qglVertexPointer (3, GL_FLOAT, 16, input->xyz); + state.numImages = 1; + if( tess.imgOverride ) { + state.image[0] = tess.imgOverride; + } else { + R_GetAnimatedImage( &tess.xstages[0]->bundle[0], qfalse, + &state.image[0] ); + } - if ( qglLockArraysEXT ) - { - qglLockArraysEXT(0, input->numVertexes); - GLimp_LogComment( "glLockArraysEXT\n" ); + if( qglBeginQueryARB && + !backEnd.viewParms.portalLevel ) { + if ( backEnd.currentEntity == &tr.worldEntity ) { + glQueryID = shader->QueryID; + glQueryResult = &shader->QueryResult; + } + } + + if( glQueryID ) { + GL_StartQuery( glQueryID, glQueryResult ); } // - // call special shade routine - // - R_BindAnimatedImage( &tess.xstages[0]->bundle[0] ); - GL_State( tess.xstages[0]->stateBits ); - R_DrawElements( input->numIndexes, input->indexes ); - - // - // now do any dynamic lighting needed + // set arrays and lock // - if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE ) { - ProjectDlightTexture(); + for( ibo = input->firstIBO; ibo; ibo = ibo->next ) { + vboInfo_t *vbo = ibo->vbo; + + SetAttrPointer( &state, AL_COLOR, vbo->vbo, + 4, GL_UNSIGNED_BYTE, sizeof(glVertex_t), + &vbo->offset->color ); + SetAttrPointer( &state, AL_TEXCOORD, vbo->vbo, + 2, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->tc1 ); + SetAttrPointer( &state, AL_VERTEX, vbo->vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->xyz ); + + // + // call special shade routine + // + GL_DrawElements( &state, ibo->numIndexes, ibo->ibo, + ibo->offset, ibo->minIndex, ibo->maxIndex ); + + if ( r_showtris->integer && !shader->isDepth ) { + DrawTris( &state, ibo->numIndexes, + ibo->ibo, ibo->offset, + ibo->minIndex, ibo->maxIndex, + 1.0f, 1.0f, 0.0f ); + } + if ( r_shownormals->integer ) { + DrawNormals( &state ); + } } + if ( input->numIndexes[1] > 0 ) { + vec3_t *vertexes; + vec2_t *tcs; + GLuint vbo; - // - // now do fog - // - if ( tess.fogNum && tess.shader->fogPass ) { - RB_FogPass(); + // + // compute colors + // + tess.svars.colors = RB_AllocScratch( tess.numVertexes * sizeof(color4ub_t) ); + RB_CalcDiffuseColor( tess.svars.colors, tess.numVertexes ); + + if ( input->vertexPtr ) { + vbo = 0; + vertexes = &input->vertexPtr[0].xyz; + tcs = &input->vertexPtr[0].tc1; + } else { + vbo = backEnd.worldVBO->vbo; + vertexes = &backEnd.worldVBO->offset->xyz; + tcs = &backEnd.worldVBO->offset->tc1; + } + + SetAttrPointer( &state, AL_COLOR, 0, + 4, GL_UNSIGNED_BYTE, sizeof(color4ub_t), + tess.svars.colors ); + SetAttrPointer( &state, AL_TEXCOORD, vbo, + 2, GL_FLOAT, sizeof(glVertex_t), + tcs ); + SetAttrPointer( &state, AL_VERTEX, vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + vertexes ); + + // + // call special shade routine + // + GL_DrawElements( &state, input->numIndexes[1], + 0, input->indexPtr, + input->minIndex[1], input->maxIndex[1] ); + + if ( r_showtris->integer && !shader->isDepth ) { + DrawTris( &state, input->numIndexes[1], + 0, input->indexPtr, + input->minIndex[1], input->maxIndex[1], + 1.0f, 0.0f, 0.0f ); + } + if ( r_shownormals->integer ) { + DrawNormals( &state ); + } + RB_FreeScratch( tess.svars.colors ); } - // - // unlock arrays - // - if (qglUnlockArraysEXT) - { - qglUnlockArraysEXT(); - GLimp_LogComment( "glUnlockArraysEXT\n" ); + if( glQueryID ) { + GL_EndQuery( glQueryID, glQueryResult ); } } -//define REPLACE_MODE - void RB_StageIteratorLightmappedMultitexture( void ) { shaderCommands_t *input; shader_t *shader; + iboInfo_t *ibo; + glRenderState_t state; + GLuint glQueryID = 0, *glQueryResult = NULL; input = &tess; shader = input->shader; + InitState( &state ); + // // log this call // if ( r_logFile->integer ) { // don't just call LogComment, or we will get // a call to va() every frame! - GLimp_LogComment( va("--- RB_StageIteratorLightmappedMultitexture( %s ) ---\n", tess.shader->name) ); + GLimp_LogComment( va("--- RB_StageIteratorLightmappedMultitexture( %s ) ---\n", shader->name) ); } // // set face culling appropriately // - GL_Cull( shader->cullType ); + state.stateBits = GLS_DEFAULT; + state.faceCulling = shader->cullType; - // - // set color, pointers, and lock - // - GL_State( GLS_DEFAULT ); - qglVertexPointer( 3, GL_FLOAT, 16, input->xyz ); - -#ifdef REPLACE_MODE - qglDisableClientState( GL_COLOR_ARRAY ); - qglColor3f( 1, 1, 1 ); - qglShadeModel( GL_FLAT ); -#else - qglEnableClientState( GL_COLOR_ARRAY ); - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.constantColor255 ); -#endif + SetAttrVec4f( &state, AL_COLOR, 1.0f, 1.0f, 1.0f, 1.0f ); - // - // select base stage - // - GL_SelectTexture( 0 ); - - qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); - R_BindAnimatedImage( &tess.xstages[0]->bundle[0] ); - qglTexCoordPointer( 2, GL_FLOAT, 16, tess.texCoords[0][0] ); + state.numImages = 2; + if( tess.imgOverride ) { + state.image[0] = tess.imgOverride; + } else { + R_GetAnimatedImage( &tess.xstages[0]->bundle[0], qfalse, + &state.image[0] ); + } + R_GetAnimatedImage( &tess.xstages[0]->bundle[1], qfalse, + &state.image[1] ); - // - // configure second stage - // - GL_SelectTexture( 1 ); - qglEnable( GL_TEXTURE_2D ); if ( r_lightmap->integer ) { - GL_TexEnv( GL_REPLACE ); + GL_TexEnv( 1, GL_REPLACE ); } else { - GL_TexEnv( GL_MODULATE ); + GL_TexEnv( 1, GL_MODULATE ); } - R_BindAnimatedImage( &tess.xstages[0]->bundle[1] ); - qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); - qglTexCoordPointer( 2, GL_FLOAT, 16, tess.texCoords[0][1] ); - // - // lock arrays - // - if ( qglLockArraysEXT ) { - qglLockArraysEXT(0, input->numVertexes); - GLimp_LogComment( "glLockArraysEXT\n" ); + if( qglBeginQueryARB && + !backEnd.viewParms.portalLevel ) { + if ( backEnd.currentEntity == &tr.worldEntity ) { + glQueryID = shader->QueryID; + glQueryResult = &shader->QueryResult; + } + } + + if( glQueryID ) { + GL_StartQuery( glQueryID, glQueryResult ); } - R_DrawElements( input->numIndexes, input->indexes ); + for( ibo = tess.firstIBO; ibo; ibo = ibo->next ) { + vboInfo_t *vbo = ibo->vbo; + + // + // set color, pointers, and lock + // + SetAttrPointer( &state, AL_VERTEX, vbo->vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->xyz ); + + // + // select base stage + // + SetAttrPointer( &state, AL_TEXCOORD, vbo->vbo, + 2, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->tc1 ); + + // + // configure second stage + // + SetAttrPointer( &state, AL_TEXCOORD2, vbo->vbo, + 2, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->tc2 ); + + GL_DrawElements( &state, ibo->numIndexes, ibo->ibo, + ibo->offset, ibo->minIndex, ibo->maxIndex ); + + if ( r_showtris->integer && !shader->isDepth ) { + DrawTris( &state, ibo->numIndexes, ibo->ibo, + ibo->offset, ibo->minIndex, ibo->maxIndex, + 1.0f, 1.0f, 0.0f ); + } + if ( r_shownormals->integer ) { + DrawNormals( &state ); + } + } + if ( tess.numIndexes[1] > 0 ) { + vec3_t *vertexes; + vec2_t *tcs1, *tcs2; + GLuint vbo; + + if ( tess.vertexPtr ) { + vbo = 0; + vertexes = &input->vertexPtr[0].xyz; + tcs1 = &input->vertexPtr[0].tc1; + tcs2 = &input->vertexPtr[0].tc2; + } else { + vbo = backEnd.worldVBO->vbo; + vertexes = &backEnd.worldVBO->offset->xyz; + tcs1 = &backEnd.worldVBO->offset->tc1; + tcs2 = &backEnd.worldVBO->offset->tc2; + } - // - // disable texturing on TEXTURE1, then select TEXTURE0 - // - qglDisable( GL_TEXTURE_2D ); - qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); + // + // set color, pointers, and lock + // + SetAttrPointer( &state, AL_VERTEX, vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + vertexes ); - GL_SelectTexture( 0 ); -#ifdef REPLACE_MODE - GL_TexEnv( GL_MODULATE ); - qglShadeModel( GL_SMOOTH ); -#endif + // + // select base stage + // + SetAttrPointer( &state, AL_TEXCOORD, vbo, + 2, GL_FLOAT, sizeof(glVertex_t), + tcs1 ); + + // + // configure second stage + // + SetAttrPointer( &state, AL_TEXCOORD2, vbo, + 2, GL_FLOAT, sizeof(glVertex_t), + tcs2 ); + + GL_DrawElements( &state, input->numIndexes[1], + 0, input->indexPtr, + input->minIndex[1], input->maxIndex[1] ); + + if ( r_showtris->integer && !shader->isDepth ) { + DrawTris( &state, input->numIndexes[1], + 0, input->indexPtr, + input->minIndex[1], input->maxIndex[1], + 1.0f, 0.0f, 0.0f ); + } + if ( r_shownormals->integer ) { + DrawNormals( &state ); + } + } - // - // now do any dynamic lighting needed - // - if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE ) { - ProjectDlightTexture(); + if( glQueryID ) { + GL_EndQuery( glQueryID, glQueryResult ); } +} + +/* +** RB_StageIteratorGLSL +*/ +void RB_StageIteratorGLSL( void ) { + shaderCommands_t *input = &tess; + shader_t *shader = input->shader; + int i, n; + iboInfo_t *ibo; + glRenderState_t state; + GLuint glQueryID = 0, *glQueryResult = NULL; + qboolean setAttrs; // - // now do fog + // log this call // - if ( tess.fogNum && tess.shader->fogPass ) { - RB_FogPass(); + if ( r_logFile->integer ) + { + // don't just call LogComment, or we will get + // a call to va() every frame! + GLimp_LogComment( va("--- RB_StageIteratorGLSL( %s ) ---\n", shader->name) ); } + InitState( &state ); // - // unlock arrays + // set face culling appropriately // - if ( qglUnlockArraysEXT ) { - qglUnlockArraysEXT(); - GLimp_LogComment( "glUnlockArraysEXT\n" ); + state.stateBits = shader->stages[0]->stateBits; + state.faceCulling = shader->cullType; + state.program = shader->GLSLprogram; + state.numImages = shader->numUnfoggedPasses; + + // bind all required textures + if( tess.imgOverride ) { + state.image[0] = tess.imgOverride; + i = 1; + } else { + i = 0; + } + for( ; i < state.numImages; i++ ) { + if( !shader->stages[i] ) + break; + + R_GetAnimatedImage( &shader->stages[i]->bundle[0], qtrue, + &state.image[i] ); + } + if( tess.dataTexture ) { + // bind data texture + state.image[state.numImages++] = tess.dataTexture; + } + + // bind attributes + SetAttrVec4f( &state, AL_CAMERAPOS, + backEnd.viewParms.or.origin[0], + backEnd.viewParms.or.origin[1], + backEnd.viewParms.or.origin[2], + 0.0f ); + + if( tess.numInstances > 1 ) { + if( qglDrawElementsInstancedARB ) { + // bind instanced arrays + n = 1; setAttrs = qfalse; + SetAttrPointerInst( &state, AL_TIMES, 0, 4, GL_FLOAT, + sizeof( instanceVars_t ), + &tess.instances[0].times , 1 ); + SetAttrPointerInst( &state, AL_TRANSX, 0, 4, GL_FLOAT, + sizeof( instanceVars_t ), + &tess.instances[0].transX, 1 ); + SetAttrPointerInst( &state, AL_TRANSY, 0, 4, GL_FLOAT, + sizeof( instanceVars_t ), + &tess.instances[0].transY, 1 ); + SetAttrPointerInst( &state, AL_TRANSZ, 0, 4, GL_FLOAT, + sizeof( instanceVars_t ), + &tess.instances[0].transZ, 1 ); + SetAttrPointerInst( &state, AL_AMBIENTLIGHT, + 0, 4, GL_FLOAT, + sizeof( instanceVars_t ), + &tess.instances[0].ambLight, 1 ); + SetAttrPointerInst( &state, AL_DIRECTEDLIGHT, + 0, 3, GL_FLOAT, + sizeof( instanceVars_t ), + &tess.instances[0].dirLight, 1 ); + SetAttrPointerInst( &state, AL_LIGHTDIR, + 0, 4, GL_FLOAT, + sizeof( instanceVars_t ), + &tess.instances[0].lightDir, 1 ); + SetAttrPointerInst( &state, AL_TEXCOORD2, + 0, 4, GL_FLOAT, + sizeof( instanceVars_t ), + &tess.instances[0].texCoord, 1 ); + SetAttrPointerInst( &state, AL_COLOR2, + 0, 4, GL_UNSIGNED_BYTE, + sizeof( instanceVars_t ), + &tess.instances[0].color, 1 ); + } else { + // pseudo-instancing + n = tess.numInstances; + setAttrs = qtrue; + } + } else { + // single instance + n = 1; + setAttrs = qtrue; + } + + if( qglBeginQueryARB && + !backEnd.viewParms.portalLevel ) { + if ( backEnd.currentEntity == &tr.worldEntity ) { + glQueryID = shader->QueryID; + glQueryResult = &shader->QueryResult; + } + } + + if( glQueryID ) { + GL_StartQuery( glQueryID, glQueryResult ); + } + + for( ibo = input->firstIBO; ibo; ibo = ibo->next ) { + glIndex_t minIndex, maxIndex; + vboInfo_t *vbo = ibo->vbo; + + if( shader->lightmapIndex == LIGHTMAP_MD3 ) { + minIndex = ibo->minIndex; + maxIndex = ibo->maxIndex; + + SetAttrPointer( &state, AL_VERTEX, vbo->vbo, + 4, GL_FLOAT, sizeof(vec4_t), + vbo->offset ); + SetAttrVec4f( &state, AL_NORMAL, + 0.0f, 0.0f, 0.0f, 0.0f ); + SetAttrVec4f( &state, AL_TEXCOORD, + 0.0f, 0.0f, 0.0f, 0.0f ); + SetAttrVec4f( &state, AL_COLOR, + 1.0f, 1.0f, 1.0f, 1.0f ); + } else { + minIndex = ibo->minIndex; + maxIndex = ibo->maxIndex; + + SetAttrPointer( &state, AL_VERTEX, vbo->vbo, + 4, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->xyz ); + SetAttrPointer( &state, AL_NORMAL, vbo->vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->normal ); + SetAttrPointer( &state, AL_COLOR, vbo->vbo, + 4, GL_UNSIGNED_BYTE, sizeof(glVertex_t), + &vbo->offset->color ); + SetAttrPointer( &state, AL_TEXCOORD, vbo->vbo, + 4, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->tc1[0] ); + } + + if( setAttrs ) { + for( i = 0; i < n; i++ ) { + SetAttrVec4f( &state, AL_TIMES, + tess.instances[i].times[0], + tess.instances[i].times[1], + tess.instances[i].times[2], + tess.instances[i].times[3] ); + SetAttrVec4f( &state, AL_TRANSX, + tess.instances[i].transX[0], + tess.instances[i].transX[1], + tess.instances[i].transX[2], + tess.instances[i].transX[3] ); + SetAttrVec4f( &state, AL_TRANSY, + tess.instances[i].transY[0], + tess.instances[i].transY[1], + tess.instances[i].transY[2], + tess.instances[i].transY[3] ); + SetAttrVec4f( &state, AL_TRANSZ, + tess.instances[i].transZ[0], + tess.instances[i].transZ[1], + tess.instances[i].transZ[2], + tess.instances[i].transZ[3] ); + SetAttrVec4f( &state, AL_TEXCOORD2, + tess.instances[i].texCoord[0], + tess.instances[i].texCoord[1], + tess.instances[i].texCoord[2], + tess.instances[i].texCoord[3] ); + SetAttrVec4f( &state, AL_AMBIENTLIGHT, + tess.instances[i].ambLight[0], + tess.instances[i].ambLight[1], + tess.instances[i].ambLight[2], + tess.instances[i].ambLight[3] ); + SetAttrVec4f( &state, AL_DIRECTEDLIGHT, + tess.instances[i].dirLight[0], + tess.instances[i].dirLight[1], + tess.instances[i].dirLight[2], + 0.0f ); + SetAttrVec4f( &state, AL_LIGHTDIR, + tess.instances[i].lightDir[0], + tess.instances[i].lightDir[1], + tess.instances[i].lightDir[2], + tess.instances[i].lightDir[3] ); + SetAttrVec4f( &state, AL_COLOR2, + tess.instances[i].color[0] / 255.0f, + tess.instances[i].color[1] / 255.0f, + tess.instances[i].color[2] / 255.0f, + tess.instances[i].color[3] / 255.0f ); + + GL_DrawElements( &state, ibo->numIndexes, + ibo->ibo, ibo->offset, + minIndex, maxIndex ); + } + } else { + GL_DrawInstanced( &state, ibo->numIndexes, + ibo->ibo, ibo->offset, + minIndex, maxIndex, + tess.numInstances ); + } + } + + if ( input->numIndexes[1] > 0 ) { + glVertex_t *ptr; + GLuint vbo; + + if ( tess.VtxBuf.mapped ) { + vbo = tess.VtxBuf.id; + ptr = BufferOffset( tess.VtxBuf.current ); + } else if ( input->vertexPtr ) { + vbo = 0; + ptr = input->vertexPtr; + } else { + vbo = backEnd.worldVBO->vbo; + ptr = backEnd.worldVBO->offset; + } + + SetAttrPointer( &state, AL_NORMAL, vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + &ptr->normal ); + + SetAttrPointer( &state, AL_VERTEX, vbo, + 4, GL_FLOAT, sizeof(glVertex_t), + &ptr->xyz ); + SetAttrPointer( &state, AL_COLOR, vbo, + 4, GL_UNSIGNED_BYTE, sizeof(glVertex_t), + &ptr->color ); + SetAttrPointer( &state, AL_TEXCOORD, vbo, + 4, GL_FLOAT, sizeof(glVertex_t), + &ptr->tc1[0] ); + + // + // call shader function + // + if( setAttrs ) { + for( i = 0; i < n; i++ ) { + SetAttrVec4f( &state, AL_TIMES, + tess.instances[i].times[0], + tess.instances[i].times[1], + tess.instances[i].times[2], + tess.instances[i].times[3] ); + SetAttrVec4f( &state, AL_TRANSX, + tess.instances[i].transX[0], + tess.instances[i].transX[1], + tess.instances[i].transX[2], + tess.instances[i].transX[3] ); + SetAttrVec4f( &state, AL_TRANSY, + tess.instances[i].transY[0], + tess.instances[i].transY[1], + tess.instances[i].transY[2], + tess.instances[i].transY[3] ); + SetAttrVec4f( &state, AL_TRANSZ, + tess.instances[i].transZ[0], + tess.instances[i].transZ[1], + tess.instances[i].transZ[2], + tess.instances[i].transZ[3] ); + SetAttrVec4f( &state, AL_TEXCOORD2, + tess.instances[i].texCoord[0], + tess.instances[i].texCoord[1], + tess.instances[i].texCoord[2], + tess.instances[i].texCoord[3] ); + SetAttrVec4f( &state, AL_AMBIENTLIGHT, + tess.instances[i].ambLight[0], + tess.instances[i].ambLight[1], + tess.instances[i].ambLight[2], + tess.instances[i].ambLight[3] ); + SetAttrVec4f( &state, AL_DIRECTEDLIGHT, + tess.instances[i].dirLight[0], + tess.instances[i].dirLight[1], + tess.instances[i].dirLight[2], + 0.0f ); + SetAttrVec4f( &state, AL_LIGHTDIR, + tess.instances[i].lightDir[0], + tess.instances[i].lightDir[1], + tess.instances[i].lightDir[2], + tess.instances[i].lightDir[3] ); + SetAttrVec4f( &state, AL_COLOR2, + tess.instances[i].color[0] / 255.0f, + tess.instances[i].color[1] / 255.0f, + tess.instances[i].color[2] / 255.0f, + tess.instances[i].color[3] / 255.0f ); + + if( tess.IdxBuf.mapped ) { + GL_DrawElements( &state, input->numIndexes[1], + tess.IdxBuf.id, + BufferOffset(tess.IdxBuf.current), + input->minIndex[1], + input->maxIndex[1] ); + } else { + GL_DrawElements( &state, input->numIndexes[1], + 0, input->indexPtr, + input->minIndex[1], + input->maxIndex[1] ); + } + } + } else { + if( tess.IdxBuf.mapped ) { + GL_DrawInstanced( &state, input->numIndexes[1], + tess.IdxBuf.id, + BufferOffset(tess.IdxBuf.current), + input->minIndex[1], + input->maxIndex[1], + tess.numInstances ); + } else { + GL_DrawInstanced( &state, input->numIndexes[1], + 0, input->indexPtr, + input->minIndex[1], + input->maxIndex[1], + tess.numInstances ); + } + } + } + + if ( r_showtris->integer && !shader->isDepth ) { + for( ibo = input->firstIBO; ibo; ibo = ibo->next ) { + int minIndex, maxIndex; + vboInfo_t *vbo = ibo->vbo; + + minIndex = ibo->minIndex; + maxIndex = ibo->maxIndex; + + SetAttrPointer( &state, AL_VERTEX, vbo->vbo, + 4, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->xyz ); + + DrawTris( &state, ibo->numIndexes, ibo->ibo, + ibo->offset, ibo->minIndex, ibo->maxIndex, + 0.0f, 0.0f, 1.0f ); + } + + if ( input->numIndexes[1] > 0 ) { + glVertex_t *ptr; + GLuint vbo; + + if ( input->vertexPtr ) { + vbo = 0; + ptr = input->vertexPtr; + } else { + vbo = backEnd.worldVBO->vbo; + ptr = backEnd.worldVBO->offset; + } + + SetAttrPointer( &state, AL_VERTEX, vbo, + 4, GL_FLOAT, sizeof(glVertex_t), + &ptr->xyz ); + + DrawTris( &state, input->numIndexes[1], + 0, input->indexPtr, + input->minIndex[1], input->maxIndex[1], + 0.0f, 1.0f, 0.0f ); + } + } + if ( r_shownormals->integer ) { + DrawNormals( &state ); + } + + if( glQueryID ) { + GL_EndQuery( glQueryID, glQueryResult ); + } +} + +/* +** RB_StageIteratorPreparePortal +*/ +void RB_StageIteratorPreparePortal( void ) { + iboInfo_t *ibo; + int level; + GLuint stencilVal, stencilMask; + glRenderState_t state; + + InitState( &state ); + + // render mirror area to stencil buffer (depth test enabled) + state.stateBits = GLS_COLORMASK_FALSE; + state.faceCulling = CT_FRONT_SIDED; + level = backEnd.viewParms.portalLevel + 1; + + SetAttrVec4f( &state, AL_COLOR, + (level&4)?1.0f:0.0f, + (level&2)?1.0f:0.0f, + (level&1)?1.0f:0.0f, + 1.0f ); + + if( !backEnd.viewParms.portalLevel && !r_measureOverdraw->integer ) { + qglEnable( GL_STENCIL_TEST ); } + + stencilMask = (level - 1) << glGlobals.shadowBits; + stencilVal = (level ^ (level >> 1)) << glGlobals.shadowBits; + + qglStencilMask( glGlobals.portalMask ); + qglStencilFunc( GL_EQUAL, stencilVal, stencilMask ); + qglStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE ); + + for( ibo = tess.firstIBO; ibo; ibo = ibo->next ) { + vboInfo_t *vbo = ibo->vbo; + + SetAttrPointer( &state, AL_VERTEX, vbo->vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->xyz ); + + SetAttrVec4f( &state, AL_TEXCOORD, 0.0f, 0.0f, 0.0f, 0.0f ); + + GL_DrawElements( &state, ibo->numIndexes, ibo->ibo, + ibo->offset, ibo->minIndex, ibo->maxIndex ); + } + + if ( tess.numIndexes[1] > 0 ) { + if ( tess.vertexPtr ) { + SetAttrPointer( &state, AL_VERTEX, 0, + 3, GL_FLOAT, sizeof(glVertex_t), + &tess.vertexPtr[0].xyz ); + } else { + SetAttrPointer( &state, AL_VERTEX, + backEnd.worldVBO->vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + &backEnd.worldVBO->offset->xyz ); + } + SetAttrVec4f( &state, AL_TEXCOORD, 0.0f, 0.0f, 0.0f, 0.0f ); + + // + // call shader function + // + GL_DrawElements( &state, tess.numIndexes[1], 0, tess.indexPtr, + tess.minIndex[1], tess.maxIndex[1] ); + } + + // set depth to max on mirror area (depth test disabled) + state.stateBits = GLS_COLORMASK_FALSE | GLS_DEPTHMASK_TRUE | + GLS_DEPTHRANGE_1_TO_1 | GLS_DEPTHFUNC_ALWAYS; + + if( r_measureOverdraw->integer ) { + qglStencilMask( glGlobals.shadowMask ); + qglStencilFunc( GL_EQUAL, stencilVal, glGlobals.portalMask ); + qglStencilOp( GL_KEEP, GL_INCR, GL_INCR ); + } else { + qglStencilFunc( GL_EQUAL, stencilVal, glGlobals.portalMask ); + qglStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); + } + + for( ibo = tess.firstIBO; ibo; ibo = ibo->next ) { + vboInfo_t *vbo = ibo->vbo; + + SetAttrPointer( &state, AL_VERTEX, vbo->vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->xyz ); + + GL_DrawElements( &state, ibo->numIndexes, ibo->ibo, + ibo->offset, ibo->minIndex, ibo->maxIndex ); + } + + if ( tess.numIndexes[1] > 0 ) { + if ( tess.vertexPtr ) { + SetAttrPointer( &state, AL_VERTEX, 0, + 3, GL_FLOAT, sizeof(glVertex_t), + &tess.vertexPtr[0].xyz ); + } else { + SetAttrPointer( &state, AL_VERTEX, + backEnd.worldVBO->vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + &backEnd.worldVBO->offset->xyz ); + } + + // + // call shader function + // + GL_DrawElements( &state, tess.numIndexes[1], 0, tess.indexPtr, + tess.minIndex[1], tess.maxIndex[1] ); + } + + // keep stencil test enabled ! +} + +/* +** RB_StageIteratorFinalisePortal +*/ +void RB_StageIteratorFinalisePortal( void ) { + iboInfo_t *ibo; + int level; + GLuint stencilVal, stencilMask; + glRenderState_t state; + + InitState( &state ); + + // clear stencil bits + state.stateBits = GLS_COLORMASK_FALSE | GLS_DEPTHMASK_TRUE | + GLS_DEPTHRANGE_1_TO_1 | GLS_DEPTHFUNC_ALWAYS; + state.faceCulling = CT_FRONT_SIDED; + SetAttrVec4f( &state, AL_COLOR, 0.0f, 0.0f, 0.0f, 0.0f ); + SetAttrVec4f( &state, AL_TEXCOORD, 0.0f, 0.0f, 0.0f, 0.0f ); + + level = backEnd.viewParms.portalLevel; + stencilMask = level << glGlobals.shadowBits; + stencilVal = (level ^ (level >> 1)) << glGlobals.shadowBits; + + qglStencilMask( glGlobals.portalMask ); + qglStencilFunc( GL_EQUAL, stencilVal, stencilMask ); + qglStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE ); + + for( ibo = tess.firstIBO; ibo; ibo = ibo->next ) { + vboInfo_t *vbo = ibo->vbo; + + SetAttrPointer( &state, AL_VERTEX, vbo->vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + &vbo->offset->xyz ); + + GL_DrawElements( &state, ibo->numIndexes, ibo->ibo, + ibo->offset, ibo->minIndex, ibo->maxIndex ); + } + + if ( tess.numIndexes[1] > 0 ) { + if ( tess.vertexPtr ) { + SetAttrPointer( &state, AL_VERTEX, 0, + 3, GL_FLOAT, sizeof(glVertex_t), + &tess.vertexPtr[0].xyz ); + } else { + SetAttrPointer( &state, AL_VERTEX, + backEnd.worldVBO->vbo, + 3, GL_FLOAT, sizeof(glVertex_t), + &backEnd.worldVBO->offset->xyz ); + } + + // + // call shader function + // + GL_DrawElements( &state, tess.numIndexes[1], 0, tess.indexPtr, + tess.minIndex[1], tess.maxIndex[1] ); + } + + if( !backEnd.viewParms.portalLevel ) { + if( r_measureOverdraw->integer ) { + qglStencilMask( glGlobals.shadowMask ); + qglStencilFunc( GL_ALWAYS, 0, 0 ); + qglStencilOp( GL_KEEP, GL_INCR, GL_INCR ); + } else { + qglDisable( GL_STENCIL_TEST ); + } + } else { + qglStencilFunc( GL_EQUAL, stencilVal, glGlobals.portalMask ); + if( r_measureOverdraw->integer ) { + qglStencilMask( glGlobals.shadowMask ); + qglStencilOp( GL_KEEP, GL_INCR, GL_INCR ); + } else { + qglStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); + } + } +} + +/* +** RB_StageIteratorCopyDepth +** Fake stage iterator to copy depth buffer into texture +*/ +void RB_StageIteratorCopyDepth( void ) { + GL_BindTexture( tr.depthImage->texnum ); + qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, + backEnd.viewParms.viewportX, + backEnd.viewParms.viewportY, + backEnd.viewParms.viewportX, + backEnd.viewParms.viewportY, + backEnd.viewParms.viewportWidth, + backEnd.viewParms.viewportHeight ); +} + +/* +** RB_StageIteratorBuildWorldVBO +** Fake stage iterator used to fill a VBO +*/ +void RB_StageIteratorBuildWorldVBO( void ) { + vboInfo_t *vbo = ri.Malloc( sizeof( vboInfo_t ) ); + + qglUnmapBufferARB( GL_ARRAY_BUFFER_ARB ); + qglGetIntegerv( GL_ARRAY_BUFFER_BINDING_ARB, (GLint *)&vbo->vbo ); + vbo->offset = (glVertex_t *)NULL; + + backEnd.worldVBO = vbo; +} + +/* +** RB_StageIteratorBuildIBO +** Fake stage iterator used to fill an IBO +*/ +void RB_StageIteratorBuildIBO( void ) { + qglUnmapBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB ); +} + +/* +** RB_DiscardSurface +** +** Free allocated surface data +*/ +void RB_DiscardSurface( void ) { + if( tess.vertexBuffer ) { + RB_FreeScratch( tess.vertexBuffer ); + tess.vertexBuffer = NULL; + } + if( tess.VtxBuf.mapped ) + qglUnmapBufferARB( GL_ARRAY_BUFFER_ARB ); + + if( tess.IdxBuf.mapped ) + qglUnmapBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB ); + tess.VtxBuf.mapped = tess.IdxBuf.mapped = qfalse; } /* @@ -1473,55 +2622,102 @@ void RB_StageIteratorLightmappedMultitexture( void ) { */ void RB_EndSurface( void ) { shaderCommands_t *input; + glRenderState_t state; input = &tess; - if (input->numIndexes == 0) { + // for debugging of sort order issues, stop rendering after a given sort value + if ( r_debugSort->integer && r_debugSort->integer < tess.shader->sort ) { + RB_DiscardSurface(); return; } - if (input->indexes[SHADER_MAX_INDEXES-1] != 0) { - ri.Error (ERR_DROP, "RB_EndSurface() - SHADER_MAX_INDEXES hit"); - } - if (input->xyz[SHADER_MAX_VERTEXES-1][0] != 0) { - ri.Error (ERR_DROP, "RB_EndSurface() - SHADER_MAX_VERTEXES hit"); + while( tess.indexRange < 2 ) { + tess.indexRange++; + tess.numIndexes[tess.indexRange] = tess.numIndexes[tess.indexRange - 1]; } - if ( tess.shader == tr.shadowShader ) { - RB_ShadowTessEnd(); - return; + if( tess.VtxBuf.mapped ) { + GL_VBO( tess.VtxBuf.id ); + qglUnmapBufferARB( GL_ARRAY_BUFFER_ARB ); + } + if( tess.IdxBuf.mapped ) { + GL_IBO( tess.IdxBuf.id ); + qglUnmapBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB ); } - // for debugging of sort order issues, stop rendering after a given sort value - if ( r_debugSort->integer && r_debugSort->integer < tess.shader->sort ) { - return; + // Merge ranges 0 and 1 into ranges 1 and 2. + // Originally range 0 means surface only, 1 means surface+dlight and + // 2 means dlight only. After the merge range 1 is all surface and + // range 2 is all dlight, but they may overlap. + tess.numIndexes[2] -= tess.numIndexes[0]; + if( tess.minIndex[2] > tess.minIndex[1] ) + tess.minIndex[2] = tess.minIndex[1]; + if( tess.maxIndex[2] < tess.maxIndex[1] ) + tess.maxIndex[2] = tess.maxIndex[1]; + + if( tess.minIndex[1] > tess.minIndex[0] ) + tess.minIndex[1] = tess.minIndex[0]; + if( tess.maxIndex[1] < tess.maxIndex[0] ) + tess.maxIndex[1] = tess.maxIndex[0]; + + if( tess.shader && + ( input->numIndexes[1] > 0 || input->firstIBO ) ) { + if ( tess.shader == tr.shadowShader ) { + RB_ShadowTessEnd(); + RB_DiscardSurface(); + return; + } + + // + // update performance counters + // + backEnd.pc.c_shaders++; + backEnd.pc.c_vertexes += tess.numVertexes; + backEnd.pc.c_indexes += tess.numIndexes[1]; + backEnd.pc.c_totalIndexes += tess.numIndexes[1] * tess.numPasses; + + // + // call off to shader specific tess end function + // + tess.currentStageIteratorFunc(); } // - // update performance counters + // add dynamic lights and fog if necessary // - backEnd.pc.c_shaders++; - backEnd.pc.c_vertexes += tess.numVertexes; - backEnd.pc.c_indexes += tess.numIndexes; - backEnd.pc.c_totalIndexes += tess.numIndexes * tess.numPasses; + if( tess.numIndexes[2] > 0 ) { + InitState( &state ); - // - // call off to shader specific tess end function - // - tess.currentStageIteratorFunc(); + // + // set face culling appropriately + // + state.faceCulling = input->shader->cullType; - // - // draw debugging stuff - // - if ( r_showtris->integer ) { - DrawTris (input); + SetAttrPointer( &state, AL_VERTEX, 0, 3, GL_FLOAT, sizeof(glVertex_t), + input->vertexPtr[0].xyz); + + // + // now do any dynamic lighting needed + // + if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE + && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) { + ProjectDlightTexture( &state ); + } + + // + // now do fog + // + if ( tess.fogNum && tess.shader->fogPass ) { + RB_FogPass( &state ); + } } - if ( r_shownormals->integer ) { - DrawNormals (input); + tess.VtxBuf.mapped = tess.IdxBuf.mapped = qfalse; + RB_DiscardSurface(); + + if ( r_flush->integer ) { + qglFlush(); } - // clear shader so we can tell we don't have any unclosed surfaces - tess.numIndexes = 0; GLimp_LogComment( "----------\n" ); } - diff --git a/code/renderer/tr_shade_calc.c b/code/renderer/tr_shade_calc.c index e8f43e4..7bfe6e7 100644 --- a/code/renderer/tr_shade_calc.c +++ b/code/renderer/tr_shade_calc.c @@ -86,7 +86,7 @@ static float EvalWaveFormClamped( const waveForm_t *wf ) /* ** RB_CalcStretchTexCoords */ -void RB_CalcStretchTexCoords( const waveForm_t *wf, float *st ) +void RB_CalcStretchTexCoords( const waveForm_t *wf, vec2_t *st, int numVertexes ) { float p; texModInfo_t tmi; @@ -101,7 +101,7 @@ void RB_CalcStretchTexCoords( const waveForm_t *wf, float *st ) tmi.matrix[1][1] = p; tmi.translate[1] = 0.5f - 0.5f * p; - RB_CalcTransformTexCoords( &tmi, st ); + RB_CalcTransformTexCoords( &tmi, st, numVertexes ); } /* @@ -121,43 +121,44 @@ RB_CalcDeformVertexes void RB_CalcDeformVertexes( deformStage_t *ds ) { int i; + vec3_t *normal; vec3_t offset; float scale; - float *xyz = ( float * ) tess.xyz; - float *normal = ( float * ) tess.normal; float *table; if ( ds->deformationWave.frequency == 0 ) { scale = EvalWaveForm( &ds->deformationWave ); - for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) + for ( i = 0; i < tess.numVertexes; i++ ) { - VectorScale( normal, scale, offset ); + normal = &tess.vertexPtr[i].normal; + VectorScale( *normal, scale, offset ); - xyz[0] += offset[0]; - xyz[1] += offset[1]; - xyz[2] += offset[2]; + tess.vertexPtr[i].xyz[0] += offset[0]; + tess.vertexPtr[i].xyz[1] += offset[1]; + tess.vertexPtr[i].xyz[2] += offset[2]; } } else { table = TableForFunc( ds->deformationWave.func ); - for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) + for ( i = 0; i < tess.numVertexes; i++ ) { - float off = ( xyz[0] + xyz[1] + xyz[2] ) * ds->deformationSpread; + float off = ( tess.vertexPtr[i].xyz[0] + tess.vertexPtr[i].xyz[1] + tess.vertexPtr[i].xyz[2] ) * ds->deformationSpread; scale = WAVEVALUE( table, ds->deformationWave.base, ds->deformationWave.amplitude, ds->deformationWave.phase + off, ds->deformationWave.frequency ); - VectorScale( normal, scale, offset ); + normal = &tess.vertexPtr[i].normal; + VectorScale( *normal, scale, offset ); - xyz[0] += offset[0]; - xyz[1] += offset[1]; - xyz[2] += offset[2]; + tess.vertexPtr[i].xyz[0] += offset[0]; + tess.vertexPtr[i].xyz[1] += offset[1]; + tess.vertexPtr[i].xyz[2] += offset[2]; } } } @@ -172,26 +173,31 @@ Wiggle the normals for wavy environment mapping void RB_CalcDeformNormals( deformStage_t *ds ) { int i; float scale; - float *xyz = ( float * ) tess.xyz; - float *normal = ( float * ) tess.normal; + vec3_t *normal; - for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) { - scale = 0.98f; - scale = R_NoiseGet4f( xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, - tess.shaderTime * ds->deformationWave.frequency ); - normal[ 0 ] += ds->deformationWave.amplitude * scale; + for ( i = 0; i < tess.numVertexes; i++ ) { + normal = &tess.vertexPtr[i].normal; scale = 0.98f; - scale = R_NoiseGet4f( 100 + xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, + scale = R_NoiseGet4f( tess.vertexPtr[i].xyz[0] * scale, + tess.vertexPtr[i].xyz[1] * scale, + tess.vertexPtr[i].xyz[2] * scale, tess.shaderTime * ds->deformationWave.frequency ); - normal[ 1 ] += ds->deformationWave.amplitude * scale; + (*normal)[ 0 ] += ds->deformationWave.amplitude * scale; scale = 0.98f; - scale = R_NoiseGet4f( 200 + xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, - tess.shaderTime * ds->deformationWave.frequency ); - normal[ 2 ] += ds->deformationWave.amplitude * scale; + scale = R_NoiseGet4f( 100 + tess.vertexPtr[i].xyz[0] * scale, + tess.vertexPtr[i].xyz[1] * scale, + tess.vertexPtr[i].xyz[2] * scale, + tess.shaderTime * ds->deformationWave.frequency ); + (*normal)[ 1 ] += ds->deformationWave.amplitude * scale; - VectorNormalizeFast( normal ); + scale = 0.98f; + scale = R_NoiseGet4f( 200 + tess.vertexPtr[i].xyz[0] * scale, + tess.vertexPtr[i].xyz[1] * scale, + tess.vertexPtr[i].xyz[2] * scale, + tess.shaderTime * ds->deformationWave.frequency ); + (*normal)[ 2 ] += ds->deformationWave.amplitude * scale; } } @@ -203,24 +209,23 @@ RB_CalcBulgeVertexes */ void RB_CalcBulgeVertexes( deformStage_t *ds ) { int i; - const float *st = ( const float * ) tess.texCoords[0]; - float *xyz = ( float * ) tess.xyz; - float *normal = ( float * ) tess.normal; float now; now = backEnd.refdef.time * ds->bulgeSpeed * 0.001f; - for ( i = 0; i < tess.numVertexes; i++, xyz += 4, st += 4, normal += 4 ) { + for ( i = 0; i < tess.numVertexes; i++ ) { int off; float scale; + vec3_t *normal; - off = (float)( FUNCTABLE_SIZE / (M_PI*2) ) * ( st[0] * ds->bulgeWidth + now ); + off = (float)( FUNCTABLE_SIZE / (M_PI*2) ) * ( tess.vertexPtr[i].tc1[0] * ds->bulgeWidth + now ); scale = tr.sinTable[ off & FUNCTABLE_MASK ] * ds->bulgeHeight; - - xyz[0] += normal[0] * scale; - xyz[1] += normal[1] * scale; - xyz[2] += normal[2] * scale; + + normal = &tess.vertexPtr[i].normal; + tess.vertexPtr[i].xyz[0] += (*normal)[0] * scale; + tess.vertexPtr[i].xyz[1] += (*normal)[1] * scale; + tess.vertexPtr[i].xyz[2] += (*normal)[2] * scale; } } @@ -234,7 +239,6 @@ A deformation that can move an entire surface along a wave path */ void RB_CalcMoveVertexes( deformStage_t *ds ) { int i; - float *xyz; float *table; float scale; vec3_t offset; @@ -248,9 +252,8 @@ void RB_CalcMoveVertexes( deformStage_t *ds ) { VectorScale( ds->moveVector, scale, offset ); - xyz = ( float * ) tess.xyz; - for ( i = 0; i < tess.numVertexes; i++, xyz += 4 ) { - VectorAdd( xyz, offset, xyz ); + for ( i = 0; i < tess.numVertexes; i++ ) { + VectorAdd( tess.vertexPtr[i].xyz, offset, tess.vertexPtr[i].xyz ); } } @@ -265,6 +268,7 @@ Change a polygon into a bunch of text polygons void DeformText( const char *text ) { int i; vec3_t origin, width, height; + vec3_t *normal; int len; int ch; byte color[4]; @@ -274,21 +278,23 @@ void DeformText( const char *text ) { height[0] = 0; height[1] = 0; height[2] = -1; - CrossProduct( tess.normal[0], height, width ); + normal = &tess.vertexPtr[0].normal; + CrossProduct( *normal, height, width ); // find the midpoint of the box VectorClear( mid ); bottom = 999999; top = -999999; for ( i = 0 ; i < 4 ; i++ ) { - VectorAdd( tess.xyz[i], mid, mid ); - if ( tess.xyz[i][2] < bottom ) { - bottom = tess.xyz[i][2]; + VectorAdd( tess.vertexPtr[i].xyz, mid, mid ); + if ( tess.vertexPtr[i].xyz[2] < bottom ) { + bottom = tess.vertexPtr[i].xyz[2]; } - if ( tess.xyz[i][2] > top ) { - top = tess.xyz[i][2]; + if ( tess.vertexPtr[i].xyz[2] > top ) { + top = tess.vertexPtr[i].xyz[2]; } } + RB_DiscardSurface( ); VectorScale( mid, 0.25f, origin ); // determine the individual character size @@ -303,8 +309,10 @@ void DeformText( const char *text ) { VectorMA( origin, (len-1), width, origin ); // clear the shader indexes - tess.numIndexes = 0; - tess.numVertexes = 0; + RB_BeginSurface( tess.shader ); + tess.numVertexes = 4 * len; + tess.numIndexes[0] = 6 * len; + RB_AllocateSurface( ); color[0] = color[1] = color[2] = color[3] = 255; @@ -352,7 +360,6 @@ quads, rebuild them as forward facing sprites static void AutospriteDeform( void ) { int i; int oldVerts; - float *xyz; vec3_t mid, delta; float radius; vec3_t left, up; @@ -361,13 +368,15 @@ static void AutospriteDeform( void ) { if ( tess.numVertexes & 3 ) { ri.Printf( PRINT_WARNING, "Autosprite shader %s had odd vertex count\n", tess.shader->name ); } - if ( tess.numIndexes != ( tess.numVertexes >> 2 ) * 6 ) { + if ( tess.numIndexes[1] != ( tess.numVertexes >> 2 ) * 6 ) { ri.Printf( PRINT_WARNING, "Autosprite shader %s had odd index count\n", tess.shader->name ); } oldVerts = tess.numVertexes; + tess.numVertexes = 0; - tess.numIndexes = 0; + tess.numIndexes[0] = tess.numIndexes[1] = tess.numIndexes[2] = 0; + tess.indexRange = 1; if ( backEnd.currentEntity != &tr.worldEntity ) { GlobalVectorToLocal( backEnd.viewParms.or.axis[1], leftDir ); @@ -379,13 +388,11 @@ static void AutospriteDeform( void ) { for ( i = 0 ; i < oldVerts ; i+=4 ) { // find the midpoint - xyz = tess.xyz[i]; - - mid[0] = 0.25f * (xyz[0] + xyz[4] + xyz[8] + xyz[12]); - mid[1] = 0.25f * (xyz[1] + xyz[5] + xyz[9] + xyz[13]); - mid[2] = 0.25f * (xyz[2] + xyz[6] + xyz[10] + xyz[14]); + mid[0] = 0.25f * (tess.vertexPtr[i].xyz[0] + tess.vertexPtr[i+1].xyz[0] + tess.vertexPtr[i+2].xyz[0] + tess.vertexPtr[i+3].xyz[0]); + mid[1] = 0.25f * (tess.vertexPtr[i].xyz[1] + tess.vertexPtr[i+1].xyz[1] + tess.vertexPtr[i+2].xyz[1] + tess.vertexPtr[i+3].xyz[1]); + mid[2] = 0.25f * (tess.vertexPtr[i].xyz[2] + tess.vertexPtr[i+1].xyz[2] + tess.vertexPtr[i+2].xyz[2] + tess.vertexPtr[i+3].xyz[2]); - VectorSubtract( xyz, mid, delta ); + VectorSubtract( tess.vertexPtr[i].xyz, mid, delta ); radius = VectorLength( delta ) * 0.707f; // / sqrt(2) VectorScale( leftDir, radius, left ); @@ -395,20 +402,20 @@ static void AutospriteDeform( void ) { VectorSubtract( vec3_origin, left, left ); } - // compensate for scale in the axes if necessary - if ( backEnd.currentEntity->e.nonNormalizedAxes ) { - float axisLength; - axisLength = VectorLength( backEnd.currentEntity->e.axis[0] ); - if ( !axisLength ) { - axisLength = 0; - } else { - axisLength = 1.0f / axisLength; - } - VectorScale(left, axisLength, left); - VectorScale(up, axisLength, up); - } - - RB_AddQuadStamp( mid, left, up, tess.vertexColors[i] ); + // compensate for scale in the axes if necessary + if ( backEnd.currentEntity->e.nonNormalizedAxes ) { + float axisLength; + axisLength = VectorLength( backEnd.currentEntity->e.axis[0] ); + if ( !axisLength ) { + axisLength = 0; + } else { + axisLength = 1.0f / axisLength; + } + VectorScale(left, axisLength, left); + VectorScale(up, axisLength, up); + } + + RB_AddQuadStamp( mid, left, up, tess.vertexPtr[i].color ); } } @@ -432,13 +439,12 @@ int edgeVerts[6][2] = { static void Autosprite2Deform( void ) { int i, j, k; int indexes; - float *xyz; vec3_t forward; if ( tess.numVertexes & 3 ) { ri.Printf( PRINT_WARNING, "Autosprite2 shader %s had odd vertex count", tess.shader->name ); } - if ( tess.numIndexes != ( tess.numVertexes >> 2 ) * 6 ) { + if ( tess.numIndexes[1] != ( tess.numVertexes >> 2 ) * 6 ) { ri.Printf( PRINT_WARNING, "Autosprite2 shader %s had odd index count", tess.shader->name ); } @@ -456,10 +462,7 @@ static void Autosprite2Deform( void ) { int nums[2]; vec3_t mid[2]; vec3_t major, minor; - float *v1, *v2; - - // find the midpoint - xyz = tess.xyz[i]; + vec3_t *v1, *v2; // identify the two shortest edges nums[0] = nums[1] = 0; @@ -469,8 +472,8 @@ static void Autosprite2Deform( void ) { float l; vec3_t temp; - v1 = xyz + 4 * edgeVerts[j][0]; - v2 = xyz + 4 * edgeVerts[j][1]; + v1 = &tess.vertexPtr[edgeVerts[j][0]].xyz; + v2 = &tess.vertexPtr[edgeVerts[j][1]].xyz; VectorSubtract( v1, v2, temp ); @@ -487,12 +490,12 @@ static void Autosprite2Deform( void ) { } for ( j = 0 ; j < 2 ; j++ ) { - v1 = xyz + 4 * edgeVerts[nums[j]][0]; - v2 = xyz + 4 * edgeVerts[nums[j]][1]; + v1 = &tess.vertexPtr[edgeVerts[j][0]].xyz; + v2 = &tess.vertexPtr[edgeVerts[j][1]].xyz; - mid[j][0] = 0.5f * (v1[0] + v2[0]); - mid[j][1] = 0.5f * (v1[1] + v2[1]); - mid[j][2] = 0.5f * (v1[2] + v2[2]); + mid[j][0] = 0.5f * ((*v1)[0] + (*v2)[0]); + mid[j][1] = 0.5f * ((*v1)[1] + (*v2)[1]); + mid[j][2] = 0.5f * ((*v1)[2] + (*v2)[2]); } // find the vector of the major axis @@ -506,26 +509,26 @@ static void Autosprite2Deform( void ) { for ( j = 0 ; j < 2 ; j++ ) { float l; - v1 = xyz + 4 * edgeVerts[nums[j]][0]; - v2 = xyz + 4 * edgeVerts[nums[j]][1]; + v1 = &tess.vertexPtr[edgeVerts[j][0]].xyz; + v2 = &tess.vertexPtr[edgeVerts[j][1]].xyz; l = 0.5 * sqrt( lengths[j] ); // we need to see which direction this edge // is used to determine direction of projection for ( k = 0 ; k < 5 ; k++ ) { - if ( tess.indexes[ indexes + k ] == i + edgeVerts[nums[j]][0] - && tess.indexes[ indexes + k + 1 ] == i + edgeVerts[nums[j]][1] ) { + if ( tess.indexPtr[ indexes + k ] == i + edgeVerts[nums[j]][0] + && tess.indexPtr[ indexes + k + 1 ] == i + edgeVerts[nums[j]][1] ) { break; } } if ( k == 5 ) { - VectorMA( mid[j], l, minor, v1 ); - VectorMA( mid[j], -l, minor, v2 ); + VectorMA( mid[j], l, minor, *v1 ); + VectorMA( mid[j], -l, minor, *v2 ); } else { - VectorMA( mid[j], -l, minor, v1 ); - VectorMA( mid[j], l, minor, v2 ); + VectorMA( mid[j], -l, minor, *v1 ); + VectorMA( mid[j], l, minor, *v2 ); } } } @@ -546,8 +549,8 @@ void RB_DeformTessGeometry( void ) { ds = &tess.shader->deforms[ i ]; switch ( ds->deformation ) { - case DEFORM_NONE: - break; + case DEFORM_NONE: + break; case DEFORM_NORMALS: RB_CalcDeformNormals( ds ); break; @@ -595,7 +598,7 @@ COLORS /* ** RB_CalcColorFromEntity */ -void RB_CalcColorFromEntity( unsigned char *dstColors ) +void RB_CalcColorFromEntity( color4ub_t *dstColors, int numVertexes ) { int i; int *pColors = ( int * ) dstColors; @@ -606,7 +609,7 @@ void RB_CalcColorFromEntity( unsigned char *dstColors ) c = * ( int * ) backEnd.currentEntity->e.shaderRGBA; - for ( i = 0; i < tess.numVertexes; i++, pColors++ ) + for ( i = 0; i < numVertexes; i++, pColors++ ) { *pColors = c; } @@ -615,7 +618,7 @@ void RB_CalcColorFromEntity( unsigned char *dstColors ) /* ** RB_CalcColorFromOneMinusEntity */ -void RB_CalcColorFromOneMinusEntity( unsigned char *dstColors ) +void RB_CalcColorFromOneMinusEntity( color4ub_t *dstColors, int numVertexes ) { int i; int *pColors = ( int * ) dstColors; @@ -632,7 +635,7 @@ void RB_CalcColorFromOneMinusEntity( unsigned char *dstColors ) c = * ( int * ) invModulate; - for ( i = 0; i < tess.numVertexes; i++, pColors++ ) + for ( i = 0; i < numVertexes; i++, pColors++ ) { *pColors = c; } @@ -641,52 +644,48 @@ void RB_CalcColorFromOneMinusEntity( unsigned char *dstColors ) /* ** RB_CalcAlphaFromEntity */ -void RB_CalcAlphaFromEntity( unsigned char *dstColors ) +void RB_CalcAlphaFromEntity( color4ub_t *dstColors, int numVertexes ) { int i; if ( !backEnd.currentEntity ) return; - dstColors += 3; - - for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) + for ( i = 0; i < numVertexes; i++, dstColors++ ) { - *dstColors = backEnd.currentEntity->e.shaderRGBA[3]; + (*dstColors)[3] = backEnd.currentEntity->e.shaderRGBA[3]; } } /* ** RB_CalcAlphaFromOneMinusEntity */ -void RB_CalcAlphaFromOneMinusEntity( unsigned char *dstColors ) +void RB_CalcAlphaFromOneMinusEntity( color4ub_t *dstColors, int numVertexes ) { int i; if ( !backEnd.currentEntity ) return; - dstColors += 3; - - for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) + for ( i = 0; i < numVertexes; i++, dstColors++ ) { - *dstColors = 0xff - backEnd.currentEntity->e.shaderRGBA[3]; + (*dstColors)[3] = 0xff - backEnd.currentEntity->e.shaderRGBA[3]; } } /* ** RB_CalcWaveColor */ -void RB_CalcWaveColor( const waveForm_t *wf, unsigned char *dstColors ) +void RB_CalcWaveColor( const waveForm_t *wf, color4ub_t *dstColors, int numVertexes ) { int i; int v; float glow; int *colors = ( int * ) dstColors; - byte color[4]; + color4ub_t color; - if ( wf->func == GF_NOISE ) { + if ( wf->func == GF_NOISE ) { glow = wf->base + R_NoiseGet4f( 0, 0, 0, ( tess.shaderTime + wf->phase ) * wf->frequency ) * wf->amplitude; } else { glow = EvalWaveForm( wf ) * tr.identityLight; @@ -704,7 +703,7 @@ void RB_CalcWaveColor( const waveForm_t *wf, unsigned char *dstColors ) color[3] = 255; v = *(int *)color; - for ( i = 0; i < tess.numVertexes; i++, colors++ ) { + for ( i = 0; i < numVertexes; i++, colors++ ) { *colors = v; } } @@ -712,7 +711,7 @@ void RB_CalcWaveColor( const waveForm_t *wf, unsigned char *dstColors ) /* ** RB_CalcWaveAlpha */ -void RB_CalcWaveAlpha( const waveForm_t *wf, unsigned char *dstColors ) +void RB_CalcWaveAlpha( const waveForm_t *wf, color4ub_t *dstColors, int numVertexes ) { int i; int v; @@ -722,69 +721,75 @@ void RB_CalcWaveAlpha( const waveForm_t *wf, unsigned char *dstColors ) v = 255 * glow; - for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) + for ( i = 0; i < numVertexes; i++, dstColors++ ) { - dstColors[3] = v; + (*dstColors)[3] = v; } } /* ** RB_CalcModulateColorsByFog */ -void RB_CalcModulateColorsByFog( unsigned char *colors ) { +void RB_CalcModulateColorsByFog( color4ub_t *colors, int numVertexes ) { int i; - float texCoords[SHADER_MAX_VERTEXES][2]; + vec2_t *texCoords; // calculate texcoords so we can derive density // this is not wasted, because it would only have // been previously called if the surface was opaque - RB_CalcFogTexCoords( texCoords[0] ); + texCoords = RB_AllocScratch( sizeof(vec2_t) * numVertexes ); + RB_CalcFogTexCoords( texCoords, 0, numVertexes ); - for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { + for ( i = 0; i < numVertexes; i++, colors++ ) { float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); - colors[0] *= f; - colors[1] *= f; - colors[2] *= f; + (*colors)[0] *= f; + (*colors)[1] *= f; + (*colors)[2] *= f; } + RB_FreeScratch( texCoords ); } /* ** RB_CalcModulateAlphasByFog */ -void RB_CalcModulateAlphasByFog( unsigned char *colors ) { +void RB_CalcModulateAlphasByFog( color4ub_t *colors, int numVertexes ) { int i; - float texCoords[SHADER_MAX_VERTEXES][2]; + vec2_t *texCoords; // calculate texcoords so we can derive density // this is not wasted, because it would only have // been previously called if the surface was opaque - RB_CalcFogTexCoords( texCoords[0] ); + texCoords = RB_AllocScratch( sizeof(vec2_t) * numVertexes ); + RB_CalcFogTexCoords( texCoords, 0, numVertexes ); - for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { + for ( i = 0; i < numVertexes; i++, colors++ ) { float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); - colors[3] *= f; + (*colors)[3] *= f; } + RB_FreeScratch( texCoords ); } /* ** RB_CalcModulateRGBAsByFog */ -void RB_CalcModulateRGBAsByFog( unsigned char *colors ) { +void RB_CalcModulateRGBAsByFog( color4ub_t *colors, int numVertexes ) { int i; - float texCoords[SHADER_MAX_VERTEXES][2]; + vec2_t *texCoords; // calculate texcoords so we can derive density // this is not wasted, because it would only have // been previously called if the surface was opaque - RB_CalcFogTexCoords( texCoords[0] ); + texCoords = RB_AllocScratch( sizeof(vec2_t) * numVertexes ); + RB_CalcFogTexCoords( texCoords, 0, numVertexes ); - for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { + for ( i = 0; i < numVertexes; i++, colors++ ) { float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); - colors[0] *= f; - colors[1] *= f; - colors[2] *= f; - colors[3] *= f; + (*colors)[0] *= f; + (*colors)[1] *= f; + (*colors)[2] *= f; + (*colors)[3] *= f; } + RB_FreeScratch( texCoords ); } @@ -805,9 +810,8 @@ projected textures, but I don't trust the drivers and it doesn't fit our shader data. ======================== */ -void RB_CalcFogTexCoords( float *st ) { +void RB_CalcFogTexCoords( vec2_t *st, int skip, int numVertexes ) { int i; - float *v; float s, t; float eyeT; qboolean eyeOutside; @@ -857,10 +861,10 @@ void RB_CalcFogTexCoords( float *st ) { fogDistanceVector[3] += 1.0/512; // calculate density for each point - for (i = 0, v = tess.xyz[0] ; i < tess.numVertexes ; i++, v += 4) { + for (i = skip, st += skip ; i < numVertexes ; i++, st++ ) { // calculate the length in fog - s = DotProduct( v, fogDistanceVector ) + fogDistanceVector[3]; - t = DotProduct( v, fogDepthVector ) + fogDepthVector[3]; + s = DotProduct( tess.vertexPtr[i].xyz, fogDistanceVector ) + fogDistanceVector[3]; + t = DotProduct( tess.vertexPtr[i].xyz, fogDepthVector ) + fogDepthVector[3]; // partially clipped fogs use the T axis if ( eyeOutside ) { @@ -877,9 +881,8 @@ void RB_CalcFogTexCoords( float *st ) { } } - st[0] = s; - st[1] = t; - st += 2; + (*st)[0] = s; + (*st)[1] = t; } } @@ -888,70 +891,69 @@ void RB_CalcFogTexCoords( float *st ) { /* ** RB_CalcEnvironmentTexCoords */ -void RB_CalcEnvironmentTexCoords( float *st ) +void RB_CalcEnvironmentTexCoords( vec2_t *st, int numVertexes ) { - int i; - float *v, *normal; + int i; + vec3_t *normal; vec3_t viewer, reflected; float d; - v = tess.xyz[0]; - normal = tess.normal[0]; - - for (i = 0 ; i < tess.numVertexes ; i++, v += 4, normal += 4, st += 2 ) + for (i = 0 ; i < numVertexes ; i++, st++ ) { - VectorSubtract (backEnd.or.viewOrigin, v, viewer); + VectorSubtract (backEnd.or.viewOrigin, tess.vertexPtr[i].xyz, + viewer); VectorNormalizeFast (viewer); - d = DotProduct (normal, viewer); + normal = &tess.vertexPtr[i].normal; + d = DotProduct (*normal, viewer); - reflected[0] = normal[0]*2*d - viewer[0]; - reflected[1] = normal[1]*2*d - viewer[1]; - reflected[2] = normal[2]*2*d - viewer[2]; + reflected[0] = (*normal[0])*2*d - viewer[0]; + reflected[1] = (*normal[1])*2*d - viewer[1]; + reflected[2] = (*normal[2])*2*d - viewer[2]; - st[0] = 0.5 + reflected[1] * 0.5; - st[1] = 0.5 - reflected[2] * 0.5; + (*st)[0] = 0.5 + reflected[1] * 0.5; + (*st)[1] = 0.5 - reflected[2] * 0.5; } } /* ** RB_CalcTurbulentTexCoords */ -void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *st ) +void RB_CalcTurbulentTexCoords( const waveForm_t *wf, vec2_t *st, int numVertexes ) { int i; float now; now = ( wf->phase + tess.shaderTime * wf->frequency ); - for ( i = 0; i < tess.numVertexes; i++, st += 2 ) + for ( i = 0; i < numVertexes; i++, st++ ) { - float s = st[0]; - float t = st[1]; + float s = (*st)[0]; + float t = (*st)[1]; - st[0] = s + tr.sinTable[ ( ( int ) ( ( ( tess.xyz[i][0] + tess.xyz[i][2] )* 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; - st[1] = t + tr.sinTable[ ( ( int ) ( ( tess.xyz[i][1] * 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; + (*st)[0] = s + tr.sinTable[ ( ( int ) ( ( ( tess.vertexPtr[i].xyz[0] + tess.vertexPtr[i].xyz[2] )* 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; + (*st)[1] = t + tr.sinTable[ ( ( int ) ( ( tess.vertexPtr[i].xyz[1] * 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; } } /* ** RB_CalcScaleTexCoords */ -void RB_CalcScaleTexCoords( const float scale[2], float *st ) +void RB_CalcScaleTexCoords( const float scale[2], vec2_t *st, int numVertexes ) { int i; - for ( i = 0; i < tess.numVertexes; i++, st += 2 ) + for ( i = 0; i < numVertexes; i++, st++ ) { - st[0] *= scale[0]; - st[1] *= scale[1]; + (*st)[0] *= scale[0]; + (*st)[1] *= scale[1]; } } /* ** RB_CalcScrollTexCoords */ -void RB_CalcScrollTexCoords( const float scrollSpeed[2], float *st ) +void RB_CalcScrollTexCoords( const float scrollSpeed[2], vec2_t *st, int numVertexes ) { int i; float timeScale = tess.shaderTime; @@ -965,34 +967,34 @@ void RB_CalcScrollTexCoords( const float scrollSpeed[2], float *st ) adjustedScrollS = adjustedScrollS - floor( adjustedScrollS ); adjustedScrollT = adjustedScrollT - floor( adjustedScrollT ); - for ( i = 0; i < tess.numVertexes; i++, st += 2 ) + for ( i = 0; i < numVertexes; i++, st++ ) { - st[0] += adjustedScrollS; - st[1] += adjustedScrollT; + (*st)[0] += adjustedScrollS; + (*st)[1] += adjustedScrollT; } } /* ** RB_CalcTransformTexCoords */ -void RB_CalcTransformTexCoords( const texModInfo_t *tmi, float *st ) +void RB_CalcTransformTexCoords( const texModInfo_t *tmi, vec2_t *st, int numVertexes ) { int i; - for ( i = 0; i < tess.numVertexes; i++, st += 2 ) + for ( i = 0; i < numVertexes; i++, st++ ) { - float s = st[0]; - float t = st[1]; + float s = (*st)[0]; + float t = (*st)[1]; - st[0] = s * tmi->matrix[0][0] + t * tmi->matrix[1][0] + tmi->translate[0]; - st[1] = s * tmi->matrix[0][1] + t * tmi->matrix[1][1] + tmi->translate[1]; + (*st)[0] = s * tmi->matrix[0][0] + t * tmi->matrix[1][0] + tmi->translate[0]; + (*st)[1] = s * tmi->matrix[0][1] + t * tmi->matrix[1][1] + tmi->translate[1]; } } /* ** RB_CalcRotateTexCoords */ -void RB_CalcRotateTexCoords( float degsPerSecond, float *st ) +void RB_CalcRotateTexCoords( float degsPerSecond, vec2_t *st, int numVertexes ) { float timeScale = tess.shaderTime; float degs; @@ -1014,7 +1016,7 @@ void RB_CalcRotateTexCoords( float degsPerSecond, float *st ) tmi.matrix[1][1] = cosValue; tmi.translate[1] = 0.5 - 0.5 * sinValue - 0.5 * cosValue; - RB_CalcTransformTexCoords( &tmi, st ); + RB_CalcTransformTexCoords( &tmi, st, numVertexes ); } @@ -1025,39 +1027,34 @@ void RB_CalcRotateTexCoords( float degsPerSecond, float *st ) */ vec3_t lightOrigin = { -960, 1980, 96 }; // FIXME: track dynamically -void RB_CalcSpecularAlpha( unsigned char *alphas ) { +void RB_CalcSpecularAlpha( color4ub_t *alphas, int numVertexes ) { int i; - float *v, *normal; + vec3_t *normal; vec3_t viewer, reflected; float l, d; int b; vec3_t lightDir; - int numVertexes; - - v = tess.xyz[0]; - normal = tess.normal[0]; - - alphas += 3; - numVertexes = tess.numVertexes; - for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4, alphas += 4) { + for (i = 0 ; i < numVertexes ; i++, alphas++) { float ilength; - VectorSubtract( lightOrigin, v, lightDir ); + VectorSubtract( lightOrigin, tess.vertexPtr[i].xyz, lightDir ); // ilength = Q_rsqrt( DotProduct( lightDir, lightDir ) ); VectorNormalizeFast( lightDir ); // calculate the specular color - d = DotProduct (normal, lightDir); + normal = &tess.vertexPtr[i].normal; + d = DotProduct (*normal, lightDir); // d *= ilength; // we don't optimize for the d < 0 case since this tends to // cause visual artifacts such as faceted "snapping" - reflected[0] = normal[0]*2*d - lightDir[0]; - reflected[1] = normal[1]*2*d - lightDir[1]; - reflected[2] = normal[2]*2*d - lightDir[2]; + reflected[0] = (*normal)[0]*2*d - lightDir[0]; + reflected[1] = (*normal)[1]*2*d - lightDir[1]; + reflected[2] = (*normal)[2]*2*d - lightDir[2]; - VectorSubtract (backEnd.or.viewOrigin, v, viewer); + VectorSubtract (backEnd.or.viewOrigin, tess.vertexPtr[i].xyz, + viewer); ilength = Q_rsqrt( DotProduct( viewer, viewer ) ); l = DotProduct (reflected, viewer); l *= ilength; @@ -1073,7 +1070,7 @@ void RB_CalcSpecularAlpha( unsigned char *alphas ) { } } - *alphas = b; + (*alphas)[3] = b; } } @@ -1083,14 +1080,12 @@ void RB_CalcSpecularAlpha( unsigned char *alphas ) { ** The basic vertex lighting calc */ #if idppc_altivec -static void RB_CalcDiffuseColor_altivec( unsigned char *colors ) +static void RB_CalcDiffuseColor_altivec( color4ub_t *colors, int numVertexes ) { int i; - float *v, *normal; trRefEntity_t *ent; int ambientLightInt; vec3_t lightDir; - int numVertexes; vector unsigned char vSel = VECCONST_UINT8(0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, @@ -1126,14 +1121,10 @@ static void RB_CalcDiffuseColor_altivec( unsigned char *colors ) zero = (vector float)vec_splat_s8(0); VectorCopy( ent->lightDir, lightDir ); - v = tess.xyz[0]; - normal = tess.normal[0]; - - normalPerm = vec_lvsl(0,normal); - numVertexes = tess.numVertexes; - for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4) { - normalVec0 = vec_ld(0,(vector float *)normal); - normalVec1 = vec_ld(11,(vector float *)normal); + normalPerm = vec_lvsl(0,&tess.vertexPtr[0].normal); + for (i = 0 ; i < numVertexes ; i++) { + normalVec0 = vec_ld(0,(vector float *)&tess.vertexPtr[i].normal); + normalVec1 = vec_ld(11,(vector float *)&tess.vertexPtr[i].normal); normalVec0 = vec_perm(normalVec0,normalVec1,normalPerm); incomingVec0 = vec_madd(normalVec0, lightDirVec, zero); incomingVec1 = vec_sld(incomingVec0,incomingVec0,4); @@ -1148,69 +1139,63 @@ static void RB_CalcDiffuseColor_altivec( unsigned char *colors ) jVecShort = vec_pack(jVecInt,jVecInt); // RGBxRGBx jVecChar = vec_packsu(jVecShort,jVecShort); // RGBxRGBxRGBxRGBx jVecChar = vec_sel(jVecChar,vSel,vSel); // RGBARGBARGBARGBA replace alpha with 255 - vec_ste((vector unsigned int)jVecChar,0,(unsigned int *)&colors[i*4]); // store color + vec_ste((vector unsigned int)jVecChar,0,(unsigned int *)&colors[i]); // store color } } #endif -static void RB_CalcDiffuseColor_scalar( unsigned char *colors ) +static void RB_CalcDiffuseColor_scalar( color4ub_t *colors, int numVertexes ) { int i, j; - float *v, *normal; float incoming; trRefEntity_t *ent; int ambientLightInt; + vec3_t *normal; vec3_t ambientLight; vec3_t lightDir; vec3_t directedLight; - int numVertexes; ent = backEnd.currentEntity; ambientLightInt = ent->ambientLightInt; VectorCopy( ent->ambientLight, ambientLight ); VectorCopy( ent->directedLight, directedLight ); VectorCopy( ent->lightDir, lightDir ); - v = tess.xyz[0]; - normal = tess.normal[0]; - - numVertexes = tess.numVertexes; - for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4) { - incoming = DotProduct (normal, lightDir); + for (i = 0 ; i < numVertexes ; i++) { + normal = &tess.vertexPtr[i].normal; + incoming = DotProduct (*normal, lightDir); if ( incoming <= 0 ) { - *(int *)&colors[i*4] = ambientLightInt; + *(int *)&colors[i] = ambientLightInt; continue; } j = ri.ftol(ambientLight[0] + incoming * directedLight[0]); if ( j > 255 ) { j = 255; } - colors[i*4+0] = j; + colors[i][0] = j; j = ri.ftol(ambientLight[1] + incoming * directedLight[1]); if ( j > 255 ) { j = 255; } - colors[i*4+1] = j; + colors[i][1] = j; j = ri.ftol(ambientLight[2] + incoming * directedLight[2]); if ( j > 255 ) { j = 255; } - colors[i*4+2] = j; - - colors[i*4+3] = 255; + colors[i][2] = j; } } -void RB_CalcDiffuseColor( unsigned char *colors ) +void RB_CalcDiffuseColor( color4ub_t *colors, int numVertexes ) { #if idppc_altivec if (com_altivec->integer) { // must be in a seperate function or G3 systems will crash. - RB_CalcDiffuseColor_altivec( colors ); + RB_CalcDiffuseColor_altivec( colors, numVertexes ); return; } #endif - RB_CalcDiffuseColor_scalar( colors ); + RB_CalcDiffuseColor_scalar( colors, numVertexes ); } diff --git a/code/renderer/tr_shader.c b/code/renderer/tr_shader.c index 9bb6416..05f504e 100644 --- a/code/renderer/tr_shader.c +++ b/code/renderer/tr_shader.c @@ -695,6 +695,10 @@ static qboolean ParseStage( shaderStage_t *stage, char **text ) stage->bundle[0].numImageAnimations++; } } + if( stage->bundle[0].numImageAnimations > 1 ) { + stage->bundle[0].combinedImage = R_CombineImages(stage->bundle[0].numImageAnimations, + stage->bundle[0].image); + } } else if ( !Q_stricmp( token, "videoMap" ) ) { @@ -833,7 +837,10 @@ static qboolean ParseStage( shaderStage_t *stage, char **text ) } else if ( !Q_stricmp( token, "identityLighting" ) ) { - stage->rgbGen = CGEN_IDENTITY_LIGHTING; + if ( r_overBrightBits->integer == 0 ) + stage->rgbGen = CGEN_IDENTITY; + else + stage->rgbGen = CGEN_IDENTITY_LIGHTING; } else if ( !Q_stricmp( token, "entity" ) ) { @@ -1008,6 +1015,43 @@ static qboolean ParseStage( shaderStage_t *stage, char **text ) } } + // I assume DST_ALPHA is always 1, so I just replace it with GL_ONE + if ( blendSrcBits == GLS_SRCBLEND_DST_ALPHA ) + blendSrcBits = GLS_SRCBLEND_ONE; + else if ( blendSrcBits == GLS_SRCBLEND_ONE_MINUS_DST_ALPHA ) + blendSrcBits = GLS_SRCBLEND_ZERO; + + if ( blendDstBits == GLS_DSTBLEND_DST_ALPHA ) + blendDstBits = GLS_DSTBLEND_ONE; + else if ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_DST_ALPHA ) + blendDstBits = GLS_DSTBLEND_ZERO; + + // If the image has no (real) alpha channel, I can do the same + // for SRC_ALPHA + if ( !stage->bundle[0].image[0]->hasAlpha && + stage->alphaGen == AGEN_IDENTITY) { + if ( blendSrcBits == GLS_SRCBLEND_SRC_ALPHA ) + blendSrcBits = GLS_SRCBLEND_ONE; + else if ( blendSrcBits == GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA ) + blendSrcBits = GLS_SRCBLEND_ZERO; + + if ( blendDstBits == GLS_DSTBLEND_SRC_ALPHA ) + blendDstBits = GLS_DSTBLEND_ONE; + else if ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ) + blendDstBits = GLS_DSTBLEND_ZERO; + + // also alphaFunc makes no sense without alpha + atestBits = 0; + } else { + // image has alpha, if it uses alpha blending I can optimise + // alphafunc NONE to alphafunc GT0 + if ( blendSrcBits == GLS_SRCBLEND_SRC_ALPHA && + blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA && + atestBits == 0 ) { + atestBits = GLS_ATEST_GT_0; + } + } + // // if cgen isn't explicitly specified, use either identity or identitylighting // @@ -1401,6 +1445,7 @@ static qboolean ParseShader( char **text ) { char *token; int s; + qboolean polygonOffset = qfalse; s = 0; @@ -1486,10 +1531,10 @@ static qboolean ParseShader( char **text ) } else if ( !Q_stricmp( token, "clampTime" ) ) { token = COM_ParseExt( text, qfalse ); - if (token[0]) { - shader.clampTime = atof(token); - } - } + if (token[0]) { + shader.clampTime = atof(token); + } + } // skip stuff that only the q3map needs else if ( !Q_stricmpn( token, "q3map", 5 ) ) { SkipRestOfLine( text ); @@ -1516,7 +1561,7 @@ static qboolean ParseShader( char **text ) // polygonOffset else if ( !Q_stricmp( token, "polygonOffset" ) ) { - shader.polygonOffset = qtrue; + polygonOffset = qtrue; continue; } // entityMergable, allowing sprite surfaces from multiple entities @@ -1609,6 +1654,13 @@ static qboolean ParseShader( char **text ) return qfalse; } + if ( polygonOffset ) { + int i; + for( i = 0; i < s; i++ ) { + stages[i].stateBits |= GLS_POLYGON_OFFSET; + } + } + shader.explicitlyDefined = qtrue; return qtrue; @@ -1632,7 +1684,14 @@ otherwise set to the generic stage function */ static void ComputeStageIteratorFunc( void ) { + int stage; + int units = glConfig.numTextureUnits; + + if (!units) units = 1; + shader.optimalStageIteratorFunc = RB_StageIteratorGeneric; + shader.anyGLAttr = 0; + shader.allGLAttr = GLA_COLOR_mask | GLA_TC1_mask | GLA_TC2_mask; // // see if this should go into the sky path @@ -1640,9 +1699,156 @@ static void ComputeStageIteratorFunc( void ) if ( shader.isSky ) { shader.optimalStageIteratorFunc = RB_StageIteratorSky; + shader.anyGLAttr = GLA_FULL_dynamic; goto done; } + // check all deformation stages + for ( stage = 0; stage < shader.numDeforms; stage ++ ) { + switch ( shader.deforms[stage].deformation ) { + case DEFORM_NONE: + // vertex data for vertex and normal + break; + case DEFORM_WAVE: + case DEFORM_BULGE: + case DEFORM_MOVE: + // dynamic vertex, normal from vertex data + shader.anyGLAttr |= GLA_VERTEX_dynamic; + break; + case DEFORM_NORMALS: + // dynamic normal, vertex from vertex data + shader.anyGLAttr |= GLA_NORMAL_dynamic; + break; + case DEFORM_PROJECTION_SHADOW: + case DEFORM_AUTOSPRITE: + case DEFORM_AUTOSPRITE2: + case DEFORM_TEXT0: + case DEFORM_TEXT1: + case DEFORM_TEXT2: + case DEFORM_TEXT3: + case DEFORM_TEXT4: + case DEFORM_TEXT5: + case DEFORM_TEXT6: + case DEFORM_TEXT7: + // dynamic vertex and normal + shader.anyGLAttr |= GLA_VERTEX_dynamic; + shader.anyGLAttr |= GLA_NORMAL_dynamic; + break; + } + } + + // check all shader stages + for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) + { + shaderStage_t *pStage = &stages[stage]; + int bundle; + short colorType, tcType; + + if ( !pStage->active ) + { + break; + } + + switch ( pStage->rgbGen ) { + case CGEN_IDENTITY_LIGHTING: + case CGEN_IDENTITY: + case CGEN_ENTITY: + case CGEN_ONE_MINUS_ENTITY: + case CGEN_CONST: + case CGEN_WAVEFORM: + // uniform color + colorType = GLA_COLOR_uniform; + break; + case CGEN_EXACT_VERTEX: + case CGEN_VERTEX: + // vertex colors + colorType = GLA_COLOR_vtxcolor; + break; + case CGEN_LIGHTING_DIFFUSE: + case CGEN_ONE_MINUS_VERTEX: + case CGEN_BAD: + case CGEN_FOG: + default: + // dynamic colors + colorType = GLA_COLOR_dynamic; + break; + } + + switch ( pStage->alphaGen ) { + case AGEN_SKIP: + // doesn't matter + break; + case AGEN_IDENTITY: + case AGEN_ENTITY: + case AGEN_ONE_MINUS_ENTITY: + case AGEN_CONST: + case AGEN_WAVEFORM: + // uniform alpha + if ( colorType != GLA_COLOR_uniform ) + colorType = GLA_COLOR_dynamic; + break; + case AGEN_VERTEX: + // vertex alpha + if( colorType != GLA_COLOR_vtxcolor ) + colorType = GLA_COLOR_dynamic; + break; + case AGEN_ONE_MINUS_VERTEX: + case AGEN_LIGHTING_SPECULAR: + case AGEN_PORTAL: + default: + colorType = GLA_COLOR_dynamic; + break; + } + if( r_greyscale->integer && colorType == GLA_COLOR_vtxcolor ) { + // grayscale conversion is dynamic + colorType = GLA_COLOR_dynamic; + } + + shader.anyGLAttr |= colorType; + shader.allGLAttr &= ~GLA_COLOR_mask | colorType; + + for ( bundle = 0; bundle < units; bundle++ ) { + int shift = bundle * GLA_TC_shift; + + if ( bundle > 0 && !pStage->bundle[bundle].multitextureEnv ) + break; + + switch ( pStage->bundle[bundle].tcGen ) { + case TCGEN_BAD: + case TCGEN_IDENTITY: + default: + // uniform texcoord + tcType = GLA_TC1_uniform; + break; + case TCGEN_TEXTURE: + // vertex texcoord + tcType = GLA_TC1_texcoord; + break; + case TCGEN_LIGHTMAP: + // vertex texcoord (lightmap) + tcType = GLA_TC1_lmcoord; + break; + case TCGEN_VECTOR: + case TCGEN_FOG: + case TCGEN_ENVIRONMENT_MAPPED: + // dynamic texcoord + tcType = GLA_TC1_dynamic; + break; + } + // texmods are always dynamic + if ( pStage->bundle[bundle].numTexMods > 0 ) + tcType = GLA_TC1_dynamic; + + shader.anyGLAttr |= tcType << shift; + shader.allGLAttr &= ~(GLA_TC1_mask << shift) + | tcType << shift; + } + } + + // mirror and portal shaders + if ( shader.sort <= SS_PORTAL ) + shader.anyGLAttr |= GLA_FULL_dynamic; + if ( r_ignoreFastPath->integer ) { return; @@ -1659,15 +1865,12 @@ static void ComputeStageIteratorFunc( void ) { if ( stages[0].bundle[0].tcGen == TCGEN_TEXTURE ) { - if ( !shader.polygonOffset ) + if ( !stages[0].bundle[1].multitextureEnv ) { - if ( !shader.multitextureEnv ) + if ( !shader.numDeforms ) { - if ( !shader.numDeforms ) - { - shader.optimalStageIteratorFunc = RB_StageIteratorVertexLitTexture; - goto done; - } + shader.optimalStageIteratorFunc = RB_StageIteratorVertexLitTexture; + goto done; } } } @@ -1685,15 +1888,12 @@ static void ComputeStageIteratorFunc( void ) if ( stages[0].bundle[0].tcGen == TCGEN_TEXTURE && stages[0].bundle[1].tcGen == TCGEN_LIGHTMAP ) { - if ( !shader.polygonOffset ) + if ( !shader.numDeforms ) { - if ( !shader.numDeforms ) + if ( stages[0].bundle[1].multitextureEnv ) { - if ( shader.multitextureEnv ) - { - shader.optimalStageIteratorFunc = RB_StageIteratorLightmappedMultitexture; - goto done; - } + shader.optimalStageIteratorFunc = RB_StageIteratorLightmappedMultitexture; + goto done; } } } @@ -1747,227 +1947,258 @@ static collapse_t collapse[] = { ================ CollapseMultitexture -Attempt to combine two stages into a single multitexture stage +Attempt to combine several stages into a single multitexture stage FIXME: I think modulated add + modulated add collapses incorrectly ================= */ -static qboolean CollapseMultitexture( void ) { +static int CollapseMultitexture( void ) { + int stage, bundle; int abits, bbits; int i; textureBundle_t tmpBundle; - if ( !qglActiveTextureARB ) { - return qfalse; - } - - // make sure both stages are active - if ( !stages[0].active || !stages[1].active ) { - return qfalse; - } + stage = 0; + bundle = 0; - // on voodoo2, don't combine different tmus - if ( glConfig.driverType == GLDRV_VOODOO ) { - if ( stages[0].bundle[0].image[0]->TMU == - stages[1].bundle[0].image[0]->TMU ) { - return qfalse; + while( stage < MAX_SHADER_STAGES && stages[stage].active ) { + if ( bundle + 1 >= glConfig.numTextureUnits ) { + // can't add next stage, no more texture units + stage++; + bundle = 0; + continue; } - } - - abits = stages[0].stateBits; - bbits = stages[1].stateBits; - - // make sure that both stages have identical state other than blend modes - if ( ( abits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) != - ( bbits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) ) { - return qfalse; - } - - abits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - bbits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - - // search for a valid multitexture blend function - for ( i = 0; collapse[i].blendA != -1 ; i++ ) { - if ( abits == collapse[i].blendA - && bbits == collapse[i].blendB ) { - break; + + // make sure both stages are active + if ( !stages[stage + 1].active ) { + // can't add next stage, it doesn't exist + stage++; + bundle = 0; + continue; } - } - - // nothing found - if ( collapse[i].blendA == -1 ) { - return qfalse; - } - // GL_ADD is a separate extension - if ( collapse[i].multitextureEnv == GL_ADD && !glConfig.textureEnvAddAvailable ) { - return qfalse; - } - - // make sure waveforms have identical parameters - if ( ( stages[0].rgbGen != stages[1].rgbGen ) || - ( stages[0].alphaGen != stages[1].alphaGen ) ) { - return qfalse; - } + // on voodoo2, don't combine different tmus + if ( glConfig.driverType == GLDRV_VOODOO ) { + if ( stages[stage].bundle[0].image[0]->TMU == + stages[stage + 1].bundle[0].image[0]->TMU ) { + stage++; + bundle = 0; + continue; + } + } - // an add collapse can only have identity colors - if ( collapse[i].multitextureEnv == GL_ADD && stages[0].rgbGen != CGEN_IDENTITY ) { - return qfalse; - } + abits = stages[stage].stateBits; + bbits = stages[stage + 1].stateBits; + /* + // can't combine if the second stage has an alpha test + if ( bbits & GLS_ATEST_BITS ) { + stage++; + bundle = 0; + continue; + } - if ( stages[0].rgbGen == CGEN_WAVEFORM ) - { - if ( memcmp( &stages[0].rgbWave, - &stages[1].rgbWave, - sizeof( stages[0].rgbWave ) ) ) + // can combine alphafunc only if depthwrite is enabled and + // the second stage has depthfunc equal + if ( abits & GLS_ATEST_BITS ) { + if (!((abits & GLS_DEPTHMASK_TRUE) && + (bbits & GLS_DEPTHFUNC_EQUAL)) ) { + stage++; + bundle = 0; + continue; + } + } else { + if ( (abits & GLS_DEPTHFUNC_EQUAL) != + (bbits & GLS_DEPTHFUNC_EQUAL) ) { + stage++; + bundle = 0; + continue; + } + } + */ + // make sure that both stages have identical state other than blend modes + if ( ( abits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) != + ( bbits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) ) { + stage++; + bundle = 0; + continue; + } + + abits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + bbits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + + // search for a valid multitexture blend function + for ( i = 0; collapse[i].blendA != -1 ; i++ ) { + if ( abits == collapse[i].blendA + && bbits == collapse[i].blendB ) { + break; + } + } + + // nothing found + if ( collapse[i].blendA == -1 ) { + stage++; + bundle = 0; + continue; + } + + // GL_ADD is a separate extension + if ( collapse[i].multitextureEnv == GL_ADD && !glConfig.textureEnvAddAvailable ) { + stage++; + bundle = 0; + continue; + } + + // make sure waveforms have identical parameters + if ( ( stages[stage].rgbGen != stages[stage + 1].rgbGen ) || + ( stages[stage].alphaGen != stages[stage + 1].alphaGen ) ) { + stage++; + bundle = 0; + continue; + } + + // an add collapse can only have identity colors + if ( collapse[i].multitextureEnv == GL_ADD && stages[stage].rgbGen != CGEN_IDENTITY ) { + stage++; + bundle = 0; + continue; + } + + if ( stages[stage].rgbGen == CGEN_WAVEFORM ) { - return qfalse; + if ( memcmp( &stages[stage].rgbWave, + &stages[stage + 1].rgbWave, + sizeof( stages[stage].rgbWave ) ) ) + { + stage++; + bundle = 0; + continue; + } } - } - if ( stages[0].alphaGen == AGEN_WAVEFORM ) - { - if ( memcmp( &stages[0].alphaWave, - &stages[1].alphaWave, - sizeof( stages[0].alphaWave ) ) ) + if ( stages[stage].alphaGen == AGEN_WAVEFORM ) { - return qfalse; + if ( memcmp( &stages[stage].alphaWave, + &stages[stage + 1].alphaWave, + sizeof( stages[stage].alphaWave ) ) ) + { + stage++; + bundle = 0; + continue; + } } - } + + + // make sure that lightmaps are in bundle 1 for 3dfx + if ( bundle == 0 && stages[stage].bundle[0].isLightmap ) + { + tmpBundle = stages[stage].bundle[0]; + stages[stage].bundle[0] = stages[stage + 1].bundle[0]; + stages[stage].bundle[1] = tmpBundle; + } + else + { + stages[stage].bundle[bundle + 1] = stages[stage + 1].bundle[0]; + } + + // set the new blend state bits + stages[stage].bundle[bundle + 1].multitextureEnv = collapse[i].multitextureEnv; + stages[stage].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + stages[stage].stateBits |= collapse[i].multitextureBlend; + bundle++; - // make sure that lightmaps are in bundle 1 for 3dfx - if ( stages[0].bundle[0].isLightmap ) - { - tmpBundle = stages[0].bundle[0]; - stages[0].bundle[0] = stages[1].bundle[0]; - stages[0].bundle[1] = tmpBundle; - } - else - { - stages[0].bundle[1] = stages[1].bundle[0]; + // + // move down subsequent shaders + // + memmove( &stages[stage + 1], &stages[stage + 2], sizeof( stages[0] ) * ( MAX_SHADER_STAGES - stage - 2 ) ); + Com_Memset( &stages[MAX_SHADER_STAGES-1], 0, sizeof( stages[0] ) ); } - // set the new blend state bits - shader.multitextureEnv = collapse[i].multitextureEnv; - stages[0].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - stages[0].stateBits |= collapse[i].multitextureBlend; - - // - // move down subsequent shaders - // - memmove( &stages[1], &stages[2], sizeof( stages[0] ) * ( MAX_SHADER_STAGES - 2 ) ); - Com_Memset( &stages[MAX_SHADER_STAGES-1], 0, sizeof( stages[0] ) ); - - return qtrue; + return stage; } /* -============= - -FixRenderCommandList -https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493 -Arnout: this is a nasty issue. Shaders can be registered after drawsurfaces are generated -but before the frame is rendered. This will, for the duration of one frame, cause drawsurfaces -to be rendered with bad shaders. To fix this, need to go through all render commands and fix -sortedIndex. ============== -*/ -static void FixRenderCommandList( int newShader ) { - renderCommandList_t *cmdList = &backEndData[tr.smpFrame]->commands; +R_SortShaders - if( cmdList ) { - const void *curCmd = cmdList->cmds; +Positions all shaders in the tr.sortedShaders[] +array so that the shader->sort key is sorted relative +to the other shaders. If shaders use the same GLSL +program, sort them all to the max occluder to avoid +expensive program switches later. - while ( 1 ) { - curCmd = PADP(curCmd, sizeof(void *)); - - switch ( *(const int *)curCmd ) { - case RC_SET_COLOR: - { - const setColorCommand_t *sc_cmd = (const setColorCommand_t *)curCmd; - curCmd = (const void *)(sc_cmd + 1); - break; - } - case RC_STRETCH_PIC: - { - const stretchPicCommand_t *sp_cmd = (const stretchPicCommand_t *)curCmd; - curCmd = (const void *)(sp_cmd + 1); - break; - } - case RC_DRAW_SURFS: - { - int i; - drawSurf_t *drawSurf; - shader_t *shader; - int fogNum; - int entityNum; - int dlightMap; - int sortedIndex; - const drawSurfsCommand_t *ds_cmd = (const drawSurfsCommand_t *)curCmd; - - for( i = 0, drawSurf = ds_cmd->drawSurfs; i < ds_cmd->numDrawSurfs; i++, drawSurf++ ) { - R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlightMap ); - sortedIndex = (( drawSurf->sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1)); - if( sortedIndex >= newShader ) { - sortedIndex++; - drawSurf->sort = (sortedIndex << QSORT_SHADERNUM_SHIFT) | entityNum | ( fogNum << QSORT_FOGNUM_SHIFT ) | (int)dlightMap; - } - } - curCmd = (const void *)(ds_cmd + 1); - break; - } - case RC_DRAW_BUFFER: - { - const drawBufferCommand_t *db_cmd = (const drawBufferCommand_t *)curCmd; - curCmd = (const void *)(db_cmd + 1); - break; - } - case RC_SWAP_BUFFERS: - { - const swapBuffersCommand_t *sb_cmd = (const swapBuffersCommand_t *)curCmd; - curCmd = (const void *)(sb_cmd + 1); - break; - } - case RC_END_OF_LIST: - default: - return; - } - } - } -} - -/* -============== -SortNewShader - -Positions the most recently created shader in the tr.sortedShaders[] -array so that the shader->sort key is sorted reletive to the other -shaders. +As sorting by anything but shader->sort is only for +better performance, and the shader->sort order may be only +changed by creating a new shader, I don't need to fully +sort the array - I just make a single round of back-to-front +bubblesort. Sets shader->sortedIndex ============== */ -static void SortNewShader( void ) { - int i; - float sort; - shader_t *newShader; +static ID_INLINE int cmpShader( shader_t *l, shader_t *r ) +{ + int diff; + int depthL = 0, depthR = 0; + GLuint resultL, resultR; + + diff = l->sort - r->sort; + if( !diff ) { + if( l->stages[0] ) + depthL = l->stages[0]->stateBits & GLS_DEPTHMASK_TRUE; + if( r->stages[0] ) + depthR = r->stages[0]->stateBits & GLS_DEPTHMASK_TRUE; + + diff = depthR - depthL; + } + if( !diff ) { + if( l->GLSLprogram ) + resultL = l->GLSLprogram->QuerySum; + else + resultL = QUERY_RESULT(&l->QueryResult); - newShader = tr.shaders[ tr.numShaders - 1 ]; - sort = newShader->sort; + if( r->GLSLprogram ) + resultR = r->GLSLprogram->QuerySum; + else + resultR = QUERY_RESULT(&r->QueryResult); - for ( i = tr.numShaders - 2 ; i >= 0 ; i-- ) { - if ( tr.sortedShaders[ i ]->sort <= sort ) { - break; + if( depthL ) + diff = resultR - resultL; + else + diff = resultL - resultR; + } + if( !diff ) { + diff = l->GLSLprogram - r->GLSLprogram; + } + if( !diff ) { + if( depthL ) + diff = QUERY_RESULT(&r->QueryResult) + - QUERY_RESULT(&l->QueryResult); + else + diff = QUERY_RESULT(&l->QueryResult) + - QUERY_RESULT(&r->QueryResult); + } + if( !diff ) { + diff = l->index - r->index; + } + return diff; +} +void R_SortShaders( void ) { + shader_t *tmp = tr.sortedShaders[tr.numShaders - 1]; + int idx = tr.numShaders - 2; + + for( idx = tr.numShaders - 2; idx >= 0; idx-- ) { + if( cmpShader( tmp, tr.sortedShaders[idx] ) >= 0 ) { + // order is correct + tr.sortedShaders[idx + 1] = tmp; + tmp->sortedIndex = idx + 1; + tmp = tr.sortedShaders[idx]; + } else { + // swap necessary + tr.sortedShaders[idx + 1] = tr.sortedShaders[idx]; + tr.sortedShaders[idx + 1]->sortedIndex = idx + 1; } - tr.sortedShaders[i+1] = tr.sortedShaders[i]; - tr.sortedShaders[i+1]->sortedIndex++; } - - // Arnout: fix rendercommandlist - // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493 - FixRenderCommandList( i+1 ); - - newShader->sortedIndex = i+1; - tr.sortedShaders[i+1] = newShader; + tr.sortedShaders[0] = tmp; + tmp->sortedIndex = 0; } @@ -2017,8 +2248,21 @@ static shader_t *GeneratePermanentShader( void ) { Com_Memcpy( newShader->stages[i]->bundle[b].texMods, stages[i].bundle[b].texMods, size ); } } + if( shader.optimalStageIteratorFunc == RB_StageIteratorGLSL ) { + newShader->stages[0]->stateBits &= ~GLS_ATEST_BITS; + } - SortNewShader(); + R_SortShaders(); + + // prepare occlusion queries for shaders that write to depth buffer + if ( qglGenQueriesARB && + !r_depthPass->integer && + !newShader->isDepth && + newShader->stages[0] && + !(newShader->stages[0]->stateBits & GLS_COLORMASK_FALSE) ) { + qglGenQueriesARB( 1, &newShader->QueryID ); + newShader->QueryResult = 0; + } hash = generateHashValue(newShader->name, FILE_HASH_SIZE); newShader->next = hashTable[hash]; @@ -2027,32 +2271,2152 @@ static shader_t *GeneratePermanentShader( void ) { return newShader; } +static void GetBufferUniforms( GLuint program, const GLchar *blockName, + int binding, GLuint *buffer, GLsizei *size, + int numUniforms, + const GLchar **uniformNames, + BufUniform_t **uniforms ) { + GLuint blockIndex; + + blockIndex = qglGetUniformBlockIndex( program, blockName ); + if( blockIndex != GL_INVALID_INDEX ) { + if( !*buffer ) { + GLuint uniformIndices[16]; + GLint uniformParams[16]; + int i; + + if( numUniforms > 16 ) + ri.Error( ERR_DROP, "too many uniforms (%d)", numUniforms ); + + qglGetActiveUniformBlockiv( program, blockIndex, + GL_UNIFORM_BLOCK_DATA_SIZE, + size ); + + // create UBO for dlights + qglGenBuffersARB( 1, buffer ); + GL_UBO( *buffer ); + qglBufferDataARB( GL_UNIFORM_BUFFER, *size, + NULL, GL_DYNAMIC_DRAW_ARB ); + GL_UBO( 0 ); + + qglBindBufferBase( GL_UNIFORM_BUFFER, binding, *buffer ); + + // get the offset and stride of all uniforms + qglGetUniformIndices( program, numUniforms, + uniformNames, uniformIndices ); + qglGetActiveUniformsiv( program, numUniforms, + uniformIndices, + GL_UNIFORM_OFFSET, + uniformParams ); + for( i = 0; i < numUniforms; i++ ) + uniforms[i]->offset = uniformParams[i]; + + qglGetActiveUniformsiv( program, numUniforms, + uniformIndices, + GL_UNIFORM_ARRAY_STRIDE, + uniformParams ); + for( i = 0; i < numUniforms; i++ ) + uniforms[i]->stride = uniformParams[i]; + } + + qglUniformBlockBinding( program, blockIndex, binding ); + } +} + /* ================= -VertexLightingCollapse +CollapseGLSL -If vertex lighting is enabled, only render a single -pass, trying to guess which is the correct one to best aproximate -what it is supposed to look like. +Try to compile a GLSL vertex and fragment shader that +computes the same effect as a multipass fixed function shader. ================= */ -static void VertexLightingCollapse( void ) { - int stage; - shaderStage_t *bestStage; - int bestImageRank; - int rank; +static unsigned short GLSLversion = 0x0000; +static char GLSLTexNames[MAX_SHADER_STAGES][6]; +static char *GLSLfnGetLight = "" + "float fresnel(const float f0, const float x) {\n" + " float y = 1.0 - x;\n" + " float y2 = y * y;\n" + " return mix(f0, 1.0, y2 * y2 * y);\n" + "}\n" + "\n" + "void getLight(const vec4 material, const vec3 pos,\n" + " const vec3 normal, const vec3 eye,\n" + " out vec3 diffuse, out vec4 specular) {\n" + " const float inv_pi = 0.318309886;\n" + " float exponent = pow(10000.0, material.x);\n" + " float normalReflectance = material.y * 0.05 + 0.017;\n" + " float NdotE = dot(normal, eye);\n" + " vec3 diffuseLight = directedLight * material.z;\n" + " vec4 specularLight = vec4(directedLight - diffuseLight, 1.0);\n" + " \n" + " vec3 L = normalize(lightDir);\n" + " float NdotL = max(dot(normal, L), 0.0);\n" + " vec3 H = normalize(eye + L);\n" + " float G = (0.039 * exponent + 0.085)/* / max(NdotL, NdotE)*/;\n" + " \n" + " diffuse = ambientLight + directedLight * (1.0 - normalReflectance) * inv_pi * NdotL;\n" + " float HdotE = max(dot(H, eye), 0.0);\n" + " float HdotN = max(dot(H, normal), 0.0);\n" + " float specFactor = G * fresnel(normalReflectance, HdotE) * NdotL;\n" + " float specPart = max(specFactor * pow(HdotN, exponent), 0.0);\n" + " diffuse += diffuseLight * specPart;\n" + " specular = specularLight * specPart;\n" + "#ifdef SHADER_DLIGHTS\n" + " int node = 0, light = 0;\n" + " while( node < dlNum ) {\n" + " vec4 sphere = dlSpheres[node];\n" + " L = sphere.xyz - pos;\n" + " if( length(L) <= sphere.w ) {\n" + " if( dlLinks[node] == 1 ) {\n" + " float attenuation = 0.125 * sphere.w / length(L);\n" + " vec3 lightColor = dlColors[light] * (attenuation * attenuation);\n" + " L = normalize(L);\n" + " NdotL = max(dot(normal, L), 0.0);\n" + " H = normalize(eye + L);\n" + " G = (0.039 * exponent + 0.085)/* / max(NdotL, NdotE)*/;\n" + " \n" + " diffuse += lightColor * (1.0 - normalReflectance) * inv_pi * NdotL;\n" + " HdotE = max(dot(H, eye), 0.0);\n" + " HdotN = max(dot(H, normal), 0.0);\n" + " specFactor = G * fresnel(normalReflectance, HdotE) * NdotL;\n" + " specPart = max(specFactor * pow(HdotN, exponent), 0.0);\n" + " diffuseLight = lightColor * material.z;\n" + " specularLight = lightColor - diffuseLight;\n" + " diffuse += diffuseLight * specPart;\n" + " specular += specularLight * specPart;\n" + " light++;\n" + " }\n" + " node++;\n" + " } else {\n" + " light += dlLinks[node];\n" + " node += max(2 * dlLinks[node] - 1, 1);\n" + " }\n" + " }\n" + "#endif\n" + "}\n\n"; +static char *GLSLfnGenSin = + "float genFuncSin(in float x) {\n" + " return sin(6.283185308 * x);\n" + "}\n\n"; +static char *GLSLfnGenSquare = + "float genFuncSquare(in float x) {\n" + " return sign(fract(x) - 0.5);\n" + "}\n\n"; +static char *GLSLfnGenTriangle = + "float genFuncTriangle(in float x) {\n" + " return 4.0 * abs(fract(x - 0.25) - 0.5) - 1.0;\n" + "}\n\n"; +static char *GLSLfnGenSawtooth = + "float genFuncSawtooth(in float x) {\n" + " return fract(x);\n" + "}\n\n"; +static char *GLSLfnGenInverseSawtooth = + "float genFuncInverseSawtooth(in float x) {\n" + " return 1.0 - fract(x);\n" + "}\n\n"; +static const char *VSHeader, *GSHeader, *FSHeader; + +static int CollapseGLSL( void ) { + enum VSFeatures { + vsfShaderTime = 0x00000001, + vsfNormal = 0x00000002, + vsfColor = 0x00000004, + vsfEntColor = 0x00000008, + vsfTexCoord = 0x00000010, + vsfTexCoord2 = 0x00000020, + vsfCameraPos = 0x00000040, + vsfEntLight = 0x00000080, + vsfLightDir = 0x00000100, + vsfFogNum = 0x00000200, + vsfGenSin = 0x00001000, + vsfGenSquare = 0x00002000, + vsfGenTri = 0x00004000, + vsfGenSaw = 0x00008000, + vsfGenInvSaw = 0x00010000, + vsfGenNoise = 0x00020000 + } vsFeatures = 0; + enum FSFeatures { + fsfShaderTime = 0x00000001, + fsfVertex = 0x00000002, + fsfReflView = 0x00000004, + fsfLightDir = 0x00000008, + fsfCameraPos = 0x00000010, + fsfNormal = 0x00000020, + fsfTangents = 0x00000040, + fsfDiffuse = 0x00000080, + fsfSpecular = 0x00000100, + fsfGenSin = 0x00001000, + fsfGenSquare = 0x00002000, + fsfGenTri = 0x00004000, + fsfGenSaw = 0x00008000, + fsfGenInvSaw = 0x00010000, + fsfGenNoise = 0x00020000, + fsfGenRotate = 0x00040000, + fsfGetLight = 0x00080000 + } fsFeatures = 0; + unsigned int attributes = (1 << AL_VERTEX) | + (1 << AL_TRANSX) | (1 << AL_TRANSY) | (1 << AL_TRANSZ); + + const char *VS[1000]; + const char *GS[1000]; + const char *FS[1000]; + byte constantColor[MAX_SHADER_STAGES][4]; + int texIndex[MAX_SHADER_STAGES]; + char shaderConsts[100][20]; + int VSidx = 0; + int GSidx = 0; + int FSidx = 0; + int constidx = 0; + int i, j; + int lightmapStage = -1; + int normalStage = -1; + int materialStage = -1; + int srcBlend, dstBlend; + alphaGen_t aGen; + qboolean showDepth = qfalse; + qboolean MultIsZero, AddIsZero, MultIsOnePlus; + int aTestStart = -1; + + // helper macros to build the Vertex and Fragment Shaders +#define VSText(text) VS[VSidx++] = text +#define VSConst(format, value) VS[VSidx++] = shaderConsts[constidx]; Com_sprintf( shaderConsts[constidx++], sizeof(shaderConsts[0]), format, value) + +#define GSText(text) GS[GSidx++] = text +#define GSConst(format, value) GS[GSidx++] = shaderConsts[constidx]; Com_sprintf( shaderConsts[constidx++], sizeof(shaderConsts[0]), format, value) + +#define FSText(text) FS[FSidx++] = text +#define FSConst(format, value) FS[FSidx++] = shaderConsts[constidx]; Com_sprintf( shaderConsts[constidx++], sizeof(shaderConsts[0]), format, value) +#define FSGenFunc(wave) FSText("("); \ + FSConst("%f", wave.base); \ + FSText(" + "); \ + FSConst("%f", wave.amplitude); \ + switch( wave.func ) { \ + case GF_NONE: \ + return qfalse; \ + case GF_SIN: \ + FSText(" * genFuncSin("); \ + break; \ + case GF_SQUARE: \ + FSText(" * genFuncSquare("); \ + break; \ + case GF_TRIANGLE: \ + FSText(" * genFuncTriangle("); \ + break; \ + case GF_SAWTOOTH: \ + FSText(" * genFuncSawtooth("); \ + break; \ + case GF_INVERSE_SAWTOOTH: \ + FSText(" * genFuncInverseSawtooth("); \ + break; \ + case GF_NOISE: \ + FSText(" * genFuncNoise("); \ + break; \ + } \ + FSConst("%f", wave.phase); \ + FSText(" + "); \ + FSConst("%f", wave.frequency); \ + FSText("* vShadertime))"); + + if( !(qglCreateShader && stages[0].active) || shader.isSky ) { + // single stage can be rendered without GLSL + return qfalse; + } - // if we aren't opaque, just use the first pass - if ( shader.sort == SS_OPAQUE ) { + if( shader.lightmapIndex == LIGHTMAP_MD3 && !qglGenBuffersARB ) { + // frame interpolation requires VBOs + return qfalse; + } - // pick the best texture for the single pass - bestStage = &stages[0]; - bestImageRank = -999999; + if( !GLSLversion ) { + const char *GLSLString = (const char *)glGetString( GL_SHADING_LANGUAGE_VERSION_ARB ); + int major, minor; - for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) { - shaderStage_t *pStage = &stages[stage]; + sscanf( GLSLString, "%d.%d", &major, &minor ); + GLSLversion = (unsigned short)(major << 8 | minor); + + for( i = 0; i < MAX_SHADER_STAGES; i++ ) { + Com_sprintf( GLSLTexNames[i], sizeof(GLSLTexNames[i]), + "tex%02d", i ); + } - if ( !pStage->active ) { + if ( GLSLversion >= 0x0132 ) { + // GLSL 1.50 (OpenGL 3.2) supported + VSHeader = + "#version 150 compatibility\n" + "\n" + "#define IN(decl) in decl\n" + "#define OUT(qual, decl) qual out decl\n" + "#define tex2D(s, tc) texture(s, tc)\n" + "#define texFetch(s, tc) texelFetch(s, tc)\n" + "\n"; + GSHeader = + "#version 150 compatibility\n" + "\n" + "#define IN(qual, decl) qual in decl\n" + "#define OUT(qual, decl) qual out decl\n" + "\n"; + FSHeader = + "#version 150 compatibility\n" + "\n" + "#define IN(qual, decl) qual in decl\n" + "#define OUT(decl) out decl\n" + "#define tex2D(s, tc) texture(s, tc)\n" + "#define tex2DBias(s, tc, bias) texture(s, tc, bias)\n" + "#define tex2DLod(s, tc, lod) textureLod(s, tc, lod)\n" + "#define tex3D(s, tc) texture(s, tc)\n" + "\n"; + } else { + VSHeader = + "#version 110\n" + "\n" + "#define IN(decl) attribute decl\n" + "#define OUT(qual, decl) varying decl\n" + "#define tex2D(s, tc) texture2D(s, tc)\n" + "#define texFetch(s, tc) texelFetchBuffer(s, tc)\n" + "\n"; + GSHeader = + "#version 110\n" + "#extension GL_EXT_geometry_shader4 : enable\n" + "#extension GL_EXT_gpu_shader4 : enable\n" + "\n" + "#define IN(qual, decl) varying in decl\n" + "#define OUT(qual, decl) varying out decl\n" + "\n"; + FSHeader = + "#version 110\n" + "\n" + "#define IN(qual, decl) varying decl\n" + "#define OUT(decl) varying out decl\n" + "#define tex2D(s, tc) texture2D(s, tc)\n" + "#define tex2DBias(s, tc, bias) texture2D(s, tc, bias)\n" + "#define tex2DLod(s, tc, lod) texture2DLod(s, tc, lod)\n" + "#define tex3D(s, tc) texture3D(s, tc)\n" + "\n"; + } + } + + // debug option + if( r_depthPass->integer >= 2 && shader.lightmapIndex != LIGHTMAP_2D ) { + if( stages[0].stateBits & GLS_COLORMASK_FALSE ) { + stages[0].stateBits &= ~GLS_COLORMASK_FALSE; + vsFeatures |= vsfCameraPos; + fsFeatures |= fsfVertex | fsfCameraPos; + showDepth = qtrue; + } else { + stages[0].stateBits |= GLS_COLORMASK_FALSE; + } + } + + srcBlend = stages[0].stateBits & GLS_SRCBLEND_BITS; + dstBlend = stages[0].stateBits & GLS_DSTBLEND_BITS; +#if 1 + // hack shader for tremulous medistation + if( srcBlend == GLS_SRCBLEND_ONE_MINUS_DST_COLOR && + dstBlend == GLS_DSTBLEND_ONE) { + stages[0].stateBits &= ~GLS_SRCBLEND_BITS; + stages[0].stateBits |= GLS_SRCBLEND_ONE; + srcBlend = GLS_SRCBLEND_ONE; + } + if( srcBlend == GLS_SRCBLEND_ONE && + dstBlend == GLS_DSTBLEND_ONE_MINUS_SRC_COLOR) { + stages[0].stateBits &= ~GLS_DSTBLEND_BITS; + stages[0].stateBits |= GLS_DSTBLEND_ONE; + dstBlend = GLS_DSTBLEND_ONE; + } +#endif + + // *** compute required features *** + + // deforms + for( i = 0; i < shader.numDeforms; i++ ) { + switch( shader.deforms[i].deformation ) { + case DEFORM_NONE: + break; + case DEFORM_WAVE: + vsFeatures |= vsfNormal; + // fall through + case DEFORM_MOVE: + switch( shader.deforms[i].deformationWave.func ) { + case GF_NONE: + return qfalse; + case GF_SIN: + vsFeatures |= vsfShaderTime | vsfGenSin; + break; + case GF_SQUARE: + vsFeatures |= vsfShaderTime | vsfGenSquare; + break; + case GF_TRIANGLE: + vsFeatures |= vsfShaderTime | vsfGenTri; + break; + case GF_SAWTOOTH: + vsFeatures |= vsfShaderTime | vsfGenSaw; + break; + case GF_INVERSE_SAWTOOTH: + vsFeatures |= vsfShaderTime | vsfGenInvSaw; + break; + case GF_NOISE: + vsFeatures |= vsfShaderTime | vsfGenNoise; + break; + } + break; + case DEFORM_NORMALS: + vsFeatures |= vsfShaderTime; + break; + case DEFORM_BULGE: + vsFeatures |= vsfTexCoord | vsfNormal | vsfShaderTime; + break; + default: + return qfalse; + } + } + // textures + for( i = 0; i < MAX_SHADER_STAGES; i++ ) { + shaderStage_t *pStage = &stages[i]; + + if( !pStage->active ) { + break; + } + + if( pStage->bundle[0].isLightmap ) { + lightmapStage = i; + } + + srcBlend = pStage->stateBits & GLS_SRCBLEND_BITS; + dstBlend = pStage->stateBits & GLS_DSTBLEND_BITS; + + // this is called before CollapseMultitexture, + // so each stages has at most one bundle + switch( pStage->bundle[0].tcGen ) { + case TCGEN_IDENTITY: + break; + case TCGEN_LIGHTMAP: + vsFeatures |= vsfTexCoord; + break; + case TCGEN_TEXTURE: + vsFeatures |= vsfTexCoord; + break; + case TCGEN_ENVIRONMENT_MAPPED: + vsFeatures |= vsfNormal | vsfCameraPos; + fsFeatures |= fsfNormal | fsfReflView | fsfCameraPos; + break; + case TCGEN_FOG: + return qfalse; + case TCGEN_VECTOR: + fsFeatures |= fsfVertex; + break; + default: + return qfalse; + } + + for( j = 0; j < pStage->bundle[0].numTexMods; j++ ) { + texModInfo_t *pTexMod = &(pStage->bundle[0].texMods[j]); + + switch( pTexMod->type ) { + case TMOD_NONE: + break; + case TMOD_TRANSFORM: + break; + case TMOD_TURBULENT: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfVertex; + break; + case TMOD_SCROLL: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime; + break; + case TMOD_SCALE: + break; + case TMOD_STRETCH: + switch( pTexMod->wave.func ) { + case GF_NONE: + return qfalse; + case GF_SIN: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenSin; + break; + case GF_SQUARE: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenSquare; + break; + case GF_TRIANGLE: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenTri; + break; + case GF_SAWTOOTH: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenSaw; + break; + case GF_INVERSE_SAWTOOTH: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenInvSaw; + break; + case GF_NOISE: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenNoise; + break; + } + break; + case TMOD_ROTATE: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenRotate; + break; + case TMOD_ENTITY_TRANSLATE: + vsFeatures |= vsfTexCoord2; + break; + default: + return qfalse; + } + } + if( pStage->bundle[0].combinedImage ) { + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime; + } + + switch( pStage->rgbGen ) { + case CGEN_IDENTITY_LIGHTING: + constantColor[i][0] = + constantColor[i][1] = + constantColor[i][2] = tr.identityLightByte; + aGen = AGEN_IDENTITY; + break; + case CGEN_IDENTITY: + constantColor[i][0] = + constantColor[i][1] = + constantColor[i][2] = 255; + aGen = AGEN_IDENTITY; + break; + case CGEN_ENTITY: + aGen = AGEN_ENTITY; + vsFeatures |= vsfEntColor; + break; + case CGEN_ONE_MINUS_ENTITY: + aGen = AGEN_ONE_MINUS_ENTITY; + vsFeatures |= vsfEntColor; + break; + case CGEN_EXACT_VERTEX: + aGen = AGEN_VERTEX; + vsFeatures |= vsfColor; + break; + case CGEN_VERTEX: + aGen = AGEN_VERTEX; + vsFeatures |= vsfColor; + break; + case CGEN_ONE_MINUS_VERTEX: + aGen = AGEN_ONE_MINUS_VERTEX; + vsFeatures |= vsfColor; + break; + case CGEN_WAVEFORM: + switch( pStage->rgbWave.func ) { + case GF_NONE: + return qfalse; + case GF_SIN: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenSin; + break; + case GF_SQUARE: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenSquare; + break; + case GF_TRIANGLE: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenTri; + break; + case GF_SAWTOOTH: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenSaw; + break; + case GF_INVERSE_SAWTOOTH: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenInvSaw; + break; + case GF_NOISE: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenNoise; + break; + }; + aGen = AGEN_IDENTITY; + break; + case CGEN_LIGHTING_DIFFUSE: + aGen = AGEN_IDENTITY; + vsFeatures |= vsfEntLight | vsfLightDir | vsfNormal; + if( r_perPixelLighting->integer ) { + vsFeatures |= vsfCameraPos; + fsFeatures |= fsfDiffuse | fsfLightDir | fsfNormal | fsfCameraPos | fsfGetLight; + } else { + fsFeatures |= fsfDiffuse; + } + break; + case CGEN_FOG: + aGen = AGEN_IDENTITY; + return qfalse; + case CGEN_CONST: + constantColor[i][0] = pStage->constantColor[0]; + constantColor[i][1] = pStage->constantColor[1]; + constantColor[i][2] = pStage->constantColor[2]; + aGen = AGEN_IDENTITY; + break; + default: + return qfalse; + } + + if ( pStage->alphaGen == AGEN_SKIP ) + pStage->alphaGen = aGen; + + switch( pStage->alphaGen ) { + case AGEN_IDENTITY: + constantColor[i][3] = 255; + break; + case AGEN_ENTITY: + vsFeatures |= vsfColor; + break; + case AGEN_ONE_MINUS_ENTITY: + vsFeatures |= vsfColor; + break; + case AGEN_VERTEX: + vsFeatures |= vsfColor; + break; + case AGEN_ONE_MINUS_VERTEX: + vsFeatures |= vsfColor; + break; + case AGEN_LIGHTING_SPECULAR: + vsFeatures |= vsfNormal | vsfLightDir | vsfCameraPos; + if( r_perPixelLighting->integer ) + fsFeatures |= fsfNormal | fsfCameraPos | fsfLightDir | fsfGetLight; + else + fsFeatures |= fsfReflView | fsfSpecular; + break; + case AGEN_WAVEFORM: + switch( pStage->alphaWave.func ) { + case GF_NONE: + return qfalse; + case GF_SIN: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenSin; + break; + case GF_SQUARE: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenSquare; + break; + case GF_TRIANGLE: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenTri; + break; + case GF_SAWTOOTH: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenSaw; + break; + case GF_INVERSE_SAWTOOTH: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenInvSaw; + break; + case GF_NOISE: + vsFeatures |= vsfShaderTime; + fsFeatures |= fsfShaderTime | fsfGenNoise; + break; + }; + break; + case AGEN_PORTAL: + vsFeatures |= vsfCameraPos | vsfNormal; + fsFeatures |= fsfCameraPos; + break; + case AGEN_CONST: + constantColor[i][3] = pStage->constantColor[3]; + break; + default: + + return qfalse; + } + } + + if( r_perPixelLighting->integer && + shader.lightmapIndex != LIGHTMAP_2D && + qglUniformBlockBinding ) { + vsFeatures |= vsfFogNum | vsfCameraPos; + fsFeatures |= fsfVertex | fsfCameraPos; + } + + if( stages[0].bundle[0].isLightmap ) { + j = 1; + } else { + j = 0; + } + if( r_perPixelLighting->integer && + shader.lightmapIndex != LIGHTMAP_2D && + stages[j].active && + stages[j].bundle[0].tcGen == TCGEN_TEXTURE && + stages[j].bundle[0].numTexMods == 0 && + stages[j].bundle[0].image[0] && + *stages[j].bundle[0].image[0]->imgName > '/' ) { + vsFeatures |= vsfNormal | vsfLightDir | vsfCameraPos; + fsFeatures |= fsfNormal | fsfVertex | fsfCameraPos | fsfLightDir | fsfGetLight; + + // check if a normal/bump map exists + if( i < MAX_SHADER_STAGES ) { + char name[MAX_QPATH]; + COM_StripExtension( stages[j].bundle[0].image[0]->imgName, name, sizeof(name) ); + strcat( name, "_nm" ); + stages[i].bundle[0].image[0] = R_FindHeightMapFile( name, qtrue, GL_REPEAT ); + if( stages[i].bundle[0].image[0] ) { + normalStage = i++; + stages[normalStage].active = qtrue; + stages[normalStage].bundle[0].tcGen = TCGEN_TEXTURE; + fsFeatures |= fsfTangents; + } + } + // check if a material map exists + if( i < MAX_SHADER_STAGES ) { + char name[MAX_QPATH]; + COM_StripExtension( stages[j].bundle[0].image[0]->imgName, name, sizeof(name) ); + strcat( name, "_mat" ); + stages[i].bundle[0].image[0] = R_FindImageFile( name, qtrue, qtrue, GL_REPEAT ); + if( stages[i].bundle[0].image[0] ) { + materialStage = i++; + stages[materialStage].active = qtrue; + stages[materialStage].bundle[0].tcGen = TCGEN_TEXTURE; + } + } + } + + // *** assemble shader fragments *** + // version pragma + if ( GLSLversion < 0x010a ) { + return qfalse; + } + //VSText("/*");VSText(shader.name);VSText("*/\n"); + VSText( VSHeader ); + if( (fsFeatures & fsfTangents) && + GLSLversion <= 0x0132 ) { + if ( qglProgramParameteriEXT ) { + // compute tangets in geometry shader for higher precision + VSText("#extension GL_EXT_geometry_shader4 : enable\n" + "#extension GL_EXT_gpu_shader4 : enable\n"); + GSText( GSHeader ); + FSText("#extension GL_EXT_geometry_shader4 : enable\n" + "#extension GL_EXT_gpu_shader4 : enable\n"); + } else { + VSText("#extension GL_EXT_gpu_shader4 : enable\n"); + FSText("#extension GL_EXT_gpu_shader4 : enable\n"); + } + } + FSText( FSHeader ); + + if( r_perPixelLighting->integer && + shader.lightmapIndex != LIGHTMAP_2D && + qglUniformBlockBinding ) { + VSText("#extension GL_ARB_uniform_buffer_object : enable\n"); + FSText("#extension GL_ARB_uniform_buffer_object : enable\n" + "\n" + "#define SHADER_DLIGHTS\n"); + } + + FSText("const vec3 constants = vec3( 0.0, 1.0, "); + FSConst("%f", tr.identityLight ); + FSText(" );\n\n"); + + // VS inputs + VSText("// IN(vec4 aVertex);\n" + "#define aVertex gl_Vertex\n"); + if( vsFeatures & vsfTexCoord ) { + VSText("// IN(vec4 aTexCoord);\n" + "#define aTexCoord gl_MultiTexCoord0\n"); + } + if( vsFeatures & (vsfTexCoord2 | vsfFogNum) ) { + VSText("// IN(vec4 aTexCoord2);\n" + "#define aTexCoord2 gl_MultiTexCoord1\n"); + } + if( vsFeatures & vsfColor ) { + VSText("// IN(vec4 aColor);\n" + "#define aColor gl_Color\n"); + } + if( vsFeatures & vsfEntColor ) { + VSText("IN(vec4 aEntColor);\n"); + } + if( shader.lightmapIndex == LIGHTMAP_MD3 || + (vsFeatures & vsfShaderTime) ) { + VSText("IN(vec4 aTimes);\n"); + } + + if( shader.lightmapIndex != LIGHTMAP_MD3 ) { + if( vsFeatures & vsfNormal ) { + VSText("// IN(vec3 aNormal);\n" + "#define aNormal gl_Normal\n"); + } + } + VSText("IN(vec4 aTransX);\n" + "IN(vec4 aTransY);\n" + "IN(vec4 aTransZ);\n"); + if( vsFeatures & vsfEntLight ) { + VSText("IN(vec3 aAmbientLight);\n"); + VSText("IN(vec3 aDirectedLight);\n"); + } + if( vsFeatures & vsfLightDir ) { + VSText("IN(vec4 aLightDir);\n"); + } + if( vsFeatures & vsfCameraPos ) { + VSText("IN(vec3 aCameraPos);\n"); + } + + // VS outputs / FS inputs +#define addVarying( qual, type, name ) \ + VSText("OUT(" #qual ", " #type " v" #name ");\n"); \ + if( GSidx ) { \ + GSText("IN(" #qual ", " #type " v" #name "[]);\n"); \ + GSText("OUT(" #qual ", " #type " g" #name ");\n"); \ + FSText("#define v" #name " g" #name "\n"); \ + } \ + FSText("IN(" #qual ", " #type " v" #name ");\n"); + + if( vsFeatures & vsfTexCoord ) { + addVarying( smooth, vec4, TexCoord ); + } + if( vsFeatures & vsfTexCoord2 ) { + addVarying( flat, vec2, EntTexCoord ); + } + if( vsFeatures & vsfColor ) { + addVarying( smooth, vec4, Color ); + } + if( vsFeatures & vsfEntColor ) { + addVarying( flat, vec4, EntColor ); + } + if( fsFeatures & fsfVertex ) { + addVarying( smooth, vec3, Vertex ); + } + if( fsFeatures & fsfNormal ) { + addVarying( smooth, vec3, Normal ); + } + if( fsFeatures & fsfLightDir ) { + addVarying( flat, vec4, LightDir ); + } + if( r_perPixelLighting->integer ) { + if( fsFeatures & fsfDiffuse ) { + // per pixel diffuse light + addVarying( flat, vec3, AmbientLight ); + addVarying( flat, vec3, DirectedLight ); + } + } else { + if( fsFeatures & fsfDiffuse ) { + // interpolated diffuse + addVarying( smooth, vec3, Diffuse ); + } + if( fsFeatures & fsfSpecular ) { + // interpolated specular + addVarying( smooth, float, Specular ); + } + } + if( fsFeatures & fsfReflView ) { + if( r_perPixelLighting->integer ) { + // calculated in fragment shader + } else { + // interpolated + addVarying( smooth, vec3, ReflView ); + } + } + if( fsFeatures & fsfShaderTime ) { + addVarying( flat, float, Shadertime ); + } + if( fsFeatures & fsfCameraPos ) { + addVarying( smooth, vec3, CameraPos ); + } + if( vsFeatures & vsfFogNum ) { + addVarying( flat, float, FogNum ); + } + if( GSidx && (fsFeatures & fsfTangents) ) { + // pass tangent vectors only from GS to FS + GSText("OUT(smooth, vec3 gUTangent);\n" + "OUT(smooth, vec3 gVTangent);\n"); + FSText("#define vUTangent gUTangent\n" + "#define vVTangent gVTangent\n" + "IN(smooth, vec3 vUTangent);\n" + "IN(smooth, vec3 vVTangent);\n"); + } + + // uniforms + if( shader.lightmapIndex == LIGHTMAP_MD3 ) { + if( qglTexBufferEXT ) { + VSText("uniform samplerBuffer texData;\n"); + } else { + VSText("uniform sampler2D texData;\n"); + } + } + for( i = 0; i < MAX_SHADER_STAGES; i++ ) { + shaderStage_t *pStage = &stages[i]; + + if( !pStage->active ) + break; + + for( j = 0; j < i; j++ ) { + if( pStage->bundle[0].image[0] == stages[j].bundle[0].image[0] ) + break; + } + if( j < i ) { + texIndex[i] = j; + } else { + texIndex[i] = i; + FSText("uniform sampler2D "); + FSText(GLSLTexNames[i]); + FSText(";\n"); + } + } + if( normalStage >= 0 ) { + FSText("const int HMLevels = "); + FSConst("%d", stages[normalStage].bundle[0].image[0]->maxMipLevel); + FSText(";\n" + "const float HMSize = "); + FSConst("%f", (float)stages[normalStage].bundle[0].image[0]->uploadWidth); + FSText(";\n" + "\n"); + } + if( r_perPixelLighting->integer && + shader.lightmapIndex != LIGHTMAP_2D && + qglUniformBlockBinding ) { + FSText("layout( shared ) uniform dLights {\n" + " vec4 dlSpheres[128+127];\n" // center, radius + " vec3 dlColors[128];\n" + " int dlLinks[128+127];\n" + " int dlNum;\n" + " vec2 dlDebug;\n" + "};\n" + "\n" + "layout( shared ) uniform fogs {\n" + " vec3 lightGridScale;\n" + " vec3 lightGridOffset;\n" + " vec4 fogColors[256];\n" + " vec4 fogPlanes[256];\n" + "};\n" + "\n" + "uniform sampler3D texLightGrid;\n" + "\n"); + } + + // other global variables + if( (vsFeatures & vsfLightDir) && !r_perPixelLighting->integer ) { + VSText("vec3 lightDir;\n" + "#define ambientLight aAmbientLight\n" + "#define directedLight aDirectedLight\n"); + } + if( fsFeatures & fsfGetLight ) { + FSText("vec3 lightDir;\n" + "vec3 ambientLight;\n" + "vec3 directedLight;\n"); + } + + // functions + VSText("\n" + "vec3 transform3(vec3 vector) {\n" + " return vec3( dot( aTransX.xyz, vector ),\n" + " dot( aTransY.xyz, vector ),\n" + " dot( aTransZ.xyz, vector ) );\n" + "}\n" + "vec3 transform4(vec4 point) {\n" + " return vec3( dot( aTransX, point ),\n" + " dot( aTransY, point ),\n" + " dot( aTransZ, point ) );\n" + "}\n"); + + if( fsFeatures & fsfGetLight ) { + FSText( GLSLfnGetLight ); + } + + if( vsFeatures & vsfGenSin ) { + VSText( GLSLfnGenSin ); + } + if( fsFeatures & fsfGenSin ) { + FSText( GLSLfnGenSin ); + } + if( vsFeatures & vsfGenSquare ) { + VSText( GLSLfnGenSquare ); + } + if( fsFeatures & fsfGenSquare ) { + FSText( GLSLfnGenSquare ); + } + if( vsFeatures & vsfGenTri ) { + VSText( GLSLfnGenTriangle ); + } + if( fsFeatures & fsfGenTri ) { + FSText( GLSLfnGenTriangle ); + } + if( vsFeatures & vsfGenSaw ) { + VSText( GLSLfnGenSawtooth ); + } + if( fsFeatures & fsfGenSaw ) { + FSText( GLSLfnGenSawtooth ); + } + if( vsFeatures & vsfGenInvSaw ) { + VSText( GLSLfnGenInverseSawtooth ); + } + if( fsFeatures & fsfGenInvSaw ) { + FSText( GLSLfnGenInverseSawtooth ); + } + if( vsFeatures & vsfGenNoise ) { + VSText("float genFuncNoise(in float x) {\n" + " //return noise1(x);\n" + " vec2 xi = floor(vec2(x, x + 1.0));\n" + " vec2 xf = x - xi;\n" + " vec2 grad = 4.0 * fract((xi * 34.0 + 1.0) * xi / 289.0) - 2.0;\n" + " grad *= xf;\n" + " return mix(grad.x, grad.y, xf.x*xf.x*xf.x*(xf.x*(xf.x*6.0-15.0)+10.0));\n" + "}\n\n"); + } + if( fsFeatures & fsfGenNoise ) { + FSText("float genFuncNoise(in float x) {\n" + " //return noise1(x);\n" + " vec2 xi = floor(vec2(x, x + 1.0));\n" + " vec2 xf = x - xi;\n" + " vec2 grad = 4.0 * fract((xi * 34.0 + 1.0) * xi / 289.0) - 2.0;\n" + " grad *= xf;\n" + " return mix(grad.x, grad.y, xf.x*xf.x*xf.x*(xf.x*(xf.x*6.0-15.0)+10.0));\n" + "}\n\n"); + } + if( fsFeatures & fsfGenRotate ) { + FSText("mat2 genFuncRotate(in float x) {\n" + " vec2 sincos = sin(6.283185308 / 360.0 * vec2(x, x + 90.0));\n" + " return mat2(sincos.y, -sincos.x, sincos.x, sincos.y);\n" + "}\n\n"); + } + if( normalStage >= 0 && r_parallax->integer ) { + if( !glGlobals.gpuShader4 ) { + // linear stepping + FSText("vec3 intersectHeightMap(const vec3 start,\n" + " const vec3 dir) {\n" + " vec3 tracePos = start;\n" + " float diff0, diff1 = 1.0;\n" + " float steps = 64.0; //HMSize * length(dir.xy);\n" + " vec3 step = dir / steps;\n" + " while( diff1 > 0.0 ) {\n" + " diff0 = diff1;\n" + " diff1 = tracePos.z - tex2DBias("); + FSText(GLSLTexNames[texIndex[normalStage]]); + FSText(", tracePos.xy, -99.0).a;\n" + " tracePos += step;\n" + " }\n" + " return tracePos + (diff1 / (diff0 - diff1) - 1.0) * step;\n" + "}\n" + "\n"); + } else { + FSText("vec3 intersectHeightMap(const vec3 start,\n" + " const vec3 dir) {\n" + " float lod = log2(max(length(dFdx(start.xy)),\n" + " length(dFdy(start.xy))));\n" + " vec3 tracePos = start;\n" + " float diff0, diff1 = 1.0;\n" + " float steps = 64.0;\n" + " vec3 step = dir / steps;\n" + " while( diff1 > 0.0 ) {\n" + " diff0 = diff1;\n" + " diff1 = tracePos.z - tex2DLod("); + FSText(GLSLTexNames[texIndex[normalStage]]); + FSText(", tracePos.xy, lod).a;\n" + " tracePos += step;\n" + " }\n" + " return tracePos + (diff1 / (diff0 - diff1) - 1.0) * step;\n" + "}\n" + "\n"); + } + } + if( shader.lightmapIndex == LIGHTMAP_MD3 ) { + if( qglTexBufferEXT && ( vsFeatures & vsfNormal ) ) { + VSText("vec4 fetchVertex(const float frameNo, const vec2 offset,\n" + " out vec4 normal) {\n" + " int tc = int(floor(frameNo + offset));\n" + " vec4 data = texFetch(texData, tc);\n" + " vec4 lo = fract(data);\n" + " vec4 hi = floor(data);\n" + " normal = vec4((hi.xyz - 128.0) / 127.0, 0.0);\n" + " return vec4(lo.xyz * 1024.0 - 512.0,\n" + " 1.0);\n" + "}\n\n"); + } else if( qglTexBufferEXT ) { + VSText("vec4 fetchVertex(const float frameNo, const vec2 offset) {\n" + " int tc = int(floor(frameNo + offset));\n" + " vec4 data = texFetch(texData, tc);\n" + " vec4 lo = fract(data);\n" + " return vec4(lo.xyz * 1024.0 - 512.0,\n" + " 1.0);\n" + "}\n\n"); + } else if( vsFeatures & vsfNormal ) { + VSText("vec4 fetchVertex(const float frameNo, const vec2 offset,\n" + " out vec4 normal) {\n" + " vec2 tc = vec2(fract(frameNo), floor(frameNo)/1024.0) + offset;\n" + " vec4 data = tex2D(texData, tc);\n" + " vec4 lo = fract(data);\n" + " vec4 hi = floor(data);\n" + " normal = vec4((hi.xyz - 128.0) / 127.0, 0.0);\n" + " return vec4(lo.xyz * 1024.0 - 512.0,\n" + " 1.0);\n" + "}\n\n"); + } else { + VSText("vec4 fetchVertex(const float frameNo, const vec2 offset) {\n" + " vec2 tc = vec2(fract(frameNo), floor(frameNo)/1024.0) + offset;\n" + " vec4 data = tex2D(texData, tc);\n" + " vec4 lo = fract(data);\n" + " return vec4(lo.xyz * 1024.0 - 512.0,\n" + " 1.0);\n" + "}\n\n"); + } + } + + // main vertex shader + VSText("\n" + "void main() {\n" + " vec4 vertex;\n"); + if( vsFeatures & vsfNormal ) { + VSText(" vec4 normal;\n"); + } + if( shader.lightmapIndex == LIGHTMAP_MD3 ) { + // interpolate position and normal from two frames + if( vsFeatures & vsfNormal ) { + VSText(" vec4 normal1, normal2;\n" + " vertex = mix(fetchVertex(aTimes.z, aVertex.zw, normal1),\n" + " fetchVertex(aTimes.w, aVertex.zw, normal2),\n" + " aTimes.y);\n" + " normal = normalize(mix(normal1, normal2, aTimes.y));\n"); + } else { + VSText(" vertex = mix(fetchVertex(aTimes.z, aVertex.zw),\n" + " fetchVertex(aTimes.w, aVertex.zw),\n" + " aTimes.y);\n"); + } + if( vsFeatures & vsfTexCoord ) { + VSText(" vTexCoord = aVertex.xyxy;\n"); + } + if( vsFeatures & vsfTexCoord2 ) { + VSText(" vEntTexCoord = aTexCoord2.xy;\n"); + } + if( vsFeatures & vsfColor ) { + VSText(" vColor = aColor;\n"); + } + if( vsFeatures & vsfEntColor ) { + VSText(" vEntColor = aEntColor;\n"); + } + } else { + VSText(" \n" + " vertex = vec4(aVertex.xyz, 1.0);\n"); + if( vsFeatures & vsfNormal ) { + VSText(" normal = vec4(aNormal, 0.0);\n"); + } + if( vsFeatures & vsfTexCoord ) { + VSText(" vTexCoord = aTexCoord;\n"); + } + if( vsFeatures & vsfTexCoord2 ) { + VSText(" vEntTexCoord = aTexCoord2.xy;\n"); + } + if( vsFeatures & vsfColor ) { + VSText(" vColor = aColor;\n"); + } + if( vsFeatures & vsfEntColor ) { + VSText(" vEntColor = aEntColor;\n"); + } + } + if( fsFeatures & fsfShaderTime ) { + VSText(" \n" + " vShadertime = aTimes.x;\n"); + } + if( vsFeatures & vsfFogNum ) { + if( shader.lightmapIndex == LIGHTMAP_MD3 ) + VSText(" vFogNum = aTexCoord2.z;\n"); + else + VSText(" vFogNum = aVertex.w + aTexCoord2.z;\n"); + } + + // apply deforms + for( i = 0; i < shader.numDeforms; i++ ) { + switch ( shader.deforms[i].deformation ) { + case DEFORM_NONE: + break; + case DEFORM_WAVE: + VSText(" \n" + " vertex += ("); + VSConst("%f", shader.deforms[i].deformationWave.base); + VSText(" + "); + VSConst("%f", shader.deforms[i].deformationWave.amplitude); + switch( shader.deforms[i].deformationWave.func ) { + case GF_NONE: + return qfalse; + case GF_SIN: + VSText(" * genFuncSin("); + break; + case GF_SQUARE: + VSText(" * genFuncSquare("); + break; + case GF_TRIANGLE: + VSText(" * genFuncTriangle("); + break; + case GF_SAWTOOTH: + VSText(" * genFuncSawtooth("); + break; + case GF_INVERSE_SAWTOOTH: + VSText(" * genFuncInverseSawtooth("); + break; + case GF_NOISE: + VSText(" * genFuncNoise("); + break; + } + VSConst("%f", shader.deforms[i].deformationWave.phase); + VSText(" + dot(vertex.xyz, vec3("); + VSConst("%f", shader.deforms[i].deformationSpread); + VSText(")) + "); + VSConst("%f", shader.deforms[i].deformationWave.frequency); + VSText(" * aTimes.x)) * normal;\n"); + break; + case DEFORM_NORMALS: + VSText(" \n" + " normal.xyz = normalize(normal.xyz + 0.98*noise3(vec4(vertex.xyz, aTimes.x * "); + VSConst("%f", shader.deforms[i].deformationWave.frequency); + VSText(")));\n"); + break; + case DEFORM_BULGE: + VSText(" \n" + " vertex += ("); + VSConst("%f", shader.deforms[i].bulgeHeight); + VSText(" * sin(aTexCoord.x * "); + VSConst("%f", shader.deforms[i].bulgeWidth); + VSText(" + aTimes.x * "); + VSConst("%f", shader.deforms[i].bulgeSpeed * 0.001f); + VSText(")) * normal;\n"); + break; + case DEFORM_MOVE: + VSText(" \n" + " vertex.xyz += ("); + VSConst("%f", shader.deforms[i].deformationWave.base); + VSText(" + "); + VSConst("%f", shader.deforms[i].deformationWave.amplitude); + switch( shader.deforms[i].deformationWave.func ) { + case GF_NONE: + return qfalse; + case GF_SIN: + VSText(" * genFuncSin("); + break; + case GF_SQUARE: + VSText(" * genFuncSquare("); + break; + case GF_TRIANGLE: + VSText(" * genFuncTriangle("); + break; + case GF_SAWTOOTH: + VSText(" * genFuncSawtooth("); + break; + case GF_INVERSE_SAWTOOTH: + VSText(" * genFuncInverseSawtooth("); + break; + case GF_NOISE: + VSText(" * genFuncNoise("); + break; + } + VSConst("%f", shader.deforms[i].deformationWave.phase); + VSText(" + "); + VSConst("%f", shader.deforms[i].deformationWave.frequency); + VSText(" * aTimes.x)) * vec3("); + VSConst("%f", shader.deforms[i].moveVector[0]); + VSText(", "); + VSConst("%f", shader.deforms[i].moveVector[1]); + VSText(", "); + VSConst("%f", shader.deforms[i].moveVector[2]); + VSText(");\n"); + break; + default: + return qfalse; + } + } + VSText(" vertex = vec4(transform4(vertex), 1.0);\n"); + if( vsFeatures & vsfNormal ) { + VSText(" normal = vec4( transform3( normal.xyz ), 0.0 );\n"); + } + if( fsFeatures & fsfVertex ) { + VSText(" vVertex = vertex.xyz;\n"); + } + if( fsFeatures & fsfNormal ) { + VSText(" \n" + " vNormal = normal.xyz;\n"); + } + if( fsFeatures & fsfCameraPos ) { + VSText(" vCameraPos = vertex.xyz - aCameraPos.xyz;\n"); + } + if( fsFeatures & fsfLightDir ) { + VSText(" vec3 lightDir = normalize(transform3(aLightDir.xyz));\n"); + if( r_perPixelLighting->integer ) { + VSText(" vLightDir = vec4(lightDir, aLightDir.w);\n" ); + } + } + + if( fsFeatures & fsfDiffuse ) { + if( r_perPixelLighting->integer ) { + VSText(" \n" + " vAmbientLight = aAmbientLight;\n" + " vDirectedLight = aDirectedLight;\n"); + } else { + VSText(" \n" + " float diffuse = max(0.0, dot(normal.xyz, lightDir.xyz));\n" + " vDiffuse = clamp(aAmbientLight + diffuse * aDirectedLight, 0.0, 1.0);\n"); + } + } + if( fsFeatures & fsfReflView ) { + if( r_perPixelLighting->integer ) { + } else { + VSText(" \n" + " vReflView = reflect(vertex.xyz - aCameraPos, normalize(normal.xyz));\n"); + } + } + if( fsFeatures & fsfSpecular ) { + if( r_perPixelLighting->integer ) { + } else { + VSText(" vSpecular = max(0.0, 4.0 * dot(lightDir.xyz, normalize(vReflView)) - 3.0);\n" + " vSpecular *= vSpecular; vSpecular *= vSpecular; vSpecular *= vSpecular;\n"); + } + } + VSText(" gl_Position = gl_ModelViewProjectionMatrix * vertex;\n" + "}\n"); + + // main geometry shader + if( GSidx ) { + GSText("\n" + "void main() {\n" + " vec3 uTangent, vTangent;\n" + " vec3 dpx = vVertex[2] - vVertex[0];\n" + " vec3 dpy = vVertex[1] - vVertex[0];\n" + " vec2 dtx = vTexCoord[2].xy - vTexCoord[0].xy;\n" + " vec2 dty = vTexCoord[1].xy - vTexCoord[0].xy;\n" + " float scale = sign(dty.y*dtx.x - dtx.y*dty.x);\n" + " vec3 normal = cross( dpx, dpy );\n" + " uTangent = dpx * dty.y - dpy * dtx.y;\n" + " vTangent = -dpx * dty.x + dpy * dtx.x;\n" + " uTangent -= normal * dot( uTangent, normal );\n" + " vTangent -= normal * dot( vTangent, normal );\n" + " uTangent = normalize( scale*uTangent );\n" + " vTangent = normalize( scale*vTangent );\n" + " int i;\n" + " for( i = 0; i < 3; i++ ) {\n" + " gTexCoord = vTexCoord[i];\n"); + if( vsFeatures & vsfTexCoord2 ) { + GSText(" gEntTexCoord = vEntTexCoord[i];\n"); + } + if( vsFeatures & vsfColor ) { + GSText(" gColor = vColor[i];\n"); + } + if( vsFeatures & vsfEntColor ) { + GSText(" gEntColor = vEntColor[i];\n"); + } + if( fsFeatures & fsfVertex ) { + GSText(" gVertex = vVertex[i];\n"); + } + if( fsFeatures & fsfNormal ) { + GSText(" gNormal = vNormal[i];\n"); + } + if( fsFeatures & fsfCameraPos ) { + GSText(" gCameraPos = vCameraPos[i];\n"); + } + if( fsFeatures & fsfLightDir ) { + if( r_perPixelLighting->integer ) { + GSText(" gLightDir = vLightDir[i];\n" ); + } + } + if( fsFeatures & fsfDiffuse ) { + if( r_perPixelLighting->integer ) { + GSText(" gAmbientLight = vAmbientLight[i];\n" + " gDirectedLight = vDirectedLight[i];\n"); + } else { + GSText(" gDiffuse = vDiffuse[i];\n"); + } + } + if( fsFeatures & fsfReflView ) { + if( r_perPixelLighting->integer ) { + } else { + GSText(" gReflView = vReflView[i];\n"); + } + } + if( fsFeatures & fsfSpecular ) { + if( r_perPixelLighting->integer ) { + } else { + GSText(" gSpecular = vSpecular[i];\n"); + } + } + if( fsFeatures & fsfShaderTime ) { + GSText(" gShadertime = vShadertime[i];\n"); + } + if( vsFeatures & vsfFogNum ) { + GSText(" gFogNum = vFogNum[i];\n"); + } + GSText(" gUTangent = uTangent;\n" + " gVTangent = vTangent;\n" + " gl_Position = gl_PositionIn[i];\n" + " EmitVertex();\n" + " }\n" + " EndPrimitive();\n" + "}\n"); + } + + // main fragment shader + if( qglBindFragDataLocationIndexed ) { + FSText("OUT(vec3 dstColorMult);\n" + "OUT(vec3 dstColorAdd);\n" + "\n" + "void main() {\n" + " vec4 srcColor = constants.xxxx;\n" + " vec3 tmpColor;\n" + " vec2 tc;\n" + " vec4 genColor;\n" + " dstColorMult = constants.yyy;\n" + " dstColorAdd = constants.xxx;\n"); + } else { + FSText("void main() {\n" + " vec4 srcColor = constants.xxxx;\n" + " vec3 tmpColor;\n" + " vec2 tc;\n" + " vec4 genColor;\n" + " vec3 dstColorMult = constants.yyy;\n" + " vec3 dstColorAdd = constants.xxx;\n"); + } + MultIsZero = qfalse; + AddIsZero = qtrue; + if( vsFeatures & vsfTexCoord ) { + FSText(" vec2 baseTC = vTexCoord.st;\n"); + } + if( fsFeatures & fsfVertex ) { + FSText(" vec3 vertex = vVertex.xyz;\n"); + } else { + FSText(" vec3 vertex = vec3(0.0);\n"); + } + + if( fsFeatures & fsfNormal ) { + FSText(" vec3 normal = normalize(vNormal);\n"); + } + if( fsFeatures & fsfTangents ) { + if( !GSidx ) { + // compute from derivates, may be unprecise + FSText(" vec3 dpx = dFdx(vVertex);\n" + " vec3 dpy = dFdy(vVertex);\n" + " vec2 dtx = dFdx(vTexCoord.xy);\n" + " vec2 dty = dFdy(vTexCoord.xy);\n" + " float scale = sign(dty.y*dtx.x - dtx.y*dty.x);\n" + " vec3 uTangent = dpx * dty.y - dpy * dtx.y;\n" + " vec3 vTangent = -dpx * dty.x + dpy * dtx.x;\n" + " uTangent -= normal * dot( uTangent, normal );\n" + " vTangent -= normal * dot( vTangent, normal );\n" + " uTangent = normalize( scale*uTangent );\n" + " vTangent = normalize( scale*vTangent );\n" + // calculate non-interpolated normal for testing + // " normal = scale * cross( uTangent, vTangent );\n" + ); + } else { + // computed in geometry shader + FSText(" vec3 uTangent = normalize( vUTangent );\n" + " vec3 vTangent = normalize( vVTangent );\n"); + } + } + if( normalStage >= 0 ) { + // parallax calculation + if( r_parallax->integer ) { + // implementation is in intersectHeightMap function + + // z coord is upscaled, change to make parallax effect stronger + FSText(" vec3 traceVec = vec3(dot(vCameraPos, uTangent),\n" + " dot(vCameraPos, vTangent),\n" + " dot(vCameraPos, normal) * 8.0);\n" + " traceVec /= -traceVec.z;\n" + " vec3 tracePos = intersectHeightMap(vec3(baseTC, 1.0), traceVec);\n" + " baseTC = tracePos.xy;\n"); + } else { + // no parallax + FSText(" vec3 tracePos = vec3(baseTC, 1.0);\n"); + } + } + + // normal mapping + if( fsFeatures & fsfNormal ) { + if( normalStage >= 0 ) { + FSText(" vec3 n = tex2D("); + FSText(GLSLTexNames[texIndex[normalStage]]); + FSText(", baseTC).xyz * 2.0 - 1.0;\n" + " normal = normalize(n.x * uTangent + n.y * vTangent + n.z * normal);\n"); + } + } + + // compute light + if( fsFeatures & fsfGetLight ) { + // getLight requires ambient and directed lights + if( r_perPixelLighting->integer && + shader.lightmapIndex != LIGHTMAP_2D && + qglUniformBlockBinding ) { + FSText(" if( lightGridScale.x > 0.0 ) {\n" + " vec3 vert = vertex.xyz * lightGridScale - lightGridOffset;\n" + " vec4 lg1 = tex3D(texLightGrid, vert);\n" + " vec4 lg2 = tex3D(texLightGrid, vec3(vert.xy, 1.0 - vert.z));\n" + " ambientLight = lg1.rgb;\n" + " directedLight = lg2.rgb;\n" + " lightDir.x = 2.0 * lg1.a - 1.0;\n" + " lightDir.y = 2.0 * lg2.a - 1.0;\n" + " lightDir.z = 1.0 - abs(lightDir.x) - abs(lightDir.y);\n" + " if( lightDir.z < 0.0 ) {\n" + " lightDir.xy = sign(lightDir.xy) - lightDir.xy;\n" + " }\n" + " lightDir = normalize(lightDir);\n" + " } else {\n" + " lightDir = vNormal;\n" + " ambientLight = constants.xxx;\n" + " directedLight = constants.xxx;\n" + " }\n"); + } else { + FSText(" lightDir = vNormal;\n" + " ambientLight = constants.xxx;\n" + " directedLight = constants.xxx;\n"); + } + + if( tr.hasDeluxemaps && lightmapStage >= 0 ) { + FSText(" lightDir = normalize(tex2D("); + FSText(GLSLTexNames[texIndex[lightmapStage]]); + FSText(", vec2(vTexCoord.p + vLightDir.w, vTexCoord.q)).xyz * 2.0 - 1.0);\n"); + } else if( fsFeatures & fsfLightDir ) { + FSText(" lightDir = normalize(vLightDir.xyz);\n"); + } + if( lightmapStage >= 0 ) { + FSText(" ambientLight = tex2D("); + FSText(GLSLTexNames[texIndex[lightmapStage]]); + FSText(", vTexCoord.pq).xyz;\n"); + } else if ( fsFeatures & fsfDiffuse ) { + FSText(" ambientLight = vAmbientLight.xyz;\n" + " directedLight = vDirectedLight.xyz;\n"); + } else if ( vsFeatures & vsfColor ){ + FSText(" ambientLight = vec3(vColor.xyz);\n"); + } + + // get material properties + if( materialStage >= 0 ) { + FSText(" vec4 material = tex2D("); + FSText(GLSLTexNames[texIndex[materialStage]]); + FSText(", baseTC);\n" + " float VdotN = max(0.0, dot(normalize(vCameraPos), normal));\n" + " float opacity = fresnel(material.w, VdotN);\n"); + } else { + FSText(" vec4 material = vec4(0.5);\n" + " float opacity = 0.0;\n"); + } + FSText(" vec3 diffuse;\n" + " vec4 specular;\n" + " getLight(material, vertex.xyz, normal, normalize(-vCameraPos), diffuse, specular);\n"); + } + if( fsFeatures & fsfReflView ) { + if( r_perPixelLighting->integer ) { + FSText(" vec3 reflView = normalize(reflect(vCameraPos, normal));\n"); + } else { + FSText(" vec3 reflView = normalize(vReflView);\n"); + } + } + if( fsFeatures & fsfSpecular ) { + if( fsFeatures & fsfGetLight ) { + } else { + FSText(" vec4 specular = vec4(vSpecular);\n"); + } + } + + for( i = 0; i < MAX_SHADER_STAGES; i++ ) { + shaderStage_t *pStage = &stages[i]; + qboolean lightBlend = qfalse; + + if( !pStage->active || i == normalStage || i == materialStage ) + break; + + srcBlend = pStage->stateBits & GLS_SRCBLEND_BITS; + dstBlend = pStage->stateBits & GLS_DSTBLEND_BITS; + + // continue alpha test ? + if( aTestStart >= 0 && !((pStage->stateBits & GLS_DEPTHFUNC_BITS) == GLS_DEPTHFUNC_EQUAL && aTestStart == 0 ) ) { + FSText(" }\n"); + aTestStart = -1; + } + + if( pStage->bundle[0].image[0] != tr.whiteImage && + pStage->bundle[0].image[0] != tr.identityLightImage ) { + switch( pStage->bundle[0].tcGen ) { + case TCGEN_IDENTITY: + FSText(" tc = constants.xx;\n"); + break; + case TCGEN_LIGHTMAP: + if( pStage->bundle[0].isLightmap ) + FSText(" tc = vTexCoord.pq;\n"); + else { + FSText(" tc = vTexCoord.pq * vec2("); + FSConst("%f", (float)tr.lightmapWidth); + FSText(", "); + FSConst("%f", (float)tr.lightmapHeight); + FSText(");\n"); + } + break; + case TCGEN_TEXTURE: + FSText(" tc = baseTC;\n"); + break; + case TCGEN_ENVIRONMENT_MAPPED: + FSText(" tc = vec2(0.5) + 0.5 * normalize(reflView).yz;\n"); + break; + case TCGEN_FOG: + return qfalse; + case TCGEN_VECTOR: + FSText(" tc = vec2(dot(vVertex, vec3("); + FSConst("%f", pStage->bundle[0].tcGenVectors[0][0]); + FSText(", "); + FSConst("%f", pStage->bundle[0].tcGenVectors[0][1]); + FSText(", "); + FSConst("%f", pStage->bundle[0].tcGenVectors[0][2]); + FSText(")),\n"); + FSText(" dot(vVertex, vec3("); + FSConst("%f", pStage->bundle[0].tcGenVectors[1][0]); + FSText(", "); + FSConst("%f", pStage->bundle[0].tcGenVectors[1][1]); + FSText(", "); + FSConst("%f", pStage->bundle[0].tcGenVectors[1][2]); + FSText(")));\n"); + break; + default: + return qfalse; + } + for( j = 0; j < pStage->bundle[0].numTexMods; j++ ) { + texModInfo_t *pTexMod = &(pStage->bundle[0].texMods[j]); + + switch( pTexMod->type ) { + case TMOD_NONE: + break; + case TMOD_TRANSFORM: + FSText(" tc = tc.s * vec2("); + FSConst("%f", pTexMod->matrix[0][0]); + FSText(", "); + FSConst("%f", pTexMod->matrix[0][1]); + FSText(") + tc.t * vec2("); + FSConst("%f", pTexMod->matrix[1][0]); + FSText(", "); + FSConst("%f", pTexMod->matrix[1][1]); + FSText(") + vec2("); + FSConst("%f", pTexMod->translate[0]); + FSText(", "); + FSConst("%f", pTexMod->translate[1]); + FSText(");\n"); + break; + case TMOD_TURBULENT: + FSText(" tc += "); + FSConst("%f", pTexMod->wave.amplitude); + FSText(" * sin(6.283185308 * (0.000976563 * vVertex.xy + vec2("); + FSConst("%f", pTexMod->wave.phase); + FSText(" + "); + FSConst("%f", pTexMod->wave.frequency); + FSText("* vShadertime)));\n"); + break; + case TMOD_SCROLL: + FSText(" tc += vShadertime * vec2("); + FSConst("%f", pTexMod->scroll[0]); + FSText(", "); + FSConst("%f", pTexMod->scroll[1]); + FSText(");\n"); + break; + case TMOD_SCALE: + FSText(" tc *= vec2("); + FSConst("%f", pTexMod->scale[0]); + FSText(", "); + FSConst("%f", pTexMod->scale[1]); + FSText(");\n"); + break; + case TMOD_STRETCH: + FSText(" tc = vec2(0.5) + (tc - 0.5) / "); + FSGenFunc(pTexMod->wave); + FSText(";\n"); + break; + case TMOD_ROTATE: + FSText(" tc = vec2(0.5) + genFuncRotate("); + FSConst("%f", pTexMod->rotateSpeed); + FSText(" * vShadertime) * (tc - vec2(0.5));\n"); + break; + case TMOD_ENTITY_TRANSLATE: + FSText(" tc += vShadertime * vEntTexCoord;\n"); + break; + default: + return qfalse; + } + } + // adjust for combined image + if ( pStage->bundle[0].combinedImage ) { + float xScale = (float)pStage->bundle[0].image[0]->uploadWidth / + (float)pStage->bundle[0].combinedImage->uploadWidth; + FSText(" tc.x = (tc.x + mod(floor(vShadertime * "); + FSConst("%f", pStage->bundle[0].imageAnimationSpeed); + FSText("), "); + FSConst("%f", (float)pStage->bundle[0].numImageAnimations); + FSText(")) * "); + FSConst("%f", xScale); + FSText(";\n"); + } + } + + if( i == lightmapStage && (fsFeatures & fsfGetLight) ) { + // use custom blend for lightmap + lightBlend = qtrue; + FSText(" srcColor = vec4(1.0);\n"); + } else { + switch( pStage->rgbGen ) { + case CGEN_IDENTITY_LIGHTING: + case CGEN_IDENTITY: + case CGEN_CONST: + FSText(" genColor = vec4("); + FSConst( "%f", constantColor[i][0] / 255.0 ); + FSText(", "); + FSConst( "%f", constantColor[i][1] / 255.0 ); + FSText(", "); + FSConst( "%f", constantColor[i][2] / 255.0 ); + FSText(", "); + switch( pStage->alphaGen ) { + case AGEN_IDENTITY: + case AGEN_CONST: + FSConst("%f", constantColor[i][3] / 255.0); + break; + case AGEN_ENTITY: + FSText("vEntColor.a"); + break; + case AGEN_ONE_MINUS_ENTITY: + FSText("constants.y - vEntColor.a"); + break; + default: + FSText("constants.x"); // will be overwritten later + break; + } + FSText(");\n"); + break; + case CGEN_ENTITY: + FSText(" genColor = vEntColor;\n"); + break; + case CGEN_ONE_MINUS_ENTITY: + FSText(" genColor = constants.yyyy - vEntColor;\n"); + break; + case CGEN_EXACT_VERTEX: + FSText(" genColor = vColor;\n"); + break; + case CGEN_VERTEX: + FSText(" genColor = vColor * constants.zzzy;\n"); + break; + case CGEN_ONE_MINUS_VERTEX: + FSText(" genColor = constants.zzzy - vColor * constants.zzzy;\n"); + break; + case CGEN_WAVEFORM: + FSText(" genColor = vec4(clamp("); + FSGenFunc(pStage->rgbWave); + FSText(", 0.0, 1.0));\n"); + break; + case CGEN_LIGHTING_DIFFUSE: + if( fsFeatures & fsfGetLight ) { + lightBlend = qtrue; + FSText(" genColor.rgb = constants.yyy;\n"); + } else { + FSText(" genColor.rgb = vDiffuse;\n"); + } + break; + case CGEN_FOG: + return qfalse; + default: + return qfalse; + } + + switch( pStage->alphaGen ) { + case AGEN_IDENTITY: + case AGEN_CONST: + if( pStage->rgbGen != CGEN_IDENTITY && + pStage->rgbGen != CGEN_IDENTITY_LIGHTING && + pStage->rgbGen != CGEN_CONST ) { + FSText(" genColor.a = "); + FSConst("%f", constantColor[i][3] / 255.0 ); + FSText(";\n"); + } + break; + case AGEN_ENTITY: + if( pStage->rgbGen != CGEN_ENTITY && + pStage->rgbGen != CGEN_CONST ) + FSText(" genColor.a = vEntColor.a;\n"); + break; + case AGEN_ONE_MINUS_ENTITY: + if( pStage->rgbGen != CGEN_ONE_MINUS_ENTITY && + pStage->rgbGen != CGEN_CONST ) + FSText(" genColor.a = constants.y - vEntColor.a;\n"); + break; + case AGEN_VERTEX: + if( pStage->rgbGen != CGEN_VERTEX && + pStage->rgbGen != CGEN_EXACT_VERTEX ) + FSText(" genColor.a = vColor.a;\n"); + break; + case AGEN_ONE_MINUS_VERTEX: + if( pStage->rgbGen != CGEN_ONE_MINUS_VERTEX ) + FSText(" genColor.a = constants.y - vColor.a;\n"); + break; + case AGEN_LIGHTING_SPECULAR: + FSText(" genColor.a = specular.a;\n"); + break; + case AGEN_WAVEFORM: + FSText(" genColor.a = clamp("); + FSGenFunc(pStage->alphaWave); + FSText(", 0.0, 1.0);\n"); + break; + case AGEN_PORTAL: + FSText(" genColor.a = clamp("); + FSConst("%f", 1.0/shader.portalRange); + FSText(" * length(vCameraPos), 0.0, 1.0);\n"); + break; + default: + return qfalse; + } + + if( pStage->bundle[0].image[0] == tr.whiteImage ) { + FSText(" srcColor = genColor;\n"); + } else if( pStage->bundle[0].image[0] == tr.identityLightImage ) { + FSText(" srcColor = constants.zzzy * genColor;\n"); + } else { + FSText(" srcColor = tex2D("); + FSText(GLSLTexNames[texIndex[i]]); + FSText(", tc) * genColor;\n"); + } + } + + // alpha test + switch( pStage->stateBits & GLS_ATEST_BITS ) { + case 0: + break; + case GLS_ATEST_GT_0: + FSText(" if( srcColor.a > 0.0 ) {\n"); + if( aTestStart < 0) aTestStart = i; + break; + case GLS_ATEST_LT_80: + FSText(" if( srcColor.a < 0.5 ) {\n"); + if( aTestStart < 0) aTestStart = i; + break; + case GLS_ATEST_GE_80: + FSText(" if( srcColor.a >= 0.5 ) {\n"); + if( aTestStart < 0) aTestStart = i; + break; + } + + // blend + MultIsOnePlus = qfalse; + switch( dstBlend ) { + case 0: + case GLS_DSTBLEND_ZERO: + FSText(" tmpColor = constants.xxx;\n"); + break; + case GLS_DSTBLEND_ONE: + FSText(" tmpColor = constants.yyy;\n"); + break; + case GLS_DSTBLEND_SRC_COLOR: + FSText(" tmpColor = srcColor.rgb;\n"); + break; + case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR: + FSText(" tmpColor = 1.0 - srcColor.rgb;\n"); + break; + case GLS_DSTBLEND_SRC_ALPHA: + FSText(" tmpColor = srcColor.aaa;\n"); + break; + case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA: + FSText(" tmpColor = 1.0 - srcColor.aaa;\n"); + break; + } + switch( srcBlend ) { + case GLS_SRCBLEND_ZERO: + FSText(" dstColorMult *= tmpColor;\n" + " dstColorAdd *= tmpColor;\n"); + MultIsZero = MultIsZero || dstBlend == GLS_DSTBLEND_ZERO; + break; + case 0: + case GLS_SRCBLEND_ONE: + FSText(" dstColorMult *= tmpColor;\n" + " dstColorAdd *= tmpColor;\n" + " dstColorAdd += srcColor.rgb;\n"); + MultIsZero = MultIsZero || dstBlend == GLS_DSTBLEND_ZERO; + AddIsZero = qfalse; + break; + case GLS_SRCBLEND_DST_COLOR: + MultIsOnePlus = (dstBlend == GLS_DSTBLEND_ONE); + FSText(" tmpColor += srcColor.rgb;\n" + " dstColorMult *= tmpColor;\n" + " dstColorAdd *= tmpColor;\n"); + break; + case GLS_SRCBLEND_ONE_MINUS_DST_COLOR: + FSText(" tmpColor -= srcColor.rgb;\n" + " dstColorMult *= tmpColor;\n" + " dstColorAdd *= tmpColor;\n" + " dstColorAdd += srcColor.rgb;\n"); + AddIsZero = qfalse; + break; + case GLS_SRCBLEND_SRC_ALPHA: + FSText(" dstColorMult *= tmpColor;\n" + " dstColorAdd *= tmpColor;\n" + " dstColorAdd += srcColor.rgb * srcColor.a;\n"); + MultIsZero = MultIsZero || dstBlend == GLS_DSTBLEND_ZERO; + AddIsZero = qfalse; + break; + case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA: + FSText(" dstColorMult *= tmpColor;\n" + " dstColorAdd *= tmpColor;\n" + " dstColorAdd += srcColor.rgb * (1.0 - srcColor.a);\n"); + MultIsZero = MultIsZero || dstBlend == GLS_DSTBLEND_ZERO; + AddIsZero = qfalse; + break; + } + if( lightBlend ) { + FSText(" dstColorMult = mix(dstColorMult, vec3(0.0), opacity);\n" + " dstColorAdd *= diffuse;\n" + " dstColorAdd += specular.rgb;\n"); + } + if ( (pStage->stateBits & GLS_ATEST_BITS) != 0 && + aTestStart < i ) { + FSText(" }\n"); + } + + } + if ( aTestStart >= 0 ) { + FSText(" }\n"); + } + if ( aTestStart == 0 ) { + FSText(" else\n" + " discard;\n"); + } + shader.numUnfoggedPasses = i; + if( normalStage >= 0 ) + shader.numUnfoggedPasses++; + if( materialStage >= 0 ) + shader.numUnfoggedPasses++; + + // add fog + if( vsFeatures & vsfFogNum ) { + FSText(" int fogNum = int(vFogNum + 0.5);\n" + " if( fogNum > 0 ) {\n" + " vec4 plane = fogPlanes[fogNum - 1];\n" + " vec4 fogCol = fogColors[fogNum - 1];\n" + " float fog = dot(plane.xyz, vVertex.xyz) - plane.w;\n" + " if( fog > -0.5 ) {\n" + " float eyeT = fog - dot(plane.xyz, vCameraPos);\n" + " if( eyeT < 0.0 )\n" + " fog = fog / (fog - eyeT);\n" + " else\n" + " fog = 1.0;\n" + " } else {\n" + " fog = 0.0;\n" + " }\n" + " fog *= clamp(length(vCameraPos) * fogCol.w, 0.0, 1.0);\n" + " dstColorAdd.xyz = mix(dstColorAdd.xyz, fogCol.xyz, fog);\n" + " }\n"); + } + + // shader debugging + if( qglUniformBlockBinding && + r_perPixelLighting->integer && + shader.lightmapIndex != LIGHTMAP_2D && + ( fsFeatures & fsfGetLight ) ) { + FSText(" if( dlDebug.x > 0.0 ) {\n" + " vec2 tileXY = floor(gl_FragCoord.xy * dlDebug);\n" + " int tile = int(tileXY.y) * 4 + int(tileXY.x);\n" + " if( tile == 0 ) {\n" + " dstColorMult = constants.xxx;\n" + " dstColorAdd = ambientLight.rgb;\n" + " } else if( tile == 4 ) {\n" + " dstColorMult = constants.xxx;\n" + " dstColorAdd = directedLight.rgb;\n" + " } else if( tile == 8 ) {\n" + " dstColorMult = constants.xxx;\n" + " dstColorAdd = 0.5 * lightDir.xyz + 0.5;\n" + " } else if( tile == 3 ) {\n" + " dstColorMult = constants.xxx;\n" + " dstColorAdd = diffuse.rgb;\n" + " } else if( tile == 7 ) {\n" + " dstColorMult = constants.xxx;\n" + " dstColorAdd = specular.rgb;\n" + " } else if( tile == 11 ) {\n" + " dstColorMult = constants.xxx;\n" + " dstColorAdd = 0.5 * normal.xyz + 0.5;\n" + " }\n" + " }\n"); + } + + if( MultIsOnePlus ) + FSText(" dstColorMult -= vec3(1.0);\n"); + + if( showDepth ) { + FSText(" gl_FragColor = vec4(vec3(0.001 * length(vCameraPos)), 0.0);\n"); + } else if( qglBindFragDataLocationIndexed ) { + // dstColorAdd and dstColorMult are already the output vars + } else if( AddIsZero ) { + FSText(" gl_FragColor = vec4(dstColorMult.xyz, 1.0);\n"); + } else if( MultIsZero ) { + FSText(" gl_FragColor = vec4(dstColorAdd.xyz, 1.0);\n"); + } else { + FSText(" gl_FragColor = vec4(dstColorAdd.xyz, dot(vec3(0.3333), dstColorMult.xyz));\n"); + } + FSText("}\n"); + + // collect attributes + if( shader.lightmapIndex != LIGHTMAP_MD3 && + (vsFeatures & vsfNormal) ) + attributes |= (1 << AL_NORMAL); + if( vsFeatures & vsfColor ) + attributes |= (1 << AL_COLOR); + if( vsFeatures & vsfEntColor ) + attributes |= (1 << AL_COLOR2); + if( vsFeatures & vsfTexCoord ) + attributes |= (1 << AL_TEXCOORD); + if( vsFeatures & vsfTexCoord2 ) + attributes |= (1 << AL_TEXCOORD2); + if( shader.lightmapIndex == LIGHTMAP_MD3 || + (vsFeatures & vsfShaderTime) ) + attributes |= (1 << AL_TIMES); + if( vsFeatures & vsfCameraPos ) { + attributes |= (1 << AL_CAMERAPOS); + } + if( vsFeatures & vsfLightDir ) + attributes |= (1 << AL_LIGHTDIR); + if( vsFeatures & vsfEntLight ) + attributes |= (1 << AL_AMBIENTLIGHT) | (1 << AL_DIRECTEDLIGHT); + + // *** compile and link *** + shader.GLSLprogram = RB_CompileProgram( shader.name, VS, VSidx, + GS, GSidx, + 3, GL_TRIANGLES, + GL_TRIANGLE_STRIP, + FS, FSidx, attributes ); + if ( !shader.GLSLprogram ) + return qfalse; + + // sampler uniforms are set to the TMU once at the start + GL_Program( shader.GLSLprogram ); + // try to move lightmap to TMU 1 to avoid rebinds + // always leave TMU 0 as it may be used for alpha test + if( lightmapStage > 1 ) { + textureBundle_t temp; + + j = qglGetUniformLocation( shader.GLSLprogram->handle, GLSLTexNames[0] ); + if( j != -1 ) { + qglUniform1i( j, 0 ); + } + + Com_Memcpy( &temp, &stages[1].bundle[0], sizeof(textureBundle_t) ); + Com_Memcpy( &stages[1].bundle[0], &stages[lightmapStage].bundle[0], sizeof(textureBundle_t) ); + Com_Memcpy( &stages[lightmapStage].bundle[0], &temp, sizeof(textureBundle_t) ); + j = qglGetUniformLocation( shader.GLSLprogram->handle, GLSLTexNames[lightmapStage] ); + if( j != -1 ) { + qglUniform1i( j, 1 ); + } + + for( i = 2; i < lightmapStage; i++ ) { + j = qglGetUniformLocation( shader.GLSLprogram->handle, GLSLTexNames[i] ); + if( j != -1 ) { + qglUniform1i( j, i ); + } + } + j = qglGetUniformLocation( shader.GLSLprogram->handle, GLSLTexNames[1] ); + if( j != -1 ) { + qglUniform1i( j, lightmapStage ); + } + for( i = lightmapStage + 1; i < shader.numUnfoggedPasses; i++ ) { + j = qglGetUniformLocation( shader.GLSLprogram->handle, GLSLTexNames[i] ); + if( j != -1 ) { + qglUniform1i( j, i ); + } + } + } else { + for( i = 0; i < shader.numUnfoggedPasses; i++ ) { + j = qglGetUniformLocation( shader.GLSLprogram->handle, GLSLTexNames[i] ); + if( j != -1 ) { + qglUniform1i( j, i ); + } + } + if( shader.lightmapIndex == LIGHTMAP_MD3 ) { + j = qglGetUniformLocation( shader.GLSLprogram->handle, "texData" ); + if( j != -1 ) { + qglUniform1i( j, shader.numUnfoggedPasses ); + } + } + } + j = qglGetUniformLocation( shader.GLSLprogram->handle, "texLightGrid" ); + if( j != -1 ) { + qglUniform1i( j, TMU_LIGHTGRID ); + } + + if( shader.lightmapIndex != LIGHTMAP_2D && qglUniformBlockBinding ) { + const GLchar *dlightNames[] = { + "dlSpheres", + "dlColors", + "dlLinks", + "dlNum", + "dlDebug" + }; + BufUniform_t *dlightUniforms[] = { + &backEnd.uDLSpheres, + &backEnd.uDLColors, + &backEnd.uDLLinks, + &backEnd.uDLNum, + &backEnd.uDLDebug + }; + const GLchar *fogNames[] = { + "lightGridScale", + "lightGridOffset", + "fogColors", + "fogPlanes" + }; + BufUniform_t *fogUniforms[] = { + &backEnd.uLightGridScale, + &backEnd.uLightGridOffset, + &backEnd.uFogColors, + &backEnd.uFogPlanes + }; + + GetBufferUniforms( shader.GLSLprogram->handle, "fogs", + 0, &backEnd.fogBuffer, + &backEnd.fogBufferSize, 4, + fogNames, fogUniforms ); + + GetBufferUniforms( shader.GLSLprogram->handle, "dLights", + 1, &backEnd.dlightBuffer, + &backEnd.dlightBufferSize, 5, + dlightNames, dlightUniforms ); + } + + stages[0].stateBits &= ~(GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS); + if( !MultIsZero ) { + if( qglBindFragDataLocationIndexed ) + stages[0].stateBits |= GLS_SRCBLEND_ONE + | GLS_DSTBLEND_SRC1_COLOR; + else if( AddIsZero ) + stages[0].stateBits |= GLS_SRCBLEND_DST_COLOR + | (MultIsOnePlus ? GLS_DSTBLEND_ONE : GLS_DSTBLEND_ZERO); + else + stages[0].stateBits |= GLS_SRCBLEND_ONE + | GLS_DSTBLEND_SRC_ALPHA; + } + shader.optimalStageIteratorFunc = RB_StageIteratorGLSL; + // mirror and portal shaders + if ( shader.sort <= SS_PORTAL ) + shader.anyGLAttr = GLA_FULL_dynamic; + else { + shader.anyGLAttr = GLA_COLOR_vtxcolor | GLA_TC1_texcoord | GLA_TC2_lmcoord; + shader.allGLAttr = GLA_COLOR_vtxcolor | GLA_TC1_texcoord | GLA_TC2_lmcoord; + } + + return qtrue; +} + +/* +================= +VertexLightingCollapse + +If vertex lighting is enabled, only render a single +pass, trying to guess which is the correct one to best aproximate +what it is supposed to look like. +================= +*/ +static void VertexLightingCollapse( void ) { + int stage; + shaderStage_t *bestStage; + int bestImageRank; + int rank; + + // if we aren't opaque, just use the first pass + if ( shader.sort == SS_OPAQUE ) { + + // pick the best texture for the single pass + bestStage = &stages[0]; + bestImageRank = -999999; + + for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) { + shaderStage_t *pStage = &stages[stage]; + + if ( !pStage->active ) { break; } rank = 0; @@ -2128,6 +4492,7 @@ static shader_t *FinishShader( void ) { int stage; qboolean hasLightmapStage; qboolean vertexLightmap; + shader_t *sh; hasLightmapStage = qfalse; vertexLightmap = qfalse; @@ -2142,7 +4507,7 @@ static shader_t *FinishShader( void ) { // // set polygon offset // - if ( shader.polygonOffset && !shader.sort ) { + if ( (stages[0].stateBits & GLS_POLYGON_OFFSET) && !shader.sort ) { shader.sort = SS_DECAL; } @@ -2156,7 +4521,7 @@ static shader_t *FinishShader( void ) { break; } - // check for a missing texture + // check for a missing texture if ( !pStage->bundle[0].image[0] ) { ri.Printf( PRINT_WARNING, "Shader %s has a stage with no image\n", shader.name ); pStage->active = qfalse; @@ -2255,6 +4620,10 @@ static shader_t *FinishShader( void ) { } } } + + if( shader.isSky ) { + pStage->stateBits |= GLS_DEPTHRANGE_1_TO_1; + } stage++; } @@ -2266,44 +4635,112 @@ static shader_t *FinishShader( void ) { } // - // if we are in r_vertexLight mode, never use a lightmap texture + // try to generate a GLSL shader if possible // - if ( stage > 1 && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) ) { - VertexLightingCollapse(); - stage = 1; - hasLightmapStage = qfalse; + if ( !CollapseGLSL() ) { + // + // if we are in r_vertexLight mode, never use a lightmap texture + // + if ( stage > 1 && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) ) { + VertexLightingCollapse(); + stage = 1; + hasLightmapStage = qfalse; + } + + // + // look for multitexture potential + // + if ( stage > 1 ) { + stage = CollapseMultitexture(); + } + + if ( shader.lightmapIndex >= 0 && !hasLightmapStage ) { + if (vertexLightmap) { + ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has VERTEX forced lightmap!\n", shader.name ); + } else { + ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has lightmap but no lightmap stage!\n", shader.name ); + shader.lightmapIndex = LIGHTMAP_NONE; + } + } + + + // + // compute number of passes + // + shader.numUnfoggedPasses = stage; + + // fogonly shaders don't have any normal passes + if (stage == 0 && !shader.isSky) + shader.sort = SS_FOG; + + // determine which stage iterator function is appropriate + ComputeStageIteratorFunc(); } - // - // look for multitexture potential - // - if ( stage > 1 && CollapseMultitexture() ) { - stage--; - } + shader.isDepth = qfalse; + sh = GeneratePermanentShader(); - if ( shader.lightmapIndex >= 0 && !hasLightmapStage ) { - if (vertexLightmap) { - ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has VERTEX forced lightmap!\n", shader.name ); + // generate depth-only shader if necessary + if( r_depthPass->integer && !shader.isSky ) { + if( (stages[0].stateBits & GLS_DEPTHMASK_TRUE) && + !(stages[0].stateBits & GLS_DEPTHFUNC_EQUAL) && + !(shader.lightmapIndex == LIGHTMAP_2D) ) { + // this shader may update depth + stages[1].active = qfalse; + strcat(shader.name, "*"); + + if( stages[0].stateBits & GLS_ATEST_BITS ) { + // alpha test requires a custom depth shader + shader.sort = SS_DEPTH; + shader.isDepth = qtrue; + stages[0].stateBits &= ~GLS_SRCBLEND_BITS & ~GLS_DSTBLEND_BITS; + stages[0].stateBits |= GLS_COLORMASK_FALSE; + + if( !CollapseGLSL() ) { + shader.numUnfoggedPasses = 1; + ComputeStageIteratorFunc(); + } + sh->depthShader = GeneratePermanentShader(); + } else if ( shader.lightmapIndex == LIGHTMAP_MD3 && + shader.cullType == 0 && + shader.numDeforms == 0 && + tr.defaultMD3Shader ) { + // can use the default MD3 depth shader + sh->depthShader = tr.defaultMD3Shader->depthShader; + } else if ( shader.lightmapIndex != LIGHTMAP_MD3 && + shader.cullType == 0 && + shader.numDeforms == 0 && + tr.defaultShader ) { + // can use the default depth shader + sh->depthShader = tr.defaultShader->depthShader; + } else { + // requires a custom depth shader, but can skip + // the texturing + shader.sort = SS_DEPTH; + stages[0].stateBits &= ~GLS_SRCBLEND_BITS & ~GLS_DSTBLEND_BITS; + stages[0].stateBits |= GLS_COLORMASK_FALSE; + stages[0].bundle[0].image[0] = tr.whiteImage; + stages[0].bundle[0].tcGen = TCGEN_IDENTITY; + stages[0].bundle[0].numTexMods = 0; + stages[0].rgbGen = CGEN_IDENTITY; + stages[0].alphaGen = AGEN_IDENTITY; + + if( !CollapseGLSL() ) { + shader.numUnfoggedPasses = 1; + ComputeStageIteratorFunc(); + } + shader.isDepth = qtrue; + sh->depthShader = GeneratePermanentShader(); + } + // disable depth writes in the main pass + sh->stages[0]->stateBits &= ~GLS_DEPTHMASK_TRUE; } else { - ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has lightmap but no lightmap stage!\n", shader.name ); - shader.lightmapIndex = LIGHTMAP_NONE; + sh->depthShader = NULL; } + } else { + sh->depthShader = NULL; } - - - // - // compute number of passes - // - shader.numUnfoggedPasses = stage; - - // fogonly shaders don't have any normal passes - if (stage == 0 && !shader.isSky) - shader.sort = SS_FOG; - - // determine which stage iterator function is appropriate - ComputeStageIteratorFunc(); - - return GeneratePermanentShader(); + return sh; } //======================================================================================== @@ -2328,18 +4765,14 @@ static char *FindShaderInShaderText( const char *shadername ) { hash = generateHashValue(shadername, MAX_SHADERTEXT_HASH); - if(shaderTextHashTable[hash]) - { - for (i = 0; shaderTextHashTable[hash][i]; i++) - { - p = shaderTextHashTable[hash][i]; - token = COM_ParseExt(&p, qtrue); - - if(!Q_stricmp(token, shadername)) - return p; + for (i = 0; shaderTextHashTable[hash][i]; i++) { + p = shaderTextHashTable[hash][i]; + token = COM_ParseExt(&p, qtrue); + if ( !Q_stricmp( token, shadername ) ) { + return p; } } - +#if 0 p = s_shaderText; if ( !p ) { @@ -2361,7 +4794,7 @@ static char *FindShaderInShaderText( const char *shadername ) { SkipBracedSection( &p ); } } - +#endif return NULL; } @@ -2441,7 +4874,7 @@ shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImag shader_t *sh; if ( name[0] == 0 ) { - return tr.defaultShader; + return lightmapIndex == LIGHTMAP_MD3 ? tr.defaultMD3Shader : tr.defaultShader; } // use (fullbright) vertex lighting if the bsp file doesn't have @@ -2488,12 +4921,6 @@ shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImag stages[i].bundle[0].texMods = texMods[i]; } - // FIXME: set these "need" values apropriately - shader.needsNormal = qtrue; - shader.needsST1 = qtrue; - shader.needsST2 = qtrue; - shader.needsColor = qtrue; - // // attempt to define shader from an explicit parameter file // @@ -2528,7 +4955,8 @@ shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImag // // create the default shading commands // - if ( shader.lightmapIndex == LIGHTMAP_NONE ) { + if ( shader.lightmapIndex == LIGHTMAP_NONE || + shader.lightmapIndex == LIGHTMAP_MD3 ) { // dynamic colors at vertexes stages[0].bundle[0].image[0] = image; stages[0].active = qtrue; @@ -2624,12 +5052,6 @@ qhandle_t RE_RegisterShaderFromImage(const char *name, int lightmapIndex, image_ stages[i].bundle[0].texMods = texMods[i]; } - // FIXME: set these "need" values apropriately - shader.needsNormal = qtrue; - shader.needsST1 = qtrue; - shader.needsST2 = qtrue; - shader.needsColor = qtrue; - // // create the default shading commands // @@ -2682,7 +5104,7 @@ qhandle_t RE_RegisterShaderFromImage(const char *name, int lightmapIndex, image_ } sh = FinishShader(); - return sh->index; + return sh->index; } @@ -2701,7 +5123,7 @@ qhandle_t RE_RegisterShaderLightMap( const char *name, int lightmapIndex ) { shader_t *sh; if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); + ri.Printf( PRINT_WARNING, "Shader name exceeds MAX_QPATH\n" ); return 0; } @@ -2735,7 +5157,7 @@ qhandle_t RE_RegisterShader( const char *name ) { shader_t *sh; if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); + ri.Printf( PRINT_WARNING, "Shader name exceeds MAX_QPATH\n" ); return 0; } @@ -2765,7 +5187,7 @@ qhandle_t RE_RegisterShaderNoMip( const char *name ) { shader_t *sh; if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); + ri.Printf( PRINT_WARNING, "Shader name exceeds MAX_QPATH\n" ); return 0; } @@ -2812,60 +5234,106 @@ A second parameter will cause it to print in sorted order =============== */ void R_ShaderList_f (void) { - int i; + int i, j, k; int count; shader_t *shader; + GLhandleARB handle; + char *source; ri.Printf (PRINT_ALL, "-----------------------\n"); - count = 0; - for ( i = 0 ; i < tr.numShaders ; i++ ) { - if ( ri.Cmd_Argc() > 1 ) { - shader = tr.sortedShaders[i]; - } else { - shader = tr.shaders[i]; - } - ri.Printf( PRINT_ALL, "%i ", shader->numUnfoggedPasses ); + if( ri.Cmd_Argc() > 1 && !strcmp( ri.Cmd_Argv(1), "glsl" ) ) { + for ( i = 0 ; i < tr.numGLSLshaders ; i++ ) { + if( !tr.GLSLshaders[i] ) + continue; - if (shader->lightmapIndex >= 0 ) { - ri.Printf (PRINT_ALL, "L "); - } else { - ri.Printf (PRINT_ALL, " "); - } - if ( shader->multitextureEnv == GL_ADD ) { - ri.Printf( PRINT_ALL, "MT(a) " ); - } else if ( shader->multitextureEnv == GL_MODULATE ) { - ri.Printf( PRINT_ALL, "MT(m) " ); - } else if ( shader->multitextureEnv == GL_DECAL ) { - ri.Printf( PRINT_ALL, "MT(d) " ); - } else { - ri.Printf( PRINT_ALL, " " ); - } - if ( shader->explicitlyDefined ) { - ri.Printf( PRINT_ALL, "E " ); - } else { - ri.Printf( PRINT_ALL, " " ); - } + handle = tr.GLSLshaders[i]->handle; - if ( shader->optimalStageIteratorFunc == RB_StageIteratorGeneric ) { - ri.Printf( PRINT_ALL, "gen " ); - } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorSky ) { - ri.Printf( PRINT_ALL, "sky " ); - } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorLightmappedMultitexture ) { - ri.Printf( PRINT_ALL, "lmmt" ); - } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorVertexLitTexture ) { - ri.Printf( PRINT_ALL, "vlt " ); - } else { - ri.Printf( PRINT_ALL, " " ); + if( !handle ) + continue; + + qglGetShaderiv( handle, GL_SHADER_TYPE, &k ); + switch( k ) { + case GL_VERTEX_SHADER_ARB: + ri.Printf( PRINT_ALL, "--- VS %d:\n", handle ); + break; + case GL_GEOMETRY_SHADER_EXT: + ri.Printf( PRINT_ALL, "--- GS %d:\n", handle ); + break; + case GL_FRAGMENT_SHADER_ARB: + ri.Printf( PRINT_ALL, "--- FS %d:\n", handle ); + break; + } + + qglGetShaderiv( handle, GL_SHADER_SOURCE_LENGTH, &k ); + source = ri.Hunk_AllocateTempMemory( k + 1 ); + qglGetShaderSource( handle, k, &k, source ); + ri.Printf( PRINT_ALL, "%s\n", source ); + ri.Hunk_FreeTempMemory( source ); + + count++; } + } else { + for ( i = 0 ; i < tr.numShaders ; i++ ) { + if ( ri.Cmd_Argc() > 1 ) { + shader = tr.sortedShaders[i]; + } else { + shader = tr.shaders[i]; + } + + ri.Printf( PRINT_ALL, "%i ", shader->numUnfoggedPasses ); - if ( shader->defaultShader ) { - ri.Printf (PRINT_ALL, ": %s (DEFAULTED)\n", shader->name); - } else { - ri.Printf (PRINT_ALL, ": %s\n", shader->name); + if (shader->lightmapIndex >= 0 ) { + ri.Printf (PRINT_ALL, "L "); + } else { + ri.Printf (PRINT_ALL, " "); + } + if ( shader->explicitlyDefined ) { + ri.Printf( PRINT_ALL, "E " ); + } else { + ri.Printf( PRINT_ALL, " " ); + } + + if ( shader->optimalStageIteratorFunc == RB_StageIteratorGeneric ) { + ri.Printf( PRINT_ALL, "gen " ); + } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorSky ) { + ri.Printf( PRINT_ALL, "sky " ); + } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorLightmappedMultitexture ) { + ri.Printf( PRINT_ALL, "lmmt" ); + } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorVertexLitTexture ) { + ri.Printf( PRINT_ALL, "vlt " ); + } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorGLSL ) { + ri.Printf( PRINT_ALL, "glsl" ); + } else { + ri.Printf( PRINT_ALL, " " ); + } + + if ( shader->GLSLprogram ) { + ri.Printf (PRINT_ALL, ": %s (id %d)\n", shader->name, + shader->GLSLprogram->handle); + } else if ( shader->defaultShader ) { + ri.Printf (PRINT_ALL, ": %s (DEFAULTED)\n", shader->name); + } else { + ri.Printf (PRINT_ALL, ": %s\n", shader->name); + } + for ( j = 0; j < shader->numUnfoggedPasses; j++ ) { + shaderStage_t *stage = shader->stages[j]; + + if ( !stage->active ) + break; + + ri.Printf (PRINT_ALL, " stage %d\n", j ); + + for ( k = 0; i < NUM_TEXTURE_BUNDLES; k++ ) { + if ( !stage->bundle[k].image[0] ) + break; + + ri.Printf (PRINT_ALL, " %s\n", stage->bundle[k].image[0]->imgName ); + } + } + count++; } - count++; } ri.Printf (PRINT_ALL, "%i total shaders\n", count); ri.Printf (PRINT_ALL, "------------------\n"); @@ -2880,21 +5348,24 @@ a single large text block that can be scanned for shader names ===================== */ #define MAX_SHADER_FILES 4096 -static void ScanAndLoadShaderFiles( void ) +static void ScanAndLoadShaderFiles( const char *extension, + char **text, + char **hashTable[MAX_SHADERTEXT_HASH]) { char **shaderFiles; char *buffers[MAX_SHADER_FILES]; char *p; int numShaderFiles; int i; - char *oldp, *token, *hashMem, *textEnd; + char *oldp, *token, *hashMem; int shaderTextHashTableSizes[MAX_SHADERTEXT_HASH], hash, size; long sum = 0, summand; // scan for shader files - shaderFiles = ri.FS_ListFiles( "scripts", ".shader", &numShaderFiles ); + shaderFiles = ri.FS_ListFiles( "scripts", extension, &numShaderFiles ); - if ( !shaderFiles || !numShaderFiles ) + if ( (!shaderFiles || !numShaderFiles) && + !strcmp( extension, ".shader") ) { ri.Printf( PRINT_WARNING, "WARNING: no shader files found\n" ); return; @@ -2939,38 +5410,35 @@ static void ScanAndLoadShaderFiles( void ) SkipBracedSection(&oldp); p = oldp; } - if (buffers[i]) sum += summand; } // build single large buffer - s_shaderText = ri.Hunk_Alloc( sum + numShaderFiles*2, h_low ); - s_shaderText[ 0 ] = '\0'; - textEnd = s_shaderText; - + *text = ri.Hunk_Alloc( sum + numShaderFiles*2, h_low ); + (*text)[ 0 ] = '\0'; + // free in reverse order, so the temp files are all dumped for ( i = numShaderFiles - 1; i >= 0 ; i-- ) { - if ( !buffers[i] ) - continue; - - strcat( textEnd, buffers[i] ); - strcat( textEnd, "\n" ); - textEnd += strlen( textEnd ); - ri.FS_FreeFile( buffers[i] ); + if(buffers[i]) + { + p = &(*text)[strlen(*text)]; + strcat( *text, buffers[i] ); + ri.FS_FreeFile( buffers[i] ); + COM_Compress(p); + strcat( *text, "\n" ); + } } - COM_Compress( s_shaderText ); - // free up memory ri.FS_FreeFileList( shaderFiles ); Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); size = 0; - p = s_shaderText; + p = *text; // look for shader names while ( 1 ) { token = COM_ParseExt( &p, qtrue ); @@ -2989,13 +5457,13 @@ static void ScanAndLoadShaderFiles( void ) hashMem = ri.Hunk_Alloc( size * sizeof(char *), h_low ); for (i = 0; i < MAX_SHADERTEXT_HASH; i++) { - shaderTextHashTable[i] = (char **) hashMem; + hashTable[i] = (char **) hashMem; hashMem = ((char *) hashMem) + ((shaderTextHashTableSizes[i] + 1) * sizeof(char *)); } Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); - p = s_shaderText; + p = *text; // look for shader names while ( 1 ) { oldp = p; @@ -3005,7 +5473,7 @@ static void ScanAndLoadShaderFiles( void ) } hash = generateHashValue(token, MAX_SHADERTEXT_HASH); - shaderTextHashTable[hash][shaderTextHashTableSizes[hash]++] = oldp; + hashTable[hash][shaderTextHashTableSizes[hash]++] = oldp; SkipBracedSection(&p); } @@ -3015,6 +5483,89 @@ static void ScanAndLoadShaderFiles( void ) } +static const char *normalVS = + "// IN(vec4 aVertex);\n" + "#define aVertex gl_Vertex\n" + "IN(vec4 aTransX);\n" + "IN(vec4 aTransY);\n" + "IN(vec4 aTransZ);\n" + "IN(vec3 aNormal);\n" + "\n" + "OUT(smooth, vec4 vVertex);\n" + "OUT(smooth, vec4 vNormal);\n" + "\n" + "vec4 transform4(vec4 point) {\n" + " return vec4( dot( aTransX, point ),\n" + " dot( aTransY, point ),\n" + " dot( aTransZ, point ),\n" + " 1.0 );\n" + "}\n" + "\n" + "void main() {\n" + " vec4 vertex = vec4(aVertex.xyz, 1.0);\n" + " vVertex = gl_ModelViewProjectionMatrix * transform4(vertex);\n" + " vNormal = gl_ModelViewProjectionMatrix * transform4(vertex + 2.0 * vec4(aNormal, 0.0));\n" + "}\n"; +static const char *normalMD3VS = + "// IN(vec4 aVertex);\n" + "#define aVertex gl_Vertex\n" + "IN(vec4 aTransX);\n" + "IN(vec4 aTransY);\n" + "IN(vec4 aTransZ);\n" + "IN(vec4 aTimes);\n" + "\n" + "uniform sampler2D texData;\n" + "\n" + "OUT(smooth, vec4 vVertex);\n" + "OUT(smooth, vec4 vNormal);\n" + "\n" + "vec4 transform4(vec4 point) {\n" + " return vec4( dot( aTransX, point ),\n" + " dot( aTransY, point ),\n" + " dot( aTransZ, point ),\n" + " 1.0 );\n" + "}\n" + "\n" + "vec4 fetchVertex(const float frameNo, const vec2 offset,\n" + " out vec4 normal) {\n" + " vec2 tc = vec2(fract(frameNo), floor(frameNo)/1024.0) + offset;\n" + " vec4 data = tex2D(texData, tc);\n" + " vec4 hi = floor(data);\n" + " vec4 lo = fract(data);\n" + " normal = vec4((hi.xyz - 128.0) / 127.0, 0.0);\n" + " return vec4(lo.xyz * 1024.0 - 512.0,\n" + " 1.0);\n" + "}\n" + "\n" + "void main() {\n" + " vec4 normal1, normal2;\n" + " vec4 vertex = mix(fetchVertex(aTimes.z, aVertex.zw, normal1),\n" + " fetchVertex(aTimes.w, aVertex.zw, normal2),\n" + " aTimes.y);\n" + " vec3 normal = normalize(mix(normal1.xyz, normal2.xyz, aTimes.y));\n" + " vVertex = gl_ModelViewProjectionMatrix * transform4(vertex);\n" + " vNormal = gl_ModelViewProjectionMatrix * transform4(vertex + 2.0*vec4(normal, 0.0));\n" + "}\n"; +static const char *normalGS = + "IN(smooth, vec4 vVertex[]);\n" + "IN(smooth, vec4 vNormal[]);\n" + "OUT(smooth, vec4 vColor);\n" + "\n" + "void main() {\n" + " vColor = vec4(1.0, 0.0, 0.0, 1.0);\n" + " gl_Position = vVertex[0];\n" + " EmitVertex();\n" + " vColor = vec4(1.0, 0.0, 0.0, 1.0);\n" + " gl_Position = vNormal[0];\n" + " EmitVertex();\n" + " EndPrimitive();\n" + "}\n"; +static const char *normalFS = + "IN(smooth, vec4 vColor);\n" + "void main() {\n" + " gl_FragColor = vColor;\n" + "}\n"; + /* ==================== CreateInternalShaders @@ -3030,15 +5581,118 @@ static void CreateInternalShaders( void ) { Q_strncpyz( shader.name, "", sizeof( shader.name ) ); shader.lightmapIndex = LIGHTMAP_NONE; + shader.sort = SS_OPAQUE; stages[0].bundle[0].image[0] = tr.defaultImage; stages[0].active = qtrue; stages[0].stateBits = GLS_DEFAULT; + stages[0].rgbGen = CGEN_VERTEX; + stages[0].alphaGen = AGEN_VERTEX; tr.defaultShader = FinishShader(); + if( qglCreateShader && qglGenBuffersARB && glGlobals.floatTextures ) { + Q_strncpyz( shader.name, "", sizeof( shader.name ) ); + shader.lightmapIndex = LIGHTMAP_MD3; + shader.sort = SS_OPAQUE; + stages[0].bundle[0].image[0] = tr.defaultImage; + stages[0].active = qtrue; + stages[0].stateBits = GLS_DEFAULT; + stages[0].rgbGen = CGEN_VERTEX; + stages[0].alphaGen = AGEN_VERTEX; + tr.defaultMD3Shader = FinishShader(); + } else { + tr.defaultMD3Shader = tr.defaultShader; + } + + // fogShader exists only to generate a GLSL program for fog blending + Q_strncpyz( shader.name, "", sizeof( shader.name ) ); + shader.lightmapIndex = LIGHTMAP_NONE; + stages[0].bundle[0].image[0] = tr.defaultImage; + stages[0].rgbGen = CGEN_VERTEX; + stages[0].stateBits = GLS_DEFAULT | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + tr.fogShader = FinishShader(); + // shadow shader is just a marker Q_strncpyz( shader.name, "", sizeof( shader.name ) ); shader.sort = SS_STENCIL_SHADOW; tr.shadowShader = FinishShader(); + + // prepare portal shader is just a marker + Q_strncpyz( shader.name, "", sizeof( shader.name ) ); + shader.sort = SS_PORTAL; + tr.preparePortalShader = FinishShader(); + tr.preparePortalShader->optimalStageIteratorFunc = RB_StageIteratorPreparePortal; + tr.preparePortalShader->anyGLAttr = GLA_FULL_dynamic; + + // finalise portal shader is just a marker + Q_strncpyz( shader.name, "", sizeof( shader.name ) ); + shader.sort = SS_PORTAL; + tr.finalisePortalShader = FinishShader(); + tr.finalisePortalShader->optimalStageIteratorFunc = RB_StageIteratorFinalisePortal; + tr.finalisePortalShader->anyGLAttr = GLA_FULL_dynamic; + + if( qglGenBuffersARB ) { + // build VBO shader + Q_strncpyz( shader.name, "", sizeof( shader.name ) ); + shader.sort = 0; + stages[0].active = qfalse; + tr.buildVBOShader = FinishShader(); + tr.buildVBOShader->optimalStageIteratorFunc = RB_StageIteratorBuildWorldVBO; + + // build IBO shader + Q_strncpyz( shader.name, "", sizeof( shader.name ) ); + tr.buildIBOShader = FinishShader(); + tr.buildIBOShader->optimalStageIteratorFunc = RB_StageIteratorBuildIBO; + } + + // internal GLSL programs for r_showNormals mode + if( qglCreateShader && qglProgramParameteriEXT ) { + int i; + const char *VS[2] = { VSHeader, normalVS }; + const char *MD3VS[2] = { VSHeader, normalMD3VS }; + const char *GS[2] = { GSHeader, normalGS }; + const char *FS[2] = { FSHeader, normalFS }; + + backEnd.normalProgram = RB_CompileProgram( "", + VS, 2, + GS, 2, + 6, GL_POINTS, + GL_LINE_STRIP, + FS, 2, + (1 << AL_VERTEX) | + (1 << AL_NORMAL) | + (1 << AL_TRANSX) | + (1 << AL_TRANSY) | + (1 << AL_TRANSZ) ); + backEnd.normalProgramMD3 = RB_CompileProgram( "", + MD3VS, 2, + GS, 2, + 6, GL_POINTS, + GL_LINE_STRIP, + FS, 2, + (1 << AL_VERTEX) | + (1 << AL_NORMAL) | + (1 << AL_TRANSX) | + (1 << AL_TRANSY) | + (1 << AL_TRANSZ) | + (1 << AL_TIMES) ); + if( backEnd.normalProgramMD3 ) { + i = qglGetUniformLocation( backEnd.normalProgramMD3->handle, "texData" ); + if( i != -1 ) { + qglUniform1i( i, 0 ); + } + } + } else { + backEnd.normalProgram = NULL; + backEnd.normalProgramMD3 = NULL; + } + + if( tr.depthImage ) { + Q_strncpyz( shader.name, "", sizeof( shader.name ) ); + shader.sort = SS_COPYDEPTH; + tr.copyDepthShader = FinishShader(); + tr.copyDepthShader->optimalStageIteratorFunc = RB_StageIteratorCopyDepth; + tr.copyDepthShader->anyGLAttr = GLA_VERTEX_dynamic; + } } static void CreateExternalShaders( void ) { @@ -3059,6 +5713,15 @@ static void CreateExternalShaders( void ) { } tr.sunShader = R_FindShader( "sun", LIGHTMAP_NONE, qtrue ); + if(!tr.sunShader->defaultShader) + { + int index; + + for(index = 0; index < tr.sunShader->numUnfoggedPasses; index++) + { + tr.sunShader->stages[index]->stateBits |= GLS_DEPTHRANGE_1_TO_1; + } + } } /* @@ -3073,7 +5736,8 @@ void R_InitShaders( void ) { CreateInternalShaders(); - ScanAndLoadShaderFiles(); + ScanAndLoadShaderFiles( ".shader", &s_shaderText, + shaderTextHashTable ); CreateExternalShaders(); } diff --git a/code/renderer/tr_shadows.c b/code/renderer/tr_shadows.c index f412b00..f5fc8b4 100644 --- a/code/renderer/tr_shadows.c +++ b/code/renderer/tr_shadows.c @@ -41,9 +41,9 @@ typedef struct { #define MAX_EDGE_DEFS 32 -static edgeDef_t edgeDefs[SHADER_MAX_VERTEXES][MAX_EDGE_DEFS]; -static int numEdgeDefs[SHADER_MAX_VERTEXES]; -static int facing[SHADER_MAX_INDEXES/3]; +static edgeDef_t *edgeDefs; +static int *numEdgeDefs; +static int *facing; void R_AddEdgeDef( int i1, int i2, int facing ) { int c; @@ -52,42 +52,120 @@ void R_AddEdgeDef( int i1, int i2, int facing ) { if ( c == MAX_EDGE_DEFS ) { return; // overflow } - edgeDefs[ i1 ][ c ].i2 = i2; - edgeDefs[ i1 ][ c ].facing = facing; + edgeDefs[ i1 * MAX_EDGE_DEFS + c ].i2 = i2; + edgeDefs[ i1 * MAX_EDGE_DEFS + c ].facing = facing; numEdgeDefs[ i1 ]++; } -void R_RenderShadowEdges( void ) { - int i; +void R_RenderShadowEdges( glRenderState_t *state ) { + int i, idx = 0; + int numTris = tess.numIndexes[1] / 3; + int numShadowTris = numTris * 6; #if 0 - int numTris; // dumb way -- render every triangle's edges - numTris = tess.numIndexes / 3; - - for ( i = 0 ; i < numTris ; i++ ) { - int i1, i2, i3; + state->program = tr.shadowShader->GLSLprogram; + SetAttrVec4f( state, AL_TRANSX, + backEnd.currentEntity->e.axis[0][0], + backEnd.currentEntity->e.axis[1][0], + backEnd.currentEntity->e.axis[2][0], + backEnd.currentEntity->e.origin[0] ); + SetAttrVec4f( state, AL_TRANSY, + backEnd.currentEntity->e.axis[0][1], + backEnd.currentEntity->e.axis[1][1], + backEnd.currentEntity->e.axis[2][1], + backEnd.currentEntity->e.origin[1] ); + SetAttrVec4f( state, AL_TRANSZ, + backEnd.currentEntity->e.axis[0][2], + backEnd.currentEntity->e.axis[1][2], + backEnd.currentEntity->e.axis[2][2], + backEnd.currentEntity->e.origin[2] ); + SetAttrPointer( state, AL_VERTEX, 0, + 3, GL_FLOAT, sizeof(glVertex_t), + &tess.vertexPtr[0].xyz ); + + if ( tess.indexInc == sizeof( GLuint ) ) { + GLuint *indexPtr32 = tess.indexPtr.p32; + GLuint *indexes; + + indexes = RB_AllocScratch( 3 * numShadowTris * sizeof( GLuint ) ); + + for ( i = 0 ; i < numTris ; i++ ) { + int i1, i2, i3; + + if ( !facing[i] ) { + continue; + } - if ( !facing[i] ) { - continue; + i1 = indexPtr32[ i*3 + 0 ]; + i2 = indexPtr32[ i*3 + 1 ]; + i3 = indexPtr32[ i*3 + 2 ]; + + indexes32[idx++] = i1; + indexes32[idx++] = i1 + tess.numVertexes; + indexes32[idx++] = i2; + indexes32[idx++] = i2; + indexes32[idx++] = i1 + tess.numVertexes; + indexes32[idx++] = i2 + tess.numVertexes; + + indexes32[idx++] = i2; + indexes32[idx++] = i2 + tess.numVertexes; + indexes32[idx++] = i3; + indexes32[idx++] = i3; + indexes32[idx++] = i2 + tess.numVertexes; + indexes32[idx++] = i3 + tess.numVertexes; + + indexes32[idx++] = i3; + indexes32[idx++] = i3 + tess.numVertexes; + indexes32[idx++] = i1; + indexes32[idx++] = i1; + indexes32[idx++] = i3 + tess.numVertexes; + indexes32[idx++] = i1 + tess.numVertexes; } + GL_DrawElements( state, idx, 0, indexes32, 0, idx-1, 65537 ); + RB_FreeScratch( indexes ); + } else { + glIndex_t *indexPtr = tess.indexPtr; + glIndex_t *indexes; + + indexes = RB_AllocScratch( 3 * numShadowTris * sizeof( glIndex_t ) ); + + for ( i = 0 ; i < numTris ; i++ ) { + glIndex_t i1, i2, i3; + + if ( !facing[i] ) { + continue; + } - i1 = tess.indexes[ i*3 + 0 ]; - i2 = tess.indexes[ i*3 + 1 ]; - i3 = tess.indexes[ i*3 + 2 ]; - - qglBegin( GL_TRIANGLE_STRIP ); - qglVertex3fv( tess.xyz[ i1 ] ); - qglVertex3fv( tess.xyz[ i1 + tess.numVertexes ] ); - qglVertex3fv( tess.xyz[ i2 ] ); - qglVertex3fv( tess.xyz[ i2 + tess.numVertexes ] ); - qglVertex3fv( tess.xyz[ i3 ] ); - qglVertex3fv( tess.xyz[ i3 + tess.numVertexes ] ); - qglVertex3fv( tess.xyz[ i1 ] ); - qglVertex3fv( tess.xyz[ i1 + tess.numVertexes ] ); - qglEnd(); + i1 = indexPtr[ i*3 + 0 ]; + i2 = indexPtr[ i*3 + 1 ]; + i3 = indexPtr[ i*3 + 2 ]; + + indexes[idx++] = i1; + indexes[idx++] = i1 + tess.numVertexes; + indexes[idx++] = i2; + indexes[idx++] = i2; + indexes[idx++] = i1 + tess.numVertexes; + indexes[idx++] = i2 + tess.numVertexes; + + indexes[idx++] = i2; + indexes[idx++] = i2 + tess.numVertexes; + indexes[idx++] = i3; + indexes[idx++] = i3; + indexes[idx++] = i2 + tess.numVertexes; + indexes[idx++] = i3 + tess.numVertexes; + + indexes[idx++] = i3; + indexes[idx++] = i3 + tess.numVertexes; + indexes[idx++] = i1; + indexes[idx++] = i1; + indexes[idx++] = i3 + tess.numVertexes; + indexes[idx++] = i1 + tess.numVertexes; + } + GL_DrawElements( state, idx, 0, indexes, 0, idx-1 ); + RB_FreeScratch( indexes ); } #else int c, c2; @@ -95,6 +173,7 @@ void R_RenderShadowEdges( void ) { int i2; int c_edges, c_rejected; int hit[2]; + glIndex_t *indexes; // an edge is NOT a silhouette edge if its face doesn't face the light, // or if it has a reverse paired edge that also faces the light. @@ -102,40 +181,64 @@ void R_RenderShadowEdges( void ) { // but lots of models have dangling edges or overfanned edges c_edges = 0; c_rejected = 0; + state->program = tr.shadowShader->GLSLprogram; + SetAttrVec4f( state, AL_TRANSX, + backEnd.currentEntity->e.axis[0][0], + backEnd.currentEntity->e.axis[1][0], + backEnd.currentEntity->e.axis[2][0], + backEnd.currentEntity->e.origin[0] ); + SetAttrVec4f( state, AL_TRANSY, + backEnd.currentEntity->e.axis[0][1], + backEnd.currentEntity->e.axis[1][1], + backEnd.currentEntity->e.axis[2][1], + backEnd.currentEntity->e.origin[1] ); + SetAttrVec4f( state, AL_TRANSZ, + backEnd.currentEntity->e.axis[0][2], + backEnd.currentEntity->e.axis[1][2], + backEnd.currentEntity->e.axis[2][2], + backEnd.currentEntity->e.origin[2] ); + SetAttrPointer( state, AL_VERTEX, 0, + 3, GL_FLOAT, sizeof(glVertex_t), + &tess.vertexPtr[0].xyz ); + + indexes = RB_AllocScratch( 3 * numShadowTris * sizeof( glIndex_t ) ); for ( i = 0 ; i < tess.numVertexes ; i++ ) { c = numEdgeDefs[ i ]; for ( j = 0 ; j < c ; j++ ) { - if ( !edgeDefs[ i ][ j ].facing ) { + if ( !edgeDefs[ i * MAX_EDGE_DEFS + j ].facing ) { continue; } hit[0] = 0; hit[1] = 0; - i2 = edgeDefs[ i ][ j ].i2; + i2 = edgeDefs[ i * MAX_EDGE_DEFS + j ].i2; c2 = numEdgeDefs[ i2 ]; for ( k = 0 ; k < c2 ; k++ ) { - if ( edgeDefs[ i2 ][ k ].i2 == i ) { - hit[ edgeDefs[ i2 ][ k ].facing ]++; + if ( edgeDefs[ i2 * MAX_EDGE_DEFS + k ].i2 == i ) { + hit[ edgeDefs[ i2 * MAX_EDGE_DEFS + k ].facing ]++; } } // if it doesn't share the edge with another front facing // triangle, it is a sil edge if ( hit[ 1 ] == 0 ) { - qglBegin( GL_TRIANGLE_STRIP ); - qglVertex3fv( tess.xyz[ i ] ); - qglVertex3fv( tess.xyz[ i + tess.numVertexes ] ); - qglVertex3fv( tess.xyz[ i2 ] ); - qglVertex3fv( tess.xyz[ i2 + tess.numVertexes ] ); - qglEnd(); + indexes[idx++] = i; + indexes[idx++] = i + tess.numVertexes; + indexes[idx++] = i2; + indexes[idx++] = i2; + indexes[idx++] = i + tess.numVertexes; + indexes[idx++] = i2 + tess.numVertexes; c_edges++; } else { c_rejected++; } } } + GL_DrawElements( state, idx, 0, indexes, + 0, 2*tess.numVertexes - 1 ); + RB_FreeScratch( indexes ); #endif } @@ -155,12 +258,7 @@ void RB_ShadowTessEnd( void ) { int i; int numTris; vec3_t lightDir; - GLboolean rgba[4]; - - // we can only do this if we have enough space in the vertex buffers - if ( tess.numVertexes >= SHADER_MAX_VERTEXES / 2 ) { - return; - } + glRenderState_t state; if ( glConfig.stencilBits < 4 ) { return; @@ -170,29 +268,34 @@ void RB_ShadowTessEnd( void ) { // project vertexes away from light direction for ( i = 0 ; i < tess.numVertexes ; i++ ) { - VectorMA( tess.xyz[i], -512, lightDir, tess.xyz[i+tess.numVertexes] ); + VectorMA( tess.vertexPtr[i].xyz, -512, + lightDir, tess.vertexPtr[i+tess.numVertexes].xyz ); } // decide which triangles face the light - Com_Memset( numEdgeDefs, 0, 4 * tess.numVertexes ); + numEdgeDefs = RB_AllocScratch( sizeof( int ) * tess.numVertexes ); + edgeDefs = RB_AllocScratch( sizeof( edgeDef_t ) * tess.numVertexes * MAX_EDGE_DEFS ); + facing = RB_AllocScratch( sizeof( int ) * tess.numIndexes[1] / 3 ); + Com_Memset( numEdgeDefs, 0, sizeof( int ) * tess.numVertexes ); - numTris = tess.numIndexes / 3; + numTris = tess.numIndexes[1] / 3; for ( i = 0 ; i < numTris ; i++ ) { - int i1, i2, i3; + glIndex_t i1, i2, i3; vec3_t d1, d2, normal; - float *v1, *v2, *v3; + vec3_t *v1, *v2, *v3; float d; - i1 = tess.indexes[ i*3 + 0 ]; - i2 = tess.indexes[ i*3 + 1 ]; - i3 = tess.indexes[ i*3 + 2 ]; + glIndex_t *indexPtr = tess.indexPtr; + i1 = indexPtr[ i*3 + 0 ]; + i2 = indexPtr[ i*3 + 1 ]; + i3 = indexPtr[ i*3 + 2 ]; - v1 = tess.xyz[ i1 ]; - v2 = tess.xyz[ i2 ]; - v3 = tess.xyz[ i3 ]; + v1 = &tess.vertexPtr[i1].xyz; + v2 = &tess.vertexPtr[i2].xyz; + v3 = &tess.vertexPtr[i3].xyz; - VectorSubtract( v2, v1, d1 ); - VectorSubtract( v3, v1, d2 ); + VectorSubtract( *v2, *v1, d1 ); + VectorSubtract( *v3, *v1, d2 ); CrossProduct( d1, d2, normal ); d = DotProduct( normal, lightDir ); @@ -209,45 +312,42 @@ void RB_ShadowTessEnd( void ) { } // draw the silhouette edges + InitState( &state ); - GL_Bind( tr.whiteImage ); - qglEnable( GL_CULL_FACE ); - GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); - qglColor3f( 0.2f, 0.2f, 0.2f ); - - // don't write to the color buffer - qglGetBooleanv(GL_COLOR_WRITEMASK, rgba); - qglColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); + state.numImages = 1; + state.image[0] = tr.whiteImage; + state.stateBits = GLS_COLORMASK_FALSE; + SetAttrVec4f( &state, AL_COLOR, 1.0f, 0.2f, 1.0f, 1.0f ); - qglEnable( GL_STENCIL_TEST ); - qglStencilFunc( GL_ALWAYS, 1, 255 ); - - // mirrors have the culling order reversed - if ( backEnd.viewParms.isMirror ) { - qglCullFace( GL_FRONT ); - qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); - - R_RenderShadowEdges(); - - qglCullFace( GL_BACK ); - qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR ); - - R_RenderShadowEdges(); + if( !backEnd.viewParms.portalLevel ) { + qglEnable( GL_STENCIL_TEST ); + qglStencilFunc( GL_ALWAYS, 1, glGlobals.shadowMask ); + } + qglStencilMask( glGlobals.shadowMask ); + + if( qglStencilOpSeparate ) { + // single pass, doesn't matter if we incr or decr as we check + // for != 0... + state.faceCulling = CT_TWO_SIDED; + qglStencilOpSeparate( GL_BACK, GL_KEEP, GL_KEEP, GL_INCR_WRAP ); + qglStencilOpSeparate( GL_FRONT, GL_KEEP, GL_KEEP, GL_DECR_WRAP ); } else { - qglCullFace( GL_BACK ); + // two passes for front/back faces + state.faceCulling = CT_BACK_SIDED; qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); - - R_RenderShadowEdges(); - - qglCullFace( GL_FRONT ); + + R_RenderShadowEdges( &state ); + + state.faceCulling = CT_FRONT_SIDED; qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR ); - - R_RenderShadowEdges(); } + R_RenderShadowEdges( &state ); + qglStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); - // reenable writing to the color buffer - qglColorMask(rgba[0], rgba[1], rgba[2], rgba[3]); + RB_FreeScratch( facing ); + RB_FreeScratch( edgeDefs ); + RB_FreeScratch( numEdgeDefs ); } @@ -262,6 +362,14 @@ overlap and double darken. ================= */ void RB_ShadowFinish( void ) { + static vec3_t vertexes[4] = { + { -100, 100, -10 }, + { 100, 100, -10 }, + { 100, -100, -10 }, + { -100, -100, -10 } + }; + glRenderState_t state; + if ( r_shadows->integer != 2 ) { return; } @@ -269,30 +377,39 @@ void RB_ShadowFinish( void ) { return; } qglEnable( GL_STENCIL_TEST ); - qglStencilFunc( GL_NOTEQUAL, 0, 255 ); - - qglDisable (GL_CLIP_PLANE0); - qglDisable (GL_CULL_FACE); + qglStencilFunc( GL_NOTEQUAL, 0, glGlobals.shadowMask ); + qglStencilMask( glGlobals.shadowMask ); + qglStencilOp( GL_ZERO, GL_ZERO, GL_ZERO ); - GL_Bind( tr.whiteImage ); + InitState( &state ); + state.faceCulling = CT_TWO_SIDED; - qglLoadIdentity (); + state.numImages = 1; + state.image[0] = tr.whiteImage; - qglColor3f( 0.6f, 0.6f, 0.6f ); - GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO ); + qglLoadIdentity (); -// qglColor3f( 1, 0, 0 ); -// GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); + state.program = NULL; + SetAttrVec4f( &state, AL_COLOR, 0.6f, 0.6f, 0.6f, 1.0f ); + state.stateBits = GLS_DEPTHMASK_TRUE | + GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; - qglBegin( GL_QUADS ); - qglVertex3f( -100, 100, -10 ); - qglVertex3f( 100, 100, -10 ); - qglVertex3f( 100, -100, -10 ); - qglVertex3f( -100, -100, -10 ); - qglEnd (); + SetAttrPointer( &state, AL_VERTEX, 0, + 3, GL_FLOAT, sizeof(vec3_t), + vertexes ); + GL_DrawArrays( &state, GL_QUADS, 0, 4 ); - qglColor4f(1,1,1,1); - qglDisable( GL_STENCIL_TEST ); + if( !backEnd.viewParms.portalLevel ) { + qglDisable( GL_STENCIL_TEST ); + } else { + int level; + GLuint stencilVal; + level = backEnd.viewParms.portalLevel; + stencilVal = (level ^ (level >> 1)) << glGlobals.shadowBits; + + qglStencilFunc( GL_EQUAL, stencilVal, glGlobals.portalMask ); + qglStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); + } } @@ -303,7 +420,6 @@ RB_ProjectionShadowDeform ================= */ void RB_ProjectionShadowDeform( void ) { - float *xyz; int i; float h; vec3_t ground; @@ -312,8 +428,6 @@ void RB_ProjectionShadowDeform( void ) { float d; vec3_t lightDir; - xyz = ( float * ) tess.xyz; - ground[0] = backEnd.or.axis[0][2]; ground[1] = backEnd.or.axis[1][2]; ground[2] = backEnd.or.axis[2][2]; @@ -333,11 +447,11 @@ void RB_ProjectionShadowDeform( void ) { light[1] = lightDir[1] * d; light[2] = lightDir[2] * d; - for ( i = 0; i < tess.numVertexes; i++, xyz += 4 ) { - h = DotProduct( xyz, ground ) + groundDist; + for ( i = 0; i < tess.numVertexes; i++ ) { + h = DotProduct( tess.vertexPtr[i].xyz, ground ) + groundDist; - xyz[0] -= light[0] * h; - xyz[1] -= light[1] * h; - xyz[2] -= light[2] * h; + tess.vertexPtr[i].xyz[0] -= light[0] * h; + tess.vertexPtr[i].xyz[1] -= light[1] * h; + tess.vertexPtr[i].xyz[2] -= light[2] * h; } } diff --git a/code/renderer/tr_sky.c b/code/renderer/tr_sky.c index 3e774a3..7844555 100644 --- a/code/renderer/tr_sky.c +++ b/code/renderer/tr_sky.c @@ -25,6 +25,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define SKY_SUBDIVISIONS 8 #define HALF_SKY_SUBDIVISIONS (SKY_SUBDIVISIONS/2) +#define SKY_MAX_QUADS (5 * SKY_SUBDIVISIONS * SKY_SUBDIVISIONS) +#define SKY_MAX_VERTEXES (4 * SKY_MAX_QUADS) +#define SKY_MAX_INDEXES (6 * SKY_MAX_QUADS) + static float s_cloudTexCoords[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2]; static float s_cloudTexP[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1]; @@ -264,13 +268,14 @@ void RB_ClipSkyPolygons( shaderCommands_t *input ) ClearSkyBox(); - for ( i = 0; i < input->numIndexes; i += 3 ) + for ( i = 0; i < input->numIndexes[1]; i += 3 ) { for (j = 0 ; j < 3 ; j++) { - VectorSubtract( input->xyz[input->indexes[i+j]], - backEnd.viewParms.or.origin, - p[j] ); + glIndex_t idx = tess.indexPtr[i+j]; + VectorSubtract( tess.vertexPtr[idx].xyz, + backEnd.viewParms.or.origin, + p[j] ); } ClipSkyPolygon( 3, p[0], 0 ); } @@ -363,25 +368,60 @@ static float s_skyTexCoords[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2]; static void DrawSkySide( struct image_s *image, const int mins[2], const int maxs[2] ) { - int s, t; + int s, t, i; + + RB_BeginSurface( tr.defaultShader ); + + tess.numVertexes = sizeof(s_skyPoints) / sizeof(s_skyPoints[0][0]); + tess.numIndexes[0] = 6 * (maxs[0] - mins[0]) * (maxs[1] - mins[1]); + + RB_AllocateSurface( ); + + tess.numVertexes = sizeof(s_skyPoints) / sizeof(s_skyPoints[0][0]); + tess.numIndexes[0] = 6 * (maxs[0] - mins[0]) * (maxs[1] - mins[1]); + + i = 0; + for( t = 0; t <= SKY_SUBDIVISIONS; t++ ) { + for( s = 0; s <= SKY_SUBDIVISIONS; s++ ) { + tess.vertexPtr[i].xyz[0] = s_skyPoints[t][s][0]; + tess.vertexPtr[i].xyz[1] = s_skyPoints[t][s][1]; + tess.vertexPtr[i].xyz[2] = s_skyPoints[t][s][2]; + tess.vertexPtr[i].fogNum = 0.0f; + tess.vertexPtr[i].tc1[0] = s_skyTexCoords[t][s][0]; + tess.vertexPtr[i].tc1[1] = s_skyTexCoords[t][s][1]; + tess.vertexPtr[i].tc2[0] = s_skyTexCoords[t][s][0]; + tess.vertexPtr[i].tc2[1] = s_skyTexCoords[t][s][1]; + tess.vertexPtr[i].normal[0] = 0.0f; + tess.vertexPtr[i].normal[1] = 0.0f; + tess.vertexPtr[i].normal[2] = 0.0f; + tess.vertexPtr[i].color[0] = 255; + tess.vertexPtr[i].color[1] = 255; + tess.vertexPtr[i].color[2] = 255; + tess.vertexPtr[i].color[3] = 255; + i++; + } + } - GL_Bind( image ); + i = 0; for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t < maxs[1]+HALF_SKY_SUBDIVISIONS; t++ ) { - qglBegin( GL_TRIANGLE_STRIP ); - - for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) + for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s < maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) { - qglTexCoord2fv( s_skyTexCoords[t][s] ); - qglVertex3fv( s_skyPoints[t][s] ); - - qglTexCoord2fv( s_skyTexCoords[t+1][s] ); - qglVertex3fv( s_skyPoints[t+1][s] ); + tess.indexPtr[i++] = t * (SKY_SUBDIVISIONS+1) + s; + tess.indexPtr[i++] = (t+1) * (SKY_SUBDIVISIONS+1) + s; + tess.indexPtr[i++] = t * (SKY_SUBDIVISIONS+1) + (s+1); + tess.indexPtr[i++] = t * (SKY_SUBDIVISIONS+1) + (s+1); + tess.indexPtr[i++] = (t+1) * (SKY_SUBDIVISIONS+1) + s; + tess.indexPtr[i++] = (t+1) * (SKY_SUBDIVISIONS+1) + (s+1); } - - qglEnd(); } + tess.minIndex[0] = (mins[1]+HALF_SKY_SUBDIVISIONS) * (SKY_SUBDIVISIONS+1) + (mins[0]+HALF_SKY_SUBDIVISIONS); + tess.maxIndex[0] = (maxs[1]+HALF_SKY_SUBDIVISIONS) * (SKY_SUBDIVISIONS+1) + (maxs[0]+HALF_SKY_SUBDIVISIONS); + tr.defaultShader->stages[0]->stateBits &= ~GLS_DEPTHMASK_TRUE; + tess.imgOverride = image; + RB_EndSurface( ); + tess.imgOverride = NULL; } static void DrawSkyBox( shader_t *shader ) @@ -448,8 +488,7 @@ static void DrawSkyBox( shader_t *shader ) } DrawSkySide( shader->sky.outerbox[sky_texorder[i]], - sky_mins_subd, - sky_maxs_subd ); + sky_mins_subd, sky_maxs_subd ); } } @@ -459,6 +498,7 @@ static void FillCloudySkySide( const int mins[2], const int maxs[2], qboolean ad int s, t; int vertexStart = tess.numVertexes; int tHeight, sWidth; + glVertex_t *vertexPtr = tess.vertexPtr + tess.numVertexes; tHeight = maxs[1] - mins[1] + 1; sWidth = maxs[0] - mins[0] + 1; @@ -467,38 +507,44 @@ static void FillCloudySkySide( const int mins[2], const int maxs[2], qboolean ad { for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) { - VectorAdd( s_skyPoints[t][s], backEnd.viewParms.or.origin, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = s_skyTexCoords[t][s][0]; - tess.texCoords[tess.numVertexes][0][1] = s_skyTexCoords[t][s][1]; + VectorAdd( s_skyPoints[t][s], backEnd.viewParms.or.origin, vertexPtr->xyz ); + vertexPtr->tc1[0] = s_skyTexCoords[t][s][0]; + vertexPtr->tc1[1] = s_skyTexCoords[t][s][1]; + vertexPtr->fogNum = tess.fogNum; tess.numVertexes++; + vertexPtr++; - if ( tess.numVertexes >= SHADER_MAX_VERTEXES ) + if ( tess.numVertexes >= SKY_MAX_VERTEXES ) { - ri.Error( ERR_DROP, "SHADER_MAX_VERTEXES hit in FillCloudySkySide()" ); + ri.Error( ERR_DROP, "SKY_MAX_VERTEXES hit in FillCloudySkySide()" ); } } } // only add indexes for one pass, otherwise it would draw multiple times for each pass if ( addIndexes ) { + if ( tess.minIndex[0] > vertexStart ) + tess.minIndex[0] = vertexStart; + if ( tess.maxIndex[0] < tess.numVertexes - 1 ) + tess.maxIndex[0] = tess.numVertexes - 1; for ( t = 0; t < tHeight-1; t++ ) { for ( s = 0; s < sWidth-1; s++ ) { - tess.indexes[tess.numIndexes] = vertexStart + s + t * ( sWidth ); - tess.numIndexes++; - tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth ); - tess.numIndexes++; - tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth ); - tess.numIndexes++; - - tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth ); - tess.numIndexes++; - tess.indexes[tess.numIndexes] = vertexStart + s + 1 + ( t + 1 ) * ( sWidth ); - tess.numIndexes++; - tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth ); - tess.numIndexes++; + tess.indexPtr[tess.numIndexes[0]] = vertexStart + s + t * ( sWidth ); + tess.numIndexes[0]++; + tess.indexPtr[tess.numIndexes[0]] = vertexStart + s + ( t + 1 ) * ( sWidth ); + tess.numIndexes[0]++; + tess.indexPtr[tess.numIndexes[0]] = vertexStart + s + 1 + t * ( sWidth ); + tess.numIndexes[0]++; + + tess.indexPtr[tess.numIndexes[0]] = vertexStart + s + ( t + 1 ) * ( sWidth ); + tess.numIndexes[0]++; + tess.indexPtr[tess.numIndexes[0]] = vertexStart + s + 1 + ( t + 1 ) * ( sWidth ); + tess.numIndexes[0]++; + tess.indexPtr[tess.numIndexes[0]] = vertexStart + s + 1 + t * ( sWidth ); + tess.numIndexes[0]++; } } } @@ -602,21 +648,20 @@ static void FillCloudBox( const shader_t *shader, int stage ) /* ** R_BuildCloudData */ -void R_BuildCloudData( shaderCommands_t *input ) +void R_BuildCloudData( shaderCommands_t *input, shader_t *shader ) { int i; - shader_t *shader; - - shader = input->shader; assert( shader->isSky ); sky_min = 1.0 / 256.0f; // FIXME: not correct? sky_max = 255.0 / 256.0f; - // set up for drawing - tess.numIndexes = 0; - tess.numVertexes = 0; + RB_BeginSurface( shader ); + + tess.numIndexes[0] = SKY_MAX_INDEXES; + tess.numVertexes = SKY_MAX_VERTEXES; + RB_AllocateSurface( ); if ( shader->sky.cloudHeight ) { @@ -701,6 +746,7 @@ void RB_DrawSun( void ) { float dist; vec3_t origin, vec1, vec2; vec3_t temp; + glVertex_t *vertexPtr; if ( !backEnd.skyRenderedThisView ) { return; @@ -721,66 +767,74 @@ void RB_DrawSun( void ) { VectorScale( vec1, size, vec1 ); VectorScale( vec2, size, vec2 ); - // farthest depth range - qglDepthRange( 1.0, 1.0 ); - // FIXME: use quad stamp - RB_BeginSurface( tr.sunShader, tess.fogNum ); - VectorCopy( origin, temp ); - VectorSubtract( temp, vec1, temp ); - VectorSubtract( temp, vec2, temp ); - VectorCopy( temp, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = 255; - tess.vertexColors[tess.numVertexes][1] = 255; - tess.vertexColors[tess.numVertexes][2] = 255; - tess.numVertexes++; - - VectorCopy( origin, temp ); - VectorAdd( temp, vec1, temp ); - VectorSubtract( temp, vec2, temp ); - VectorCopy( temp, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = 255; - tess.vertexColors[tess.numVertexes][1] = 255; - tess.vertexColors[tess.numVertexes][2] = 255; - tess.numVertexes++; - - VectorCopy( origin, temp ); - VectorAdd( temp, vec1, temp ); - VectorAdd( temp, vec2, temp ); - VectorCopy( temp, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = 1; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = 255; - tess.vertexColors[tess.numVertexes][1] = 255; - tess.vertexColors[tess.numVertexes][2] = 255; - tess.numVertexes++; - - VectorCopy( origin, temp ); - VectorSubtract( temp, vec1, temp ); - VectorAdd( temp, vec2, temp ); - VectorCopy( temp, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = 1; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = 255; - tess.vertexColors[tess.numVertexes][1] = 255; - tess.vertexColors[tess.numVertexes][2] = 255; - tess.numVertexes++; - - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 1; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 3; + RB_BeginSurface( tr.sunShader ); + + tess.numVertexes = 4; + tess.numIndexes[0] = 6; + RB_AllocateSurface( ); + + vertexPtr = tess.vertexPtr; + + VectorCopy( origin, temp ); + VectorSubtract( temp, vec1, temp ); + VectorSubtract( temp, vec2, temp ); + VectorCopy( temp, vertexPtr->xyz ); + vertexPtr->tc1[0] = 0; + vertexPtr->tc1[1] = 0; + vertexPtr->color[0] = 255; + vertexPtr->color[1] = 255; + vertexPtr->color[2] = 255; + vertexPtr->fogNum = tess.fogNum; + vertexPtr++; + + VectorCopy( origin, temp ); + VectorAdd( temp, vec1, temp ); + VectorSubtract( temp, vec2, temp ); + VectorCopy( temp, vertexPtr->xyz ); + vertexPtr->tc1[0] = 0; + vertexPtr->tc1[1] = 1; + vertexPtr->color[0] = 255; + vertexPtr->color[1] = 255; + vertexPtr->color[2] = 255; + vertexPtr->fogNum = tess.fogNum; + vertexPtr++; + + VectorCopy( origin, temp ); + VectorAdd( temp, vec1, temp ); + VectorAdd( temp, vec2, temp ); + VectorCopy( temp, vertexPtr->xyz ); + vertexPtr->tc1[0] = 1; + vertexPtr->tc1[1] = 1; + vertexPtr->color[0] = 255; + vertexPtr->color[1] = 255; + vertexPtr->color[2] = 255; + vertexPtr->fogNum = tess.fogNum; + vertexPtr++; + + VectorCopy( origin, temp ); + VectorSubtract( temp, vec1, temp ); + VectorAdd( temp, vec2, temp ); + VectorCopy( temp, vertexPtr->xyz ); + vertexPtr->tc1[0] = 1; + vertexPtr->tc1[1] = 0; + vertexPtr->color[0] = 255; + vertexPtr->color[1] = 255; + vertexPtr->color[2] = 255; + vertexPtr->fogNum = tess.fogNum; + vertexPtr++; + + tess.indexPtr[0] = 0; + tess.indexPtr[1] = 1; + tess.indexPtr[2] = 2; + tess.indexPtr[3] = 0; + tess.indexPtr[4] = 2; + tess.indexPtr[5] = 3; + + tess.numVertexes = 4; + tess.numIndexes[0] = 6; RB_EndSurface(); - - // back to normal depth range - qglDepthRange( 0.0, 1.0 ); } @@ -796,6 +850,8 @@ Other things could be stuck in here, like birds in the sky, etc ================ */ void RB_StageIteratorSky( void ) { + shader_t *shader = tess.shader; + if ( r_fastsky->integer ) { return; } @@ -804,22 +860,20 @@ void RB_StageIteratorSky( void ) { // the sky box to see which blocks on each side need // to be drawn RB_ClipSkyPolygons( &tess ); + RB_DiscardSurface( ); // r_showsky will let all the sky blocks be drawn in // front of everything to allow developers to see how // much sky is getting sucked in - if ( r_showsky->integer ) { - qglDepthRange( 0.0, 0.0 ); - } else { - qglDepthRange( 1.0, 1.0 ); - } + //if ( r_showsky->integer ) { + // qglDepthRange( 0.0, 0.0 ); + //} else { + // qglDepthRange( 1.0, 1.0 ); + //} // draw the outer skybox if ( tess.shader->sky.outerbox[0] && tess.shader->sky.outerbox[0] != tr.defaultImage ) { - qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight ); - qglPushMatrix (); - GL_State( 0 ); qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); DrawSkyBox( tess.shader ); @@ -829,15 +883,10 @@ void RB_StageIteratorSky( void ) { // generate the vertexes for all the clouds, which will be drawn // by the generic shader routine - R_BuildCloudData( &tess ); + R_BuildCloudData( &tess, shader ); - RB_StageIteratorGeneric(); - - // draw the inner skybox - - - // back to normal depth range - qglDepthRange( 0.0, 1.0 ); + tess.currentStageIteratorFunc = RB_StageIteratorGeneric; + RB_EndSurface(); // note that sky was drawn so we will draw a sun later backEnd.skyRenderedThisView = qtrue; diff --git a/code/renderer/tr_surface.c b/code/renderer/tr_surface.c index 7559215..b9b12a7 100644 --- a/code/renderer/tr_surface.c +++ b/code/renderer/tr_surface.c @@ -45,98 +45,98 @@ use the shader system. /* ============== -RB_CheckOverflow -============== -*/ -void RB_CheckOverflow( int verts, int indexes ) { - if (tess.numVertexes + verts < SHADER_MAX_VERTEXES - && tess.numIndexes + indexes < SHADER_MAX_INDEXES) { - return; - } - - RB_EndSurface(); - - if ( verts >= SHADER_MAX_VERTEXES ) { - ri.Error(ERR_DROP, "RB_CheckOverflow: verts > MAX (%d > %d)", verts, SHADER_MAX_VERTEXES ); - } - if ( indexes >= SHADER_MAX_INDEXES ) { - ri.Error(ERR_DROP, "RB_CheckOverflow: indices > MAX (%d > %d)", indexes, SHADER_MAX_INDEXES ); - } - - RB_BeginSurface(tess.shader, tess.fogNum ); -} - - -/* -============== RB_AddQuadStampExt ============== */ -void RB_AddQuadStampExt( vec3_t origin, vec3_t left, vec3_t up, byte *color, float s1, float t1, float s2, float t2 ) { - vec3_t normal; +void RB_AddQuadStampExt( vec3_t origin, vec3_t left, vec3_t up, byte *color, + float s1, float t1, float s2, float t2 ) { int ndx; - RB_CHECKOVERFLOW( 4, 6 ); - ndx = tess.numVertexes; - // triangle indexes for a simple quad - tess.indexes[ tess.numIndexes ] = ndx; - tess.indexes[ tess.numIndexes + 1 ] = ndx + 1; - tess.indexes[ tess.numIndexes + 2 ] = ndx + 3; - - tess.indexes[ tess.numIndexes + 3 ] = ndx + 3; - tess.indexes[ tess.numIndexes + 4 ] = ndx + 1; - tess.indexes[ tess.numIndexes + 5 ] = ndx + 2; - - tess.xyz[ndx][0] = origin[0] + left[0] + up[0]; - tess.xyz[ndx][1] = origin[1] + left[1] + up[1]; - tess.xyz[ndx][2] = origin[2] + left[2] + up[2]; - - tess.xyz[ndx+1][0] = origin[0] - left[0] + up[0]; - tess.xyz[ndx+1][1] = origin[1] - left[1] + up[1]; - tess.xyz[ndx+1][2] = origin[2] - left[2] + up[2]; + tess.vertexPtr[ndx+0].xyz[0] = origin[0] + left[0] + up[0]; + tess.vertexPtr[ndx+0].xyz[1] = origin[1] + left[1] + up[1]; + tess.vertexPtr[ndx+0].xyz[2] = origin[2] + left[2] + up[2]; + tess.vertexPtr[ndx+0].fogNum = (float)tess.fogNum; + + tess.vertexPtr[ndx+1].xyz[0] = origin[0] - left[0] + up[0]; + tess.vertexPtr[ndx+1].xyz[1] = origin[1] - left[1] + up[1]; + tess.vertexPtr[ndx+1].xyz[2] = origin[2] - left[2] + up[2]; + tess.vertexPtr[ndx+1].fogNum = (float)tess.fogNum; + + tess.vertexPtr[ndx+2].xyz[0] = origin[0] - left[0] - up[0]; + tess.vertexPtr[ndx+2].xyz[1] = origin[1] - left[1] - up[1]; + tess.vertexPtr[ndx+2].xyz[2] = origin[2] - left[2] - up[2]; + tess.vertexPtr[ndx+2].fogNum = (float)tess.fogNum; + + tess.vertexPtr[ndx+3].xyz[0] = origin[0] + left[0] - up[0]; + tess.vertexPtr[ndx+3].xyz[1] = origin[1] + left[1] - up[1]; + tess.vertexPtr[ndx+3].xyz[2] = origin[2] + left[2] - up[2]; + tess.vertexPtr[ndx+3].fogNum = (float)tess.fogNum; + + // sprites don't use normal, so I reuse it to store the + // shadertimes + tess.vertexPtr[ndx+0].normal[0] = tess.shaderTime; + tess.vertexPtr[ndx+0].normal[1] = 0.0f; + tess.vertexPtr[ndx+0].normal[2] = 0.0f; + tess.vertexPtr[ndx+1].normal[0] = tess.shaderTime; + tess.vertexPtr[ndx+1].normal[1] = 0.0f; + tess.vertexPtr[ndx+1].normal[2] = 0.0f; + tess.vertexPtr[ndx+2].normal[0] = tess.shaderTime; + tess.vertexPtr[ndx+2].normal[1] = 0.0f; + tess.vertexPtr[ndx+2].normal[2] = 0.0f; + tess.vertexPtr[ndx+3].normal[0] = tess.shaderTime; + tess.vertexPtr[ndx+3].normal[1] = 0.0f; + tess.vertexPtr[ndx+3].normal[2] = 0.0f; - tess.xyz[ndx+2][0] = origin[0] - left[0] - up[0]; - tess.xyz[ndx+2][1] = origin[1] - left[1] - up[1]; - tess.xyz[ndx+2][2] = origin[2] - left[2] - up[2]; - - tess.xyz[ndx+3][0] = origin[0] + left[0] - up[0]; - tess.xyz[ndx+3][1] = origin[1] + left[1] - up[1]; - tess.xyz[ndx+3][2] = origin[2] + left[2] - up[2]; - - - // constant normal all the way around - VectorSubtract( vec3_origin, backEnd.viewParms.or.axis[0], normal ); - - tess.normal[ndx][0] = tess.normal[ndx+1][0] = tess.normal[ndx+2][0] = tess.normal[ndx+3][0] = normal[0]; - tess.normal[ndx][1] = tess.normal[ndx+1][1] = tess.normal[ndx+2][1] = tess.normal[ndx+3][1] = normal[1]; - tess.normal[ndx][2] = tess.normal[ndx+1][2] = tess.normal[ndx+2][2] = tess.normal[ndx+3][2] = normal[2]; - // standard square texture coordinates - tess.texCoords[ndx][0][0] = tess.texCoords[ndx][1][0] = s1; - tess.texCoords[ndx][0][1] = tess.texCoords[ndx][1][1] = t1; + tess.vertexPtr[ndx].tc1[0] = tess.vertexPtr[ndx].tc2[0] = s1; + tess.vertexPtr[ndx].tc1[1] = tess.vertexPtr[ndx].tc2[1] = t1; - tess.texCoords[ndx+1][0][0] = tess.texCoords[ndx+1][1][0] = s2; - tess.texCoords[ndx+1][0][1] = tess.texCoords[ndx+1][1][1] = t1; + tess.vertexPtr[ndx+1].tc1[0] = tess.vertexPtr[ndx+1].tc2[0] = s2; + tess.vertexPtr[ndx+1].tc1[1] = tess.vertexPtr[ndx+1].tc2[1] = t1; - tess.texCoords[ndx+2][0][0] = tess.texCoords[ndx+2][1][0] = s2; - tess.texCoords[ndx+2][0][1] = tess.texCoords[ndx+2][1][1] = t2; + tess.vertexPtr[ndx+2].tc1[0] = tess.vertexPtr[ndx+2].tc2[0] = s2; + tess.vertexPtr[ndx+2].tc1[1] = tess.vertexPtr[ndx+2].tc2[1] = t2; - tess.texCoords[ndx+3][0][0] = tess.texCoords[ndx+3][1][0] = s1; - tess.texCoords[ndx+3][0][1] = tess.texCoords[ndx+3][1][1] = t2; + tess.vertexPtr[ndx+3].tc1[0] = tess.vertexPtr[ndx+3].tc2[0] = s1; + tess.vertexPtr[ndx+3].tc1[1] = tess.vertexPtr[ndx+3].tc2[1] = t2; // constant color all the way around // should this be identity and let the shader specify from entity? - * ( unsigned int * ) &tess.vertexColors[ndx] = - * ( unsigned int * ) &tess.vertexColors[ndx+1] = - * ( unsigned int * ) &tess.vertexColors[ndx+2] = - * ( unsigned int * ) &tess.vertexColors[ndx+3] = - * ( unsigned int * )color; + tess.vertexPtr[ndx].color[0] = color[0]; + tess.vertexPtr[ndx].color[1] = color[1]; + tess.vertexPtr[ndx].color[2] = color[2]; + tess.vertexPtr[ndx].color[3] = color[3]; + tess.vertexPtr[ndx+1].color[0] = color[0]; + tess.vertexPtr[ndx+1].color[1] = color[1]; + tess.vertexPtr[ndx+1].color[2] = color[2]; + tess.vertexPtr[ndx+1].color[3] = color[3]; + tess.vertexPtr[ndx+2].color[0] = color[0]; + tess.vertexPtr[ndx+2].color[1] = color[1]; + tess.vertexPtr[ndx+2].color[2] = color[2]; + tess.vertexPtr[ndx+2].color[3] = color[3]; + tess.vertexPtr[ndx+3].color[0] = color[0]; + tess.vertexPtr[ndx+3].color[1] = color[1]; + tess.vertexPtr[ndx+3].color[2] = color[2]; + tess.vertexPtr[ndx+3].color[3] = color[3]; + + if ( tess.minIndex[tess.indexRange] > ndx ) + tess.minIndex[tess.indexRange] = ndx; + if ( tess.maxIndex[tess.indexRange] < ndx + 3 ) + tess.maxIndex[tess.indexRange] = ndx + 3; + // triangle indexes for a simple quad + tess.indexPtr[ tess.numIndexes[tess.indexRange] ] = ndx; + tess.indexPtr[ tess.numIndexes[tess.indexRange] + 1 ] = ndx + 1; + tess.indexPtr[ tess.numIndexes[tess.indexRange] + 2 ] = ndx + 3; + tess.indexPtr[ tess.numIndexes[tess.indexRange] + 3 ] = ndx + 3; + tess.indexPtr[ tess.numIndexes[tess.indexRange] + 4 ] = ndx + 1; + tess.indexPtr[ tess.numIndexes[tess.indexRange] + 5 ] = ndx + 2; + tess.numVertexes += 4; - tess.numIndexes += 6; + tess.numIndexes[tess.indexRange] += 6; } /* @@ -153,10 +153,16 @@ void RB_AddQuadStamp( vec3_t origin, vec3_t left, vec3_t up, byte *color ) { RB_SurfaceSprite ============== */ -static void RB_SurfaceSprite( void ) { +static void RB_SurfaceSprite( tessMode_t mode ) { vec3_t left, up; float radius; + if( mode == TESS_COUNT ) { + tess.numVertexes += 4; + tess.numIndexes[tess.indexRange] += 6; + return; + } + // calculate the xyz locations for the four corners radius = backEnd.currentEntity->e.radius; if ( backEnd.currentEntity->e.rotation == 0 ) { @@ -180,7 +186,8 @@ static void RB_SurfaceSprite( void ) { VectorSubtract( vec3_origin, left, left ); } - RB_AddQuadStamp( backEnd.currentEntity->e.origin, left, up, backEnd.currentEntity->e.shaderRGBA ); + RB_AddQuadStamp( backEnd.currentEntity->e.origin, left, up, + backEnd.currentEntity->e.shaderRGBA ); } @@ -189,32 +196,49 @@ static void RB_SurfaceSprite( void ) { RB_SurfacePolychain ============= */ -static void RB_SurfacePolychain( srfPoly_t *p ) { +static void RB_SurfacePolychain( tessMode_t mode, surfaceType_t *surface ) { + srfPoly_t *p = (srfPoly_t *)surface; int i; int numv; + glVertex_t *vertexPtr; - RB_CHECKOVERFLOW( p->numVerts, 3*(p->numVerts - 2) ); - - // fan triangles into the tess array - numv = tess.numVertexes; - for ( i = 0; i < p->numVerts; i++ ) { - VectorCopy( p->verts[i].xyz, tess.xyz[numv] ); - tess.texCoords[numv][0][0] = p->verts[i].st[0]; - tess.texCoords[numv][0][1] = p->verts[i].st[1]; - *(int *)&tess.vertexColors[numv] = *(int *)p->verts[ i ].modulate; - - numv++; + if ( mode & TESS_VERTEX ) { + vertexPtr = tess.vertexPtr + tess.numVertexes; + + // fan triangles into the tess array + numv = tess.numVertexes; + for ( i = 0; i < p->numVerts; i++ ) { + VectorCopy ( p->verts[i].xyz, *(vec3_t *)vertexPtr->xyz ); + vertexPtr->tc1[0] = p->verts[i].st[0]; + vertexPtr->tc1[1] = p->verts[i].st[1]; + *(int *)(&vertexPtr->color) = *(int *)p->verts[ i ].modulate; + vertexPtr->fogNum = tess.fogNum; + + vertexPtr++; + } + } else { + numv = 0; //ERROR, should never have coordinates in VBO } - // generate fan indexes into the tess array - for ( i = 0; i < p->numVerts-2; i++ ) { - tess.indexes[tess.numIndexes + 0] = tess.numVertexes; - tess.indexes[tess.numIndexes + 1] = tess.numVertexes + i + 1; - tess.indexes[tess.numIndexes + 2] = tess.numVertexes + i + 2; - tess.numIndexes += 3; - } + if ( mode & TESS_INDEX ) { + glIndex_t *indexPtr = tess.indexPtr + tess.numIndexes[tess.indexRange]; - tess.numVertexes = numv; + if ( tess.minIndex[tess.indexRange] > numv ) + tess.minIndex[tess.indexRange] = numv; + if ( tess.maxIndex[tess.indexRange] < numv + p->numVerts - 1 ) + tess.maxIndex[tess.indexRange] = numv + p->numVerts - 1; + + // generate fan indexes into the tess array + for ( i = 0; i < p->numVerts-2; i++ ) { + indexPtr[0] = numv; + indexPtr[1] = numv + i + 1; + indexPtr[2] = numv + i + 2; + indexPtr += 3; + } + } + + tess.numVertexes += p->numVerts; + tess.numIndexes[tess.indexRange] += 3*(p->numVerts - 2); } @@ -223,57 +247,55 @@ static void RB_SurfacePolychain( srfPoly_t *p ) { RB_SurfaceTriangles ============= */ -static void RB_SurfaceTriangles( srfTriangles_t *srf ) { - int i; +static void RB_SurfaceTriangles( tessMode_t mode, surfaceType_t *surface ) { + srfTriangles_t *srf = (srfTriangles_t *)surface; + int i; drawVert_t *dv; - float *xyz, *normal, *texCoords; - byte *color; - int dlightBits; - qboolean needsNormal; + int dlightBits; + glIndex_t *indexPtr; + glVertex_t *vertexPtr; + int numv; dlightBits = srf->dlightBits[backEnd.smpFrame]; tess.dlightBits |= dlightBits; - RB_CHECKOVERFLOW( srf->numVerts, srf->numIndexes ); + if ( mode & TESS_VERTEX ) { + numv = tess.numVertexes; + + vertexPtr = tess.vertexPtr + numv; - for ( i = 0 ; i < srf->numIndexes ; i += 3 ) { - tess.indexes[ tess.numIndexes + i + 0 ] = tess.numVertexes + srf->indexes[ i + 0 ]; - tess.indexes[ tess.numIndexes + i + 1 ] = tess.numVertexes + srf->indexes[ i + 1 ]; - tess.indexes[ tess.numIndexes + i + 2 ] = tess.numVertexes + srf->indexes[ i + 2 ]; - } - tess.numIndexes += srf->numIndexes; - - dv = srf->verts; - xyz = tess.xyz[ tess.numVertexes ]; - normal = tess.normal[ tess.numVertexes ]; - texCoords = tess.texCoords[ tess.numVertexes ][0]; - color = tess.vertexColors[ tess.numVertexes ]; - needsNormal = tess.shader->needsNormal; - - for ( i = 0 ; i < srf->numVerts ; i++, dv++, xyz += 4, normal += 4, texCoords += 4, color += 4 ) { - xyz[0] = dv->xyz[0]; - xyz[1] = dv->xyz[1]; - xyz[2] = dv->xyz[2]; - - if ( needsNormal ) { - normal[0] = dv->normal[0]; - normal[1] = dv->normal[1]; - normal[2] = dv->normal[2]; + dv = srf->verts; + + for ( i = 0 ; i < srf->numVerts ; i++, dv++ ) { + VectorCopy( dv->xyz, *(vec3_t *)vertexPtr->xyz ); + VectorCopy( dv->normal, vertexPtr->normal ); + vertexPtr->tc1[0] = dv->st[0]; + vertexPtr->tc1[1] = dv->st[1]; + vertexPtr->tc2[0] = dv->lightmap[0]; + vertexPtr->tc2[1] = dv->lightmap[1]; + *(int *)&vertexPtr->color = *(int *)dv->color; + vertexPtr->fogNum = tess.fogNum; + vertexPtr++; } - - texCoords[0] = dv->st[0]; - texCoords[1] = dv->st[1]; - - texCoords[2] = dv->lightmap[0]; - texCoords[3] = dv->lightmap[1]; - - *(int *)color = *(int *)dv->color; + } else { + numv = srf->vboStart; } - for ( i = 0 ; i < srf->numVerts ; i++ ) { - tess.vertexDlightBits[ tess.numVertexes + i] = dlightBits; + if ( mode & TESS_INDEX ) { + if ( tess.minIndex[tess.indexRange] > numv ) + tess.minIndex[tess.indexRange] = numv; + if ( tess.maxIndex[tess.indexRange] < numv + srf->numVerts - 1 ) + tess.maxIndex[tess.indexRange] = numv + srf->numVerts - 1; + + indexPtr = tess.indexPtr + tess.numIndexes[tess.indexRange]; + for ( i = 0 ; i < srf->numIndexes ; i += 3 ) { + indexPtr[ i + 0 ] = numv + srf->indexes[ i + 0 ]; + indexPtr[ i + 1 ] = numv + srf->indexes[ i + 1 ]; + indexPtr[ i + 2 ] = numv + srf->indexes[ i + 2 ]; + } } + tess.numIndexes[tess.indexRange] += srf->numIndexes; tess.numVertexes += srf->numVerts; } @@ -284,15 +306,21 @@ static void RB_SurfaceTriangles( srfTriangles_t *srf ) { RB_SurfaceBeam ============== */ -static void RB_SurfaceBeam( void ) +static void RB_SurfaceBeam( tessMode_t mode ) { #define NUM_BEAM_SEGS 6 refEntity_t *e; int i; vec3_t perpvec; vec3_t direction, normalized_direction; - vec3_t start_points[NUM_BEAM_SEGS], end_points[NUM_BEAM_SEGS]; + vec3_t points[2 * (NUM_BEAM_SEGS+1)]; + vec3_t *start_point, *end_point; vec3_t oldorigin, origin; + glRenderState_t state; + + if( mode == TESS_COUNT ) { + return; + } e = &backEnd.currentEntity->e; @@ -315,98 +343,118 @@ static void RB_SurfaceBeam( void ) VectorScale( perpvec, 4, perpvec ); + start_point = &points[0]; + end_point = &points[1]; for ( i = 0; i < NUM_BEAM_SEGS ; i++ ) { - RotatePointAroundVector( start_points[i], normalized_direction, perpvec, (360.0/NUM_BEAM_SEGS)*i ); -// VectorAdd( start_points[i], origin, start_points[i] ); - VectorAdd( start_points[i], direction, end_points[i] ); + RotatePointAroundVector( *start_point, normalized_direction, perpvec, (360.0/NUM_BEAM_SEGS)*i ); +// VectorAdd( *start_point, origin, *start_point ); + VectorAdd( *start_point, direction, *end_point ); + + start_point += 2; end_point += 2; } + VectorCopy( points[0], *start_point ); + VectorCopy( points[1], *end_point ); - GL_Bind( tr.whiteImage ); + InitState( &state ); - GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + state.numImages = 1; + state.image[0] = tr.whiteImage; - qglColor3f( 1, 0, 0 ); + state.stateBits = GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE; - qglBegin( GL_TRIANGLE_STRIP ); - for ( i = 0; i <= NUM_BEAM_SEGS; i++ ) { - qglVertex3fv( start_points[ i % NUM_BEAM_SEGS] ); - qglVertex3fv( end_points[ i % NUM_BEAM_SEGS] ); - } - qglEnd(); + SetAttrVec4f( &state, AL_COLOR, 1.0f, 1.0f, 1.0f, 1.0f ); + + SetAttrPointer( &state, AL_VERTEX, 0, + 3, GL_FLOAT, sizeof(vec3_t), + points ); + GL_DrawArrays( &state, GL_TRIANGLE_STRIP, 0, 2*(NUM_BEAM_SEGS+1) ); } //================================================================================ -static void DoRailCore( const vec3_t start, const vec3_t end, const vec3_t up, float len, float spanWidth ) +static void DoRailCore( tessMode_t mode, + const vec3_t start, const vec3_t end, + const vec3_t up, float len, float spanWidth ) { float spanWidth2; - int vbase; + glIndex_t vbase; float t = len / 256.0f; - vbase = tess.numVertexes; - spanWidth2 = -spanWidth; - // FIXME: use quad stamp? - VectorMA( start, spanWidth, up, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0] * 0.25; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1] * 0.25; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2] * 0.25; - tess.numVertexes++; - - VectorMA( start, spanWidth2, up, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0]; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1]; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2]; - tess.numVertexes++; - - VectorMA( end, spanWidth, up, tess.xyz[tess.numVertexes] ); - - tess.texCoords[tess.numVertexes][0][0] = t; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0]; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1]; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2]; - tess.numVertexes++; - - VectorMA( end, spanWidth2, up, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = t; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0]; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1]; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2]; - tess.numVertexes++; - - tess.indexes[tess.numIndexes++] = vbase; - tess.indexes[tess.numIndexes++] = vbase + 1; - tess.indexes[tess.numIndexes++] = vbase + 2; - - tess.indexes[tess.numIndexes++] = vbase + 2; - tess.indexes[tess.numIndexes++] = vbase + 1; - tess.indexes[tess.numIndexes++] = vbase + 3; + if ( mode & TESS_VERTEX ) { + vbase = tess.numVertexes; + + if ( tess.minIndex[tess.indexRange] > vbase ) + tess.minIndex[tess.indexRange] = vbase; + if ( tess.maxIndex[tess.indexRange] < vbase + 3 ) + tess.maxIndex[tess.indexRange] = vbase + 3; + + // FIXME: use quad stamp? + VectorMA( start, spanWidth, up, tess.vertexPtr[vbase].xyz ); + tess.vertexPtr[vbase].tc1[0] = 0; + tess.vertexPtr[vbase].tc1[1] = 0; + tess.vertexPtr[vbase].color[0] = backEnd.currentEntity->e.shaderRGBA[0] * 0.25; + tess.vertexPtr[vbase].color[1] = backEnd.currentEntity->e.shaderRGBA[1] * 0.25; + tess.vertexPtr[vbase].color[2] = backEnd.currentEntity->e.shaderRGBA[2] * 0.25; + + VectorMA( start, spanWidth2, up, tess.vertexPtr[vbase+1].xyz ); + tess.vertexPtr[vbase+1].tc1[0] = 0; + tess.vertexPtr[vbase+1].tc1[1] = 1; + tess.vertexPtr[vbase+1].color[0] = backEnd.currentEntity->e.shaderRGBA[0]; + tess.vertexPtr[vbase+1].color[1] = backEnd.currentEntity->e.shaderRGBA[1]; + tess.vertexPtr[vbase+1].color[2] = backEnd.currentEntity->e.shaderRGBA[2]; + + VectorMA( end, spanWidth, up, tess.vertexPtr[vbase+2].xyz ); + tess.vertexPtr[vbase+2].tc1[0] = t; + tess.vertexPtr[vbase+2].tc1[1] = 0; + tess.vertexPtr[vbase+2].color[0] = backEnd.currentEntity->e.shaderRGBA[0]; + tess.vertexPtr[vbase+2].color[1] = backEnd.currentEntity->e.shaderRGBA[1]; + tess.vertexPtr[vbase+2].color[2] = backEnd.currentEntity->e.shaderRGBA[2]; + + VectorMA( end, spanWidth2, up, tess.vertexPtr[vbase+3].xyz ); + tess.vertexPtr[vbase+3].tc1[0] = t; + tess.vertexPtr[vbase+3].tc1[1] = 1; + tess.vertexPtr[vbase+3].color[0] = backEnd.currentEntity->e.shaderRGBA[0]; + tess.vertexPtr[vbase+3].color[1] = backEnd.currentEntity->e.shaderRGBA[1]; + tess.vertexPtr[vbase+3].color[2] = backEnd.currentEntity->e.shaderRGBA[2]; + } else { + vbase = 0; + } + + if ( mode & TESS_INDEX ) { + int idx = tess.numIndexes[tess.indexRange]; + tess.indexPtr[idx] = vbase; + tess.indexPtr[idx+1] = vbase + 1; + tess.indexPtr[idx+2] = vbase + 2; + + tess.indexPtr[idx+3] = vbase + 2; + tess.indexPtr[idx+4] = vbase + 1; + tess.indexPtr[idx+5] = vbase + 3; + } + + tess.numVertexes += 4; + tess.numIndexes[tess.indexRange] += 6; } -static void DoRailDiscs( int numSegs, const vec3_t start, const vec3_t dir, const vec3_t right, const vec3_t up ) +static void DoRailDiscs( tessMode_t mode, int numSegs, + const vec3_t start, const vec3_t dir, + const vec3_t right, const vec3_t up ) { int i; vec3_t pos[4]; vec3_t v; int spanWidth = r_railWidth->integer; + glIndex_t vbase; float c, s; - float scale; + float scale = 0.25; if ( numSegs > 1 ) numSegs--; if ( !numSegs ) return; - scale = 0.25; - for ( i = 0; i < 4; i++ ) { c = cos( DEG2RAD( 45 + i * 90 ) ); @@ -423,38 +471,52 @@ static void DoRailDiscs( int numSegs, const vec3_t start, const vec3_t dir, cons } } - for ( i = 0; i < numSegs; i++ ) - { - int j; - - RB_CHECKOVERFLOW( 4, 6 ); - - for ( j = 0; j < 4; j++ ) + if ( mode & TESS_VERTEX ) { + vbase = tess.numVertexes; + for ( i = 0; i < numSegs; i++ ) { - VectorCopy( pos[j], tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = ( j < 2 ); - tess.texCoords[tess.numVertexes][0][1] = ( j && j != 3 ); - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0]; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1]; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2]; - tess.numVertexes++; - - VectorAdd( pos[j], dir, pos[j] ); + int j; + + for ( j = 0; j < 4; j++ ) + { + VectorCopy( pos[j], tess.vertexPtr[vbase].xyz ); + tess.vertexPtr[vbase].tc1[0] = ( j < 2 ); + tess.vertexPtr[vbase].tc1[1] = ( j && j != 3 ); + tess.vertexPtr[vbase].color[0] = backEnd.currentEntity->e.shaderRGBA[0]; + tess.vertexPtr[vbase].color[1] = backEnd.currentEntity->e.shaderRGBA[1]; + tess.vertexPtr[vbase].color[2] = backEnd.currentEntity->e.shaderRGBA[2]; + vbase++; + + VectorAdd( pos[j], dir, pos[j] ); + } } + vbase = tess.numVertexes; + } else { + vbase = 0; + } - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 0; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 1; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 3; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 3; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 1; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 2; + if ( mode & TESS_INDEX ) { + for ( i = 0; i < numSegs; i++ ) + { + int iwrite = tess.numIndexes[tess.indexRange]; + tess.indexPtr[iwrite++] = vbase + 0; + tess.indexPtr[iwrite++] = vbase + 1; + tess.indexPtr[iwrite++] = vbase + 3; + tess.indexPtr[iwrite++] = vbase + 3; + tess.indexPtr[iwrite++] = vbase + 1; + tess.indexPtr[iwrite++] = vbase + 2; + vbase += 4; + } } + + tess.numVertexes += numSegs * 4; + tess.numIndexes[tess.indexRange] += numSegs * 6; } /* ** RB_SurfaceRailRinges */ -static void RB_SurfaceRailRings( void ) { +static void RB_SurfaceRailRings( tessMode_t mode ) { refEntity_t *e; int numSegs; int len; @@ -478,13 +540,13 @@ static void RB_SurfaceRailRings( void ) { VectorScale( vec, r_railSegmentLength->value, vec ); - DoRailDiscs( numSegs, start, vec, right, up ); + DoRailDiscs( mode, numSegs, start, vec, right, up ); } /* ** RB_SurfaceRailCore */ -static void RB_SurfaceRailCore( void ) { +static void RB_SurfaceRailCore( tessMode_t mode ) { refEntity_t *e; int len; vec3_t right; @@ -508,13 +570,13 @@ static void RB_SurfaceRailCore( void ) { CrossProduct( v1, v2, right ); VectorNormalize( right ); - DoRailCore( start, end, right, len, r_railCoreWidth->integer ); + DoRailCore( mode, start, end, right, len, r_railCoreWidth->integer ); } /* ** RB_SurfaceLightningBolt */ -static void RB_SurfaceLightningBolt( void ) { +static void RB_SurfaceLightningBolt( tessMode_t mode ) { refEntity_t *e; int len; vec3_t right; @@ -543,68 +605,12 @@ static void RB_SurfaceLightningBolt( void ) { for ( i = 0 ; i < 4 ; i++ ) { vec3_t temp; - DoRailCore( start, end, right, len, 8 ); + DoRailCore( mode, start, end, right, len, 8 ); RotatePointAroundVector( temp, vec, right, 45 ); VectorCopy( temp, right ); } } -/* -** VectorArrayNormalize -* -* The inputs to this routing seem to always be close to length = 1.0 (about 0.6 to 2.0) -* This means that we don't have to worry about zero length or enormously long vectors. -*/ -static void VectorArrayNormalize(vec4_t *normals, unsigned int count) -{ -// assert(count); - -#if idppc - { - register float half = 0.5; - register float one = 1.0; - float *components = (float *)normals; - - // Vanilla PPC code, but since PPC has a reciprocal square root estimate instruction, - // runs *much* faster than calling sqrt(). We'll use a single Newton-Raphson - // refinement step to get a little more precision. This seems to yeild results - // that are correct to 3 decimal places and usually correct to at least 4 (sometimes 5). - // (That is, for the given input range of about 0.6 to 2.0). - do { - float x, y, z; - float B, y0, y1; - - x = components[0]; - y = components[1]; - z = components[2]; - components += 4; - B = x*x + y*y + z*z; - -#ifdef __GNUC__ - asm("frsqrte %0,%1" : "=f" (y0) : "f" (B)); -#else - y0 = __frsqrte(B); -#endif - y1 = y0 + half*y0*(one - B*y0*y0); - - x = x * y1; - y = y * y1; - components[-4] = x; - z = z * y1; - components[-3] = y; - components[-2] = z; - } while(count--); - } -#else // No assembly version for this architecture, or C_ONLY defined - // given the input, it's safe to call VectorNormalizeFast - while (count--) { - VectorNormalizeFast(normals[0]); - normals++; - } -#endif - -} - /* @@ -614,17 +620,16 @@ static void VectorArrayNormalize(vec4_t *normals, unsigned int count) static void LerpMeshVertexes_altivec(md3Surface_t *surf, float backlerp) { short *oldXyz, *newXyz, *oldNormals, *newNormals; - float *outXyz, *outNormal; - float oldXyzScale QALIGN(16); - float newXyzScale QALIGN(16); - float oldNormalScale QALIGN(16); - float newNormalScale QALIGN(16); + glVertex_t *vertexPtr; + float oldXyzScale ALIGNED(16); + float newXyzScale ALIGNED(16); + float oldNormalScale ALIGNED(16); + float newNormalScale ALIGNED(16); int vertNum; unsigned lat, lng; int numVerts; - outXyz = tess.xyz[tess.numVertexes]; - outNormal = tess.normal[tess.numVertexes]; + vertexPtr = tess.vertexPtr + tess.numVertexes; newXyz = (short *)((byte *)surf + surf->ofsXyzNormals) + (backEnd.currentEntity->e.frame * surf->numVerts * 4); @@ -656,8 +661,7 @@ static void LerpMeshVertexes_altivec(md3Surface_t *surf, float backlerp) // just copy the vertexes // for (vertNum=0 ; vertNum < numVerts ; vertNum++, - newXyz += 4, newNormals += 4, - outXyz += 4, outNormal += 4) + newXyz += 4, newNormals += 4, vertexPtr++ ) { newNormalsLoadPermute = vec_lvsl(0,newXyz); newNormalsStorePermute = vec_lvsr(0,outXyz); @@ -681,13 +685,13 @@ static void LerpMeshVertexes_altivec(md3Surface_t *surf, float backlerp) // decode Y as sin( lat ) * sin( long ) // decode Z as cos( long ) - outNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - outNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - outNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + vertexPtr->normal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + vertexPtr->normal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + vertexPtr->normal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - vec_ste(newNormalsFloatVec,0,outXyz); - vec_ste(newNormalsFloatVec,4,outXyz); - vec_ste(newNormalsFloatVec,8,outXyz); + vec_ste(newNormalsFloatVec,0,vertexPtr->xyz); + vec_ste(newNormalsFloatVec,4,vertexPtr->xyz); + vec_ste(newNormalsFloatVec,8,vertexPtr->xyz); } } else { // @@ -701,15 +705,15 @@ static void LerpMeshVertexes_altivec(md3Surface_t *surf, float backlerp) oldNormalScale = backlerp; for (vertNum=0 ; vertNum < numVerts ; vertNum++, - oldXyz += 4, newXyz += 4, oldNormals += 4, newNormals += 4, - outXyz += 4, outNormal += 4) + oldXyz += 4, newXyz += 4, oldNormals += 4, + newNormals += 4, vertexPtr++ ) { vec3_t uncompressedOldNormal, uncompressedNewNormal; // interpolate the xyz - outXyz[0] = oldXyz[0] * oldXyzScale + newXyz[0] * newXyzScale; - outXyz[1] = oldXyz[1] * oldXyzScale + newXyz[1] * newXyzScale; - outXyz[2] = oldXyz[2] * oldXyzScale + newXyz[2] * newXyzScale; + vertexPtr->xyz[0] = oldXyz[0] * oldXyzScale + newXyz[0] * newXyzScale; + vertexPtr->xyz[1] = oldXyz[1] * oldXyzScale + newXyz[1] * newXyzScale; + vertexPtr->xyz[2] = oldXyz[2] * oldXyzScale + newXyz[2] * newXyzScale; // FIXME: interpolate lat/long instead? lat = ( newNormals[0] >> 8 ) & 0xff; @@ -729,13 +733,12 @@ static void LerpMeshVertexes_altivec(md3Surface_t *surf, float backlerp) uncompressedOldNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; uncompressedOldNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - outNormal[0] = uncompressedOldNormal[0] * oldNormalScale + uncompressedNewNormal[0] * newNormalScale; - outNormal[1] = uncompressedOldNormal[1] * oldNormalScale + uncompressedNewNormal[1] * newNormalScale; - outNormal[2] = uncompressedOldNormal[2] * oldNormalScale + uncompressedNewNormal[2] * newNormalScale; + vertexPtr->normal[0] = uncompressedOldNormal[0] * oldNormalScale + uncompressedNewNormal[0] * newNormalScale; + vertexPtr->normal[1] = uncompressedOldNormal[1] * oldNormalScale + uncompressedNewNormal[1] * newNormalScale; + vertexPtr->normal[2] = uncompressedOldNormal[2] * oldNormalScale + uncompressedNewNormal[2] * newNormalScale; -// VectorNormalize (outNormal); + VectorNormalize (vertexPtr->normal); } - VectorArrayNormalize((vec4_t *)tess.normal[tess.numVertexes], numVerts); } } #endif @@ -743,15 +746,14 @@ static void LerpMeshVertexes_altivec(md3Surface_t *surf, float backlerp) static void LerpMeshVertexes_scalar(md3Surface_t *surf, float backlerp) { short *oldXyz, *newXyz, *oldNormals, *newNormals; - float *outXyz, *outNormal; + glVertex_t *vertexPtr; float oldXyzScale, newXyzScale; float oldNormalScale, newNormalScale; int vertNum; unsigned lat, lng; int numVerts; - outXyz = tess.xyz[tess.numVertexes]; - outNormal = tess.normal[tess.numVertexes]; + vertexPtr = tess.vertexPtr + tess.numVertexes; newXyz = (short *)((byte *)surf + surf->ofsXyzNormals) + (backEnd.currentEntity->e.frame * surf->numVerts * 4); @@ -767,13 +769,12 @@ static void LerpMeshVertexes_scalar(md3Surface_t *surf, float backlerp) // just copy the vertexes // for (vertNum=0 ; vertNum < numVerts ; vertNum++, - newXyz += 4, newNormals += 4, - outXyz += 4, outNormal += 4) + newXyz += 4, newNormals += 4, vertexPtr++ ) { - outXyz[0] = newXyz[0] * newXyzScale; - outXyz[1] = newXyz[1] * newXyzScale; - outXyz[2] = newXyz[2] * newXyzScale; + vertexPtr->xyz[0] = newXyz[0] * newXyzScale; + vertexPtr->xyz[1] = newXyz[1] * newXyzScale; + vertexPtr->xyz[2] = newXyz[2] * newXyzScale; lat = ( newNormals[0] >> 8 ) & 0xff; lng = ( newNormals[0] & 0xff ); @@ -784,9 +785,9 @@ static void LerpMeshVertexes_scalar(md3Surface_t *surf, float backlerp) // decode Y as sin( lat ) * sin( long ) // decode Z as cos( long ) - outNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - outNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - outNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + vertexPtr->normal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + vertexPtr->normal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + vertexPtr->normal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; } } else { // @@ -800,15 +801,15 @@ static void LerpMeshVertexes_scalar(md3Surface_t *surf, float backlerp) oldNormalScale = backlerp; for (vertNum=0 ; vertNum < numVerts ; vertNum++, - oldXyz += 4, newXyz += 4, oldNormals += 4, newNormals += 4, - outXyz += 4, outNormal += 4) + oldXyz += 4, newXyz += 4, oldNormals += 4, + newNormals += 4, vertexPtr++ ) { vec3_t uncompressedOldNormal, uncompressedNewNormal; // interpolate the xyz - outXyz[0] = oldXyz[0] * oldXyzScale + newXyz[0] * newXyzScale; - outXyz[1] = oldXyz[1] * oldXyzScale + newXyz[1] * newXyzScale; - outXyz[2] = oldXyz[2] * oldXyzScale + newXyz[2] * newXyzScale; + vertexPtr->xyz[0] = oldXyz[0] * oldXyzScale + newXyz[0] * newXyzScale; + vertexPtr->xyz[1] = oldXyz[1] * oldXyzScale + newXyz[1] * newXyzScale; + vertexPtr->xyz[2] = oldXyz[2] * oldXyzScale + newXyz[2] * newXyzScale; // FIXME: interpolate lat/long instead? lat = ( newNormals[0] >> 8 ) & 0xff; @@ -828,42 +829,31 @@ static void LerpMeshVertexes_scalar(md3Surface_t *surf, float backlerp) uncompressedOldNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; uncompressedOldNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - outNormal[0] = uncompressedOldNormal[0] * oldNormalScale + uncompressedNewNormal[0] * newNormalScale; - outNormal[1] = uncompressedOldNormal[1] * oldNormalScale + uncompressedNewNormal[1] * newNormalScale; - outNormal[2] = uncompressedOldNormal[2] * oldNormalScale + uncompressedNewNormal[2] * newNormalScale; + vertexPtr->normal[0] = uncompressedOldNormal[0] * oldNormalScale + uncompressedNewNormal[0] * newNormalScale; + vertexPtr->normal[1] = uncompressedOldNormal[1] * oldNormalScale + uncompressedNewNormal[1] * newNormalScale; + vertexPtr->normal[2] = uncompressedOldNormal[2] * oldNormalScale + uncompressedNewNormal[2] * newNormalScale; -// VectorNormalize (outNormal); + VectorNormalize (vertexPtr->normal); } - VectorArrayNormalize((vec4_t *)tess.normal[tess.numVertexes], numVerts); } } -static void LerpMeshVertexes(md3Surface_t *surf, float backlerp) -{ -#if idppc_altivec - if (com_altivec->integer) { - // must be in a seperate function or G3 systems will crash. - LerpMeshVertexes_altivec( surf, backlerp ); - return; - } -#endif // idppc_altivec - LerpMeshVertexes_scalar( surf, backlerp ); -} - /* ============= RB_SurfaceMesh ============= */ -static void RB_SurfaceMesh(md3Surface_t *surface) { +#if idppc_altivec +static void RB_SurfaceMesh_altivec( tessMode_t mode, md3Surface_t *surface ) { int j; float backlerp; int *triangles; float *texCoords; int indexes; - int Bob, Doug; + int Doug; int numVerts; + glVertex_t *vertexPtr; if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) { backlerp = 0; @@ -871,30 +861,111 @@ static void RB_SurfaceMesh(md3Surface_t *surface) { backlerp = backEnd.currentEntity->e.backlerp; } - RB_CHECKOVERFLOW( surface->numVerts, surface->numTriangles*3 ); + Doug = tess.numVertexes; - LerpMeshVertexes (surface, backlerp); + if ( mode & TESS_VERTEX ) { + LerpMeshVertexes_altivec (surface, backlerp); + + texCoords = (float *) ((byte *)surface + surface->ofsSt); + vertexPtr = tess.vertexPtr + Doug; + + numVerts = surface->numVerts; + for ( j = 0; j < numVerts; j++, vertexPtr++ ) { + vertexPtr->tc1[0] = vertexPtr->tc2[0] = texCoords[j*2+0]; + vertexPtr->tc1[1] = vertexPtr->tc2[1] = texCoords[j*2+1]; + vertexPtr->fogNum = tess.fogNum; + vertexPtr->color[0] = 255; + vertexPtr->color[1] = 255; + vertexPtr->color[2] = 255; + vertexPtr->color[3] = 255; + } + } - triangles = (int *) ((byte *)surface + surface->ofsTriangles); - indexes = surface->numTriangles * 3; - Bob = tess.numIndexes; - Doug = tess.numVertexes; - for (j = 0 ; j < indexes ; j++) { - tess.indexes[Bob + j] = Doug + triangles[j]; + if ( mode & TESS_INDEX ) { + glIndex_t *indexPtr = tess.indexPtr + tess.numIndexes[tess.indexRange]; + + if ( tess.minIndex[tess.indexRange] > Doug ) + tess.minIndex[tess.indexRange] = Doug; + if ( tess.maxIndex[tess.indexRange] < Doug + surface->numVerts - 1 ) + tess.maxIndex[tess.indexRange] = Doug + surface->numVerts - 1; + + triangles = (int *) ((byte *)surface + surface->ofsTriangles); + indexes = surface->numTriangles * 3; + + for (j = 0 ; j < indexes ; j++) { + *indexPtr++ = Doug + triangles[j]; + } } - tess.numIndexes += indexes; + tess.numVertexes += surface->numVerts; + tess.numIndexes[tess.indexRange] += 3*surface->numTriangles; +} +#endif - texCoords = (float *) ((byte *)surface + surface->ofsSt); +static void RB_SurfaceMesh_scalar( tessMode_t mode, md3Surface_t *surface ) { + int j; + float backlerp; + int *triangles; + float *texCoords; + int indexes; + int Doug; + int numVerts; + glVertex_t *vertexPtr; + + Doug = tess.numVertexes; + + if ( mode & TESS_VERTEX ) { + if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) { + backlerp = 0; + } else { + backlerp = backEnd.currentEntity->e.backlerp; + } + + LerpMeshVertexes_scalar (surface, backlerp); - numVerts = surface->numVerts; - for ( j = 0; j < numVerts; j++ ) { - tess.texCoords[Doug + j][0][0] = texCoords[j*2+0]; - tess.texCoords[Doug + j][0][1] = texCoords[j*2+1]; - // FIXME: fill in lightmapST for completeness? + texCoords = (float *) ((byte *)surface + surface->ofsSt); + vertexPtr = tess.vertexPtr + Doug; + + numVerts = surface->numVerts; + for ( j = 0; j < numVerts; j++ ) { + vertexPtr->tc1[0] = vertexPtr->tc2[0] = texCoords[j*2+0]; + vertexPtr->tc1[1] = vertexPtr->tc2[1] = texCoords[j*2+1]; + vertexPtr->fogNum = tess.fogNum; + vertexPtr->color[0] = 255; + vertexPtr->color[1] = 255; + vertexPtr->color[2] = 255; + vertexPtr->color[3] = 255; + vertexPtr++; + } } + if ( mode & TESS_INDEX ) { + glIndex_t *indexPtr = tess.indexPtr + tess.numIndexes[tess.indexRange]; + + if ( tess.minIndex[tess.indexRange] > Doug ) + tess.minIndex[tess.indexRange] = Doug; + if ( tess.maxIndex[tess.indexRange] < Doug + surface->numVerts - 1 ) + tess.maxIndex[tess.indexRange] = Doug + surface->numVerts - 1; + + triangles = (int *) ((byte *)surface + surface->ofsTriangles); + indexes = surface->numTriangles * 3; + + for (j = 0 ; j < indexes ; j++) { + *indexPtr++ = Doug + triangles[j]; + } + } tess.numVertexes += surface->numVerts; + tess.numIndexes[tess.indexRange] += 3*surface->numTriangles; +} +void RB_SurfaceMesh( tessMode_t mode, surfaceType_t *surf) { + md3Surface_t *surface = (md3Surface_t *)surf; +#if idppc_altivec + if (com_altivec->integer) { + RB_SurfaceMesh_altivec( mode, surface ); + return; + } +#endif // idppc_altivec + RB_SurfaceMesh_scalar( mode, surface ); } @@ -903,52 +974,68 @@ static void RB_SurfaceMesh(md3Surface_t *surface) { RB_SurfaceFace ============== */ -static void RB_SurfaceFace( srfSurfaceFace_t *surf ) { +#if id386_sse >= 2 +void RB_SurfaceFace_sse2( tessMode_t mode, srfSurfaceFace_t *surf ); +#endif +static ID_INLINE void RB_SurfaceFace_scalar( tessMode_t mode, + srfSurfaceFace_t *surf ) { int i; - unsigned *indices, *tessIndexes; + unsigned *indices; float *v; - float *normal; - int ndx; int Bob; int numPoints; int dlightBits; - - RB_CHECKOVERFLOW( surf->numPoints, surf->numIndices ); + glVertex_t *vertexPtr; dlightBits = surf->dlightBits[backEnd.smpFrame]; tess.dlightBits |= dlightBits; - indices = ( unsigned * ) ( ( ( char * ) surf ) + surf->ofsIndices ); + if ( mode & TESS_VERTEX ) { + Bob = tess.numVertexes; - Bob = tess.numVertexes; - tessIndexes = tess.indexes + tess.numIndexes; - for ( i = surf->numIndices-1 ; i >= 0 ; i-- ) { - tessIndexes[i] = indices[i] + Bob; - } - - tess.numIndexes += surf->numIndices; - - numPoints = surf->numPoints; - - if ( tess.shader->needsNormal ) { - normal = surf->plane.normal; - for ( i = 0, ndx = tess.numVertexes; i < numPoints; i++, ndx++ ) { - VectorCopy( normal, tess.normal[ndx] ); + v = surf->points[0]; + + numPoints = surf->numPoints; + + vertexPtr = tess.vertexPtr + tess.numVertexes; + + for ( i = 0, v = surf->points[0]; i < numPoints; + i++, v += VERTEXSIZE, vertexPtr++ ) { + VectorCopy( surf->plane.normal, vertexPtr->normal ); + + VectorCopy ( v, vertexPtr->xyz); + vertexPtr->fogNum = tess.fogNum; + vertexPtr->tc1[0] = v[3]; + vertexPtr->tc1[1] = v[4]; + vertexPtr->tc2[0] = v[5]; + vertexPtr->tc2[1] = v[6]; + *(int *)&vertexPtr->color = *(int *)&v[7]; } + } else { + Bob = surf->vboStart; } - for ( i = 0, v = surf->points[0], ndx = tess.numVertexes; i < numPoints; i++, v += VERTEXSIZE, ndx++ ) { - VectorCopy( v, tess.xyz[ndx]); - tess.texCoords[ndx][0][0] = v[3]; - tess.texCoords[ndx][0][1] = v[4]; - tess.texCoords[ndx][1][0] = v[5]; - tess.texCoords[ndx][1][1] = v[6]; - * ( unsigned int * ) &tess.vertexColors[ndx] = * ( unsigned int * ) &v[7]; - tess.vertexDlightBits[ndx] = dlightBits; + if ( mode & TESS_INDEX ) { + glIndex_t *indexPtr = tess.indexPtr + tess.numIndexes[tess.indexRange]; + + if ( tess.minIndex[tess.indexRange] > Bob ) + tess.minIndex[tess.indexRange] = Bob; + if ( tess.maxIndex[tess.indexRange] < Bob + surf->numPoints - 1 ) + tess.maxIndex[tess.indexRange] = Bob + surf->numPoints - 1; + + indices = ( unsigned * ) ( ( ( char * ) surf ) + surf->ofsIndices ); + for ( i = surf->numIndices-1 ; i >= 0 ; i-- ) { + indexPtr[i] = indices[i] + Bob; + } } - tess.numVertexes += surf->numPoints; + tess.numIndexes[tess.indexRange] += surf->numIndices; +} + +static void RB_SurfaceFace( tessMode_t mode, surfaceType_t *surface ) { + srfSurfaceFace_t *surf = (srfSurfaceFace_t *)surface; + RB_SurfaceFace_scalar ( mode, surf ); } @@ -989,29 +1076,28 @@ RB_SurfaceGrid Just copy the grid of points and triangulate ============= */ -static void RB_SurfaceGrid( srfGridMesh_t *cv ) { +static void RB_SurfaceGrid( tessMode_t mode, surfaceType_t *surface ) { + srfGridMesh_t *cv = (srfGridMesh_t *)surface; int i, j; - float *xyz; - float *texCoords; - float *normal; - unsigned char *color; + glVertex_t *vertexPtr; drawVert_t *dv; - int rows, irows, vrows; - int used; int widthTable[MAX_GRID_SIZE]; int heightTable[MAX_GRID_SIZE]; float lodError; int lodWidth, lodHeight; - int numVertexes; + int baseVertex; int dlightBits; - int *vDlightBits; - qboolean needsNormal; dlightBits = cv->dlightBits[backEnd.smpFrame]; tess.dlightBits |= dlightBits; // determine the allowable discrepance - lodError = LodErrorForVolume( cv->lodOrigin, cv->lodRadius ); + if ( r_ext_vertex_buffer_object->integer ) { + // always render max res for VBOs + lodError = r_lodCurveError->value; + } else { + lodError = LodErrorForVolume( cv->lodOrigin, cv->lodRadius ); + } // determine which rows and columns of the subdivision // we are actually going to use @@ -1037,106 +1123,66 @@ static void RB_SurfaceGrid( srfGridMesh_t *cv ) { heightTable[lodHeight] = cv->height-1; lodHeight++; - - // very large grids may have more points or indexes than can be fit - // in the tess structure, so we may have to issue it in multiple passes - - used = 0; - while ( used < lodHeight - 1 ) { - // see how many rows of both verts and indexes we can add without overflowing - do { - vrows = ( SHADER_MAX_VERTEXES - tess.numVertexes ) / lodWidth; - irows = ( SHADER_MAX_INDEXES - tess.numIndexes ) / ( lodWidth * 6 ); - - // if we don't have enough space for at least one strip, flush the buffer - if ( vrows < 2 || irows < 1 ) { - RB_EndSurface(); - RB_BeginSurface(tess.shader, tess.fogNum ); - } else { - break; - } - } while ( 1 ); + if ( mode & TESS_VERTEX ) { + baseVertex = tess.numVertexes; + vertexPtr = tess.vertexPtr + baseVertex; - rows = irows; - if ( vrows < irows + 1 ) { - rows = vrows - 1; - } - if ( used + rows > lodHeight ) { - rows = lodHeight - used; - } - - numVertexes = tess.numVertexes; - - xyz = tess.xyz[numVertexes]; - normal = tess.normal[numVertexes]; - texCoords = tess.texCoords[numVertexes][0]; - color = ( unsigned char * ) &tess.vertexColors[numVertexes]; - vDlightBits = &tess.vertexDlightBits[numVertexes]; - needsNormal = tess.shader->needsNormal; - - for ( i = 0 ; i < rows ; i++ ) { - for ( j = 0 ; j < lodWidth ; j++ ) { - dv = cv->verts + heightTable[ used + i ] * cv->width + for ( i = 0 ; i < lodHeight ; i++ ) { + for ( j = 0 ; j < lodWidth ; j++, vertexPtr++ ) { + dv = cv->verts + heightTable[ i ] * cv->width + widthTable[ j ]; - - xyz[0] = dv->xyz[0]; - xyz[1] = dv->xyz[1]; - xyz[2] = dv->xyz[2]; - texCoords[0] = dv->st[0]; - texCoords[1] = dv->st[1]; - texCoords[2] = dv->lightmap[0]; - texCoords[3] = dv->lightmap[1]; - if ( needsNormal ) { - normal[0] = dv->normal[0]; - normal[1] = dv->normal[1]; - normal[2] = dv->normal[2]; - } - * ( unsigned int * ) color = * ( unsigned int * ) dv->color; - *vDlightBits++ = dlightBits; - xyz += 4; - normal += 4; - texCoords += 4; - color += 4; + + VectorCopy( dv->xyz, vertexPtr->xyz ); + vertexPtr->tc1[0] = dv->st[0]; + vertexPtr->tc1[1] = dv->st[1]; + vertexPtr->tc2[0] = dv->lightmap[0]; + vertexPtr->tc2[1] = dv->lightmap[1]; + VectorCopy( dv->normal, vertexPtr->normal ); + *(int *)&vertexPtr->color = *(int *)dv->color; + vertexPtr->fogNum = tess.fogNum; } } + } else { + baseVertex = cv->vboStart; + } + + if ( mode & TESS_INDEX ) { + int w, h; + glIndex_t *indexPtr = tess.indexPtr + tess.numIndexes[tess.indexRange]; + if ( tess.minIndex[tess.indexRange] > baseVertex ) + tess.minIndex[tess.indexRange] = baseVertex; + if ( tess.maxIndex[tess.indexRange] < baseVertex + lodWidth*lodHeight - 1 ) + tess.maxIndex[tess.indexRange] = baseVertex + lodWidth*lodHeight - 1; // add the indexes - { - int numIndexes; - int w, h; - - h = rows - 1; - w = lodWidth - 1; - numIndexes = tess.numIndexes; - for (i = 0 ; i < h ; i++) { - for (j = 0 ; j < w ; j++) { - int v1, v2, v3, v4; - - // vertex order to be reckognized as tristrips - v1 = numVertexes + i*lodWidth + j + 1; - v2 = v1 - 1; - v3 = v2 + lodWidth; - v4 = v3 + 1; - - tess.indexes[numIndexes] = v2; - tess.indexes[numIndexes+1] = v3; - tess.indexes[numIndexes+2] = v1; + + h = lodHeight - 1; + w = lodWidth - 1; + + for (i = 0 ; i < h ; i++) { + for (j = 0 ; j < w ; j++) { + glIndex_t v1, v2, v3, v4; + + // vertex order to be reckognized as tristrips + v1 = baseVertex + i*lodWidth + j + 1; + v2 = v1 - 1; + v3 = v2 + lodWidth; + v4 = v3 + 1; + + *indexPtr++ = v2; + *indexPtr++ = v3; + *indexPtr++ = v1; - tess.indexes[numIndexes+3] = v1; - tess.indexes[numIndexes+4] = v3; - tess.indexes[numIndexes+5] = v4; - numIndexes += 6; - } + *indexPtr++ = v1; + *indexPtr++ = v3; + *indexPtr++ = v4; } - - tess.numIndexes = numIndexes; } - - tess.numVertexes += rows * lodWidth; - - used += rows - 1; } + + tess.numVertexes += lodWidth * lodHeight; + tess.numIndexes[tess.indexRange] += 6 * (lodWidth - 1) * (lodHeight - 1); } @@ -1155,20 +1201,41 @@ RB_SurfaceAxis Draws x/y/z lines from the origin for orientation debugging =================== */ -static void RB_SurfaceAxis( void ) { - GL_Bind( tr.whiteImage ); +static void RB_SurfaceAxis( tessMode_t mode ) { + static vec3_t vertexes[6] = { + { 0, 0, 0 }, + { 16, 0, 0 }, + { 0, 0, 0 }, + { 0, 16, 0 }, + { 0, 0, 0 }, + { 0, 0, 16 } + }; + static color4ub_t colors[6] = { + { 255, 0, 0, 255 }, + { 255, 0, 0, 255 }, + { 0, 255, 0, 255 }, + { 0, 255, 0, 255 }, + { 0, 0, 255, 255 }, + { 0, 0, 255, 255 } + }; + glRenderState_t state; + + if( mode == TESS_COUNT ) { + return; + } + + InitState( &state ); + + state.numImages = 1; + state.image[0] = tr.whiteImage; qglLineWidth( 3 ); - qglBegin( GL_LINES ); - qglColor3f( 1,0,0 ); - qglVertex3f( 0,0,0 ); - qglVertex3f( 16,0,0 ); - qglColor3f( 0,1,0 ); - qglVertex3f( 0,0,0 ); - qglVertex3f( 0,16,0 ); - qglColor3f( 0,0,1 ); - qglVertex3f( 0,0,0 ); - qglVertex3f( 0,0,16 ); - qglEnd(); + SetAttrPointer( &state, AL_VERTEX, 0, + 3, GL_FLOAT, sizeof(vec3_t), + vertexes ); + SetAttrPointer( &state, AL_COLOR, 0, + 4, GL_UNSIGNED_BYTE, sizeof(color4ub_t), + colors ); + GL_DrawArrays( &state, GL_LINES, 0, 6 ); qglLineWidth( 1 ); } @@ -1181,64 +1248,114 @@ RB_SurfaceEntity Entities that have a single procedurally generated surface ==================== */ -static void RB_SurfaceEntity( surfaceType_t *surfType ) { +static void RB_SurfaceEntity( tessMode_t mode, surfaceType_t *surfType ) { switch( backEnd.currentEntity->e.reType ) { case RT_SPRITE: - RB_SurfaceSprite(); + RB_SurfaceSprite( mode ); break; case RT_BEAM: - RB_SurfaceBeam(); + RB_SurfaceBeam( mode ); break; case RT_RAIL_CORE: - RB_SurfaceRailCore(); + RB_SurfaceRailCore( mode ); break; case RT_RAIL_RINGS: - RB_SurfaceRailRings(); + RB_SurfaceRailRings( mode ); break; case RT_LIGHTNING: - RB_SurfaceLightningBolt(); + RB_SurfaceLightningBolt( mode ); break; default: - RB_SurfaceAxis(); + RB_SurfaceAxis( mode ); break; } return; } -static void RB_SurfaceBad( surfaceType_t *surfType ) { +static void RB_SurfaceMD3Texture( tessMode_t mode, surfaceType_t *surf ) { + srfMD3Texture_t *srf = (srfMD3Texture_t *)surf; + + if( mode == TESS_COUNT ) + return; + + srf->IBO->next = tess.firstIBO; + tess.firstIBO = srf->IBO; + tess.dataTexture = srf->image; + tess.framesPerRow = srf->framesPerRow; + tess.scaleX = srf->scaleX; + tess.scaleY = srf->scaleY; +} + +static void RB_SurfaceFarPlane( tessMode_t mode, surfaceType_t *surf ) { + viewParms_t *p = &backEnd.viewParms; + vec3_t mid, left, up; + color4ub_t color = { 255,255,255,255 }; + + if( mode == TESS_COUNT ) { + tess.numVertexes += 4; + tess.numIndexes[tess.indexRange] += 6; + return; + } + + VectorMA( p->or.origin, p->zFar, p->or.axis[0], mid ); + VectorScale( p->or.axis[1], p->zFar * tan(p->fovY * M_PI / 360.0f), up ); + VectorScale( p->or.axis[2], p->zFar * tan(p->fovX * M_PI / 360.0f), left ); + RB_AddQuadStampExt( mid, left, up, color, 0.0f, 0.0f, 1.0f, 1.0f ); +} + +static void RB_SurfaceBad( tessMode_t mode, surfaceType_t *surfType ) { ri.Printf( PRINT_ALL, "Bad surface tesselated.\n" ); } -static void RB_SurfaceFlare(srfFlare_t *surf) +static void RB_SurfaceFlare( tessMode_t mode, surfaceType_t *surface ) { - if (r_flares->integer) + srfFlare_t *surf = (srfFlare_t *)surface; + + if( mode && r_flares->integer ) RB_AddFlare(surf, tess.fogNum, surf->origin, surf->color, surf->normal); } -static void RB_SurfaceDisplayList( srfDisplayList_t *surf ) { +static void RB_SurfaceDisplayList( tessMode_t mode, surfaceType_t *surface ) { + srfDisplayList_t *surf = (srfDisplayList_t *)surface; // all apropriate state must be set in RB_BeginSurface // this isn't implemented yet... + if( mode == TESS_COUNT ) { + return; + } qglCallList( surf->listNum ); } -static void RB_SurfaceSkip( void *surf ) { +static void RB_SurfaceIBO( tessMode_t mode, surfaceType_t *surface ) { + srfIBO_t *srf = (srfIBO_t *)surface; + + if( mode == TESS_COUNT ) + return; + + srf->ibo.next = tess.firstIBO; + tess.firstIBO = &srf->ibo; +} + +static void RB_SurfaceSkip( tessMode_t mode, surfaceType_t *surf ) { } -void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])( void *) = { - (void(*)(void*))RB_SurfaceBad, // SF_BAD, - (void(*)(void*))RB_SurfaceSkip, // SF_SKIP, - (void(*)(void*))RB_SurfaceFace, // SF_FACE, - (void(*)(void*))RB_SurfaceGrid, // SF_GRID, - (void(*)(void*))RB_SurfaceTriangles, // SF_TRIANGLES, - (void(*)(void*))RB_SurfacePolychain, // SF_POLY, - (void(*)(void*))RB_SurfaceMesh, // SF_MD3, - (void(*)(void*))RB_SurfaceAnim, // SF_MD4, +void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])( tessMode_t, surfaceType_t * ) = { + RB_SurfaceBad, // SF_BAD, + RB_SurfaceSkip, // SF_SKIP, + RB_SurfaceFace, // SF_FACE, + RB_SurfaceGrid, // SF_GRID, + RB_SurfaceTriangles, // SF_TRIANGLES, + RB_SurfacePolychain, // SF_POLY, + RB_SurfaceMesh, // SF_MD3, + RB_SurfaceAnim, // SF_MD4, #ifdef RAVENMD4 - (void(*)(void*))RB_MDRSurfaceAnim, // SF_MDR, + RB_MDRSurfaceAnim, // SF_MDR, #endif - (void(*)(void*))RB_IQMSurfaceAnim, // SF_IQM, - (void(*)(void*))RB_SurfaceFlare, // SF_FLARE, - (void(*)(void*))RB_SurfaceEntity, // SF_ENTITY - (void(*)(void*))RB_SurfaceDisplayList // SF_DISPLAY_LIST + RB_IQMSurfaceAnim, // SF_IQM, + RB_SurfaceFlare, // SF_FLARE, + RB_SurfaceEntity, // SF_ENTITY, + RB_SurfaceDisplayList, // SF_DISPLAY_LIST, + RB_SurfaceIBO, // SF_IBO, + RB_SurfaceMD3Texture, // SF_MD3_TEXTURE, + RB_SurfaceFarPlane // SF_FAR_PLANE }; diff --git a/code/renderer/tr_types.h b/code/renderer/tr_types.h index 2706b76..44f55c2 100644 --- a/code/renderer/tr_types.h +++ b/code/renderer/tr_types.h @@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define ENTITYNUM_BITS 10 // can't be increased without changing drawsurf bit packing #define MAX_ENTITIES ((1<dlightBits[ tr.smpFrame ] = dlightBits; + return dlightBits; + } + + for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { if ( ! ( dlightBits & ( 1 << i ) ) ) { continue; } @@ -188,7 +193,12 @@ static int R_DlightGrid( srfGridMesh_t *grid, int dlightBits ) { int i; dlight_t *dl; - for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { + if ( !dlightBits ) { + grid->dlightBits[ tr.smpFrame ] = dlightBits; + return dlightBits; + } + + for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { if ( ! ( dlightBits & ( 1 << i ) ) ) { continue; } @@ -214,10 +224,6 @@ static int R_DlightGrid( srfGridMesh_t *grid, int dlightBits ) { static int R_DlightTrisurf( srfTriangles_t *surf, int dlightBits ) { - // FIXME: more dlight culling to trisurfs... - surf->dlightBits[ tr.smpFrame ] = dlightBits; - return dlightBits; -#if 0 int i; dlight_t *dl; @@ -226,12 +232,12 @@ static int R_DlightTrisurf( srfTriangles_t *surf, int dlightBits ) { continue; } dl = &tr.refdef.dlights[i]; - if ( dl->origin[0] - dl->radius > grid->meshBounds[1][0] - || dl->origin[0] + dl->radius < grid->meshBounds[0][0] - || dl->origin[1] - dl->radius > grid->meshBounds[1][1] - || dl->origin[1] + dl->radius < grid->meshBounds[0][1] - || dl->origin[2] - dl->radius > grid->meshBounds[1][2] - || dl->origin[2] + dl->radius < grid->meshBounds[0][2] ) { + if ( dl->origin[0] - dl->radius > surf->bounds[1][0] + || dl->origin[0] + dl->radius < surf->bounds[0][0] + || dl->origin[1] - dl->radius > surf->bounds[1][1] + || dl->origin[1] + dl->radius < surf->bounds[0][1] + || dl->origin[2] - dl->radius > surf->bounds[1][2] + || dl->origin[2] + dl->radius < surf->bounds[0][2] ) { // dlight doesn't reach the bounds dlightBits &= ~( 1 << i ); } @@ -241,9 +247,8 @@ static int R_DlightTrisurf( srfTriangles_t *surf, int dlightBits ) { tr.pc.c_dlightSurfacesCulled++; } - grid->dlightBits[ tr.smpFrame ] = dlightBits; + surf->dlightBits[ tr.smpFrame ] = dlightBits; return dlightBits; -#endif } /* @@ -256,11 +261,11 @@ more dlights if possible. ==================== */ static int R_DlightSurface( msurface_t *surf, int dlightBits ) { - if ( *surf->data == SF_FACE ) { + if ( surf->type == SF_FACE ) { dlightBits = R_DlightFace( (srfSurfaceFace_t *)surf->data, dlightBits ); - } else if ( *surf->data == SF_GRID ) { + } else if ( surf->type == SF_GRID ) { dlightBits = R_DlightGrid( (srfGridMesh_t *)surf->data, dlightBits ); - } else if ( *surf->data == SF_TRIANGLES ) { + } else if ( surf->type == SF_TRIANGLES ) { dlightBits = R_DlightTrisurf( (srfTriangles_t *)surf->data, dlightBits ); } else { dlightBits = 0; @@ -280,7 +285,7 @@ static int R_DlightSurface( msurface_t *surf, int dlightBits ) { R_AddWorldSurface ====================== */ -static void R_AddWorldSurface( msurface_t *surf, int dlightBits ) { +static void R_AddWorldSurface( msurface_t *surf, int dlight, int dlightBits ) { if ( surf->viewCount == tr.viewCount ) { return; // already in this view } @@ -288,18 +293,29 @@ static void R_AddWorldSurface( msurface_t *surf, int dlightBits ) { surf->viewCount = tr.viewCount; // FIXME: bmodel fog? + if( qglUniformBlockBinding ) { + if( dlight == 2 ) + return; + dlight = 0; + } + // try to cull before dlighting or adding - if ( R_CullSurface( surf->data, surf->shader ) ) { + if ( R_CullSurface( surf->data, surf->shader ) ) return; - } // check for dlighting - if ( dlightBits ) { - dlightBits = R_DlightSurface( surf, dlightBits ); - dlightBits = ( dlightBits != 0 ); + dlightBits = R_DlightSurface( surf, dlightBits ); + if( !dlightBits && !surf->fogIndex ) { + if( dlight == 2 ) + return; + dlight = 0; } - R_AddDrawSurf( surf->data, surf->shader, surf->fogIndex, dlightBits ); + if( surf->shader->depthShader ) { + // no fog or light for the depth shader needed + R_AddDrawSurf( surf->data, surf->shader->depthShader, 0, 0 ); + } + R_AddDrawSurf( surf->data, surf->shader, surf->fogIndex, dlight ); } /* @@ -310,6 +326,113 @@ static void R_AddWorldSurface( msurface_t *surf, int dlightBits ) { ============================================================= */ +static ID_INLINE qboolean R_IsVBOSurface( msurface_t *surf ) { + const short dynamicFlags = GLA_COLOR_dynamic | + GLA_TC1_dynamic | GLA_TC2_dynamic | + GLA_NORMAL_dynamic | GLA_VERTEX_dynamic; + return + !(surf->shader->anyGLAttr & dynamicFlags) && + surf->shader->sort >= SS_OPAQUE && ( + surf->type == SF_FACE || + surf->type == SF_GRID || + surf->type == SF_TRIANGLES); +} +static ID_INLINE int R_BuildIBOSurfaces( drawSurf_t *surfs, int numSurfs, + srfIBO_t **ibos ) { + srfIBO_t *ibo; + GLuint IBO; + int i, baseIndex; + int lastShader, numShaders; + + // FIXME: using backend functions may break SMP mode + RB_BeginSurface( tr.buildIBOShader ); + lastShader = -1; + numShaders = 0; + for( i = 0; i < numSurfs; i++ ) { + tesselate( TESS_COUNT, surfs[i].surface ); + if( surfs[i].shaderIndex != lastShader ) + numShaders++; + lastShader = surfs[i].shaderIndex; + } + if( tess.numIndexes <= 0 ) { + *ibos = NULL; + return -1; + } else { + qglGenBuffersARB( 1, &IBO ); + GL_IBO( IBO ); + RB_AllocateSurface( ); + + ibo = ri.Hunk_Alloc( numShaders * sizeof( srfIBO_t ), + h_dontcare ); + *ibos = ibo; + + baseIndex = 0; + lastShader = -1; + for( i = 0; i < numSurfs; i++ ) { + if( surfs[i].shaderIndex != lastShader ) { + if( lastShader != -1 ) { + ibo->ibo.numIndexes = tess.numIndexes[tess.indexRange] - baseIndex; + ibo->ibo.minIndex = tess.minIndex[tess.indexRange]; + ibo->ibo.maxIndex = tess.maxIndex[tess.indexRange]; + tess.minIndex[tess.indexRange] = 0x7fffffff; + tess.maxIndex[tess.indexRange] = 0; + baseIndex = tess.numIndexes[tess.indexRange]; + ibo++; + } + ibo->surfaceType = SF_IBO; + ibo->shader = tr.shaders[surfs[i].shaderIndex]; + ibo->ibo.next = NULL; + ibo->ibo.vbo = backEnd.worldVBO; + ibo->ibo.ibo = IBO; + ibo->ibo.offset = (glIndex_t *)NULL + baseIndex; + + lastShader = surfs[i].shaderIndex; + } + + tesselate( TESS_INDEX, surfs[i].surface ); + } + ibo->ibo.numIndexes = tess.numIndexes[tess.indexRange] - baseIndex; + ibo->ibo.minIndex = tess.minIndex[tess.indexRange]; + ibo->ibo.maxIndex = tess.maxIndex[tess.indexRange]; + + RB_EndSurface( ); + } + + return numShaders; +} + +/* +================= +R_BruchModelFogNum +See if a brush model is inside a fog volume +================= +*/ +int R_BrushModelFogNum( trRefEntity_t *ent, bmodel_t *model ) { + int i, j; + fog_t *fog; + + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + return 0; + } + + for ( i = 1 ; i < tr.world->numfogs ; i++ ) { + fog = &tr.world->fogs[i]; + for ( j = 0 ; j < 3 ; j++ ) { + if ( ent->e.origin[j] + model->bounds[0][j] >= fog->bounds[1][j] ) { + break; + } + if ( ent->e.origin[j] + model->bounds[1][j] <= fog->bounds[0][j] ) { + break; + } + } + if ( j == 3 ) { + return i; + } + } + + return 0; +} + /* ================= R_AddBrushModelSurfaces @@ -320,6 +443,8 @@ void R_AddBrushModelSurfaces ( trRefEntity_t *ent ) { int clip; model_t *pModel; int i; + qboolean skipVBO = qfalse; + int fogNum; pModel = R_GetModelByHandle( ent->e.hModel ); @@ -332,9 +457,41 @@ void R_AddBrushModelSurfaces ( trRefEntity_t *ent ) { R_SetupEntityLighting( &tr.refdef, ent ); R_DlightBmodel( bmodel ); + fogNum = R_BrushModelFogNum( ent, bmodel ); + + if( bmodel->numIBOSurfaces == 0 && backEnd.worldVBO && + !(tr.currentEntity->needDlights) && !fogNum ) { + drawSurf_t *surfs = ri.Hunk_AllocateTempMemory( bmodel->numSurfaces * sizeof( drawSurf_t ) ); + int numSurfs = 0; + + for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) { + if( !R_IsVBOSurface( &bmodel->firstSurface[i] ) ) + continue; + surfs[numSurfs].sort = 0; + surfs[numSurfs].shaderIndex = bmodel->firstSurface[i].shader->index; + surfs[numSurfs].surface = bmodel->firstSurface[i].data; + numSurfs++; + } + + bmodel->numIBOSurfaces = R_BuildIBOSurfaces( surfs, numSurfs, + &bmodel->iboSurfaces ); + ri.Hunk_FreeTempMemory( surfs ); + } + + if( bmodel->numIBOSurfaces > 0 && !tr.currentEntity->needDlights && !fogNum ) { + for( i = 0; i < bmodel->numIBOSurfaces; i++ ) { + srfIBO_t *surf = &bmodel->iboSurfaces[i]; + R_AddDrawSurf( &surf->surfaceType, surf->shader, 0, 0 ); + } + skipVBO = qtrue; + } for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) { - R_AddWorldSurface( bmodel->firstSurface + i, tr.currentEntity->needDlights ); + if( skipVBO && R_IsVBOSurface( &bmodel->firstSurface[i] ) ) + continue; + R_AddWorldSurface( bmodel->firstSurface + i, + tr.currentEntity->needDlights, + 0xffffffff ); } } @@ -347,16 +504,79 @@ void R_AddBrushModelSurfaces ( trRefEntity_t *ent ) { ============================================================= */ +static void R_ComputeIBOSurfaces( mnode_t *node ) { + int c; + msurface_t *surf, **mark; + + do { + int r; + + // if the node wasn't marked as potentially visible, exit + if (node->visframe != tr.visCount) { + return; + } + + // if the bounding volume is outside the frustum, nothing + // inside can be visible OPTIMIZE: don't do this all the way to leafs? + if( tr.viewParms.frustType ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[4]); + if (r == 2) { + return; // culled + } + } + + if ( node->contents != -1 ) { + break; + } + + // node is just a decision point, so go down both sides + // since we don't care about sort orders, just go positive to negative + R_ComputeIBOSurfaces (node->children[0] ); + + // tail recurse + node = node->children[1]; + } while ( 1 ); + + // leaf node, so add mark surfaces + tr.pc.c_leafs++; + + // add the individual surfaces + mark = node->firstmarksurface; + c = node->nummarksurfaces; + while (c--) { + // the surface may have already been added if it + // spans multiple leafs + surf = *mark; + if( surf->viewCount != tr.viewCount && + R_IsVBOSurface( surf ) ) { + surf->viewCount = tr.viewCount; + + if( surf->shader->depthShader ) { + R_AddDrawSurf( surf->data, + surf->shader->depthShader, + 0, 0 ); + } + R_AddDrawSurf( surf->data, surf->shader, + surf->fogIndex, 0 ); + } + mark++; + } +} + /* ================ R_RecursiveWorldNode ================ */ -static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits ) { +static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits, + qboolean skipVBO ) { + int c; + msurface_t *surf, **mark; do { int newDlights[2]; + int r; // if the node wasn't marked as potentially visible, exit if (node->visframe != tr.visCount) { @@ -366,49 +586,44 @@ static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits ) // if the bounding volume is outside the frustum, nothing // inside can be visible OPTIMIZE: don't do this all the way to leafs? - if ( !r_nocull->integer ) { - int r; - - if ( planeBits & 1 ) { - r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[0]); - if (r == 2) { - return; // culled - } - if ( r == 1 ) { - planeBits &= ~1; // all descendants will also be in front - } + if ( planeBits & 1 ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[0]); + if (r == 2) { + return; // culled } - - if ( planeBits & 2 ) { - r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[1]); - if (r == 2) { - return; // culled - } - if ( r == 1 ) { - planeBits &= ~2; // all descendants will also be in front - } + if ( r == 1 ) { + planeBits &= ~1; // all descendants will also be in front } - - if ( planeBits & 4 ) { - r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[2]); - if (r == 2) { - return; // culled - } - if ( r == 1 ) { - planeBits &= ~4; // all descendants will also be in front - } + } + + if ( planeBits & 2 ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[1]); + if (r == 2) { + return; // culled } - - if ( planeBits & 8 ) { - r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[3]); - if (r == 2) { - return; // culled - } - if ( r == 1 ) { - planeBits &= ~8; // all descendants will also be in front - } + if ( r == 1 ) { + planeBits &= ~2; // all descendants will also be in front + } + } + + if ( planeBits & 4 ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[2]); + if (r == 2) { + return; // culled + } + if ( r == 1 ) { + planeBits &= ~4; // all descendants will also be in front + } + } + + if ( planeBits & 8 ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[3]); + if (r == 2) { + return; // culled + } + if ( r == 1 ) { + planeBits &= ~8; // all descendants will also be in front } - } if ( node->contents != -1 ) { @@ -443,53 +658,53 @@ static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits ) } // recurse down the children, front side first - R_RecursiveWorldNode (node->children[0], planeBits, newDlights[0] ); + R_RecursiveWorldNode (node->children[0], planeBits, + newDlights[0], skipVBO ); // tail recurse node = node->children[1]; dlightBits = newDlights[1]; } while ( 1 ); - { - // leaf node, so add mark surfaces - int c; - msurface_t *surf, **mark; - - tr.pc.c_leafs++; - - // add to z buffer bounds - if ( node->mins[0] < tr.viewParms.visBounds[0][0] ) { - tr.viewParms.visBounds[0][0] = node->mins[0]; - } - if ( node->mins[1] < tr.viewParms.visBounds[0][1] ) { - tr.viewParms.visBounds[0][1] = node->mins[1]; - } - if ( node->mins[2] < tr.viewParms.visBounds[0][2] ) { - tr.viewParms.visBounds[0][2] = node->mins[2]; - } + // leaf node, so add mark surfaces + tr.pc.c_leafs++; - if ( node->maxs[0] > tr.viewParms.visBounds[1][0] ) { - tr.viewParms.visBounds[1][0] = node->maxs[0]; - } - if ( node->maxs[1] > tr.viewParms.visBounds[1][1] ) { - tr.viewParms.visBounds[1][1] = node->maxs[1]; - } - if ( node->maxs[2] > tr.viewParms.visBounds[1][2] ) { - tr.viewParms.visBounds[1][2] = node->maxs[2]; - } - - // add the individual surfaces - mark = node->firstmarksurface; - c = node->nummarksurfaces; - while (c--) { - // the surface may have already been added if it - // spans multiple leafs - surf = *mark; - R_AddWorldSurface( surf, dlightBits ); - mark++; + // add to z buffer bounds + if ( node->mins[0] < tr.viewParms.visBounds[0][0] ) { + tr.viewParms.visBounds[0][0] = node->mins[0]; + } + if ( node->mins[1] < tr.viewParms.visBounds[0][1] ) { + tr.viewParms.visBounds[0][1] = node->mins[1]; + } + if ( node->mins[2] < tr.viewParms.visBounds[0][2] ) { + tr.viewParms.visBounds[0][2] = node->mins[2]; + } + + if ( node->maxs[0] > tr.viewParms.visBounds[1][0] ) { + tr.viewParms.visBounds[1][0] = node->maxs[0]; + } + if ( node->maxs[1] > tr.viewParms.visBounds[1][1] ) { + tr.viewParms.visBounds[1][1] = node->maxs[1]; + } + if ( node->maxs[2] > tr.viewParms.visBounds[1][2] ) { + tr.viewParms.visBounds[1][2] = node->maxs[2]; + } + + // add the individual surfaces + mark = node->firstmarksurface; + c = node->nummarksurfaces; + while (c--) { + // the surface may have already been added if it + // spans multiple leafs + surf = *mark; + if( skipVBO && R_IsVBOSurface( surf ) ) { + if( dlightBits || surf->fogIndex ) + R_AddWorldSurface( surf, 2, dlightBits ); + } else { + R_AddWorldSurface( surf, 1, dlightBits ); } + mark++; } - } @@ -579,12 +794,13 @@ static void R_MarkLeaves (void) { // current viewcluster leaf = R_PointInLeaf( tr.viewParms.pvsOrigin ); cluster = leaf->cluster; + tr.viewParms.viewCluster = cluster; // if the cluster is the same and the area visibility matrix // hasn't changed, we don't need to mark everything again // if r_showcluster was just turned on, remark everything - if ( tr.viewCluster == cluster && !tr.refdef.areamaskModified + if ( tr.visCluster == cluster && !tr.refdef.areamaskModified && !r_showcluster->modified ) { return; } @@ -597,9 +813,9 @@ static void R_MarkLeaves (void) { } tr.visCount++; - tr.viewCluster = cluster; + tr.visCluster = cluster; - if ( r_novis->integer || tr.viewCluster == -1 ) { + if ( r_novis->integer || tr.visCluster == -1 ) { for (i=0 ; inumnodes ; i++) { if (tr.world->nodes[i].contents != CONTENTS_SOLID) { tr.world->nodes[i].visframe = tr.visCount; @@ -608,7 +824,7 @@ static void R_MarkLeaves (void) { return; } - vis = R_ClusterPVS (tr.viewCluster); + vis = R_ClusterPVS (tr.viewParms.viewCluster); for (i=0,leaf=tr.world->nodes ; inumnodes ; i++, leaf++) { cluster = leaf->cluster; @@ -622,7 +838,8 @@ static void R_MarkLeaves (void) { } // check for door connection - if ( (tr.refdef.areamask[leaf->area>>3] & (1<<(leaf->area&7)) ) ) { + if ( !qglBindBufferARB && + (tr.refdef.areamask[leaf->area>>3] & (1<<(leaf->area&7)) ) ) { continue; // not visible } @@ -643,6 +860,10 @@ R_AddWorldSurfaces ============= */ void R_AddWorldSurfaces (void) { + mcluster_t *cluster; + qboolean skipVBO = qfalse; + int dlightBits; + if ( !r_drawworld->integer ) { return; } @@ -656,13 +877,77 @@ void R_AddWorldSurfaces (void) { // determine which leaves are in the PVS / areamask R_MarkLeaves (); + cluster = &tr.clusters[tr.viewParms.viewCluster]; // clear out the visible min/max ClearBounds( tr.viewParms.visBounds[0], tr.viewParms.visBounds[1] ); + switch ( tr.viewParms.frustType ) { + case 1: + tr.viewParms.frustum[4].dist = cluster->mins[0]; + break; + case 2: + tr.viewParms.frustum[4].dist = -cluster->maxs[0]; + break; + case 3: + tr.viewParms.frustum[4].dist = cluster->mins[1]; + break; + case 4: + tr.viewParms.frustum[4].dist = -cluster->maxs[1]; + break; + case 5: + tr.viewParms.frustum[4].dist = cluster->mins[2]; + break; + case 6: + tr.viewParms.frustum[4].dist = -cluster->maxs[2]; + break; + } + // perform frustum culling and add all the potentially visible surfaces - if ( tr.refdef.num_dlights > 32 ) { - tr.refdef.num_dlights = 32 ; + if ( tr.refdef.num_dlights > MAX_DLIGHTS ) { + tr.refdef.num_dlights = MAX_DLIGHTS ; + } + + + // build an IBO for all VBO-surfaces visible from the current cluster + if( cluster->numIBOSurfaces[tr.viewParms.frustType] == 0 && + backEnd.worldVBO ) { + int firstsurface = tr.refdef.numDrawSurfs; + + R_ComputeIBOSurfaces( tr.world->nodes ); + tr.viewCount++; + if( tr.refdef.numDrawSurfs <= firstsurface ) { + // mark as unused to avoid recomputing in later frames + cluster->numIBOSurfaces[tr.viewParms.frustType] = -1; + } else { + R_SortSurfaces( tr.refdef.drawSurfs + firstsurface, + tr.refdef.numDrawSurfs - firstsurface ); + + cluster->numIBOSurfaces[tr.viewParms.frustType] + = R_BuildIBOSurfaces( tr.refdef.drawSurfs + firstsurface, + tr.refdef.numDrawSurfs - firstsurface, + &cluster->iboSurfaces[tr.viewParms.frustType] ); + } + tr.refdef.numDrawSurfs = firstsurface; } - R_RecursiveWorldNode( tr.world->nodes, 15, ( 1 << tr.refdef.num_dlights ) - 1 ); + + if( cluster->numIBOSurfaces[tr.viewParms.frustType] > 0 ) { + int i; + srfIBO_t *surf; + + for( i = 0, surf = cluster->iboSurfaces[tr.viewParms.frustType]; + i < cluster->numIBOSurfaces[tr.viewParms.frustType]; + i++, surf++ ) { + R_AddDrawSurf( &surf->surfaceType, surf->shader, 0, 0 ); + } + skipVBO = qtrue; + } + + if( qglUniformBlockBinding ) { + dlightBits = 0; + } else { + dlightBits = ( 1 << tr.refdef.num_dlights ) - 1; + } + + R_RecursiveWorldNode( tr.world->nodes, 15, dlightBits, skipVBO ); } diff --git a/code/sdl/sdl_glimp.c b/code/sdl/sdl_glimp.c index cbdbcef..4b67c1a 100644 --- a/code/sdl/sdl_glimp.c +++ b/code/sdl/sdl_glimp.c @@ -22,8 +22,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #ifdef USE_LOCAL_HEADERS # include "SDL.h" +# include "SDL_syswm.h" #else # include +# include #endif #ifdef SMP @@ -32,6 +34,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # else # include # endif +#ifdef SDL_VIDEO_DRIVER_X11 +# include +#endif #endif #include @@ -48,15 +53,209 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #ifdef MACOS_X #include typedef CGLContextObj QGLContext; -#define GLimp_GetCurrentContext() CGLGetCurrentContext() -#define GLimp_SetCurrentContext(ctx) CGLSetCurrentContext(ctx) + +static void GLimp_GetCurrentContext( QGLContext *ctx ) +{ + *ctx = CGLGetCurrentContext(); +} + +static void GLimp_SetCurrentContext( QGLContext *ctx ) +{ + CGLSetCurrentContext( ctx ? *ctx : NULL ); +} +static void GLimp_CreateSharedContext( QGLContext *old, qboolean debug, + qboolean nodraw, QGLContext *new ) { + // AFAIK debug contexts are not supported + CGLCreateContext ( CGLGetPixelFormat( *old ), *old, new ); + + if( nodraw ) { + glDrawBuffer( GL_NONE ); + } +} +static void GLimp_DestroyContext( QGLContext *parent, QGLContext *ctx ) { + if( *parent != *ctx ) { + CGLDestroyContext( *ctx ); + *ctx = *parent; + } +} +#elif SDL_VIDEO_DRIVER_X11 +#include +typedef struct +{ + GLXContext ctx; + Display *dpy; + GLXDrawable drawable; +} QGLContext; + +static void GLimp_GetCurrentContext( QGLContext *ctx ) +{ + ctx->ctx = glXGetCurrentContext(); + ctx->dpy = glXGetCurrentDisplay(); + ctx->drawable = glXGetCurrentDrawable(); +} + +static void GLimp_SetCurrentContext( QGLContext *ctx ) +{ + if( ctx ) + glXMakeCurrent( ctx->dpy, ctx->drawable, ctx->ctx ); + else + glXMakeCurrent( glXGetCurrentDisplay(), None, NULL ); +} +static void GLimp_CreateSharedContext( QGLContext *old, qboolean debug, + qboolean nodraw, QGLContext *new ) { + GLubyte *procName = (GLubyte *)"glXCreateContextAttribsARB"; + GLXContext (APIENTRYP glXCreateContextAttribsARB)( Display *dpy, + GLXFBConfig config, + GLXContext share_context, + Bool direct, + const int *attrib_list); + int config_attrib_list[] = { + GLX_FBCONFIG_ID, 0, + None + }; + int pbuffer_attrib_list[] = { + None + }; + int attrib_list[] = { + GLX_RENDER_TYPE, GLX_RGBA_TYPE, + GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, + None + }; + GLXFBConfig *config; + int count; + + if( glXQueryContext(old->dpy, old->ctx, + GLX_FBCONFIG_ID, + &config_attrib_list[1]) != Success ) + return; + + config = glXChooseFBConfig( old->dpy, 0, + config_attrib_list, + &count ); + if( count != 1 ) + return; + + if( debug ) { + glXCreateContextAttribsARB = (void *)glXGetProcAddress(procName); + if( !glXCreateContextAttribsARB ) + debug = qfalse; + } else { + glXCreateContextAttribsARB = NULL; + } + + new->dpy = old->dpy; + new->drawable = None; + if( nodraw ) + new->drawable = glXCreatePbuffer( old->dpy, config[0], + pbuffer_attrib_list ); + if( new->drawable == None ) + new->drawable = old->drawable; + if( debug ) { + new->ctx = glXCreateContextAttribsARB(old->dpy, + config[0], + old->ctx, + GL_TRUE, + attrib_list); + } else { + new->ctx = glXCreateNewContext(old->dpy, + config[0], + GLX_RGBA_TYPE, + old->ctx, + GL_TRUE); + } + + if( nodraw ) { + GLimp_SetCurrentContext( new ); + glDrawBuffer( GL_NONE ); + } +} +static void GLimp_DestroyContext( QGLContext *parent, QGLContext *ctx ) { + if( parent->ctx != ctx->ctx ) { + glXDestroyContext( ctx->dpy, ctx->ctx ); + ctx->ctx = parent->ctx; + } + if( parent->drawable != ctx->drawable ) { + glXDestroyPbuffer( ctx->dpy, ctx->drawable ); + ctx->drawable = parent->drawable; + } +} +#elif WIN32 +typedef struct +{ + HDC hDC; // handle to device context + HGLRC hGLRC; // handle to GL rendering context +} QGLContext; + +static void GLimp_GetCurrentContext( QGLContext *ctx ) { + SDL_SysWMinfo info; + + SDL_VERSION(&info.version); + if(!SDL_GetWMInfo(&info)) + { + ri.Printf(PRINT_WARNING, "Failed to obtain HWND from SDL (InputRegistry)"); + return; + } + + ctx->hDC = wglGetCurrentDC( ); + ctx->hGLRC = wglGetCurrentContext( ); +} + +static void GLimp_SetCurrentContext( QGLContext *ctx ) { + if( ctx ) { + wglMakeCurrent( ctx->hDC, ctx->hGLRC ); + } else { + wglMakeCurrent( NULL, NULL ); + } +} +static void GLimp_CreateSharedContext( QGLContext *old, qboolean debug, + qboolean nodraw, QGLContext *new ) { + LPCSTR procName = "wglCreateContextAttribsARB"; + HGLRC (APIENTRYP wglCreateContextAttribsARB) (HDC hDC, + HGLRC hshareContext, + const int *attribList); + int attrib_list[] = { + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB, + 0 + }; + + if( debug ) { + wglCreateContextAttribsARB = (void *)wglGetProcAddress(procName); + if( !wglCreateContextAttribsARB ) + debug = qfalse; + } + + new->hDC = old->hDC; + if( debug ) { + new->hGLRC = wglCreateContextAttribsARB(new->hDC, + old->hGLRC, + attrib_list); + } else { + new->hGLRC = wglCreateContext(new->hDC); + GLimp_SetCurrentContext( NULL ); + wglShareLists( old->hGLRC, new->hGLRC ); + } + + if( nodraw ) { + GLimp_SetCurrentContext( new ); + glDrawBuffer( GL_NONE ); + } +} +static void GLimp_DestroyContext( QGLContext *parent, QGLContext *ctx ) { + if( parent->hGLRC != ctx->hGLRC ) { + wglDeleteContext( ctx->hGLRC ); + ctx->hGLRC = parent->hGLRC; + } +} #else -typedef void *QGLContext; -#define GLimp_GetCurrentContext() (NULL) -#define GLimp_SetCurrentContext(ctx) +typedef int QGLContext; // dummy +static void GLimp_GetCurrentContext( QGLContext *ctx ) {} +static void GLimp_SetCurrentContext( QGLContext *ctx ) {} +static void GLimp_CreateSharedContext( QGLContext *old, qboolean debug, + qboolean nodraw, QGLContext *new ) {} +static void GLimp_DestroyContext( QGLContext *parent, QGLContext *ctx ) {} #endif -static QGLContext opengl_context; +static QGLContext initial_context, frontend_context, backend_context; typedef enum { @@ -76,13 +275,306 @@ cvar_t *r_allowResize; // make window resizable cvar_t *r_centerWindow; cvar_t *r_sdlDriver; +void (APIENTRYP qglDrawRangeElementsEXT) (GLenum mode, GLsizei count, GLuint start, GLuint end, GLenum type, const GLvoid *indices); + void (APIENTRYP qglActiveTextureARB) (GLenum texture); void (APIENTRYP qglClientActiveTextureARB) (GLenum texture); void (APIENTRYP qglMultiTexCoord2fARB) (GLenum target, GLfloat s, GLfloat t); +void (APIENTRYP qglMultiTexCoord4fvARB) (GLenum target, GLfloat *v); void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count); void (APIENTRYP qglUnlockArraysEXT) (void); +// GL_ARB_vertex_buffer_object +void (APIENTRYP qglBindBufferARB) (GLenum target, GLuint buffer); +void (APIENTRYP qglDeleteBuffersARB) (GLsizei n, const GLuint *buffers); +void (APIENTRYP qglGenBuffersARB) (GLsizei n, GLuint *buffers); +GLboolean (APIENTRYP qglIsBufferARB) (GLuint buffer); +void (APIENTRYP qglBufferDataARB) (GLenum target, GLsizeiptrARB size, const GLvoid *data, GLenum usage); +void (APIENTRYP qglBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid *data); +void (APIENTRYP qglGetBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid *data); +GLvoid *(APIENTRYP qglMapBufferARB) (GLenum target, GLenum access); +GLboolean (APIENTRYP qglUnmapBufferARB) (GLenum target); +void (APIENTRYP qglGetBufferParameterivARB) (GLenum target, GLenum pname, GLint *params); +void (APIENTRYP qglGetBufferPointervARB) (GLenum target, GLenum pname, GLvoid **params); + +// GL_ARB_map_buffer_range +GLvoid *(APIENTRYP qglMapBufferRange) (GLenum target, GLintptr offset, + GLsizeiptr length, GLbitfield access); +GLvoid (APIENTRYP qglFlushMappedBufferRange) (GLenum target, GLintptr offset, + GLsizeiptr length); + +// GL_ARB_shader_objects +GLvoid (APIENTRYP qglDeleteShader) (GLuint shader); +GLvoid (APIENTRYP qglDeleteProgram) (GLuint program); +GLvoid (APIENTRYP qglDetachShader) (GLuint program, GLuint shader); +GLuint (APIENTRYP qglCreateShader) (GLenum type); +GLvoid (APIENTRYP qglShaderSource) (GLuint shader, GLsizei count, const char **string, + const GLint *length); +GLvoid (APIENTRYP qglCompileShader) (GLuint shader); +GLuint (APIENTRYP qglCreateProgram) (void); +GLvoid (APIENTRYP qglAttachShader) (GLuint program, GLuint shader); +GLvoid (APIENTRYP qglLinkProgram) (GLuint program); +GLvoid (APIENTRYP qglUseProgram) (GLuint program); +GLvoid (APIENTRYP qglValidateProgram) (GLuint program); +GLvoid (APIENTRYP qglUniform1f) (GLint location, GLfloat v0); +GLvoid (APIENTRYP qglUniform2f) (GLint location, GLfloat v0, GLfloat v1); +GLvoid (APIENTRYP qglUniform3f) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLvoid (APIENTRYP qglUniform4f) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLvoid (APIENTRYP qglUniform1i) (GLint location, GLint v0); +GLvoid (APIENTRYP qglUniform2i) (GLint location, GLint v0, GLint v1); +GLvoid (APIENTRYP qglUniform3i) (GLint location, GLint v0, GLint v1, GLint v2); +GLvoid (APIENTRYP qglUniform4i) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLvoid (APIENTRYP qglUniform1fv) (GLint location, GLsizei count, const GLfloat *value); +GLvoid (APIENTRYP qglUniform2fv) (GLint location, GLsizei count, const GLfloat *value); +GLvoid (APIENTRYP qglUniform3fv) (GLint location, GLsizei count, const GLfloat *value); +GLvoid (APIENTRYP qglUniform4fv) (GLint location, GLsizei count, const GLfloat *value); +GLvoid (APIENTRYP qglUniform1iv) (GLint location, GLsizei count, const GLint *value); +GLvoid (APIENTRYP qglUniform2iv) (GLint location, GLsizei count, const GLint *value); +GLvoid (APIENTRYP qglUniform3iv) (GLint location, GLsizei count, const GLint *value); +GLvoid (APIENTRYP qglUniform4iv) (GLint location, GLsizei count, const GLint *value); +GLvoid (APIENTRYP qglUniformMatrix2fv) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLvoid (APIENTRYP qglUniformMatrix3fv) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLvoid (APIENTRYP qglUniformMatrix4fv) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLvoid (APIENTRYP qglGetShaderiv) (GLuint shader, GLenum pname, GLint *params); +GLvoid (APIENTRYP qglGetProgramiv) (GLuint program, GLenum pname, GLint *params); +GLvoid (APIENTRYP qglGetShaderInfoLog) (GLuint shader, GLsizei maxLength, GLsizei *length, char *infoLog); +GLvoid (APIENTRYP qglGetProgramInfoLog) (GLuint program, GLsizei maxLength, GLsizei *length, char *infoLog); +GLvoid (APIENTRYP qglGetAttachedShaders) (GLuint program, GLsizei maxCount, GLsizei *count, + GLuint *shaders); +GLint (APIENTRYP qglGetUniformLocation) (GLuint program, const char *name); +GLvoid (APIENTRYP qglGetActiveUniform) (GLuint program, GLuint index, GLsizei maxLength, + GLsizei *length, GLint *size, GLenum *type, char *name); +GLvoid (APIENTRYP qglGetUniformfv) (GLuint program, GLint location, GLfloat *params); +GLvoid (APIENTRYP qglGetUniformiv) (GLuint program, GLint location, GLint *params); +GLvoid (APIENTRYP qglGetShaderSource) (GLuint shader, GLsizei maxLength, GLsizei *length, + char *source); + +// GL_ARB_vertex_shader +GLvoid (APIENTRYP qglVertexAttrib1fARB) (GLuint index, GLfloat v0); +GLvoid (APIENTRYP qglVertexAttrib1sARB) (GLuint index, GLshort v0); +GLvoid (APIENTRYP qglVertexAttrib1dARB) (GLuint index, GLdouble v0); +GLvoid (APIENTRYP qglVertexAttrib2fARB) (GLuint index, GLfloat v0, GLfloat v1); +GLvoid (APIENTRYP qglVertexAttrib2sARB) (GLuint index, GLshort v0, GLshort v1); +GLvoid (APIENTRYP qglVertexAttrib2dARB) (GLuint index, GLdouble v0, GLdouble v1); +GLvoid (APIENTRYP qglVertexAttrib3fARB) (GLuint index, GLfloat v0, GLfloat v1, GLfloat v2); +GLvoid (APIENTRYP qglVertexAttrib3sARB) (GLuint index, GLshort v0, GLshort v1, GLshort v2); +GLvoid (APIENTRYP qglVertexAttrib3dARB) (GLuint index, GLdouble v0, GLdouble v1, GLdouble v2); +GLvoid (APIENTRYP qglVertexAttrib4fARB) (GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLvoid (APIENTRYP qglVertexAttrib4sARB) (GLuint index, GLshort v0, GLshort v1, GLshort v2, GLshort v3); +GLvoid (APIENTRYP qglVertexAttrib4dARB) (GLuint index, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +GLvoid (APIENTRYP qglVertexAttrib4NubARB) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLvoid (APIENTRYP qglVertexAttrib1fvARB) (GLuint index, GLfloat *v); +GLvoid (APIENTRYP qglVertexAttrib1svARB) (GLuint index, GLshort *v); +GLvoid (APIENTRYP qglVertexAttrib1dvARB) (GLuint index, GLdouble *v); +GLvoid (APIENTRYP qglVertexAttrib2fvARB) (GLuint index, GLfloat *v); +GLvoid (APIENTRYP qglVertexAttrib2svARB) (GLuint index, GLshort *v); +GLvoid (APIENTRYP qglVertexAttrib2dvARB) (GLuint index, GLdouble *v); +GLvoid (APIENTRYP qglVertexAttrib3fvARB) (GLuint index, GLfloat *v); +GLvoid (APIENTRYP qglVertexAttrib3svARB) (GLuint index, GLshort *v); +GLvoid (APIENTRYP qglVertexAttrib3dvARB) (GLuint index, GLdouble *v); +GLvoid (APIENTRYP qglVertexAttrib4fvARB) (GLuint index, GLfloat *v); +GLvoid (APIENTRYP qglVertexAttrib4svARB) (GLuint index, GLshort *v); +GLvoid (APIENTRYP qglVertexAttrib4dvARB) (GLuint index, GLdouble *v); +GLvoid (APIENTRYP qglVertexAttrib4ivARB) (GLuint index, GLint *v); +GLvoid (APIENTRYP qglVertexAttrib4bvARB) (GLuint index, GLbyte *v); +GLvoid (APIENTRYP qglVertexAttrib4ubvARB) (GLuint index, GLubyte *v); +GLvoid (APIENTRYP qglVertexAttrib4usvARB) (GLuint index, GLushort *v); +GLvoid (APIENTRYP qglVertexAttrib4uivARB) (GLuint index, GLuint *v); +GLvoid (APIENTRYP qglVertexAttrib4NbvARB) (GLuint index, const GLbyte *v); +GLvoid (APIENTRYP qglVertexAttrib4NsvARB) (GLuint index, const GLshort *v); +GLvoid (APIENTRYP qglVertexAttrib4NivARB) (GLuint index, const GLint *v); +GLvoid (APIENTRYP qglVertexAttrib4NubvARB) (GLuint index, const GLubyte *v); +GLvoid (APIENTRYP qglVertexAttrib4NusvARB) (GLuint index, const GLushort *v); +GLvoid (APIENTRYP qglVertexAttrib4NuivARB) (GLuint index, const GLuint *v); +GLvoid (APIENTRYP qglVertexAttribPointerARB) (GLuint index, GLint size, GLenum type, GLboolean normalized, + GLsizei stride, const GLvoid *pointer); +GLvoid (APIENTRYP qglEnableVertexAttribArrayARB) (GLuint index); +GLvoid (APIENTRYP qglDisableVertexAttribArrayARB) (GLuint index); +GLvoid (APIENTRYP qglBindAttribLocationARB) (GLhandleARB programObj, GLuint index, const GLcharARB *name); +GLvoid (APIENTRYP qglGetActiveAttribARB) (GLhandleARB programObj, GLuint index, GLsizei maxLength, + GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +GLint (APIENTRYP qglGetAttribLocationARB) (GLhandleARB programObj, const GLcharARB *name); +GLvoid (APIENTRYP qglGetVertexAttribdvARB) (GLuint index, GLenum pname, GLdouble *params); +GLvoid (APIENTRYP qglGetVertexAttribfvARB) (GLuint index, GLenum pname, GLfloat *params); +GLvoid (APIENTRYP qglGetVertexAttribivARB) (GLuint index, GLenum pname, GLint *params); +GLvoid (APIENTRYP qglGetVertexAttribPointervARB) (GLuint index, GLenum pname, GLvoid **pointer); + +// GL_EXT_geometry_shader4 +GLvoid (APIENTRYP qglProgramParameteriEXT) (GLuint program, GLenum pname, GLint value); +GLvoid (APIENTRYP qglFramebufferTextureEXT) (GLenum target, GLenum attachment, + GLuint texture, GLint level); +GLvoid (APIENTRYP qglFramebufferTextureLayerEXT) (GLenum target, GLenum attachment, + GLuint texture, GLint level, int layer); +GLvoid (APIENTRYP qglFramebufferTextureFaceEXT) (GLenum target, GLenum attachment, + GLuint texture, GLint level, GLenum face); + +// GL_EXT_texture_buffer_object +GLvoid (APIENTRYP qglTexBufferEXT) (GLenum target, GLenum internalFormat, + GLuint buffer); + +// GL_ARB_uniform_buffer_object +GLvoid (APIENTRYP qglGetUniformIndices) (GLuint program, + GLsizei uniformCount, + const GLchar** uniformNames, + GLuint* uniformIndices); +GLvoid (APIENTRYP qglGetActiveUniformsiv) (GLuint program, + GLsizei uniformCount, + const GLuint* uniformIndices, + GLenum pname, + GLint* params); +GLvoid (APIENTRYP qglGetActiveUniformName) (GLuint program, + GLuint uniformIndex, + GLsizei bufSize, + GLsizei* length, + GLchar* uniformName); +GLuint (APIENTRYP qglGetUniformBlockIndex) (GLuint program, + const GLchar* uniformBlockName); +GLvoid (APIENTRYP qglGetActiveUniformBlockiv) (GLuint program, + GLuint uniformBlockIndex, + GLenum pname, + GLint* params); +GLvoid (APIENTRYP qglGetActiveUniformBlockName) (GLuint program, + GLuint uniformBlockIndex, + GLsizei bufSize, + GLsizei* length, + GLchar* uniformBlockName); +GLvoid (APIENTRYP qglBindBufferRange) (GLenum target, + GLuint index, + GLuint buffer, + GLintptr offset, + GLsizeiptr size); +GLvoid (APIENTRYP qglBindBufferBase) (GLenum target, + GLuint index, + GLuint buffer); +GLvoid (APIENTRYP qglGetIntegeri_v) (GLenum target, + GLuint index, + GLint* data); +GLvoid (APIENTRYP qglUniformBlockBinding) (GLuint program, + GLuint uniformBlockIndex, + GLuint uniformBlockBinding); + +// GL_EXT_texture3D +GLvoid (APIENTRYP qglTexImage3DEXT) (GLenum target, GLint level, + GLenum internalformat, GLsizei width, + GLsizei height, GLsizei depth, + GLint border, GLenum format, GLenum type, + const GLvoid *pixels); + +// GL_ARB_framebuffer_object +GLboolean (APIENTRYP qglIsRenderbuffer) (GLuint renderbuffer); +GLvoid (APIENTRYP qglBindRenderbuffer) (GLenum target, GLuint renderbuffer); +GLvoid (APIENTRYP qglDeleteRenderbuffers) (GLsizei n, const GLuint *renderbuffers); +GLvoid (APIENTRYP qglGenRenderbuffers) (GLsizei n, GLuint *renderbuffers); +GLvoid (APIENTRYP qglRenderbufferStorage) (GLenum target, GLenum internalformat, + GLsizei width, GLsizei height); +GLvoid (APIENTRYP qglRenderbufferStorageMultisample) (GLenum target, GLsizei samples, + GLenum internalformat, + GLsizei width, GLsizei height); +GLvoid (APIENTRYP qglGetRenderbufferParameteriv) (GLenum target, GLenum pname, GLint *params); +GLboolean (APIENTRYP qglIsFramebuffer) (GLuint framebuffer); +GLvoid (APIENTRYP qglBindFramebuffer) (GLenum target, GLuint framebuffer); +GLvoid (APIENTRYP qglDeleteFramebuffers) (GLsizei n, const GLuint *framebuffers); +GLvoid (APIENTRYP qglGenFramebuffers) (GLsizei n, GLuint *framebuffers); +GLenum (APIENTRYP qglCheckFramebufferStatus) (GLenum target); +GLvoid (APIENTRYP qglFramebufferTexture1D) (GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, GLint level); +GLvoid (APIENTRYP qglFramebufferTexture2D) (GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, GLint level); +GLvoid (APIENTRYP qglFramebufferTexture3D) (GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, + GLint level, GLint layer); +GLvoid (APIENTRYP qglFramebufferTextureLayer) (GLenum target, GLenum attachment, + GLuint texture, GLint level, GLint layer); +GLvoid (APIENTRYP qglFramebufferRenderbuffer) (GLenum target, GLenum attachment, + GLenum renderbuffertarget, GLuint renderbuffer); +GLvoid (APIENTRYP qglGetFramebufferAttachmentParameteriv) (GLenum target, GLenum attachment, + GLenum pname, GLint *params); +GLvoid (APIENTRYP qglBlitFramebuffer) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter); +GLvoid (APIENTRYP qglGenerateMipmap) (GLenum target); + +// GL_EXT_occlusion_query +GLvoid (APIENTRYP qglGenQueriesARB) (GLsizei n, GLuint *ids); +GLvoid (APIENTRYP qglDeleteQueriesARB) (GLsizei n, const GLuint *ids); +GLboolean (APIENTRYP qglIsQueryARB) (GLuint id); +GLvoid (APIENTRYP qglBeginQueryARB) (GLenum target, GLuint id); +GLvoid (APIENTRYP qglEndQueryARB) (GLenum target); +GLvoid (APIENTRYP qglGetQueryivARB) (GLenum target, GLenum pname, GLint *params); +GLvoid (APIENTRYP qglGetQueryObjectivARB) (GLuint id, GLenum pname, GLint *params); +GLvoid (APIENTRYP qglGetQueryObjectuivARB) (GLuint id, GLenum pname, GLuint *params); + +// GL_EXT_timer_query +GLvoid (APIENTRYP qglGetQueryObjecti64vEXT) (GLuint id, GLenum pname, GLint64EXT *params); +GLvoid (APIENTRYP qglGetQueryObjectui64vEXT) (GLuint id, GLenum pname, GLuint64EXT *params); + +// GL_ARB_instanced_arrays +GLvoid (APIENTRYP qglVertexAttribDivisorARB) (GLuint index, GLuint divisor); +GLvoid (APIENTRYP qglDrawArraysInstancedARB) (GLenum mode, GLint first, GLsizei count, + GLsizei primcount); +GLvoid (APIENTRYP qglDrawElementsInstancedARB) (GLenum mode, GLsizei count, GLenum type, + const GLvoid *indices, GLsizei primcount); + +// GL_ARB_separate_stencil +GLvoid (APIENTRYP qglStencilOpSeparate) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLvoid (APIENTRYP qglStencilFuncSeparate) (GLenum face, GLenum func, GLint ref, GLuint mask); +GLvoid (APIENTRYP qglStencilMaskSeparate) (GLenum face, GLuint mask); + +// GL_ARB_debug_output, not in core +GLvoid (APIENTRYP qglDebugMessageControlARB) (GLenum source, + GLenum type, + GLenum severity, + GLsizei count, + const GLuint* ids, + GLboolean enabled); +GLvoid (APIENTRYP qglDebugMessageInsertARB) (GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* buf); +GLvoid (APIENTRYP qglDebugMessageCallbackARB) (GLDEBUGPROCARB callback, + GLvoid *userParam); +GLuint (APIENTRYP qglGetDebugMessageLogARB) (GLuint count, + GLsizei bufsize, + GLenum *sources, + GLenum *types, + GLuint *ids, + GLenum *severities, + GLsizei *lengths, + GLchar *messageLog); +// GL_AMD_debug_output, predecessor to GL_ARB_debug_output, but has only +// a category parameter instead of source and type +GLvoid (APIENTRYP qglDebugMessageEnableAMD) (GLenum category, + GLenum severity, + GLsizei count, + const GLuint* ids, + GLboolean enabled); +GLvoid (APIENTRYP qglDebugMessageInsertAMD) (GLenum category, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* buf); +GLvoid (APIENTRYP qglDebugMessageCallbackAMD) (GLDEBUGPROCAMD callback, + GLvoid *userParam); +GLuint (APIENTRYP qglGetDebugMessageLogAMD) (GLuint count, + GLsizei bufsize, + GLenum *categories, + GLuint *ids, + GLenum *severities, + GLsizei *lengths, + GLchar *messageLog); + +// GL_ARB_blend_func_extended +GLvoid (APIENTRYP qglBindFragDataLocationIndexed) (GLuint program, + GLuint colorNumber, + GLuint index, + const GLchar *name); +GLint (APIENTRYP qglGetFragDataIndex) (GLuint program, + const GLchar *name); + +static unsigned renderThreadID = 0; /* =============== GLimp_Shutdown @@ -90,8 +582,23 @@ GLimp_Shutdown */ void GLimp_Shutdown( void ) { + if( glGlobals.timerQuery ) { + qglDeleteQueriesARB( 1, &glGlobals.timerQuery ); + } + ri.IN_Shutdown(); + GLimp_SetCurrentContext( &initial_context ); + +#ifdef SMP + if( r_smp->integer ) { + GLimp_DestroyContext( &backend_context, &frontend_context ); + } +#endif + if( r_ext_debug_output->integer ) { + GLimp_DestroyContext( &initial_context, &backend_context ); + } + SDL_QuitSubSystem( SDL_INIT_VIDEO ); screen = NULL; @@ -135,8 +642,8 @@ static int GLimp_CompareModes( const void *a, const void *b ) float aspectB = (float)modeB->w / (float)modeB->h; int areaA = modeA->w * modeA->h; int areaB = modeB->w * modeB->h; - float aspectDiffA = fabs( aspectA - displayAspect ); - float aspectDiffB = fabs( aspectB - displayAspect ); + float aspectDiffA = fabs( aspectA - glGlobals.displayAspect ); + float aspectDiffB = fabs( aspectB - glGlobals.displayAspect ); float aspectDiffsDiff = aspectDiffA - aspectDiffB; if( aspectDiffsDiff > ASPECT_EPSILON ) @@ -237,9 +744,9 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder) // Guess the display aspect ratio through the desktop resolution // by assuming (relatively safely) that it is set at or close to // the display's native aspect ratio - displayAspect = (float)videoInfo->current_w / (float)videoInfo->current_h; + glGlobals.displayAspect = (float)videoInfo->current_w / (float)videoInfo->current_h; - ri.Printf( PRINT_ALL, "Estimated display aspect: %.3f\n", displayAspect ); + ri.Printf( PRINT_ALL, "Estimated display aspect: %.3f\n", glGlobals.displayAspect ); } else { @@ -415,9 +922,29 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder) continue; } - opengl_context = GLimp_GetCurrentContext(); + GLimp_GetCurrentContext( &initial_context ); - ri.Printf( PRINT_ALL, "Using %d/%d/%d Color bits, %d depth, %d stencil display.\n", + if( r_ext_debug_output->integer ) { + GLimp_CreateSharedContext( &initial_context, + qtrue, qfalse, + &backend_context ); + } else { + backend_context = initial_context; + } + +#ifdef SMP + if( r_smp->integer ) { + GLimp_CreateSharedContext( &initial_context, + !!r_ext_debug_output->integer, + qtrue, + &frontend_context ); + } else +#endif + frontend_context = backend_context; + + GLimp_SetCurrentContext( &frontend_context ); + + ri.Printf( PRINT_DEVELOPER, "Using %d/%d/%d Color bits, %d depth, %d stencil display.\n", sdlcolorbits, sdlcolorbits, sdlcolorbits, tdepthbits, tstencilbits); glConfig.colorBits = tcolorbits; @@ -507,20 +1034,38 @@ static qboolean GLimp_HaveExtension(const char *ext) GLimp_InitExtensions =============== */ -static void GLimp_InitExtensions( void ) +static void GLimp_InitExtensions( int GLversion ) { +#ifndef __GNUC__ +#define qglGetProc2(var,proc) q##var = (void *)SDL_GL_GetProcAddress( #proc ) +#else +#define qglGetProc2(var,proc) q##var = (typeof(q##var))SDL_GL_GetProcAddress( #proc ) +#endif +#define qglGetProc(name,ext) qglGetProc2(name##ext,name) + if ( !r_allowExtensions->integer ) { ri.Printf( PRINT_ALL, "* IGNORING OPENGL EXTENSIONS *\n" ); - return; + GLversion = 0x0000; + } else { + ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" ); } - ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" ); + // GL_EXT_draw_range_elements, mandatory since OpenGL 1.2 + if ( GLversion >= 0x0102 ) { + qglGetProc(glDrawRangeElements, EXT ); + } else if ( GLversion && + GLimp_HaveExtension( "GL_EXT_draw_range_elements" ) ) { + qglGetProc(glDrawRangeElementsEXT, ); + } else { + qglDrawRangeElementsEXT = NULL; + } glConfig.textureCompression = TC_NONE; // GL_EXT_texture_compression_s3tc - if ( GLimp_HaveExtension( "GL_ARB_texture_compression" ) && + if ( GLversion && + GLimp_HaveExtension( "GL_ARB_texture_compression" ) && GLimp_HaveExtension( "GL_EXT_texture_compression_s3tc" ) ) { if ( r_ext_compressed_textures->value ) @@ -541,7 +1086,8 @@ static void GLimp_InitExtensions( void ) // GL_S3_s3tc ... legacy extension before GL_EXT_texture_compression_s3tc. if (glConfig.textureCompression == TC_NONE) { - if ( GLimp_HaveExtension( "GL_S3_s3tc" ) ) + if ( GLversion && + GLimp_HaveExtension( "GL_S3_s3tc" ) ) { if ( r_ext_compressed_textures->value ) { @@ -560,112 +1106,1105 @@ static void GLimp_InitExtensions( void ) } - // GL_EXT_texture_env_add - glConfig.textureEnvAddAvailable = qfalse; - if ( GLimp_HaveExtension( "EXT_texture_env_add" ) ) - { - if ( r_ext_texture_env_add->integer ) - { - glConfig.textureEnvAddAvailable = qtrue; - ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" ); - } - else - { - glConfig.textureEnvAddAvailable = qfalse; - ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" ); - } + // GL_EXT_texture_env_add, mandatory since OpenGL 1.3 + if ( !r_ext_texture_env_add->integer ) { + glConfig.textureEnvAddAvailable = qfalse; + ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" ); + } else if ( GLversion >= 0x0103 ) { + glConfig.textureEnvAddAvailable = qtrue; + ri.Printf( PRINT_ALL, "...using GL_texture_env_add\n" ); + } else if ( GLversion && + GLimp_HaveExtension( "EXT_texture_env_add" ) ) { + glConfig.textureEnvAddAvailable = qtrue; + ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" ); } else { + glConfig.textureEnvAddAvailable = qfalse; ri.Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" ); } - // GL_ARB_multitexture - qglMultiTexCoord2fARB = NULL; - qglActiveTextureARB = NULL; - qglClientActiveTextureARB = NULL; - if ( GLimp_HaveExtension( "GL_ARB_multitexture" ) ) + // GL_ARB_multitexture, mandatory since OpenGL 1.3 + if ( !r_ext_multitexture->value ) { + qglMultiTexCoord2fARB = NULL; + qglMultiTexCoord4fvARB = NULL; + qglActiveTextureARB = NULL; + qglClientActiveTextureARB = NULL; + ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" ); + } else if ( GLversion >= 0x0103 ) { + qglGetProc(glMultiTexCoord2f, ARB); + qglGetProc(glMultiTexCoord4fv, ARB); + qglGetProc(glActiveTexture, ARB); + qglGetProc(glClientActiveTexture, ARB); + } else if ( GLversion && + GLimp_HaveExtension( "GL_ARB_multitexture" ) ) { + qglGetProc(glMultiTexCoord2fARB, ); + qglGetProc(glMultiTexCoord4fvARB, ); + qglGetProc(glActiveTextureARB, ); + qglGetProc(glClientActiveTextureARB, ); + } else { + qglMultiTexCoord2fARB = NULL; + qglMultiTexCoord4fvARB = NULL; + qglActiveTextureARB = NULL; + qglClientActiveTextureARB = NULL; + ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" ); + } + if ( qglActiveTextureARB ) { - if ( r_ext_multitexture->value ) + qglGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &glGlobals.maxTextureUnits ); + glConfig.numTextureUnits = (int) glGlobals.maxTextureUnits; + if ( glConfig.numTextureUnits > NUM_TEXTURE_BUNDLES ) + glConfig.numTextureUnits = NUM_TEXTURE_BUNDLES; + if ( r_ext_multitexture->integer > 1 && + glConfig.numTextureUnits > r_ext_multitexture->integer ) + glConfig.numTextureUnits = r_ext_multitexture->integer; + if ( glConfig.numTextureUnits > 1 ) { - qglMultiTexCoord2fARB = SDL_GL_GetProcAddress( "glMultiTexCoord2fARB" ); - qglActiveTextureARB = SDL_GL_GetProcAddress( "glActiveTextureARB" ); - qglClientActiveTextureARB = SDL_GL_GetProcAddress( "glClientActiveTextureARB" ); - - if ( qglActiveTextureARB ) - { - GLint glint = 0; - qglGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &glint ); - glConfig.numTextureUnits = (int) glint; - if ( glConfig.numTextureUnits > 1 ) - { - ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" ); - } - else - { - qglMultiTexCoord2fARB = NULL; - qglActiveTextureARB = NULL; - qglClientActiveTextureARB = NULL; - ri.Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 texture units\n" ); - } - } + ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture (%d of %d units)\n", glConfig.numTextureUnits, glGlobals.maxTextureUnits ); } else { - ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" ); + qglMultiTexCoord2fARB = NULL; + qglActiveTextureARB = NULL; + qglClientActiveTextureARB = NULL; + ri.Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 texture units\n" ); } } - else - { - ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" ); + + // GL_ARB_vertex_buffer_object, mandatory since OpenGL 1.5 + if ( !r_ext_vertex_buffer_object->integer ) { + qglBindBufferARB = NULL; + qglDeleteBuffersARB = NULL; + qglGenBuffersARB = NULL; + qglIsBufferARB = NULL; + qglBufferDataARB = NULL; + qglBufferSubDataARB = NULL; + qglGetBufferSubDataARB = NULL; + qglMapBufferARB = NULL; + qglUnmapBufferARB = NULL; + qglGetBufferParameterivARB = NULL; + qglGetBufferPointervARB = NULL; + ri.Printf( PRINT_ALL, "...ignoring GL_ARB_vertex_buffer_object\n" ); + } else if ( GLversion >= 0x0105 ) { + qglGetProc(glBindBuffer, ARB); + qglGetProc(glDeleteBuffers, ARB); + qglGetProc(glGenBuffers, ARB); + qglGetProc(glIsBuffer, ARB); + qglGetProc(glBufferData, ARB); + qglGetProc(glBufferSubData, ARB); + qglGetProc(glGetBufferSubData, ARB); + qglGetProc(glMapBuffer, ARB); + qglGetProc(glUnmapBuffer, ARB); + qglGetProc(glGetBufferParameteriv, ARB); + qglGetProc(glGetBufferPointerv, ARB); + ri.Printf( PRINT_ALL, "...using GL_vertex_buffer_object\n"); + } else if ( GLversion && + GLimp_HaveExtension( "GL_ARB_vertex_buffer_object" ) ) { + qglGetProc(glBindBufferARB, ); + qglGetProc(glDeleteBuffersARB, ); + qglGetProc(glGenBuffersARB, ); + qglGetProc(glIsBufferARB, ); + qglGetProc(glBufferDataARB, ); + qglGetProc(glBufferSubDataARB, ); + qglGetProc(glGetBufferSubDataARB, ); + qglGetProc(glMapBufferARB, ); + qglGetProc(glUnmapBufferARB, ); + qglGetProc(glGetBufferParameterivARB, ); + qglGetProc(glGetBufferPointervARB, ); + ri.Printf( PRINT_ALL, "...using GL_ARB_vertex_buffer_object\n" ); + } else { + qglBindBufferARB = NULL; + qglDeleteBuffersARB = NULL; + qglGenBuffersARB = NULL; + qglIsBufferARB = NULL; + qglBufferDataARB = NULL; + qglBufferSubDataARB = NULL; + qglGetBufferSubDataARB = NULL; + qglMapBufferARB = NULL; + qglUnmapBufferARB = NULL; + qglGetBufferParameterivARB = NULL; + qglGetBufferPointervARB = NULL; + ri.Printf( PRINT_ALL, "...GL_ARB_vertex_buffer_object not found\n" ); + } + + // GL_ARB_map_buffer_range, mandatory since OpenGL 3.0 + if ( !r_ext_map_buffer_range->integer ) { + qglMapBufferRange = NULL; + qglFlushMappedBufferRange = NULL; + ri.Printf( PRINT_DEVELOPER, "...ignoring GL_EXT_map_buffer_range\n" ); + } else if ( GLversion >= 0x0300 ) { + qglGetProc(glMapBufferRange, ); + qglGetProc(glFlushMappedBufferRange, ); + ri.Printf( PRINT_DEVELOPER, "...using GL_map_buffer_range\n"); + } else if ( GLversion && + GLimp_HaveExtension( "GL_ARB_map_buffer_range" ) ) { + qglGetProc(glMapBufferRange, ); + qglGetProc(glFlushMappedBufferRange, ); + ri.Printf( PRINT_DEVELOPER, "...using GL_ARB_map_buffer_range\n" ); + } else { + qglMapBufferRange = NULL; + qglFlushMappedBufferRange = NULL; + ri.Printf( PRINT_DEVELOPER, "...GL_ARB_map_buffer_range not found\n" ); } // GL_EXT_compiled_vertex_array - if ( GLimp_HaveExtension( "GL_EXT_compiled_vertex_array" ) ) - { - if ( r_ext_compiled_vertex_array->value ) - { - ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" ); - qglLockArraysEXT = ( void ( APIENTRY * )( GLint, GLint ) ) SDL_GL_GetProcAddress( "glLockArraysEXT" ); - qglUnlockArraysEXT = ( void ( APIENTRY * )( void ) ) SDL_GL_GetProcAddress( "glUnlockArraysEXT" ); - if (!qglLockArraysEXT || !qglUnlockArraysEXT) - { - ri.Error (ERR_FATAL, "bad getprocaddress"); - } + if ( !r_ext_compiled_vertex_array->value ) { + qglLockArraysEXT = NULL; + qglUnlockArraysEXT = NULL; + ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" ); + } else if ( GLversion && + GLimp_HaveExtension( "GL_EXT_compiled_vertex_array" ) ) { + qglGetProc(glLockArraysEXT, ); + qglGetProc(glUnlockArraysEXT, ); + ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" ); + } else { + qglLockArraysEXT = NULL; + qglUnlockArraysEXT = NULL; + ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" ); + } + + // GL_EXT_texture_filter_anisotropic + if ( !r_ext_texture_filter_anisotropic->integer ) { + glGlobals.textureFilterAnisotropic = qfalse; + ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_filter_anisotropic\n" ); + } else if ( GLversion && + GLimp_HaveExtension( "GL_EXT_texture_filter_anisotropic" ) ) { + qglGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint *)&glGlobals.maxAnisotropy ); + if ( glGlobals.maxAnisotropy <= 0 ) { + ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not properly supported!\n" ); + glGlobals.maxAnisotropy = 0; } else { - ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" ); + ri.Printf( PRINT_ALL, "...using GL_EXT_texture_filter_anisotropic (max: %i)\n", glGlobals.maxAnisotropy ); + glGlobals.textureFilterAnisotropic = qtrue; } + } else { + glGlobals.textureFilterAnisotropic = qfalse; + ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n" ); + } + + // GLSL support, mandatory since OpenGL 2.0 + if ( !r_ext_vertex_shader->integer ) { + qglDeleteShader = NULL; + qglDeleteProgram = NULL; + qglDetachShader = NULL; + qglCreateShader = NULL; + qglShaderSource = NULL; + qglCompileShader = NULL; + qglCreateProgram = NULL; + qglAttachShader = NULL; + qglLinkProgram = NULL; + qglUseProgram = NULL; + qglValidateProgram = NULL; + qglUniform1f = NULL; + qglUniform2f = NULL; + qglUniform3f = NULL; + qglUniform4f = NULL; + qglUniform1i = NULL; + qglUniform2i = NULL; + qglUniform3i = NULL; + qglUniform4i = NULL; + qglUniform1fv = NULL; + qglUniform2fv = NULL; + qglUniform3fv = NULL; + qglUniform4fv = NULL; + qglUniform1iv = NULL; + qglUniform2iv = NULL; + qglUniform3iv = NULL; + qglUniform4iv = NULL; + qglUniformMatrix2fv = NULL; + qglUniformMatrix3fv = NULL; + qglUniformMatrix4fv = NULL; + qglGetShaderiv = NULL; + qglGetProgramiv = NULL; + qglGetShaderInfoLog = NULL; + qglGetProgramInfoLog = NULL; + qglGetAttachedShaders = NULL; + qglGetUniformLocation = NULL; + qglGetActiveUniform = NULL; + qglGetUniformfv = NULL; + qglGetUniformiv = NULL; + qglGetShaderSource = NULL; + + qglVertexAttrib1fARB = NULL; + qglVertexAttrib1sARB = NULL; + qglVertexAttrib1dARB = NULL; + qglVertexAttrib2fARB = NULL; + qglVertexAttrib2sARB = NULL; + qglVertexAttrib2dARB = NULL; + qglVertexAttrib3fARB = NULL; + qglVertexAttrib3sARB = NULL; + qglVertexAttrib3dARB = NULL; + qglVertexAttrib4fARB = NULL; + qglVertexAttrib4sARB = NULL; + qglVertexAttrib4dARB = NULL; + qglVertexAttrib4NubARB = NULL; + qglVertexAttrib1fvARB = NULL; + qglVertexAttrib1svARB = NULL; + qglVertexAttrib1dvARB = NULL; + qglVertexAttrib2fvARB = NULL; + qglVertexAttrib2svARB = NULL; + qglVertexAttrib2dvARB = NULL; + qglVertexAttrib3fvARB = NULL; + qglVertexAttrib3svARB = NULL; + qglVertexAttrib3dvARB = NULL; + qglVertexAttrib4fvARB = NULL; + qglVertexAttrib4svARB = NULL; + qglVertexAttrib4dvARB = NULL; + qglVertexAttrib4ivARB = NULL; + qglVertexAttrib4bvARB = NULL; + qglVertexAttrib4ubvARB = NULL; + qglVertexAttrib4usvARB = NULL; + qglVertexAttrib4uivARB = NULL; + qglVertexAttrib4NbvARB = NULL; + qglVertexAttrib4NsvARB = NULL; + qglVertexAttrib4NivARB = NULL; + qglVertexAttrib4NubvARB = NULL; + qglVertexAttrib4NusvARB = NULL; + qglVertexAttrib4NuivARB = NULL; + qglVertexAttribPointerARB = NULL; + qglEnableVertexAttribArrayARB = NULL; + qglDisableVertexAttribArrayARB = NULL; + qglBindAttribLocationARB = NULL; + qglGetActiveAttribARB = NULL; + qglGetAttribLocationARB = NULL; + qglGetVertexAttribdvARB = NULL; + qglGetVertexAttribfvARB = NULL; + qglGetVertexAttribivARB = NULL; + qglGetVertexAttribPointervARB = NULL; + ri.Printf( PRINT_ALL, "...ignoring GL_ARB_vertex_shader\n" ); + } else if ( GLversion >= 0x0200 ) { + qglGetProc(glDeleteShader, ); + qglGetProc(glDeleteProgram, ); + qglGetProc(glDetachShader, ); + qglGetProc(glCreateShader, ); + qglGetProc(glShaderSource, ); + qglGetProc(glCompileShader, ); + qglGetProc(glCreateProgram, ); + qglGetProc(glAttachShader, ); + qglGetProc(glLinkProgram, ); + qglGetProc(glUseProgram, ); + qglGetProc(glValidateProgram, ); + qglGetProc(glUniform1f, ); + qglGetProc(glUniform2f, ); + qglGetProc(glUniform3f, ); + qglGetProc(glUniform4f, ); + qglGetProc(glUniform1i, ); + qglGetProc(glUniform2i, ); + qglGetProc(glUniform3i, ); + qglGetProc(glUniform4i, ); + qglGetProc(glUniform1fv, ); + qglGetProc(glUniform2fv, ); + qglGetProc(glUniform3fv, ); + qglGetProc(glUniform4fv, ); + qglGetProc(glUniform1iv, ); + qglGetProc(glUniform2iv, ); + qglGetProc(glUniform3iv, ); + qglGetProc(glUniform4iv, ); + qglGetProc(glUniformMatrix2fv, ); + qglGetProc(glUniformMatrix3fv, ); + qglGetProc(glUniformMatrix4fv, ); + qglGetProc(glGetShaderiv, ); + qglGetProc(glGetProgramiv, ); + qglGetProc(glGetShaderInfoLog, ); + qglGetProc(glGetProgramInfoLog, ); + qglGetProc(glGetAttachedShaders, ); + qglGetProc(glGetUniformLocation, ); + qglGetProc(glGetActiveUniform, ); + qglGetProc(glGetUniformfv, ); + qglGetProc(glGetUniformiv, ); + qglGetProc(glGetShaderSource, ); + + qglGetProc(glVertexAttrib1f, ARB); + qglGetProc(glVertexAttrib1s, ARB); + qglGetProc(glVertexAttrib1d, ARB); + qglGetProc(glVertexAttrib2f, ARB); + qglGetProc(glVertexAttrib2s, ARB); + qglGetProc(glVertexAttrib2d, ARB); + qglGetProc(glVertexAttrib3f, ARB); + qglGetProc(glVertexAttrib3s, ARB); + qglGetProc(glVertexAttrib3d, ARB); + qglGetProc(glVertexAttrib4f, ARB); + qglGetProc(glVertexAttrib4s, ARB); + qglGetProc(glVertexAttrib4d, ARB); + qglGetProc(glVertexAttrib4Nub, ARB); + qglGetProc(glVertexAttrib1fv, ARB); + qglGetProc(glVertexAttrib1sv, ARB); + qglGetProc(glVertexAttrib1dv, ARB); + qglGetProc(glVertexAttrib2fv, ARB); + qglGetProc(glVertexAttrib2sv, ARB); + qglGetProc(glVertexAttrib2dv, ARB); + qglGetProc(glVertexAttrib3fv, ARB); + qglGetProc(glVertexAttrib3sv, ARB); + qglGetProc(glVertexAttrib3dv, ARB); + qglGetProc(glVertexAttrib4fv, ARB); + qglGetProc(glVertexAttrib4sv, ARB); + qglGetProc(glVertexAttrib4dv, ARB); + qglGetProc(glVertexAttrib4iv, ARB); + qglGetProc(glVertexAttrib4bv, ARB); + qglGetProc(glVertexAttrib4ubv, ARB); + qglGetProc(glVertexAttrib4usv, ARB); + qglGetProc(glVertexAttrib4uiv, ARB); + qglGetProc(glVertexAttrib4Nbv, ARB); + qglGetProc(glVertexAttrib4Nsv, ARB); + qglGetProc(glVertexAttrib4Niv, ARB); + qglGetProc(glVertexAttrib4Nubv, ARB); + qglGetProc(glVertexAttrib4Nusv, ARB); + qglGetProc(glVertexAttrib4Nuiv, ARB); + qglGetProc(glVertexAttribPointer, ARB); + qglGetProc(glEnableVertexAttribArray, ARB); + qglGetProc(glDisableVertexAttribArray, ARB); + qglGetProc(glBindAttribLocation, ARB); + qglGetProc(glGetActiveAttrib, ARB); + qglGetProc(glGetAttribLocation, ARB); + qglGetProc(glGetVertexAttribdv, ARB); + qglGetProc(glGetVertexAttribfv, ARB); + qglGetProc(glGetVertexAttribiv, ARB); + qglGetProc(glGetVertexAttribPointerv, ARB); + + ri.Printf( PRINT_ALL, "...using GL_vertex_shader\n" ); + + } else if ( GLversion && + GLimp_HaveExtension( "GL_ARB_shader_objects" ) && + GLimp_HaveExtension( "GL_ARB_fragment_shader" ) && + GLimp_HaveExtension( "GL_ARB_vertex_shader" ) && + GLimp_HaveExtension( "GL_ARB_shading_language_100" ) ) + { + // many functions have been renamed from the ARB ext to GL 2.0 + qglGetProc2(glDeleteShader, glDeleteObjectARB); + qglGetProc2(glDeleteProgram, glDeleteObjectARB); + qglGetProc2(glDetachShader, glDetachObjectARB); + qglGetProc2(glCreateShader, glCreateShaderObjectARB); + qglGetProc2(glShaderSource, glShaderSourceARB); + qglGetProc2(glCompileShader, glCompileShaderARB); + qglGetProc2(glCreateProgram, glCreateProgramObjectARB); + qglGetProc2(glAttachShader, glAttachObjectARB); + qglGetProc2(glLinkProgram, glLinkProgramARB); + qglGetProc2(glUseProgram, glUseProgramObjectARB); + qglGetProc2(glValidateProgram, glValidateProgramARB); + qglGetProc2(glUniform1f, glUniform1fARB); + qglGetProc2(glUniform2f, glUniform2fARB); + qglGetProc2(glUniform3f, glUniform3fARB); + qglGetProc2(glUniform4f, glUniform4fARB); + qglGetProc2(glUniform1i, glUniform1iARB); + qglGetProc2(glUniform2i, glUniform2iARB); + qglGetProc2(glUniform3i, glUniform3iARB); + qglGetProc2(glUniform4i, glUniform4iARB); + qglGetProc2(glUniform1fv, glUniform1fvARB); + qglGetProc2(glUniform2fv, glUniform2fvARB); + qglGetProc2(glUniform3fv, glUniform3fvARB); + qglGetProc2(glUniform4fv, glUniform4fvARB); + qglGetProc2(glUniform1iv, glUniform1ivARB); + qglGetProc2(glUniform2iv, glUniform2ivARB); + qglGetProc2(glUniform3iv, glUniform3ivARB); + qglGetProc2(glUniform4iv, glUniform4ivARB); + qglGetProc2(glUniform2fv, glUniformMatrix2fvARB); + qglGetProc2(glUniform3fv, glUniformMatrix3fvARB); + qglGetProc2(glUniform4fv, glUniformMatrix4fvARB); + qglGetProc2(glGetShaderiv, glGetObjectParameterivARB); + qglGetProc2(glGetProgramiv, glGetObjectParameterivARB); + qglGetProc2(glGetShaderInfoLog, glGetInfoLogARB); + qglGetProc2(glGetProgramInfoLog, glGetInfoLogARB); + qglGetProc2(glGetAttachedShaders, glGetAttachedObjectsARB); + qglGetProc2(glGetUniformLocation, glGetUniformLocationARB); + qglGetProc2(glGetActiveUniform, glGetActiveUniformARB); + qglGetProc2(glGetUniformfv, glGetUniformfvARB); + qglGetProc2(glGetUniformiv, glGetUniformivARB); + qglGetProc2(glGetShaderSource, glGetShaderSourceARB); + + qglGetProc(glVertexAttrib1fARB, ); + qglGetProc(glVertexAttrib1sARB, ); + qglGetProc(glVertexAttrib1dARB, ); + qglGetProc(glVertexAttrib2fARB, ); + qglGetProc(glVertexAttrib2sARB, ); + qglGetProc(glVertexAttrib2dARB, ); + qglGetProc(glVertexAttrib3fARB, ); + qglGetProc(glVertexAttrib3sARB, ); + qglGetProc(glVertexAttrib3dARB, ); + qglGetProc(glVertexAttrib4fARB, ); + qglGetProc(glVertexAttrib4sARB, ); + qglGetProc(glVertexAttrib4dARB, ); + qglGetProc(glVertexAttrib4NubARB, ); + qglGetProc(glVertexAttrib1fvARB, ); + qglGetProc(glVertexAttrib1svARB, ); + qglGetProc(glVertexAttrib1dvARB, ); + qglGetProc(glVertexAttrib2fvARB, ); + qglGetProc(glVertexAttrib2svARB, ); + qglGetProc(glVertexAttrib2dvARB, ); + qglGetProc(glVertexAttrib3fvARB, ); + qglGetProc(glVertexAttrib3svARB, ); + qglGetProc(glVertexAttrib3dvARB, ); + qglGetProc(glVertexAttrib4fvARB, ); + qglGetProc(glVertexAttrib4svARB, ); + qglGetProc(glVertexAttrib4dvARB, ); + qglGetProc(glVertexAttrib4ivARB, ); + qglGetProc(glVertexAttrib4bvARB, ); + qglGetProc(glVertexAttrib4ubvARB, ); + qglGetProc(glVertexAttrib4usvARB, ); + qglGetProc(glVertexAttrib4uivARB, ); + qglGetProc(glVertexAttrib4NbvARB, ); + qglGetProc(glVertexAttrib4NsvARB, ); + qglGetProc(glVertexAttrib4NivARB, ); + qglGetProc(glVertexAttrib4NubvARB, ); + qglGetProc(glVertexAttrib4NusvARB, ); + qglGetProc(glVertexAttrib4NuivARB, ); + qglGetProc(glVertexAttribPointerARB, ); + qglGetProc(glEnableVertexAttribArrayARB, ); + qglGetProc(glDisableVertexAttribArrayARB, ); + qglGetProc(glBindAttribLocationARB, ); + qglGetProc(glGetActiveAttribARB, ); + qglGetProc(glGetAttribLocationARB, ); + qglGetProc(glGetVertexAttribdvARB, ); + qglGetProc(glGetVertexAttribfvARB, ); + qglGetProc(glGetVertexAttribivARB, ); + qglGetProc(glGetVertexAttribPointervARB, ); + + ri.Printf( PRINT_ALL, "...using GL_ARB_vertex_shader\n" ); } else { - ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" ); + qglDeleteShader = NULL; + qglDeleteProgram = NULL; + qglDetachShader = NULL; + qglCreateShader = NULL; + qglShaderSource = NULL; + qglCompileShader = NULL; + qglCreateProgram = NULL; + qglAttachShader = NULL; + qglLinkProgram = NULL; + qglUseProgram = NULL; + qglValidateProgram = NULL; + qglUniform1f = NULL; + qglUniform2f = NULL; + qglUniform3f = NULL; + qglUniform4f = NULL; + qglUniform1i = NULL; + qglUniform2i = NULL; + qglUniform3i = NULL; + qglUniform4i = NULL; + qglUniform1fv = NULL; + qglUniform2fv = NULL; + qglUniform3fv = NULL; + qglUniform4fv = NULL; + qglUniform1iv = NULL; + qglUniform2iv = NULL; + qglUniform3iv = NULL; + qglUniform4iv = NULL; + qglUniformMatrix2fv = NULL; + qglUniformMatrix3fv = NULL; + qglUniformMatrix4fv = NULL; + qglGetShaderiv = NULL; + qglGetProgramiv = NULL; + qglGetShaderInfoLog = NULL; + qglGetProgramInfoLog = NULL; + qglGetAttachedShaders = NULL; + qglGetUniformLocation = NULL; + qglGetActiveUniform = NULL; + qglGetUniformfv = NULL; + qglGetUniformiv = NULL; + qglGetShaderSource = NULL; + + qglVertexAttrib1fARB = NULL; + qglVertexAttrib1sARB = NULL; + qglVertexAttrib1dARB = NULL; + qglVertexAttrib2fARB = NULL; + qglVertexAttrib2sARB = NULL; + qglVertexAttrib2dARB = NULL; + qglVertexAttrib3fARB = NULL; + qglVertexAttrib3sARB = NULL; + qglVertexAttrib3dARB = NULL; + qglVertexAttrib4fARB = NULL; + qglVertexAttrib4sARB = NULL; + qglVertexAttrib4dARB = NULL; + qglVertexAttrib4NubARB = NULL; + qglVertexAttrib1fvARB = NULL; + qglVertexAttrib1svARB = NULL; + qglVertexAttrib1dvARB = NULL; + qglVertexAttrib2fvARB = NULL; + qglVertexAttrib2svARB = NULL; + qglVertexAttrib2dvARB = NULL; + qglVertexAttrib3fvARB = NULL; + qglVertexAttrib3svARB = NULL; + qglVertexAttrib3dvARB = NULL; + qglVertexAttrib4fvARB = NULL; + qglVertexAttrib4svARB = NULL; + qglVertexAttrib4dvARB = NULL; + qglVertexAttrib4ivARB = NULL; + qglVertexAttrib4bvARB = NULL; + qglVertexAttrib4ubvARB = NULL; + qglVertexAttrib4usvARB = NULL; + qglVertexAttrib4uivARB = NULL; + qglVertexAttrib4NbvARB = NULL; + qglVertexAttrib4NsvARB = NULL; + qglVertexAttrib4NivARB = NULL; + qglVertexAttrib4NubvARB = NULL; + qglVertexAttrib4NusvARB = NULL; + qglVertexAttrib4NuivARB = NULL; + qglVertexAttribPointerARB = NULL; + qglEnableVertexAttribArrayARB = NULL; + qglDisableVertexAttribArrayARB = NULL; + qglBindAttribLocationARB = NULL; + qglGetActiveAttribARB = NULL; + qglGetAttribLocationARB = NULL; + qglGetVertexAttribdvARB = NULL; + qglGetVertexAttribfvARB = NULL; + qglGetVertexAttribivARB = NULL; + qglGetVertexAttribPointervARB = NULL; + + ri.Printf( PRINT_ALL, "...GL_ARB_vertex_shader not found\n" ); } - textureFilterAnisotropic = qfalse; - if ( GLimp_HaveExtension( "GL_EXT_texture_filter_anisotropic" ) ) - { - if ( r_ext_texture_filter_anisotropic->integer ) { - qglGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint *)&maxAnisotropy ); - if ( maxAnisotropy <= 0 ) { - ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not properly supported!\n" ); - maxAnisotropy = 0; - } - else - { - ri.Printf( PRINT_ALL, "...using GL_EXT_texture_filter_anisotropic (max: %i)\n", maxAnisotropy ); - textureFilterAnisotropic = qtrue; - } - } - else - { - ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_filter_anisotropic\n" ); + if ( qglCreateShader ) { + // check that fragment shaders may access enough texture image units + // to render a whole shader in one pass + qglGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &glGlobals.maxTextureImageUnits ); + qglGetIntegerv( GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB, &glGlobals.maxVertexTextureImageUnits ); + if ( glGlobals.maxTextureImageUnits < MAX_SHADER_STAGES + 4 ) { + qglDeleteShader = NULL; + qglDeleteProgram = NULL; + qglDetachShader = NULL; + qglCreateShader = NULL; + qglShaderSource = NULL; + qglCompileShader = NULL; + qglCreateProgram = NULL; + qglAttachShader = NULL; + qglLinkProgram = NULL; + qglUseProgram = NULL; + qglValidateProgram = NULL; + qglUniform1f = NULL; + qglUniform2f = NULL; + qglUniform3f = NULL; + qglUniform4f = NULL; + qglUniform1i = NULL; + qglUniform2i = NULL; + qglUniform3i = NULL; + qglUniform4i = NULL; + qglUniform1fv = NULL; + qglUniform2fv = NULL; + qglUniform3fv = NULL; + qglUniform4fv = NULL; + qglUniform1iv = NULL; + qglUniform2iv = NULL; + qglUniform3iv = NULL; + qglUniform4iv = NULL; + qglUniformMatrix2fv = NULL; + qglUniformMatrix3fv = NULL; + qglUniformMatrix4fv = NULL; + qglGetShaderiv = NULL; + qglGetProgramiv = NULL; + qglGetShaderInfoLog = NULL; + qglGetProgramInfoLog = NULL; + qglGetAttachedShaders = NULL; + qglGetUniformLocation = NULL; + qglGetActiveUniform = NULL; + qglGetUniformfv = NULL; + qglGetUniformiv = NULL; + qglGetShaderSource = NULL; + + qglVertexAttrib1fARB = NULL; + qglVertexAttrib1sARB = NULL; + qglVertexAttrib1dARB = NULL; + qglVertexAttrib2fARB = NULL; + qglVertexAttrib2sARB = NULL; + qglVertexAttrib2dARB = NULL; + qglVertexAttrib3fARB = NULL; + qglVertexAttrib3sARB = NULL; + qglVertexAttrib3dARB = NULL; + qglVertexAttrib4fARB = NULL; + qglVertexAttrib4sARB = NULL; + qglVertexAttrib4dARB = NULL; + qglVertexAttrib4NubARB = NULL; + qglVertexAttrib1fvARB = NULL; + qglVertexAttrib1svARB = NULL; + qglVertexAttrib1dvARB = NULL; + qglVertexAttrib2fvARB = NULL; + qglVertexAttrib2svARB = NULL; + qglVertexAttrib2dvARB = NULL; + qglVertexAttrib3fvARB = NULL; + qglVertexAttrib3svARB = NULL; + qglVertexAttrib3dvARB = NULL; + qglVertexAttrib4fvARB = NULL; + qglVertexAttrib4svARB = NULL; + qglVertexAttrib4dvARB = NULL; + qglVertexAttrib4ivARB = NULL; + qglVertexAttrib4bvARB = NULL; + qglVertexAttrib4ubvARB = NULL; + qglVertexAttrib4usvARB = NULL; + qglVertexAttrib4uivARB = NULL; + qglVertexAttrib4NbvARB = NULL; + qglVertexAttrib4NsvARB = NULL; + qglVertexAttrib4NivARB = NULL; + qglVertexAttrib4NubvARB = NULL; + qglVertexAttrib4NusvARB = NULL; + qglVertexAttrib4NuivARB = NULL; + qglVertexAttribPointerARB = NULL; + qglEnableVertexAttribArrayARB = NULL; + qglDisableVertexAttribArrayARB = NULL; + qglBindAttribLocationARB = NULL; + qglGetActiveAttribARB = NULL; + qglGetAttribLocationARB = NULL; + qglGetVertexAttribdvARB = NULL; + qglGetVertexAttribfvARB = NULL; + qglGetVertexAttribivARB = NULL; + qglGetVertexAttribPointervARB = NULL; + + ri.Printf( PRINT_ALL, "Fragment/Vertex shaders support only %d/%d texture image units - disabled\n", + glGlobals.maxTextureImageUnits, + glGlobals.maxVertexTextureImageUnits ); } + } else { + glGlobals.maxTextureImageUnits = glGlobals.maxTextureUnits; + } + + // GL_EXT_geometry_shader4, mandatory since OpenGL 3.2 + if ( !r_ext_geometry_shader->integer ) { + qglProgramParameteriEXT = NULL; + qglFramebufferTextureEXT = NULL; + qglFramebufferTextureLayerEXT = NULL; + qglFramebufferTextureFaceEXT = NULL; + + ri.Printf( PRINT_ALL, "...ignoring GL_EXT_geometry_shader4\n" ); + } else if ( GLversion >= 0x0302 ) { + qglGetProc(glProgramParameteri, EXT); + qglGetProc(glFramebufferTexture, EXT); + qglGetProc(glFramebufferTextureLayer, EXT); + qglGetProc(glFramebufferTextureFace, EXT); + + ri.Printf( PRINT_ALL, "...using GL_geometry_shader\n" ); + + } else if ( GLversion && + GLimp_HaveExtension( "GL_EXT_geometry_shader4" ) ) + { + qglGetProc(glProgramParameteriEXT, ); + qglGetProc(glFramebufferTextureEXT, ); + qglGetProc(glFramebufferTextureLayerEXT, ); + qglGetProc(glFramebufferTextureFaceEXT, ); + + ri.Printf( PRINT_ALL, "...using GL_EXT_geometry_shader4\n" ); } else { - ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n" ); + qglProgramParameteriEXT = NULL; + qglFramebufferTextureEXT = NULL; + qglFramebufferTextureLayerEXT = NULL; + qglFramebufferTextureFaceEXT = NULL; + + ri.Printf( PRINT_ALL, "...GL_EXT_geometry_shader4 not found\n" ); + } + + // GL_ARB_texture_float, mandatory since OpenGL 3.0 + if ( !r_ext_texture_float->integer ) { + glGlobals.floatTextures = qfalse; + ri.Printf( PRINT_ALL, "...ignoring GL_ARB_texture_float\n" ); + } else if ( GLversion >= 0x0300 && + glGlobals.maxVertexTextureImageUnits > 0 ) { + glGlobals.floatTextures = qtrue; + ri.Printf( PRINT_ALL, "...using GL_texture_float\n" ); + } else if ( GLversion && + GLimp_HaveExtension( "GL_ARB_texture_float" ) && + glGlobals.maxVertexTextureImageUnits > 0 ) { + glGlobals.floatTextures = qtrue; + ri.Printf( PRINT_ALL, "...using GL_ARB_texture_float\n" ); + } else { + glGlobals.floatTextures = qfalse; + ri.Printf( PRINT_ALL, "...GL_ARB_texture_float not found\n" ); + } + + // GL_EXT_texture_buffer_object, mandatory since OpenGL 3.0 + if( !glGlobals.floatTextures || !r_ext_texture_buffer_object->integer ) { + qglTexBufferEXT = NULL; + ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_buffer_object\n" ); + } else if( GLversion >= 0x0300 ) { + qglGetProc(glTexBuffer, EXT); + ri.Printf( PRINT_ALL, "...using GL_texture_buffer_object\n" ); + } else if ( GLversion && + GLimp_HaveExtension( "GL_EXT_texture_buffer_object" ) ) { + qglGetProc(glTexBufferEXT, ); + ri.Printf( PRINT_ALL, "...using GL_EXT_texture_buffer_object\n" ); + } else { + qglTexBufferEXT = NULL; + ri.Printf( PRINT_ALL, "...GL_EXT_texture_buffer_object not found\n" ); + } + + // GL_ARB_uniform_buffer_object, mandatory since OpenGL 3.1 + if( !r_ext_uniform_buffer_object->integer ) { + qglGetUniformIndices = NULL; + qglGetActiveUniformsiv = NULL; + qglGetActiveUniformName = NULL; + qglGetUniformBlockIndex = NULL; + qglGetActiveUniformBlockiv = NULL; + qglGetActiveUniformBlockName = NULL; + qglBindBufferRange = NULL; + qglBindBufferBase = NULL; + qglGetIntegeri_v = NULL; + qglUniformBlockBinding = NULL; + ri.Printf( PRINT_DEVELOPER, "...ignoring GL_ARB_uniform_buffer_object\n" ); + } else if( GLversion >= 0x0301 ) { + qglGetProc(glGetUniformIndices, ); + qglGetProc(glGetActiveUniformsiv, ); + qglGetProc(glGetActiveUniformName, ); + qglGetProc(glGetUniformBlockIndex, ); + qglGetProc(glGetActiveUniformBlockiv, ); + qglGetProc(glGetActiveUniformBlockName, ); + qglGetProc(glBindBufferRange, ); + qglGetProc(glBindBufferBase, ); + qglGetProc(glGetIntegeri_v, ); + qglGetProc(glUniformBlockBinding, ); + ri.Printf( PRINT_DEVELOPER, "...using GL_uniform_buffer_object\n" ); + } else if ( GLversion && + GLimp_HaveExtension( "GL_ARB_uniform_buffer_object" ) ) { + qglGetProc(glGetUniformIndices, ); + qglGetProc(glGetActiveUniformsiv, ); + qglGetProc(glGetActiveUniformName, ); + qglGetProc(glGetUniformBlockIndex, ); + qglGetProc(glGetActiveUniformBlockiv, ); + qglGetProc(glGetActiveUniformBlockName, ); + qglGetProc(glBindBufferRange, ); + qglGetProc(glBindBufferBase, ); + qglGetProc(glGetIntegeri_v, ); + qglGetProc(glUniformBlockBinding, ); + ri.Printf( PRINT_DEVELOPER, "...using GL_ARB_uniform_buffer_object\n" ); + } else { + qglGetUniformIndices = NULL; + qglGetActiveUniformsiv = NULL; + qglGetActiveUniformName = NULL; + qglGetUniformBlockIndex = NULL; + qglGetActiveUniformBlockiv = NULL; + qglGetActiveUniformBlockName = NULL; + qglBindBufferRange = NULL; + qglBindBufferBase = NULL; + qglGetIntegeri_v = NULL; + qglUniformBlockBinding = NULL; + ri.Printf( PRINT_DEVELOPER, "...GL_ARB_uniform_buffer_object not found\n" ); + } + + // GL_EXT_texture3D, mandatory since OpenGL 1.2 + if ( !r_ext_texture3D->integer ) { + qglTexImage3DEXT = NULL; + ri.Printf( PRINT_DEVELOPER, "...ignoring GL_EXT_texture3D\n" ); + } else if ( GLversion >= 0x0102 ) { + qglGetProc(glTexImage3D, EXT); + ri.Printf( PRINT_DEVELOPER, "...using GL_texture3D\n" ); + } else if ( GLversion && + GLimp_HaveExtension( "GL_EXT_texture3D" ) ) { + qglGetProc(glTexImage3DEXT, ); + ri.Printf( PRINT_DEVELOPER, "...using GL_EXT_texture3D\n" ); + } else { + qglTexImage3DEXT = NULL; + ri.Printf( PRINT_DEVELOPER, "...GL_EXT_texture3D not found\n" ); + } + + // GL_ARB_framebuffer_object, mandatory since OpenGL 3.0 + if ( !r_ext_framebuffer_object->integer ) { + qglIsRenderbuffer = NULL; + qglBindRenderbuffer = NULL; + qglDeleteRenderbuffers = NULL; + qglGenRenderbuffers = NULL; + qglRenderbufferStorage = NULL; + qglRenderbufferStorageMultisample = NULL; + qglGetRenderbufferParameteriv = NULL; + qglIsFramebuffer = NULL; + qglBindFramebuffer = NULL; + qglDeleteFramebuffers = NULL; + qglGenFramebuffers = NULL; + qglCheckFramebufferStatus = NULL; + qglFramebufferTexture1D = NULL; + qglFramebufferTexture2D = NULL; + qglFramebufferTexture3D = NULL; + qglFramebufferTextureLayer = NULL; + qglFramebufferRenderbuffer = NULL; + qglGetFramebufferAttachmentParameteriv = NULL; + qglBlitFramebuffer = NULL; + qglGenerateMipmap = NULL; + + ri.Printf( PRINT_ALL, "...ignoring GL_ARB_framebuffer_object\n" ); + } else if ( GLversion >= 0x0300 ) { + qglGetProc(glIsRenderbuffer, ); + qglGetProc(glBindRenderbuffer, ); + qglGetProc(glDeleteRenderbuffers, ); + qglGetProc(glGenRenderbuffers, ); + qglGetProc(glRenderbufferStorage, ); + qglGetProc(glRenderbufferStorageMultisample, ); + qglGetProc(glGetRenderbufferParameteriv, ); + qglGetProc(glIsFramebuffer, ); + qglGetProc(glBindFramebuffer, ); + qglGetProc(glDeleteFramebuffers, ); + qglGetProc(glGenFramebuffers, ); + qglGetProc(glCheckFramebufferStatus, ); + qglGetProc(glFramebufferTexture1D, ); + qglGetProc(glFramebufferTexture2D, ); + qglGetProc(glFramebufferTexture3D, ); + qglGetProc(glFramebufferTextureLayer, ); + qglGetProc(glFramebufferRenderbuffer, ); + qglGetProc(glGetFramebufferAttachmentParameteriv, ); + qglGetProc(glBlitFramebuffer, ); + qglGetProc(glGenerateMipmap, ); + + ri.Printf( PRINT_ALL, "...using GL_framebuffer_object\n" ); + } else if ( GLversion && + GLimp_HaveExtension( "GL_ARB_framebuffer_object" ) ) + { + qglGetProc(glIsRenderbuffer, ); + qglGetProc(glBindRenderbuffer, ); + qglGetProc(glDeleteRenderbuffers, ); + qglGetProc(glGenRenderbuffers, ); + qglGetProc(glRenderbufferStorage, ); + qglGetProc(glRenderbufferStorageMultisample, ); + qglGetProc(glGetRenderbufferParameteriv, ); + qglGetProc(glIsFramebuffer, ); + qglGetProc(glBindFramebuffer, ); + qglGetProc(glDeleteFramebuffers, ); + qglGetProc(glGenFramebuffers, ); + qglGetProc(glCheckFramebufferStatus, ); + qglGetProc(glFramebufferTexture1D, ); + qglGetProc(glFramebufferTexture2D, ); + qglGetProc(glFramebufferTexture3D, ); + qglGetProc(glFramebufferTextureLayer, ); + qglGetProc(glFramebufferRenderbuffer, ); + qglGetProc(glGetFramebufferAttachmentParameteriv, ); + qglGetProc(glBlitFramebuffer, ); + qglGetProc(glGenerateMipmap, ); + + ri.Printf( PRINT_ALL, "...using GL_ARB_framebuffer_object\n" ); + } + else + { + qglIsRenderbuffer = NULL; + qglBindRenderbuffer = NULL; + qglDeleteRenderbuffers = NULL; + qglGenRenderbuffers = NULL; + qglRenderbufferStorage = NULL; + qglRenderbufferStorageMultisample = NULL; + qglGetRenderbufferParameteriv = NULL; + qglIsFramebuffer = NULL; + qglBindFramebuffer = NULL; + qglDeleteFramebuffers = NULL; + qglGenFramebuffers = NULL; + qglCheckFramebufferStatus = NULL; + qglFramebufferTexture1D = NULL; + qglFramebufferTexture2D = NULL; + qglFramebufferTexture3D = NULL; + qglFramebufferTextureLayer = NULL; + qglFramebufferRenderbuffer = NULL; + qglGetFramebufferAttachmentParameteriv = NULL; + qglBlitFramebuffer = NULL; + qglGenerateMipmap = NULL; + + ri.Printf( PRINT_ALL, "...GL_ARB_framebuffer_object not found\n" ); + } + + // GL_ARB_occlusion_query, mandatory since OpenGL 1.5 + if ( !r_ext_occlusion_query->integer ) { + qglGenQueriesARB = NULL; + qglDeleteQueriesARB = NULL; + qglIsQueryARB = NULL; + qglBeginQueryARB = NULL; + qglEndQueryARB = NULL; + qglGetQueryivARB = NULL; + qglGetQueryObjectivARB = NULL; + qglGetQueryObjectuivARB = NULL; + + ri.Printf( PRINT_ALL, "...ignoring GL_ARB_occlusion_query\n" ); + } else if ( GLversion >= 0x0105 ) { + qglGetProc(glGenQueries, ARB); + qglGetProc(glDeleteQueries, ARB); + qglGetProc(glIsQuery, ARB); + qglGetProc(glBeginQuery, ARB); + qglGetProc(glEndQuery, ARB); + qglGetProc(glGetQueryiv, ARB); + qglGetProc(glGetQueryObjectiv, ARB); + qglGetProc(glGetQueryObjectuiv, ARB); + ri.Printf( PRINT_ALL, "...using GL_occlusion_query\n" ); + } else if ( GLversion && + GLimp_HaveExtension( "GL_ARB_occlusion_query" ) ) { + qglGetProc(glGenQueriesARB, ); + qglGetProc(glDeleteQueriesARB, ); + qglGetProc(glIsQueryARB, ); + qglGetProc(glBeginQueryARB, ); + qglGetProc(glEndQueryARB, ); + qglGetProc(glGetQueryivARB, ); + qglGetProc(glGetQueryObjectivARB, ); + qglGetProc(glGetQueryObjectuivARB, ); + ri.Printf( PRINT_ALL, "...using GL_ARB_occlusion_query\n" ); + } else { + qglGenQueriesARB = NULL; + qglDeleteQueriesARB = NULL; + qglIsQueryARB = NULL; + qglBeginQueryARB = NULL; + qglEndQueryARB = NULL; + qglGetQueryivARB = NULL; + qglGetQueryObjectivARB = NULL; + qglGetQueryObjectuivARB = NULL; + + ri.Printf( PRINT_ALL, "...GL_ARB_occlusion_query not found\n" ); + } + + // GL_EXT_timer_query, in core since OpenGL 3.3 + if ( !r_ext_timer_query->integer || !qglGenQueriesARB ) { + glGlobals.timerQuery = 0; + qglGetQueryObjecti64vEXT = NULL; + qglGetQueryObjectui64vEXT = NULL; + + ri.Printf( PRINT_ALL, "...ignoring GL_ARB_timer_query\n" ); + } else if ( GLversion >= 0x0303 ) { + qglGetProc(glGetQueryObjecti64v, EXT); + qglGetProc(glGetQueryObjectui64v, EXT); + qglGenQueriesARB( 1, &glGlobals.timerQuery ); + ri.Printf( PRINT_ALL, "...using GL_timer_query\n" ); + } else if ( GLversion && + GLimp_HaveExtension( "GL_EXT_timer_query" ) ) { + qglGetProc(glGetQueryObjecti64vEXT, ); + qglGetProc(glGetQueryObjectui64vEXT, ); + qglGenQueriesARB( 1, &glGlobals.timerQuery ); + ri.Printf( PRINT_ALL, "...using GL_ARB_timer_query\n" ); + } + else + { + glGlobals.timerQuery = 0; + qglGetQueryObjecti64vEXT = NULL; + qglGetQueryObjectui64vEXT = NULL; + + ri.Printf( PRINT_ALL, "...GL_ARB_timer_query not found\n" ); + } + + // GL_ARB_instanced_arrays, in core since OpenGL 3.3 + if ( !r_ext_instanced_arrays->integer ) { + qglVertexAttribDivisorARB = NULL; + qglDrawArraysInstancedARB = NULL; + qglDrawElementsInstancedARB = NULL; + + ri.Printf( PRINT_ALL, "...ignoring GL_ARB_instanced_arrays\n" ); + } else if ( GLversion >= 0x0303 ) { + qglGetProc(glVertexAttribDivisor, ARB); + qglGetProc(glDrawArraysInstanced, ARB); + qglGetProc(glDrawElementsInstanced, ARB); + + ri.Printf( PRINT_ALL, "...using GL_instanced_arrays\n" ); + } else if ( GLversion && + GLimp_HaveExtension( "GL_ARB_instanced_arrays" ) ) { + qglGetProc(glVertexAttribDivisorARB, ); + qglGetProc(glDrawArraysInstancedARB, ); + qglGetProc(glDrawElementsInstancedARB, ); + + ri.Printf( PRINT_ALL, "...using GL_ARB_instanced_arrays\n" ); + } + else + { + qglVertexAttribDivisorARB = NULL; + qglDrawArraysInstancedARB = NULL; + qglDrawElementsInstancedARB = NULL; + + ri.Printf( PRINT_ALL, "...GL_ARB_instanced_arrays not found\n" ); + } + + // GL_ARB_separate_stencil, part of 2.0 but not a separate extension + if ( !r_ext_separate_stencil->integer ) { + qglStencilFuncSeparate = NULL; + qglStencilOpSeparate = NULL; + qglStencilMaskSeparate = NULL; + + ri.Printf( PRINT_ALL, "...ignoring GL_ARB_separate_stencil\n" ); + } else if ( GLversion >= 0x0200 ) { + qglGetProc(glStencilFuncSeparate, ); + qglGetProc(glStencilOpSeparate, ); + qglGetProc(glStencilMaskSeparate, ); + + ri.Printf( PRINT_ALL, "...using GL_separate_stencil\n" ); + } else { + qglStencilFuncSeparate = NULL; + qglStencilOpSeparate = NULL; + qglStencilMaskSeparate = NULL; + + ri.Printf( PRINT_ALL, "...GL_ARB_separate_stencil not found\n" ); + } + + // GL_{AMD/ARB}_debug_output, not in core + if ( !r_ext_debug_output->integer ) { + qglDebugMessageControlARB = NULL; + qglDebugMessageInsertARB = NULL; + qglDebugMessageCallbackARB = NULL; + qglGetDebugMessageLogARB = NULL; + + qglDebugMessageEnableAMD = NULL; + qglDebugMessageInsertAMD = NULL; + qglDebugMessageCallbackAMD = NULL; + qglGetDebugMessageLogAMD = NULL; + + ri.Printf( PRINT_ALL, "...ignoring GL_ARB_debug_output\n" ); + } else if ( GLversion && + GLimp_HaveExtension( "GL_ARB_debug_output" ) ) { + qglGetProc(glDebugMessageControlARB, ); + qglGetProc(glDebugMessageInsertARB, ); + qglGetProc(glDebugMessageCallbackARB, ); + qglGetProc(glGetDebugMessageLogARB, ); + + qglDebugMessageEnableAMD = NULL; + qglDebugMessageInsertAMD = NULL; + qglDebugMessageCallbackAMD = NULL; + qglGetDebugMessageLogAMD = NULL; + + ri.Printf( PRINT_ALL, "...using GL_ARB_debug_output\n" ); + } else if ( GLversion && + GLimp_HaveExtension( "GL_AMD_debug_output" ) ) { + qglDebugMessageControlARB = NULL; + qglDebugMessageInsertARB = NULL; + qglDebugMessageCallbackARB = NULL; + qglGetDebugMessageLogARB = NULL; + + qglGetProc(glDebugMessageEnableAMD, ); + qglGetProc(glDebugMessageInsertAMD, ); + qglGetProc(glDebugMessageCallbackAMD, ); + qglGetProc(glGetDebugMessageLogAMD, ); + + ri.Printf( PRINT_ALL, "...using GL_AMD_debug_output\n" ); + } else { + qglDebugMessageControlARB = NULL; + qglDebugMessageInsertARB = NULL; + qglDebugMessageCallbackARB = NULL; + qglGetDebugMessageLogARB = NULL; + + qglDebugMessageEnableAMD = NULL; + qglDebugMessageInsertAMD = NULL; + qglDebugMessageCallbackAMD = NULL; + qglGetDebugMessageLogAMD = NULL; + + ri.Printf( PRINT_ALL, "...GL_ARB_debug_output not found\n" ); + } + + if( !r_ext_gpu_shader4->integer ) { + glGlobals.gpuShader4 = qfalse; + ri.Printf( PRINT_ALL, "...ignoring GL_EXT_gpu_shader4\n" ); + } else if( GLversion >= 0x300 ) { + glGlobals.gpuShader4 = qtrue; + ri.Printf( PRINT_ALL, "...using GL_gpu_shader4\n" ); + } else if( GLversion && + GLimp_HaveExtension( "GL_EXT_gpu_shader4" ) ) { + glGlobals.gpuShader4 = qtrue; + ri.Printf( PRINT_ALL, "...using GL_EXT_gpu_shader4\n" ); + } else { + glGlobals.gpuShader4 = qfalse; + ri.Printf( PRINT_ALL, "...GL_EXT_gpu_shader4 not found\n" ); + } + + // GL_ARB_blend_func_extended, mandatory since OpenGL 3.3 + if( !r_ext_blend_func_extended->integer ) { + qglBindFragDataLocationIndexed = NULL; + qglGetFragDataIndex = NULL; + ri.Printf( PRINT_DEVELOPER, "...ignoring GL_ARB_blend_func_extended\n" ); + } else if( GLversion >= 0x303 ) { + qglGetProc( glBindFragDataLocationIndexed, ); + qglGetProc( glGetFragDataIndex, ); + ri.Printf( PRINT_DEVELOPER, "...ignoring GL_blend_func_extended\n" ); + } else if( GLversion && + GLimp_HaveExtension( "GL_ARB_blend_func_extended" ) ) { + qglGetProc( glBindFragDataLocationIndexed, ); + qglGetProc( glGetFragDataIndex, ); + ri.Printf( PRINT_DEVELOPER, "...ignoring GL_ARB_blend_func_extended\n" ); + } else { + qglBindFragDataLocationIndexed = NULL; + qglGetFragDataIndex = NULL; + ri.Printf( PRINT_DEVELOPER, "...GL_ARB_blend_func_extended not found\n" ); } } @@ -681,6 +2220,8 @@ of OpenGL */ void GLimp_Init( void ) { + int GLmajor, GLminor; + r_allowSoftwareGL = ri.Cvar_Get( "r_allowSoftwareGL", "0", CVAR_LATCH ); r_sdlDriver = ri.Cvar_Get( "r_sdlDriver", "", CVAR_ROM ); r_allowResize = ri.Cvar_Get( "r_allowResize", "0", CVAR_ARCHIVE ); @@ -698,6 +2239,11 @@ void GLimp_Init( void ) ri.Sys_GLimpInit( ); +#ifdef SDL_VIDEO_DRIVER_X11 + XInitThreads( ); +#endif + renderThreadID = SDL_ThreadID( ); + // Create the window and set up the context if(GLimp_StartDriverAndSetMode(r_mode->integer, r_fullscreen->integer, r_noborder->integer)) goto success; @@ -739,10 +2285,11 @@ success: if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n') glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0; Q_strncpyz( glConfig.version_string, (char *) qglGetString (GL_VERSION), sizeof( glConfig.version_string ) ); + sscanf( glConfig.version_string, "%d.%d", &GLmajor, &GLminor ); Q_strncpyz( glConfig.extensions_string, (char *) qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) ); // initialize extensions - GLimp_InitExtensions( ); + GLimp_InitExtensions( (GLmajor << 8) | GLminor ); ri.Cvar_Get( "r_availableModes", "", CVAR_ROM ); @@ -836,6 +2383,12 @@ GLimp_ShutdownRenderThread */ static void GLimp_ShutdownRenderThread(void) { + if (renderThread != NULL) + { + SDL_WaitThread(renderThread, NULL); + renderThread = NULL; + } + if (smpMutex != NULL) { SDL_DestroyMutex(smpMutex); @@ -855,6 +2408,7 @@ static void GLimp_ShutdownRenderThread(void) } glimpRenderThread = NULL; + renderThreadID = SDL_ThreadID( ); } /* @@ -864,13 +2418,16 @@ GLimp_RenderThreadWrapper */ static int GLimp_RenderThreadWrapper( void *arg ) { - Com_Printf( "Render thread starting\n" ); + // These printfs cause race conditions which mess up the console output + //ri.Printf( PRINT_ALL, "Render thread starting\n" ); + GLimp_SetCurrentContext( &backend_context ); + renderThreadID = SDL_ThreadID( ); glimpRenderThread(); - GLimp_SetCurrentContext(NULL); + GLimp_SetCurrentContext( NULL ); - Com_Printf( "Render thread terminating\n" ); + ri.Printf( PRINT_ALL, "Render thread terminating\n" ); return 0; } @@ -885,26 +2442,24 @@ qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) static qboolean warned = qfalse; if (!warned) { - Com_Printf("WARNING: You enable r_smp at your own risk!\n"); + ri.Printf( PRINT_WARNING, "You enable r_smp at your own risk!\n"); warned = qtrue; } -#ifndef MACOS_X +#if !defined(MACOS_X) && !defined(WIN32) && !defined (SDL_VIDEO_DRIVER_X11) return qfalse; /* better safe than sorry for now. */ #endif if (renderThread != NULL) /* hopefully just a zombie at this point... */ { - Com_Printf("Already a render thread? Trying to clean it up...\n"); - SDL_WaitThread(renderThread, NULL); - renderThread = NULL; + ri.Printf( PRINT_ALL, "Already a render thread? Trying to clean it up...\n" ); GLimp_ShutdownRenderThread(); } smpMutex = SDL_CreateMutex(); if (smpMutex == NULL) { - Com_Printf( "smpMutex creation failed: %s\n", SDL_GetError() ); + ri.Printf( PRINT_ERROR, "smpMutex creation failed: %s\n", SDL_GetError() ); GLimp_ShutdownRenderThread(); return qfalse; } @@ -912,7 +2467,7 @@ qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) renderCommandsEvent = SDL_CreateCond(); if (renderCommandsEvent == NULL) { - Com_Printf( "renderCommandsEvent creation failed: %s\n", SDL_GetError() ); + ri.Printf( PRINT_ERROR, "renderCommandsEvent creation failed: %s\n", SDL_GetError() ); GLimp_ShutdownRenderThread(); return qfalse; } @@ -920,7 +2475,7 @@ qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) renderCompletedEvent = SDL_CreateCond(); if (renderCompletedEvent == NULL) { - Com_Printf( "renderCompletedEvent creation failed: %s\n", SDL_GetError() ); + ri.Printf( PRINT_ERROR, "renderCompletedEvent creation failed: %s\n", SDL_GetError() ); GLimp_ShutdownRenderThread(); return qfalse; } @@ -929,7 +2484,7 @@ qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) renderThread = SDL_CreateThread(GLimp_RenderThreadWrapper, NULL); if ( renderThread == NULL ) { - ri.Printf( PRINT_ALL, "SDL_CreateThread() returned %s", SDL_GetError() ); + ri.Printf( PRINT_ERROR, "SDL_CreateThread() returned %s", SDL_GetError() ); GLimp_ShutdownRenderThread(); return qfalse; } @@ -959,25 +2514,21 @@ void *GLimp_RendererSleep( void ) { void *data = NULL; - GLimp_SetCurrentContext(NULL); - SDL_LockMutex(smpMutex); { smpData = NULL; smpDataReady = qfalse; // after this, the front end can exit GLimp_FrontEndSleep - SDL_CondSignal(renderCompletedEvent); - - while ( !smpDataReady ) + do { + SDL_CondSignal(renderCompletedEvent); SDL_CondWait(renderCommandsEvent, smpMutex); + } while( !smpDataReady); data = (void *)smpData; } SDL_UnlockMutex(smpMutex); - GLimp_SetCurrentContext(opengl_context); - return data; } @@ -994,8 +2545,6 @@ void GLimp_FrontEndSleep( void ) SDL_CondWait(renderCompletedEvent, smpMutex); } SDL_UnlockMutex(smpMutex); - - GLimp_SetCurrentContext(opengl_context); } /* @@ -1005,8 +2554,6 @@ GLimp_WakeRenderer */ void GLimp_WakeRenderer( void *data ) { - GLimp_SetCurrentContext(NULL); - SDL_LockMutex(smpMutex); { assert( smpData == NULL ); @@ -1019,6 +2566,15 @@ void GLimp_WakeRenderer( void *data ) SDL_UnlockMutex(smpMutex); } +/* +=============== +GLimp_InBackend +=============== +*/ +qboolean GLimp_InBackend( void ) +{ + return SDL_ThreadID() == renderThreadID; +} #else // No SMP - stubs @@ -1045,4 +2601,8 @@ void GLimp_WakeRenderer( void *data ) { } +qboolean GLimp_InBackend( void ) +{ + return qtrue; +} #endif