renderer -> renderergl1, rend2 -> renderergl2
This commit is contained in:
parent
bff574e04e
commit
f6fb9eb602
120 changed files with 1277 additions and 1294 deletions
380
code/renderergl1/qgl.h
Normal file
380
code/renderergl1/qgl.h
Normal file
|
@ -0,0 +1,380 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
/*
|
||||
** QGL.H
|
||||
*/
|
||||
|
||||
#ifndef __QGL_H__
|
||||
#define __QGL_H__
|
||||
|
||||
#ifdef USE_LOCAL_HEADERS
|
||||
# include "SDL_opengl.h"
|
||||
#else
|
||||
# include <SDL_opengl.h>
|
||||
#endif
|
||||
|
||||
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 qglLockArraysEXT) (GLint first, GLsizei count);
|
||||
extern void (APIENTRYP qglUnlockArraysEXT) (void);
|
||||
|
||||
|
||||
//===========================================================================
|
||||
|
||||
#define qglAccum glAccum
|
||||
#define qglAlphaFunc glAlphaFunc
|
||||
#define qglAreTexturesResident glAreTexturesResident
|
||||
#define qglArrayElement glArrayElement
|
||||
#define qglBegin glBegin
|
||||
#define qglBindTexture glBindTexture
|
||||
#define qglBitmap glBitmap
|
||||
#define qglBlendFunc glBlendFunc
|
||||
#define qglCallList glCallList
|
||||
#define qglCallLists glCallLists
|
||||
#define qglClear glClear
|
||||
#define qglClearAccum glClearAccum
|
||||
#define qglClearColor glClearColor
|
||||
#define qglClearDepth glClearDepth
|
||||
#define qglClearIndex glClearIndex
|
||||
#define qglClearStencil glClearStencil
|
||||
#define qglClipPlane glClipPlane
|
||||
#define qglColor3b glColor3b
|
||||
#define qglColor3bv glColor3bv
|
||||
#define qglColor3d glColor3d
|
||||
#define qglColor3dv glColor3dv
|
||||
#define qglColor3f glColor3f
|
||||
#define qglColor3fv glColor3fv
|
||||
#define qglColor3i glColor3i
|
||||
#define qglColor3iv glColor3iv
|
||||
#define qglColor3s glColor3s
|
||||
#define qglColor3sv glColor3sv
|
||||
#define qglColor3ub glColor3ub
|
||||
#define qglColor3ubv glColor3ubv
|
||||
#define qglColor3ui glColor3ui
|
||||
#define qglColor3uiv glColor3uiv
|
||||
#define qglColor3us glColor3us
|
||||
#define qglColor3usv glColor3usv
|
||||
#define qglColor4b glColor4b
|
||||
#define qglColor4bv glColor4bv
|
||||
#define qglColor4d glColor4d
|
||||
#define qglColor4dv glColor4dv
|
||||
#define qglColor4f glColor4f
|
||||
#define qglColor4fv glColor4fv
|
||||
#define qglColor4i glColor4i
|
||||
#define qglColor4iv glColor4iv
|
||||
#define qglColor4s glColor4s
|
||||
#define qglColor4sv glColor4sv
|
||||
#define qglColor4ub glColor4ub
|
||||
#define qglColor4ubv glColor4ubv
|
||||
#define qglColor4ui glColor4ui
|
||||
#define qglColor4uiv glColor4uiv
|
||||
#define qglColor4us glColor4us
|
||||
#define qglColor4usv glColor4usv
|
||||
#define qglColorMask glColorMask
|
||||
#define qglColorMaterial glColorMaterial
|
||||
#define qglColorPointer glColorPointer
|
||||
#define qglCopyPixels glCopyPixels
|
||||
#define qglCopyTexImage1D glCopyTexImage1D
|
||||
#define qglCopyTexImage2D glCopyTexImage2D
|
||||
#define qglCopyTexSubImage1D glCopyTexSubImage1D
|
||||
#define qglCopyTexSubImage2D glCopyTexSubImage2D
|
||||
#define qglCullFace glCullFace
|
||||
#define qglDeleteLists glDeleteLists
|
||||
#define qglDeleteTextures glDeleteTextures
|
||||
#define qglDepthFunc glDepthFunc
|
||||
#define qglDepthMask glDepthMask
|
||||
#define qglDepthRange glDepthRange
|
||||
#define qglDisable glDisable
|
||||
#define qglDisableClientState glDisableClientState
|
||||
#define qglDrawArrays glDrawArrays
|
||||
#define qglDrawBuffer glDrawBuffer
|
||||
#define qglDrawElements glDrawElements
|
||||
#define qglDrawPixels glDrawPixels
|
||||
#define qglEdgeFlag glEdgeFlag
|
||||
#define qglEdgeFlagPointer glEdgeFlagPointer
|
||||
#define qglEdgeFlagv glEdgeFlagv
|
||||
#define qglEnable glEnable
|
||||
#define qglEnableClientState glEnableClientState
|
||||
#define qglEnd glEnd
|
||||
#define qglEndList glEndList
|
||||
#define qglEvalCoord1d glEvalCoord1d
|
||||
#define qglEvalCoord1dv glEvalCoord1dv
|
||||
#define qglEvalCoord1f glEvalCoord1f
|
||||
#define qglEvalCoord1fv glEvalCoord1fv
|
||||
#define qglEvalCoord2d glEvalCoord2d
|
||||
#define qglEvalCoord2dv glEvalCoord2dv
|
||||
#define qglEvalCoord2f glEvalCoord2f
|
||||
#define qglEvalCoord2fv glEvalCoord2fv
|
||||
#define qglEvalMesh1 glEvalMesh1
|
||||
#define qglEvalMesh2 glEvalMesh2
|
||||
#define qglEvalPoint1 glEvalPoint1
|
||||
#define qglEvalPoint2 glEvalPoint2
|
||||
#define qglFeedbackBuffer glFeedbackBuffer
|
||||
#define qglFinish glFinish
|
||||
#define qglFlush glFlush
|
||||
#define qglFogf glFogf
|
||||
#define qglFogfv glFogfv
|
||||
#define qglFogi glFogi
|
||||
#define qglFogiv glFogiv
|
||||
#define qglFrontFace glFrontFace
|
||||
#define qglFrustum glFrustum
|
||||
#define qglGenLists glGenLists
|
||||
#define qglGenTextures glGenTextures
|
||||
#define qglGetBooleanv glGetBooleanv
|
||||
#define qglGetClipPlane glGetClipPlane
|
||||
#define qglGetDoublev glGetDoublev
|
||||
#define qglGetError glGetError
|
||||
#define qglGetFloatv glGetFloatv
|
||||
#define qglGetIntegerv glGetIntegerv
|
||||
#define qglGetLightfv glGetLightfv
|
||||
#define qglGetLightiv glGetLightiv
|
||||
#define qglGetMapdv glGetMapdv
|
||||
#define qglGetMapfv glGetMapfv
|
||||
#define qglGetMapiv glGetMapiv
|
||||
#define qglGetMaterialfv glGetMaterialfv
|
||||
#define qglGetMaterialiv glGetMaterialiv
|
||||
#define qglGetPixelMapfv glGetPixelMapfv
|
||||
#define qglGetPixelMapuiv glGetPixelMapuiv
|
||||
#define qglGetPixelMapusv glGetPixelMapusv
|
||||
#define qglGetPointerv glGetPointerv
|
||||
#define qglGetPolygonStipple glGetPolygonStipple
|
||||
#define qglGetString glGetString
|
||||
#define qglGetTexGendv glGetTexGendv
|
||||
#define qglGetTexGenfv glGetTexGenfv
|
||||
#define qglGetTexGeniv glGetTexGeniv
|
||||
#define qglGetTexImage glGetTexImage
|
||||
#define qglGetTexLevelParameterfv glGetTexLevelParameterfv
|
||||
#define qglGetTexLevelParameteriv glGetTexLevelParameteriv
|
||||
#define qglGetTexParameterfv glGetTexParameterfv
|
||||
#define qglGetTexParameteriv glGetTexParameteriv
|
||||
#define qglHint glHint
|
||||
#define qglIndexMask glIndexMask
|
||||
#define qglIndexPointer glIndexPointer
|
||||
#define qglIndexd glIndexd
|
||||
#define qglIndexdv glIndexdv
|
||||
#define qglIndexf glIndexf
|
||||
#define qglIndexfv glIndexfv
|
||||
#define qglIndexi glIndexi
|
||||
#define qglIndexiv glIndexiv
|
||||
#define qglIndexs glIndexs
|
||||
#define qglIndexsv glIndexsv
|
||||
#define qglIndexub glIndexub
|
||||
#define qglIndexubv glIndexubv
|
||||
#define qglInitNames glInitNames
|
||||
#define qglInterleavedArrays glInterleavedArrays
|
||||
#define qglIsEnabled glIsEnabled
|
||||
#define qglIsList glIsList
|
||||
#define qglIsTexture glIsTexture
|
||||
#define qglLightModelf glLightModelf
|
||||
#define qglLightModelfv glLightModelfv
|
||||
#define qglLightModeli glLightModeli
|
||||
#define qglLightModeliv glLightModeliv
|
||||
#define qglLightf glLightf
|
||||
#define qglLightfv glLightfv
|
||||
#define qglLighti glLighti
|
||||
#define qglLightiv glLightiv
|
||||
#define qglLineStipple glLineStipple
|
||||
#define qglLineWidth glLineWidth
|
||||
#define qglListBase glListBase
|
||||
#define qglLoadIdentity glLoadIdentity
|
||||
#define qglLoadMatrixd glLoadMatrixd
|
||||
#define qglLoadMatrixf glLoadMatrixf
|
||||
#define qglLoadName glLoadName
|
||||
#define qglLogicOp glLogicOp
|
||||
#define qglMap1d glMap1d
|
||||
#define qglMap1f glMap1f
|
||||
#define qglMap2d glMap2d
|
||||
#define qglMap2f glMap2f
|
||||
#define qglMapGrid1d glMapGrid1d
|
||||
#define qglMapGrid1f glMapGrid1f
|
||||
#define qglMapGrid2d glMapGrid2d
|
||||
#define qglMapGrid2f glMapGrid2f
|
||||
#define qglMaterialf glMaterialf
|
||||
#define qglMaterialfv glMaterialfv
|
||||
#define qglMateriali glMateriali
|
||||
#define qglMaterialiv glMaterialiv
|
||||
#define qglMatrixMode glMatrixMode
|
||||
#define qglMultMatrixd glMultMatrixd
|
||||
#define qglMultMatrixf glMultMatrixf
|
||||
#define qglNewList glNewList
|
||||
#define qglNormal3b glNormal3b
|
||||
#define qglNormal3bv glNormal3bv
|
||||
#define qglNormal3d glNormal3d
|
||||
#define qglNormal3dv glNormal3dv
|
||||
#define qglNormal3f glNormal3f
|
||||
#define qglNormal3fv glNormal3fv
|
||||
#define qglNormal3i glNormal3i
|
||||
#define qglNormal3iv glNormal3iv
|
||||
#define qglNormal3s glNormal3s
|
||||
#define qglNormal3sv glNormal3sv
|
||||
#define qglNormalPointer glNormalPointer
|
||||
#define qglOrtho glOrtho
|
||||
#define qglPassThrough glPassThrough
|
||||
#define qglPixelMapfv glPixelMapfv
|
||||
#define qglPixelMapuiv glPixelMapuiv
|
||||
#define qglPixelMapusv glPixelMapusv
|
||||
#define qglPixelStoref glPixelStoref
|
||||
#define qglPixelStorei glPixelStorei
|
||||
#define qglPixelTransferf glPixelTransferf
|
||||
#define qglPixelTransferi glPixelTransferi
|
||||
#define qglPixelZoom glPixelZoom
|
||||
#define qglPointSize glPointSize
|
||||
#define qglPolygonMode glPolygonMode
|
||||
#define qglPolygonOffset glPolygonOffset
|
||||
#define qglPolygonStipple glPolygonStipple
|
||||
#define qglPopAttrib glPopAttrib
|
||||
#define qglPopClientAttrib glPopClientAttrib
|
||||
#define qglPopMatrix glPopMatrix
|
||||
#define qglPopName glPopName
|
||||
#define qglPrioritizeTextures glPrioritizeTextures
|
||||
#define qglPushAttrib glPushAttrib
|
||||
#define qglPushClientAttrib glPushClientAttrib
|
||||
#define qglPushMatrix glPushMatrix
|
||||
#define qglPushName glPushName
|
||||
#define qglRasterPos2d glRasterPos2d
|
||||
#define qglRasterPos2dv glRasterPos2dv
|
||||
#define qglRasterPos2f glRasterPos2f
|
||||
#define qglRasterPos2fv glRasterPos2fv
|
||||
#define qglRasterPos2i glRasterPos2i
|
||||
#define qglRasterPos2iv glRasterPos2iv
|
||||
#define qglRasterPos2s glRasterPos2s
|
||||
#define qglRasterPos2sv glRasterPos2sv
|
||||
#define qglRasterPos3d glRasterPos3d
|
||||
#define qglRasterPos3dv glRasterPos3dv
|
||||
#define qglRasterPos3f glRasterPos3f
|
||||
#define qglRasterPos3fv glRasterPos3fv
|
||||
#define qglRasterPos3i glRasterPos3i
|
||||
#define qglRasterPos3iv glRasterPos3iv
|
||||
#define qglRasterPos3s glRasterPos3s
|
||||
#define qglRasterPos3sv glRasterPos3sv
|
||||
#define qglRasterPos4d glRasterPos4d
|
||||
#define qglRasterPos4dv glRasterPos4dv
|
||||
#define qglRasterPos4f glRasterPos4f
|
||||
#define qglRasterPos4fv glRasterPos4fv
|
||||
#define qglRasterPos4i glRasterPos4i
|
||||
#define qglRasterPos4iv glRasterPos4iv
|
||||
#define qglRasterPos4s glRasterPos4s
|
||||
#define qglRasterPos4sv glRasterPos4sv
|
||||
#define qglReadBuffer glReadBuffer
|
||||
#define qglReadPixels glReadPixels
|
||||
#define qglRectd glRectd
|
||||
#define qglRectdv glRectdv
|
||||
#define qglRectf glRectf
|
||||
#define qglRectfv glRectfv
|
||||
#define qglRecti glRecti
|
||||
#define qglRectiv glRectiv
|
||||
#define qglRects glRects
|
||||
#define qglRectsv glRectsv
|
||||
#define qglRenderMode glRenderMode
|
||||
#define qglRotated glRotated
|
||||
#define qglRotatef glRotatef
|
||||
#define qglScaled glScaled
|
||||
#define qglScalef glScalef
|
||||
#define qglScissor glScissor
|
||||
#define qglSelectBuffer glSelectBuffer
|
||||
#define qglShadeModel glShadeModel
|
||||
#define qglStencilFunc glStencilFunc
|
||||
#define qglStencilMask glStencilMask
|
||||
#define qglStencilOp glStencilOp
|
||||
#define qglTexCoord1d glTexCoord1d
|
||||
#define qglTexCoord1dv glTexCoord1dv
|
||||
#define qglTexCoord1f glTexCoord1f
|
||||
#define qglTexCoord1fv glTexCoord1fv
|
||||
#define qglTexCoord1i glTexCoord1i
|
||||
#define qglTexCoord1iv glTexCoord1iv
|
||||
#define qglTexCoord1s glTexCoord1s
|
||||
#define qglTexCoord1sv glTexCoord1sv
|
||||
#define qglTexCoord2d glTexCoord2d
|
||||
#define qglTexCoord2dv glTexCoord2dv
|
||||
#define qglTexCoord2f glTexCoord2f
|
||||
#define qglTexCoord2fv glTexCoord2fv
|
||||
#define qglTexCoord2i glTexCoord2i
|
||||
#define qglTexCoord2iv glTexCoord2iv
|
||||
#define qglTexCoord2s glTexCoord2s
|
||||
#define qglTexCoord2sv glTexCoord2sv
|
||||
#define qglTexCoord3d glTexCoord3d
|
||||
#define qglTexCoord3dv glTexCoord3dv
|
||||
#define qglTexCoord3f glTexCoord3f
|
||||
#define qglTexCoord3fv glTexCoord3fv
|
||||
#define qglTexCoord3i glTexCoord3i
|
||||
#define qglTexCoord3iv glTexCoord3iv
|
||||
#define qglTexCoord3s glTexCoord3s
|
||||
#define qglTexCoord3sv glTexCoord3sv
|
||||
#define qglTexCoord4d glTexCoord4d
|
||||
#define qglTexCoord4dv glTexCoord4dv
|
||||
#define qglTexCoord4f glTexCoord4f
|
||||
#define qglTexCoord4fv glTexCoord4fv
|
||||
#define qglTexCoord4i glTexCoord4i
|
||||
#define qglTexCoord4iv glTexCoord4iv
|
||||
#define qglTexCoord4s glTexCoord4s
|
||||
#define qglTexCoord4sv glTexCoord4sv
|
||||
#define qglTexCoordPointer glTexCoordPointer
|
||||
#define qglTexEnvf glTexEnvf
|
||||
#define qglTexEnvfv glTexEnvfv
|
||||
#define qglTexEnvi glTexEnvi
|
||||
#define qglTexEnviv glTexEnviv
|
||||
#define qglTexGend glTexGend
|
||||
#define qglTexGendv glTexGendv
|
||||
#define qglTexGenf glTexGenf
|
||||
#define qglTexGenfv glTexGenfv
|
||||
#define qglTexGeni glTexGeni
|
||||
#define qglTexGeniv glTexGeniv
|
||||
#define qglTexImage1D glTexImage1D
|
||||
#define qglTexImage2D glTexImage2D
|
||||
#define qglTexParameterf glTexParameterf
|
||||
#define qglTexParameterfv glTexParameterfv
|
||||
#define qglTexParameteri glTexParameteri
|
||||
#define qglTexParameteriv glTexParameteriv
|
||||
#define qglTexSubImage1D glTexSubImage1D
|
||||
#define qglTexSubImage2D glTexSubImage2D
|
||||
#define qglTranslated glTranslated
|
||||
#define qglTranslatef glTranslatef
|
||||
#define qglVertex2d glVertex2d
|
||||
#define qglVertex2dv glVertex2dv
|
||||
#define qglVertex2f glVertex2f
|
||||
#define qglVertex2fv glVertex2fv
|
||||
#define qglVertex2i glVertex2i
|
||||
#define qglVertex2iv glVertex2iv
|
||||
#define qglVertex2s glVertex2s
|
||||
#define qglVertex2sv glVertex2sv
|
||||
#define qglVertex3d glVertex3d
|
||||
#define qglVertex3dv glVertex3dv
|
||||
#define qglVertex3f glVertex3f
|
||||
#define qglVertex3fv glVertex3fv
|
||||
#define qglVertex3i glVertex3i
|
||||
#define qglVertex3iv glVertex3iv
|
||||
#define qglVertex3s glVertex3s
|
||||
#define qglVertex3sv glVertex3sv
|
||||
#define qglVertex4d glVertex4d
|
||||
#define qglVertex4dv glVertex4dv
|
||||
#define qglVertex4f glVertex4f
|
||||
#define qglVertex4fv glVertex4fv
|
||||
#define qglVertex4i glVertex4i
|
||||
#define qglVertex4iv glVertex4iv
|
||||
#define qglVertex4s glVertex4s
|
||||
#define qglVertex4sv glVertex4sv
|
||||
#define qglVertexPointer glVertexPointer
|
||||
#define qglViewport glViewport
|
||||
|
||||
#endif
|
658
code/renderergl1/tr_animation.c
Normal file
658
code/renderergl1/tr_animation.c
Normal file
|
@ -0,0 +1,658 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
/*
|
||||
|
||||
All bones should be an identity orientation to display the mesh exactly
|
||||
as it is specified.
|
||||
|
||||
For all other frames, the bones represent the transformation from the
|
||||
orientation of the bone in the base frame to the orientation in this
|
||||
frame.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
==============
|
||||
R_AddAnimSurfaces
|
||||
==============
|
||||
*/
|
||||
void R_AddAnimSurfaces( trRefEntity_t *ent ) {
|
||||
md4Header_t *header;
|
||||
md4Surface_t *surface;
|
||||
md4LOD_t *lod;
|
||||
shader_t *shader;
|
||||
int i;
|
||||
|
||||
header = (md4Header_t *) tr.currentModel->modelData;
|
||||
lod = (md4LOD_t *)( (byte *)header + header->ofsLODs );
|
||||
|
||||
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 );
|
||||
surface = (md4Surface_t *)( (byte *)surface + surface->ofsEnd );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
RB_SurfaceAnim
|
||||
==============
|
||||
*/
|
||||
void RB_SurfaceAnim( md4Surface_t *surface ) {
|
||||
int i, j, k;
|
||||
float frontlerp, backlerp;
|
||||
int *triangles;
|
||||
int indexes;
|
||||
int baseIndex, baseVertex;
|
||||
int numVerts;
|
||||
md4Vertex_t *v;
|
||||
md4Bone_t bones[MD4_MAX_BONES];
|
||||
md4Bone_t *bonePtr, *bone;
|
||||
md4Header_t *header;
|
||||
md4Frame_t *frame;
|
||||
md4Frame_t *oldFrame;
|
||||
int frameSize;
|
||||
|
||||
|
||||
if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) {
|
||||
backlerp = 0;
|
||||
frontlerp = 1;
|
||||
} else {
|
||||
backlerp = backEnd.currentEntity->e.backlerp;
|
||||
frontlerp = 1.0f - backlerp;
|
||||
}
|
||||
header = (md4Header_t *)((byte *)surface + surface->ofsHeader);
|
||||
|
||||
frameSize = (size_t)( &((md4Frame_t *)0)->bones[ header->numBones ] );
|
||||
|
||||
frame = (md4Frame_t *)((byte *)header + header->ofsFrames +
|
||||
backEnd.currentEntity->e.frame * frameSize );
|
||||
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]
|
||||
+ 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];
|
||||
|
||||
// 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];
|
||||
}
|
||||
|
||||
tess.numVertexes += surface->numVerts;
|
||||
}
|
||||
|
||||
|
||||
#ifdef RAVENMD4
|
||||
|
||||
// copied and adapted from tr_mesh.c
|
||||
|
||||
/*
|
||||
=============
|
||||
R_MDRCullModel
|
||||
=============
|
||||
*/
|
||||
|
||||
static int R_MDRCullModel( mdrHeader_t *header, trRefEntity_t *ent ) {
|
||||
vec3_t bounds[2];
|
||||
mdrFrame_t *oldFrame, *newFrame;
|
||||
int i, frameSize;
|
||||
|
||||
frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] );
|
||||
|
||||
// compute frame pointers
|
||||
newFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame);
|
||||
oldFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.oldframe);
|
||||
|
||||
// cull bounding sphere ONLY if this is not an upscaled entity
|
||||
if ( !ent->e.nonNormalizedAxes )
|
||||
{
|
||||
if ( ent->e.frame == ent->e.oldframe )
|
||||
{
|
||||
switch ( R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ) )
|
||||
{
|
||||
// Ummm... yeah yeah I know we don't really have an md3 here.. but we pretend
|
||||
// we do. After all, the purpose of md4s are not that different, are they?
|
||||
|
||||
case CULL_OUT:
|
||||
tr.pc.c_sphere_cull_md3_out++;
|
||||
return CULL_OUT;
|
||||
|
||||
case CULL_IN:
|
||||
tr.pc.c_sphere_cull_md3_in++;
|
||||
return CULL_IN;
|
||||
|
||||
case CULL_CLIP:
|
||||
tr.pc.c_sphere_cull_md3_clip++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int sphereCull, sphereCullB;
|
||||
|
||||
sphereCull = R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius );
|
||||
if ( newFrame == oldFrame ) {
|
||||
sphereCullB = sphereCull;
|
||||
} else {
|
||||
sphereCullB = R_CullLocalPointAndRadius( oldFrame->localOrigin, oldFrame->radius );
|
||||
}
|
||||
|
||||
if ( sphereCull == sphereCullB )
|
||||
{
|
||||
if ( sphereCull == CULL_OUT )
|
||||
{
|
||||
tr.pc.c_sphere_cull_md3_out++;
|
||||
return CULL_OUT;
|
||||
}
|
||||
else if ( sphereCull == CULL_IN )
|
||||
{
|
||||
tr.pc.c_sphere_cull_md3_in++;
|
||||
return CULL_IN;
|
||||
}
|
||||
else
|
||||
{
|
||||
tr.pc.c_sphere_cull_md3_clip++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// calculate a bounding box in the current coordinate system
|
||||
for (i = 0 ; i < 3 ; i++) {
|
||||
bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i];
|
||||
bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i];
|
||||
}
|
||||
|
||||
switch ( R_CullLocalBox( bounds ) )
|
||||
{
|
||||
case CULL_IN:
|
||||
tr.pc.c_box_cull_md3_in++;
|
||||
return CULL_IN;
|
||||
case CULL_CLIP:
|
||||
tr.pc.c_box_cull_md3_clip++;
|
||||
return CULL_CLIP;
|
||||
case CULL_OUT:
|
||||
default:
|
||||
tr.pc.c_box_cull_md3_out++;
|
||||
return CULL_OUT;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_MDRComputeFogNum
|
||||
|
||||
=================
|
||||
*/
|
||||
|
||||
int R_MDRComputeFogNum( mdrHeader_t *header, trRefEntity_t *ent ) {
|
||||
int i, j;
|
||||
fog_t *fog;
|
||||
mdrFrame_t *mdrFrame;
|
||||
vec3_t localOrigin;
|
||||
int frameSize;
|
||||
|
||||
if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] );
|
||||
|
||||
// FIXME: non-normalized axis issues
|
||||
mdrFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame);
|
||||
VectorAdd( ent->e.origin, mdrFrame->localOrigin, localOrigin );
|
||||
for ( i = 1 ; i < tr.world->numfogs ; i++ ) {
|
||||
fog = &tr.world->fogs[i];
|
||||
for ( j = 0 ; j < 3 ; j++ ) {
|
||||
if ( localOrigin[j] - mdrFrame->radius >= fog->bounds[1][j] ) {
|
||||
break;
|
||||
}
|
||||
if ( localOrigin[j] + mdrFrame->radius <= fog->bounds[0][j] ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( j == 3 ) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
R_MDRAddAnimSurfaces
|
||||
==============
|
||||
*/
|
||||
|
||||
// much stuff in there is just copied from R_AddMd3Surfaces in tr_mesh.c
|
||||
|
||||
void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) {
|
||||
mdrHeader_t *header;
|
||||
mdrSurface_t *surface;
|
||||
mdrLOD_t *lod;
|
||||
shader_t *shader;
|
||||
skin_t *skin;
|
||||
int i, j;
|
||||
int lodnum = 0;
|
||||
int fogNum = 0;
|
||||
int cull;
|
||||
qboolean personalModel;
|
||||
|
||||
header = (mdrHeader_t *) tr.currentModel->modelData;
|
||||
|
||||
personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal;
|
||||
|
||||
if ( ent->e.renderfx & RF_WRAP_FRAMES )
|
||||
{
|
||||
ent->e.frame %= header->numFrames;
|
||||
ent->e.oldframe %= header->numFrames;
|
||||
}
|
||||
|
||||
//
|
||||
// Validate the frames so there is no chance of a crash.
|
||||
// This will write directly into the entity structure, so
|
||||
// when the surfaces are rendered, they don't need to be
|
||||
// range checked again.
|
||||
//
|
||||
if ((ent->e.frame >= header->numFrames)
|
||||
|| (ent->e.frame < 0)
|
||||
|| (ent->e.oldframe >= header->numFrames)
|
||||
|| (ent->e.oldframe < 0) )
|
||||
{
|
||||
ri.Printf( PRINT_DEVELOPER, "R_MDRAddAnimSurfaces: no such frame %d to %d for '%s'\n",
|
||||
ent->e.oldframe, ent->e.frame, tr.currentModel->name );
|
||||
ent->e.frame = 0;
|
||||
ent->e.oldframe = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// cull the entire model if merged bounding box of both frames
|
||||
// is outside the view frustum.
|
||||
//
|
||||
cull = R_MDRCullModel (header, ent);
|
||||
if ( cull == CULL_OUT ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// figure out the current LOD of the model we're rendering, and set the lod pointer respectively.
|
||||
lodnum = R_ComputeLOD(ent);
|
||||
// check whether this model has as that many LODs at all. If not, try the closest thing we got.
|
||||
if(header->numLODs <= 0)
|
||||
return;
|
||||
if(header->numLODs <= lodnum)
|
||||
lodnum = header->numLODs - 1;
|
||||
|
||||
lod = (mdrLOD_t *)( (byte *)header + header->ofsLODs);
|
||||
for(i = 0; i < lodnum; i++)
|
||||
{
|
||||
lod = (mdrLOD_t *) ((byte *) lod + lod->ofsEnd);
|
||||
}
|
||||
|
||||
// set up lighting
|
||||
if ( !personalModel || r_shadows->integer > 1 )
|
||||
{
|
||||
R_SetupEntityLighting( &tr.refdef, ent );
|
||||
}
|
||||
|
||||
// fogNum?
|
||||
fogNum = R_MDRComputeFogNum( header, ent );
|
||||
|
||||
surface = (mdrSurface_t *)( (byte *)lod + lod->ofsSurfaces );
|
||||
|
||||
for ( i = 0 ; i < lod->numSurfaces ; i++ )
|
||||
{
|
||||
|
||||
if(ent->e.customShader)
|
||||
shader = R_GetShaderByHandle(ent->e.customShader);
|
||||
else if(ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins)
|
||||
{
|
||||
skin = R_GetSkinByHandle(ent->e.customSkin);
|
||||
shader = tr.defaultShader;
|
||||
|
||||
for(j = 0; j < skin->numSurfaces; j++)
|
||||
{
|
||||
if (!strcmp(skin->surfaces[j]->name, surface->name))
|
||||
{
|
||||
shader = skin->surfaces[j]->shader;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(surface->shaderIndex > 0)
|
||||
shader = R_GetShaderByHandle( surface->shaderIndex );
|
||||
else
|
||||
shader = tr.defaultShader;
|
||||
|
||||
// we will add shadows even if the main object isn't visible in the view
|
||||
|
||||
// stencil shadows can't do personal models unless I polyhedron clip
|
||||
if ( !personalModel
|
||||
&& r_shadows->integer == 2
|
||||
&& fogNum == 0
|
||||
&& !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) )
|
||||
&& shader->sort == SS_OPAQUE )
|
||||
{
|
||||
R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse );
|
||||
}
|
||||
|
||||
// projection shadows work fine with personal models
|
||||
if ( r_shadows->integer == 3
|
||||
&& fogNum == 0
|
||||
&& (ent->e.renderfx & RF_SHADOW_PLANE )
|
||||
&& shader->sort == SS_OPAQUE )
|
||||
{
|
||||
R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse );
|
||||
}
|
||||
|
||||
if (!personalModel)
|
||||
R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse );
|
||||
|
||||
surface = (mdrSurface_t *)( (byte *)surface + surface->ofsEnd );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
RB_MDRSurfaceAnim
|
||||
==============
|
||||
*/
|
||||
void RB_MDRSurfaceAnim( md4Surface_t *surface )
|
||||
{
|
||||
int i, j, k;
|
||||
float frontlerp, backlerp;
|
||||
int *triangles;
|
||||
int indexes;
|
||||
int baseIndex, baseVertex;
|
||||
int numVerts;
|
||||
mdrVertex_t *v;
|
||||
mdrHeader_t *header;
|
||||
mdrFrame_t *frame;
|
||||
mdrFrame_t *oldFrame;
|
||||
mdrBone_t bones[MD4_MAX_BONES], *bonePtr, *bone;
|
||||
|
||||
int frameSize;
|
||||
|
||||
// don't lerp if lerping off, or this is the only frame, or the last frame...
|
||||
//
|
||||
if (backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame)
|
||||
{
|
||||
backlerp = 0; // if backlerp is 0, lerping is off and frontlerp is never used
|
||||
frontlerp = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
backlerp = backEnd.currentEntity->e.backlerp;
|
||||
frontlerp = 1.0f - backlerp;
|
||||
}
|
||||
|
||||
header = (mdrHeader_t *)((byte *)surface + surface->ofsHeader);
|
||||
|
||||
frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] );
|
||||
|
||||
frame = (mdrFrame_t *)((byte *)header + header->ofsFrames +
|
||||
backEnd.currentEntity->e.frame * frameSize );
|
||||
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++ )
|
||||
{
|
||||
((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++ )
|
||||
{
|
||||
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];
|
||||
|
||||
v = (mdrVertex_t *)&v->weights[v->numWeights];
|
||||
}
|
||||
|
||||
tess.numVertexes += surface->numVerts;
|
||||
}
|
||||
|
||||
|
||||
#define MC_MASK_X ((1<<(MC_BITS_X))-1)
|
||||
#define MC_MASK_Y ((1<<(MC_BITS_Y))-1)
|
||||
#define MC_MASK_Z ((1<<(MC_BITS_Z))-1)
|
||||
#define MC_MASK_VECT ((1<<(MC_BITS_VECT))-1)
|
||||
|
||||
#define MC_SCALE_VECT (1.0f/(float)((1<<(MC_BITS_VECT-1))-2))
|
||||
|
||||
#define MC_POS_X (0)
|
||||
#define MC_SHIFT_X (0)
|
||||
|
||||
#define MC_POS_Y ((((MC_BITS_X))/8))
|
||||
#define MC_SHIFT_Y ((((MC_BITS_X)%8)))
|
||||
|
||||
#define MC_POS_Z ((((MC_BITS_X+MC_BITS_Y))/8))
|
||||
#define MC_SHIFT_Z ((((MC_BITS_X+MC_BITS_Y)%8)))
|
||||
|
||||
#define MC_POS_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z))/8))
|
||||
#define MC_SHIFT_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z)%8)))
|
||||
|
||||
#define MC_POS_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT))/8))
|
||||
#define MC_SHIFT_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT)%8)))
|
||||
|
||||
#define MC_POS_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2))/8))
|
||||
#define MC_SHIFT_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2)%8)))
|
||||
|
||||
#define MC_POS_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3))/8))
|
||||
#define MC_SHIFT_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3)%8)))
|
||||
|
||||
#define MC_POS_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4))/8))
|
||||
#define MC_SHIFT_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4)%8)))
|
||||
|
||||
#define MC_POS_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5))/8))
|
||||
#define MC_SHIFT_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5)%8)))
|
||||
|
||||
#define MC_POS_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6))/8))
|
||||
#define MC_SHIFT_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6)%8)))
|
||||
|
||||
#define MC_POS_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7))/8))
|
||||
#define MC_SHIFT_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7)%8)))
|
||||
|
||||
#define MC_POS_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8))/8))
|
||||
#define MC_SHIFT_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8)%8)))
|
||||
|
||||
void MC_UnCompress(float mat[3][4],const unsigned char * comp)
|
||||
{
|
||||
int val;
|
||||
|
||||
val=(int)((unsigned short *)(comp))[0];
|
||||
val-=1<<(MC_BITS_X-1);
|
||||
mat[0][3]=((float)(val))*MC_SCALE_X;
|
||||
|
||||
val=(int)((unsigned short *)(comp))[1];
|
||||
val-=1<<(MC_BITS_Y-1);
|
||||
mat[1][3]=((float)(val))*MC_SCALE_Y;
|
||||
|
||||
val=(int)((unsigned short *)(comp))[2];
|
||||
val-=1<<(MC_BITS_Z-1);
|
||||
mat[2][3]=((float)(val))*MC_SCALE_Z;
|
||||
|
||||
val=(int)((unsigned short *)(comp))[3];
|
||||
val-=1<<(MC_BITS_VECT-1);
|
||||
mat[0][0]=((float)(val))*MC_SCALE_VECT;
|
||||
|
||||
val=(int)((unsigned short *)(comp))[4];
|
||||
val-=1<<(MC_BITS_VECT-1);
|
||||
mat[0][1]=((float)(val))*MC_SCALE_VECT;
|
||||
|
||||
val=(int)((unsigned short *)(comp))[5];
|
||||
val-=1<<(MC_BITS_VECT-1);
|
||||
mat[0][2]=((float)(val))*MC_SCALE_VECT;
|
||||
|
||||
|
||||
val=(int)((unsigned short *)(comp))[6];
|
||||
val-=1<<(MC_BITS_VECT-1);
|
||||
mat[1][0]=((float)(val))*MC_SCALE_VECT;
|
||||
|
||||
val=(int)((unsigned short *)(comp))[7];
|
||||
val-=1<<(MC_BITS_VECT-1);
|
||||
mat[1][1]=((float)(val))*MC_SCALE_VECT;
|
||||
|
||||
val=(int)((unsigned short *)(comp))[8];
|
||||
val-=1<<(MC_BITS_VECT-1);
|
||||
mat[1][2]=((float)(val))*MC_SCALE_VECT;
|
||||
|
||||
|
||||
val=(int)((unsigned short *)(comp))[9];
|
||||
val-=1<<(MC_BITS_VECT-1);
|
||||
mat[2][0]=((float)(val))*MC_SCALE_VECT;
|
||||
|
||||
val=(int)((unsigned short *)(comp))[10];
|
||||
val-=1<<(MC_BITS_VECT-1);
|
||||
mat[2][1]=((float)(val))*MC_SCALE_VECT;
|
||||
|
||||
val=(int)((unsigned short *)(comp))[11];
|
||||
val-=1<<(MC_BITS_VECT-1);
|
||||
mat[2][2]=((float)(val))*MC_SCALE_VECT;
|
||||
}
|
||||
#endif
|
1161
code/renderergl1/tr_backend.c
Normal file
1161
code/renderergl1/tr_backend.c
Normal file
File diff suppressed because it is too large
Load diff
1869
code/renderergl1/tr_bsp.c
Normal file
1869
code/renderergl1/tr_bsp.c
Normal file
File diff suppressed because it is too large
Load diff
505
code/renderergl1/tr_cmds.c
Normal file
505
code/renderergl1/tr_cmds.c
Normal file
|
@ -0,0 +1,505 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
#include "tr_local.h"
|
||||
|
||||
volatile renderCommandList_t *renderCommandList;
|
||||
|
||||
/*
|
||||
=====================
|
||||
R_PerformanceCounters
|
||||
=====================
|
||||
*/
|
||||
void R_PerformanceCounters( void ) {
|
||||
if ( !r_speeds->integer ) {
|
||||
// clear the counters even if we aren't printing
|
||||
Com_Memset( &tr.pc, 0, sizeof( tr.pc ) );
|
||||
Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) );
|
||||
return;
|
||||
}
|
||||
|
||||
if (r_speeds->integer == 1) {
|
||||
ri.Printf (PRINT_ALL, "%i/%i shaders/surfs %i leafs %i verts %i/%i tris %.2f mtex %.2f dc\n",
|
||||
backEnd.pc.c_shaders, backEnd.pc.c_surfaces, tr.pc.c_leafs, backEnd.pc.c_vertexes,
|
||||
backEnd.pc.c_indexes/3, backEnd.pc.c_totalIndexes/3,
|
||||
R_SumOfUsedImages()/(1000000.0f), backEnd.pc.c_overDraw / (float)(glConfig.vidWidth * glConfig.vidHeight) );
|
||||
} else if (r_speeds->integer == 2) {
|
||||
ri.Printf (PRINT_ALL, "(patch) %i sin %i sclip %i sout %i bin %i bclip %i bout\n",
|
||||
tr.pc.c_sphere_cull_patch_in, tr.pc.c_sphere_cull_patch_clip, tr.pc.c_sphere_cull_patch_out,
|
||||
tr.pc.c_box_cull_patch_in, tr.pc.c_box_cull_patch_clip, tr.pc.c_box_cull_patch_out );
|
||||
ri.Printf (PRINT_ALL, "(md3) %i sin %i sclip %i sout %i bin %i bclip %i bout\n",
|
||||
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 );
|
||||
} 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",
|
||||
tr.pc.c_dlightSurfaces, tr.pc.c_dlightSurfacesCulled,
|
||||
backEnd.pc.c_dlightVertexes, backEnd.pc.c_dlightIndexes / 3 );
|
||||
}
|
||||
}
|
||||
else if (r_speeds->integer == 5 )
|
||||
{
|
||||
ri.Printf( PRINT_ALL, "zFar: %.0f\n", tr.viewParms.zFar );
|
||||
}
|
||||
else if (r_speeds->integer == 6 )
|
||||
{
|
||||
ri.Printf( PRINT_ALL, "flare adds:%i tests:%i renders:%i\n",
|
||||
backEnd.pc.c_flareAdds, backEnd.pc.c_flareTests, backEnd.pc.c_flareRenders );
|
||||
}
|
||||
|
||||
Com_Memset( &tr.pc, 0, sizeof( tr.pc ) );
|
||||
Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
R_IssueRenderCommands
|
||||
====================
|
||||
*/
|
||||
void R_IssueRenderCommands( qboolean runPerformanceCounters ) {
|
||||
renderCommandList_t *cmdList;
|
||||
|
||||
cmdList = &backEndData->commands;
|
||||
assert(cmdList);
|
||||
// add an end-of-list command
|
||||
*(int *)(cmdList->cmds + cmdList->used) = RC_END_OF_LIST;
|
||||
|
||||
// clear it out, in case this is a sync and not a buffer flip
|
||||
cmdList->used = 0;
|
||||
|
||||
if ( runPerformanceCounters ) {
|
||||
R_PerformanceCounters();
|
||||
}
|
||||
|
||||
// actually start the commands going
|
||||
if ( !r_skipBackEnd->integer ) {
|
||||
// let it start on the new batch
|
||||
RB_ExecuteRenderCommands( cmdList->cmds );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
R_IssuePendingRenderCommands
|
||||
|
||||
Issue any pending commands and wait for them to complete.
|
||||
====================
|
||||
*/
|
||||
void R_IssuePendingRenderCommands( void ) {
|
||||
if ( !tr.registered ) {
|
||||
return;
|
||||
}
|
||||
R_IssueRenderCommands( qfalse );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
R_GetCommandBuffer
|
||||
|
||||
make sure there is enough command space
|
||||
============
|
||||
*/
|
||||
void *R_GetCommandBuffer( int bytes ) {
|
||||
renderCommandList_t *cmdList;
|
||||
|
||||
cmdList = &backEndData->commands;
|
||||
bytes = PAD(bytes, sizeof(void *));
|
||||
|
||||
// always leave room for the end of list command
|
||||
if ( cmdList->used + bytes + 4 > MAX_RENDER_COMMANDS ) {
|
||||
if ( bytes > MAX_RENDER_COMMANDS - 4 ) {
|
||||
ri.Error( ERR_FATAL, "R_GetCommandBuffer: bad size %i", bytes );
|
||||
}
|
||||
// if we run out of room, just start dropping commands
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmdList->used += bytes;
|
||||
|
||||
return cmdList->cmds + cmdList->used - bytes;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
R_AddDrawSurfCmd
|
||||
|
||||
=============
|
||||
*/
|
||||
void R_AddDrawSurfCmd( drawSurf_t *drawSurfs, int numDrawSurfs ) {
|
||||
drawSurfsCommand_t *cmd;
|
||||
|
||||
cmd = R_GetCommandBuffer( sizeof( *cmd ) );
|
||||
if ( !cmd ) {
|
||||
return;
|
||||
}
|
||||
cmd->commandId = RC_DRAW_SURFS;
|
||||
|
||||
cmd->drawSurfs = drawSurfs;
|
||||
cmd->numDrawSurfs = numDrawSurfs;
|
||||
|
||||
cmd->refdef = tr.refdef;
|
||||
cmd->viewParms = tr.viewParms;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
RE_SetColor
|
||||
|
||||
Passing NULL will set the color to white
|
||||
=============
|
||||
*/
|
||||
void RE_SetColor( const float *rgba ) {
|
||||
setColorCommand_t *cmd;
|
||||
|
||||
if ( !tr.registered ) {
|
||||
return;
|
||||
}
|
||||
cmd = R_GetCommandBuffer( sizeof( *cmd ) );
|
||||
if ( !cmd ) {
|
||||
return;
|
||||
}
|
||||
cmd->commandId = RC_SET_COLOR;
|
||||
if ( !rgba ) {
|
||||
static float colorWhite[4] = { 1, 1, 1, 1 };
|
||||
|
||||
rgba = colorWhite;
|
||||
}
|
||||
|
||||
cmd->color[0] = rgba[0];
|
||||
cmd->color[1] = rgba[1];
|
||||
cmd->color[2] = rgba[2];
|
||||
cmd->color[3] = rgba[3];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
RE_StretchPic
|
||||
=============
|
||||
*/
|
||||
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;
|
||||
}
|
||||
cmd = R_GetCommandBuffer( sizeof( *cmd ) );
|
||||
if ( !cmd ) {
|
||||
return;
|
||||
}
|
||||
cmd->commandId = RC_STRETCH_PIC;
|
||||
cmd->shader = R_GetShaderByHandle( hShader );
|
||||
cmd->x = x;
|
||||
cmd->y = y;
|
||||
cmd->w = w;
|
||||
cmd->h = h;
|
||||
cmd->s1 = s1;
|
||||
cmd->t1 = t1;
|
||||
cmd->s2 = s2;
|
||||
cmd->t2 = t2;
|
||||
}
|
||||
|
||||
#define MODE_RED_CYAN 1
|
||||
#define MODE_RED_BLUE 2
|
||||
#define MODE_RED_GREEN 3
|
||||
#define MODE_GREEN_MAGENTA 4
|
||||
#define MODE_MAX MODE_GREEN_MAGENTA
|
||||
|
||||
void R_SetColorMode(GLboolean *rgba, stereoFrame_t stereoFrame, int colormode)
|
||||
{
|
||||
rgba[0] = rgba[1] = rgba[2] = rgba[3] = GL_TRUE;
|
||||
|
||||
if(colormode > MODE_MAX)
|
||||
{
|
||||
if(stereoFrame == STEREO_LEFT)
|
||||
stereoFrame = STEREO_RIGHT;
|
||||
else if(stereoFrame == STEREO_RIGHT)
|
||||
stereoFrame = STEREO_LEFT;
|
||||
|
||||
colormode -= MODE_MAX;
|
||||
}
|
||||
|
||||
if(colormode == MODE_GREEN_MAGENTA)
|
||||
{
|
||||
if(stereoFrame == STEREO_LEFT)
|
||||
rgba[0] = rgba[2] = GL_FALSE;
|
||||
else if(stereoFrame == STEREO_RIGHT)
|
||||
rgba[1] = GL_FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(stereoFrame == STEREO_LEFT)
|
||||
rgba[1] = rgba[2] = GL_FALSE;
|
||||
else if(stereoFrame == STEREO_RIGHT)
|
||||
{
|
||||
rgba[0] = GL_FALSE;
|
||||
|
||||
if(colormode == MODE_RED_BLUE)
|
||||
rgba[1] = GL_FALSE;
|
||||
else if(colormode == MODE_RED_GREEN)
|
||||
rgba[2] = GL_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
RE_BeginFrame
|
||||
|
||||
If running in stereo, RE_BeginFrame will be called twice
|
||||
for each RE_EndFrame
|
||||
====================
|
||||
*/
|
||||
void RE_BeginFrame( stereoFrame_t stereoFrame ) {
|
||||
drawBufferCommand_t *cmd = NULL;
|
||||
colorMaskCommand_t *colcmd = NULL;
|
||||
|
||||
if ( !tr.registered ) {
|
||||
return;
|
||||
}
|
||||
glState.finishCalled = qfalse;
|
||||
|
||||
tr.frameCount++;
|
||||
tr.frameSceneNum = 0;
|
||||
|
||||
//
|
||||
// do overdraw measurement
|
||||
//
|
||||
if ( r_measureOverdraw->integer )
|
||||
{
|
||||
if ( glConfig.stencilBits < 4 )
|
||||
{
|
||||
ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits );
|
||||
ri.Cvar_Set( "r_measureOverdraw", "0" );
|
||||
r_measureOverdraw->modified = qfalse;
|
||||
}
|
||||
else if ( r_shadows->integer == 2 )
|
||||
{
|
||||
ri.Printf( PRINT_ALL, "Warning: stencil shadows and overdraw measurement are mutually exclusive\n" );
|
||||
ri.Cvar_Set( "r_measureOverdraw", "0" );
|
||||
r_measureOverdraw->modified = qfalse;
|
||||
}
|
||||
else
|
||||
{
|
||||
R_IssuePendingRenderCommands();
|
||||
qglEnable( GL_STENCIL_TEST );
|
||||
qglStencilMask( ~0U );
|
||||
qglClearStencil( 0U );
|
||||
qglStencilFunc( GL_ALWAYS, 0U, ~0U );
|
||||
qglStencilOp( GL_KEEP, GL_INCR, GL_INCR );
|
||||
}
|
||||
r_measureOverdraw->modified = qfalse;
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is only reached if it was on and is now off
|
||||
if ( r_measureOverdraw->modified ) {
|
||||
R_IssuePendingRenderCommands();
|
||||
qglDisable( GL_STENCIL_TEST );
|
||||
}
|
||||
r_measureOverdraw->modified = qfalse;
|
||||
}
|
||||
|
||||
//
|
||||
// texturemode stuff
|
||||
//
|
||||
if ( r_textureMode->modified ) {
|
||||
R_IssuePendingRenderCommands();
|
||||
GL_TextureMode( r_textureMode->string );
|
||||
r_textureMode->modified = qfalse;
|
||||
}
|
||||
|
||||
//
|
||||
// gamma stuff
|
||||
//
|
||||
if ( r_gamma->modified ) {
|
||||
r_gamma->modified = qfalse;
|
||||
|
||||
R_IssuePendingRenderCommands();
|
||||
R_SetColorMappings();
|
||||
}
|
||||
|
||||
// check for errors
|
||||
if ( !r_ignoreGLErrors->integer )
|
||||
{
|
||||
int err;
|
||||
|
||||
R_IssuePendingRenderCommands();
|
||||
if ((err = qglGetError()) != GL_NO_ERROR)
|
||||
ri.Error(ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!", err);
|
||||
}
|
||||
|
||||
if (glConfig.stereoEnabled) {
|
||||
if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) )
|
||||
return;
|
||||
|
||||
cmd->commandId = RC_DRAW_BUFFER;
|
||||
|
||||
if ( stereoFrame == STEREO_LEFT ) {
|
||||
cmd->buffer = (int)GL_BACK_LEFT;
|
||||
} else if ( stereoFrame == STEREO_RIGHT ) {
|
||||
cmd->buffer = (int)GL_BACK_RIGHT;
|
||||
} else {
|
||||
ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(r_anaglyphMode->integer)
|
||||
{
|
||||
if(r_anaglyphMode->modified)
|
||||
{
|
||||
// clear both, front and backbuffer.
|
||||
qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
qglClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
qglDrawBuffer(GL_FRONT);
|
||||
qglClear(GL_COLOR_BUFFER_BIT);
|
||||
qglDrawBuffer(GL_BACK);
|
||||
qglClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
r_anaglyphMode->modified = qfalse;
|
||||
}
|
||||
|
||||
if(stereoFrame == STEREO_LEFT)
|
||||
{
|
||||
if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) )
|
||||
return;
|
||||
|
||||
if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) )
|
||||
return;
|
||||
}
|
||||
else if(stereoFrame == STEREO_RIGHT)
|
||||
{
|
||||
clearDepthCommand_t *cldcmd;
|
||||
|
||||
if( !(cldcmd = R_GetCommandBuffer(sizeof(*cldcmd))) )
|
||||
return;
|
||||
|
||||
cldcmd->commandId = RC_CLEARDEPTH;
|
||||
|
||||
if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) )
|
||||
return;
|
||||
}
|
||||
else
|
||||
ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame );
|
||||
|
||||
R_SetColorMode(colcmd->rgba, stereoFrame, r_anaglyphMode->integer);
|
||||
colcmd->commandId = RC_COLORMASK;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(stereoFrame != STEREO_CENTER)
|
||||
ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame );
|
||||
|
||||
if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) )
|
||||
return;
|
||||
}
|
||||
|
||||
if(cmd)
|
||||
{
|
||||
cmd->commandId = RC_DRAW_BUFFER;
|
||||
|
||||
if(r_anaglyphMode->modified)
|
||||
{
|
||||
qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
r_anaglyphMode->modified = qfalse;
|
||||
}
|
||||
|
||||
if (!Q_stricmp(r_drawBuffer->string, "GL_FRONT"))
|
||||
cmd->buffer = (int)GL_FRONT;
|
||||
else
|
||||
cmd->buffer = (int)GL_BACK;
|
||||
}
|
||||
}
|
||||
|
||||
tr.refdef.stereoFrame = stereoFrame;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
RE_EndFrame
|
||||
|
||||
Returns the number of msec spent in the back end
|
||||
=============
|
||||
*/
|
||||
void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) {
|
||||
swapBuffersCommand_t *cmd;
|
||||
|
||||
if ( !tr.registered ) {
|
||||
return;
|
||||
}
|
||||
cmd = R_GetCommandBuffer( sizeof( *cmd ) );
|
||||
if ( !cmd ) {
|
||||
return;
|
||||
}
|
||||
cmd->commandId = RC_SWAP_BUFFERS;
|
||||
|
||||
R_IssueRenderCommands( qtrue );
|
||||
|
||||
R_InitNextFrame();
|
||||
|
||||
if ( frontEndMsec ) {
|
||||
*frontEndMsec = tr.frontEndMsec;
|
||||
}
|
||||
tr.frontEndMsec = 0;
|
||||
if ( backEndMsec ) {
|
||||
*backEndMsec = backEnd.pc.msec;
|
||||
}
|
||||
backEnd.pc.msec = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
RE_TakeVideoFrame
|
||||
=============
|
||||
*/
|
||||
void RE_TakeVideoFrame( int width, int height,
|
||||
byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg )
|
||||
{
|
||||
videoFrameCommand_t *cmd;
|
||||
|
||||
if( !tr.registered ) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd = R_GetCommandBuffer( sizeof( *cmd ) );
|
||||
if( !cmd ) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd->commandId = RC_VIDEOFRAME;
|
||||
|
||||
cmd->width = width;
|
||||
cmd->height = height;
|
||||
cmd->captureBuffer = captureBuffer;
|
||||
cmd->encodeBuffer = encodeBuffer;
|
||||
cmd->motionJpeg = motionJpeg;
|
||||
}
|
625
code/renderergl1/tr_curve.c
Normal file
625
code/renderergl1/tr_curve.c
Normal file
|
@ -0,0 +1,625 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
/*
|
||||
|
||||
This file does all of the processing necessary to turn a raw grid of points
|
||||
read from the map file into a srfGridMesh_t ready for rendering.
|
||||
|
||||
The level of detail solution is direction independent, based only on subdivided
|
||||
distance from the true curve.
|
||||
|
||||
Only a single entry point:
|
||||
|
||||
srfGridMesh_t *R_SubdividePatchToGrid( int width, int height,
|
||||
drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) {
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
LerpDrawVert
|
||||
============
|
||||
*/
|
||||
static void LerpDrawVert( drawVert_t *a, drawVert_t *b, drawVert_t *out ) {
|
||||
out->xyz[0] = 0.5f * (a->xyz[0] + b->xyz[0]);
|
||||
out->xyz[1] = 0.5f * (a->xyz[1] + b->xyz[1]);
|
||||
out->xyz[2] = 0.5f * (a->xyz[2] + b->xyz[2]);
|
||||
|
||||
out->st[0] = 0.5f * (a->st[0] + b->st[0]);
|
||||
out->st[1] = 0.5f * (a->st[1] + b->st[1]);
|
||||
|
||||
out->lightmap[0] = 0.5f * (a->lightmap[0] + b->lightmap[0]);
|
||||
out->lightmap[1] = 0.5f * (a->lightmap[1] + b->lightmap[1]);
|
||||
|
||||
out->color[0] = (a->color[0] + b->color[0]) >> 1;
|
||||
out->color[1] = (a->color[1] + b->color[1]) >> 1;
|
||||
out->color[2] = (a->color[2] + b->color[2]) >> 1;
|
||||
out->color[3] = (a->color[3] + b->color[3]) >> 1;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Transpose
|
||||
============
|
||||
*/
|
||||
static void Transpose( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) {
|
||||
int i, j;
|
||||
drawVert_t temp;
|
||||
|
||||
if ( width > height ) {
|
||||
for ( i = 0 ; i < height ; i++ ) {
|
||||
for ( j = i + 1 ; j < width ; j++ ) {
|
||||
if ( j < height ) {
|
||||
// swap the value
|
||||
temp = ctrl[j][i];
|
||||
ctrl[j][i] = ctrl[i][j];
|
||||
ctrl[i][j] = temp;
|
||||
} else {
|
||||
// just copy
|
||||
ctrl[j][i] = ctrl[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for ( i = 0 ; i < width ; i++ ) {
|
||||
for ( j = i + 1 ; j < height ; j++ ) {
|
||||
if ( j < width ) {
|
||||
// swap the value
|
||||
temp = ctrl[i][j];
|
||||
ctrl[i][j] = ctrl[j][i];
|
||||
ctrl[j][i] = temp;
|
||||
} else {
|
||||
// just copy
|
||||
ctrl[i][j] = ctrl[j][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
MakeMeshNormals
|
||||
|
||||
Handles all the complicated wrapping and degenerate cases
|
||||
=================
|
||||
*/
|
||||
static void MakeMeshNormals( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) {
|
||||
int i, j, k, dist;
|
||||
vec3_t normal;
|
||||
vec3_t sum;
|
||||
int count = 0;
|
||||
vec3_t base;
|
||||
vec3_t delta;
|
||||
int x, y;
|
||||
drawVert_t *dv;
|
||||
vec3_t around[8], temp;
|
||||
qboolean good[8];
|
||||
qboolean wrapWidth, wrapHeight;
|
||||
float len;
|
||||
static int neighbors[8][2] = {
|
||||
{0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1}
|
||||
};
|
||||
|
||||
wrapWidth = qfalse;
|
||||
for ( i = 0 ; i < height ; i++ ) {
|
||||
VectorSubtract( ctrl[i][0].xyz, ctrl[i][width-1].xyz, delta );
|
||||
len = VectorLengthSquared( delta );
|
||||
if ( len > 1.0 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( i == height ) {
|
||||
wrapWidth = qtrue;
|
||||
}
|
||||
|
||||
wrapHeight = qfalse;
|
||||
for ( i = 0 ; i < width ; i++ ) {
|
||||
VectorSubtract( ctrl[0][i].xyz, ctrl[height-1][i].xyz, delta );
|
||||
len = VectorLengthSquared( delta );
|
||||
if ( len > 1.0 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( i == width) {
|
||||
wrapHeight = qtrue;
|
||||
}
|
||||
|
||||
|
||||
for ( i = 0 ; i < width ; i++ ) {
|
||||
for ( j = 0 ; j < height ; j++ ) {
|
||||
count = 0;
|
||||
dv = &ctrl[j][i];
|
||||
VectorCopy( dv->xyz, base );
|
||||
for ( k = 0 ; k < 8 ; k++ ) {
|
||||
VectorClear( around[k] );
|
||||
good[k] = qfalse;
|
||||
|
||||
for ( dist = 1 ; dist <= 3 ; dist++ ) {
|
||||
x = i + neighbors[k][0] * dist;
|
||||
y = j + neighbors[k][1] * dist;
|
||||
if ( wrapWidth ) {
|
||||
if ( x < 0 ) {
|
||||
x = width - 1 + x;
|
||||
} else if ( x >= width ) {
|
||||
x = 1 + x - width;
|
||||
}
|
||||
}
|
||||
if ( wrapHeight ) {
|
||||
if ( y < 0 ) {
|
||||
y = height - 1 + y;
|
||||
} else if ( y >= height ) {
|
||||
y = 1 + y - height;
|
||||
}
|
||||
}
|
||||
|
||||
if ( x < 0 || x >= width || y < 0 || y >= height ) {
|
||||
break; // edge of patch
|
||||
}
|
||||
VectorSubtract( ctrl[y][x].xyz, base, temp );
|
||||
if ( VectorNormalize2( temp, temp ) == 0 ) {
|
||||
continue; // degenerate edge, get more dist
|
||||
} else {
|
||||
good[k] = qtrue;
|
||||
VectorCopy( temp, around[k] );
|
||||
break; // good edge
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VectorClear( sum );
|
||||
for ( k = 0 ; k < 8 ; k++ ) {
|
||||
if ( !good[k] || !good[(k+1)&7] ) {
|
||||
continue; // didn't get two points
|
||||
}
|
||||
CrossProduct( around[(k+1)&7], around[k], normal );
|
||||
if ( VectorNormalize2( normal, normal ) == 0 ) {
|
||||
continue;
|
||||
}
|
||||
VectorAdd( normal, sum, sum );
|
||||
count++;
|
||||
}
|
||||
//if ( count == 0 ) {
|
||||
// printf("bad normal\n");
|
||||
//}
|
||||
VectorNormalize2( sum, dv->normal );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
InvertCtrl
|
||||
============
|
||||
*/
|
||||
static void InvertCtrl( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) {
|
||||
int i, j;
|
||||
drawVert_t temp;
|
||||
|
||||
for ( i = 0 ; i < height ; i++ ) {
|
||||
for ( j = 0 ; j < width/2 ; j++ ) {
|
||||
temp = ctrl[i][j];
|
||||
ctrl[i][j] = ctrl[i][width-1-j];
|
||||
ctrl[i][width-1-j] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
InvertErrorTable
|
||||
=================
|
||||
*/
|
||||
static void InvertErrorTable( float errorTable[2][MAX_GRID_SIZE], int width, int height ) {
|
||||
int i;
|
||||
float copy[2][MAX_GRID_SIZE];
|
||||
|
||||
Com_Memcpy( copy, errorTable, sizeof( copy ) );
|
||||
|
||||
for ( i = 0 ; i < width ; i++ ) {
|
||||
errorTable[1][i] = copy[0][i]; //[width-1-i];
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < height ; i++ ) {
|
||||
errorTable[0][i] = copy[1][height-1-i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
PutPointsOnCurve
|
||||
==================
|
||||
*/
|
||||
static void PutPointsOnCurve( drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE],
|
||||
int width, int height ) {
|
||||
int i, j;
|
||||
drawVert_t prev, next;
|
||||
|
||||
for ( i = 0 ; i < width ; i++ ) {
|
||||
for ( j = 1 ; j < height ; j += 2 ) {
|
||||
LerpDrawVert( &ctrl[j][i], &ctrl[j+1][i], &prev );
|
||||
LerpDrawVert( &ctrl[j][i], &ctrl[j-1][i], &next );
|
||||
LerpDrawVert( &prev, &next, &ctrl[j][i] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for ( j = 0 ; j < height ; j++ ) {
|
||||
for ( i = 1 ; i < width ; i += 2 ) {
|
||||
LerpDrawVert( &ctrl[j][i], &ctrl[j][i+1], &prev );
|
||||
LerpDrawVert( &ctrl[j][i], &ctrl[j][i-1], &next );
|
||||
LerpDrawVert( &prev, &next, &ctrl[j][i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_CreateSurfaceGridMesh
|
||||
=================
|
||||
*/
|
||||
srfGridMesh_t *R_CreateSurfaceGridMesh(int width, int height,
|
||||
drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], float errorTable[2][MAX_GRID_SIZE] ) {
|
||||
int i, j, size;
|
||||
drawVert_t *vert;
|
||||
vec3_t tmpVec;
|
||||
srfGridMesh_t *grid;
|
||||
|
||||
// copy the results out to a grid
|
||||
size = (width * height - 1) * sizeof( drawVert_t ) + sizeof( *grid );
|
||||
|
||||
#ifdef PATCH_STITCHING
|
||||
grid = /*ri.Hunk_Alloc*/ ri.Malloc( size );
|
||||
Com_Memset(grid, 0, size);
|
||||
|
||||
grid->widthLodError = /*ri.Hunk_Alloc*/ ri.Malloc( width * 4 );
|
||||
Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 );
|
||||
|
||||
grid->heightLodError = /*ri.Hunk_Alloc*/ ri.Malloc( height * 4 );
|
||||
Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 );
|
||||
#else
|
||||
grid = ri.Hunk_Alloc( size );
|
||||
Com_Memset(grid, 0, size);
|
||||
|
||||
grid->widthLodError = ri.Hunk_Alloc( width * 4 );
|
||||
Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 );
|
||||
|
||||
grid->heightLodError = ri.Hunk_Alloc( height * 4 );
|
||||
Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 );
|
||||
#endif
|
||||
|
||||
grid->width = width;
|
||||
grid->height = height;
|
||||
grid->surfaceType = SF_GRID;
|
||||
ClearBounds( grid->meshBounds[0], grid->meshBounds[1] );
|
||||
for ( i = 0 ; i < width ; i++ ) {
|
||||
for ( j = 0 ; j < height ; j++ ) {
|
||||
vert = &grid->verts[j*width+i];
|
||||
*vert = ctrl[j][i];
|
||||
AddPointToBounds( vert->xyz, grid->meshBounds[0], grid->meshBounds[1] );
|
||||
}
|
||||
}
|
||||
|
||||
// compute local origin and bounds
|
||||
VectorAdd( grid->meshBounds[0], grid->meshBounds[1], grid->localOrigin );
|
||||
VectorScale( grid->localOrigin, 0.5f, grid->localOrigin );
|
||||
VectorSubtract( grid->meshBounds[0], grid->localOrigin, tmpVec );
|
||||
grid->meshRadius = VectorLength( tmpVec );
|
||||
|
||||
VectorCopy( grid->localOrigin, grid->lodOrigin );
|
||||
grid->lodRadius = grid->meshRadius;
|
||||
//
|
||||
return grid;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_FreeSurfaceGridMesh
|
||||
=================
|
||||
*/
|
||||
void R_FreeSurfaceGridMesh( srfGridMesh_t *grid ) {
|
||||
ri.Free(grid->widthLodError);
|
||||
ri.Free(grid->heightLodError);
|
||||
ri.Free(grid);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_SubdividePatchToGrid
|
||||
=================
|
||||
*/
|
||||
srfGridMesh_t *R_SubdividePatchToGrid( int width, int height,
|
||||
drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) {
|
||||
int i, j, k, l;
|
||||
drawVert_t_cleared( prev );
|
||||
drawVert_t_cleared( next );
|
||||
drawVert_t_cleared( mid );
|
||||
float len, maxLen;
|
||||
int dir;
|
||||
int t;
|
||||
drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE];
|
||||
float errorTable[2][MAX_GRID_SIZE];
|
||||
|
||||
for ( i = 0 ; i < width ; i++ ) {
|
||||
for ( j = 0 ; j < height ; j++ ) {
|
||||
ctrl[j][i] = points[j*width+i];
|
||||
}
|
||||
}
|
||||
|
||||
for ( dir = 0 ; dir < 2 ; dir++ ) {
|
||||
|
||||
for ( j = 0 ; j < MAX_GRID_SIZE ; j++ ) {
|
||||
errorTable[dir][j] = 0;
|
||||
}
|
||||
|
||||
// horizontal subdivisions
|
||||
for ( j = 0 ; j + 2 < width ; j += 2 ) {
|
||||
// check subdivided midpoints against control points
|
||||
|
||||
// FIXME: also check midpoints of adjacent patches against the control points
|
||||
// this would basically stitch all patches in the same LOD group together.
|
||||
|
||||
maxLen = 0;
|
||||
for ( i = 0 ; i < height ; i++ ) {
|
||||
vec3_t midxyz;
|
||||
vec3_t midxyz2;
|
||||
vec3_t dir;
|
||||
vec3_t projected;
|
||||
float d;
|
||||
|
||||
// calculate the point on the curve
|
||||
for ( l = 0 ; l < 3 ; l++ ) {
|
||||
midxyz[l] = (ctrl[i][j].xyz[l] + ctrl[i][j+1].xyz[l] * 2
|
||||
+ ctrl[i][j+2].xyz[l] ) * 0.25f;
|
||||
}
|
||||
|
||||
// see how far off the line it is
|
||||
// using dist-from-line will not account for internal
|
||||
// texture warping, but it gives a lot less polygons than
|
||||
// dist-from-midpoint
|
||||
VectorSubtract( midxyz, ctrl[i][j].xyz, midxyz );
|
||||
VectorSubtract( ctrl[i][j+2].xyz, ctrl[i][j].xyz, dir );
|
||||
VectorNormalize( dir );
|
||||
|
||||
d = DotProduct( midxyz, dir );
|
||||
VectorScale( dir, d, projected );
|
||||
VectorSubtract( midxyz, projected, midxyz2);
|
||||
len = VectorLengthSquared( midxyz2 ); // we will do the sqrt later
|
||||
if ( len > maxLen ) {
|
||||
maxLen = len;
|
||||
}
|
||||
}
|
||||
|
||||
maxLen = sqrt(maxLen);
|
||||
|
||||
// if all the points are on the lines, remove the entire columns
|
||||
if ( maxLen < 0.1f ) {
|
||||
errorTable[dir][j+1] = 999;
|
||||
continue;
|
||||
}
|
||||
|
||||
// see if we want to insert subdivided columns
|
||||
if ( width + 2 > MAX_GRID_SIZE ) {
|
||||
errorTable[dir][j+1] = 1.0f/maxLen;
|
||||
continue; // can't subdivide any more
|
||||
}
|
||||
|
||||
if ( maxLen <= r_subdivisions->value ) {
|
||||
errorTable[dir][j+1] = 1.0f/maxLen;
|
||||
continue; // didn't need subdivision
|
||||
}
|
||||
|
||||
errorTable[dir][j+2] = 1.0f/maxLen;
|
||||
|
||||
// insert two columns and replace the peak
|
||||
width += 2;
|
||||
for ( i = 0 ; i < height ; i++ ) {
|
||||
LerpDrawVert( &ctrl[i][j], &ctrl[i][j+1], &prev );
|
||||
LerpDrawVert( &ctrl[i][j+1], &ctrl[i][j+2], &next );
|
||||
LerpDrawVert( &prev, &next, &mid );
|
||||
|
||||
for ( k = width - 1 ; k > j + 3 ; k-- ) {
|
||||
ctrl[i][k] = ctrl[i][k-2];
|
||||
}
|
||||
ctrl[i][j + 1] = prev;
|
||||
ctrl[i][j + 2] = mid;
|
||||
ctrl[i][j + 3] = next;
|
||||
}
|
||||
|
||||
// back up and recheck this set again, it may need more subdivision
|
||||
j -= 2;
|
||||
|
||||
}
|
||||
|
||||
Transpose( width, height, ctrl );
|
||||
t = width;
|
||||
width = height;
|
||||
height = t;
|
||||
}
|
||||
|
||||
|
||||
// put all the aproximating points on the curve
|
||||
PutPointsOnCurve( ctrl, width, height );
|
||||
|
||||
// cull out any rows or columns that are colinear
|
||||
for ( i = 1 ; i < width-1 ; i++ ) {
|
||||
if ( errorTable[0][i] != 999 ) {
|
||||
continue;
|
||||
}
|
||||
for ( j = i+1 ; j < width ; j++ ) {
|
||||
for ( k = 0 ; k < height ; k++ ) {
|
||||
ctrl[k][j-1] = ctrl[k][j];
|
||||
}
|
||||
errorTable[0][j-1] = errorTable[0][j];
|
||||
}
|
||||
width--;
|
||||
}
|
||||
|
||||
for ( i = 1 ; i < height-1 ; i++ ) {
|
||||
if ( errorTable[1][i] != 999 ) {
|
||||
continue;
|
||||
}
|
||||
for ( j = i+1 ; j < height ; j++ ) {
|
||||
for ( k = 0 ; k < width ; k++ ) {
|
||||
ctrl[j-1][k] = ctrl[j][k];
|
||||
}
|
||||
errorTable[1][j-1] = errorTable[1][j];
|
||||
}
|
||||
height--;
|
||||
}
|
||||
|
||||
#if 1
|
||||
// flip for longest tristrips as an optimization
|
||||
// the results should be visually identical with or
|
||||
// without this step
|
||||
if ( height > width ) {
|
||||
Transpose( width, height, ctrl );
|
||||
InvertErrorTable( errorTable, width, height );
|
||||
t = width;
|
||||
width = height;
|
||||
height = t;
|
||||
InvertCtrl( width, height, ctrl );
|
||||
}
|
||||
#endif
|
||||
|
||||
// calculate normals
|
||||
MakeMeshNormals( width, height, ctrl );
|
||||
|
||||
return R_CreateSurfaceGridMesh( width, height, ctrl, errorTable );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
R_GridInsertColumn
|
||||
===============
|
||||
*/
|
||||
srfGridMesh_t *R_GridInsertColumn( srfGridMesh_t *grid, int column, int row, vec3_t point, float loderror ) {
|
||||
int i, j;
|
||||
int width, height, oldwidth;
|
||||
drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE];
|
||||
float errorTable[2][MAX_GRID_SIZE];
|
||||
float lodRadius;
|
||||
vec3_t lodOrigin;
|
||||
|
||||
oldwidth = 0;
|
||||
width = grid->width + 1;
|
||||
if (width > MAX_GRID_SIZE)
|
||||
return NULL;
|
||||
height = grid->height;
|
||||
for (i = 0; i < width; i++) {
|
||||
if (i == column) {
|
||||
//insert new column
|
||||
for (j = 0; j < grid->height; j++) {
|
||||
LerpDrawVert( &grid->verts[j * grid->width + i-1], &grid->verts[j * grid->width + i], &ctrl[j][i] );
|
||||
if (j == row)
|
||||
VectorCopy(point, ctrl[j][i].xyz);
|
||||
}
|
||||
errorTable[0][i] = loderror;
|
||||
continue;
|
||||
}
|
||||
errorTable[0][i] = grid->widthLodError[oldwidth];
|
||||
for (j = 0; j < grid->height; j++) {
|
||||
ctrl[j][i] = grid->verts[j * grid->width + oldwidth];
|
||||
}
|
||||
oldwidth++;
|
||||
}
|
||||
for (j = 0; j < grid->height; j++) {
|
||||
errorTable[1][j] = grid->heightLodError[j];
|
||||
}
|
||||
// put all the aproximating points on the curve
|
||||
//PutPointsOnCurve( ctrl, width, height );
|
||||
// calculate normals
|
||||
MakeMeshNormals( width, height, ctrl );
|
||||
|
||||
VectorCopy(grid->lodOrigin, lodOrigin);
|
||||
lodRadius = grid->lodRadius;
|
||||
// free the old grid
|
||||
R_FreeSurfaceGridMesh(grid);
|
||||
// create a new grid
|
||||
grid = R_CreateSurfaceGridMesh( width, height, ctrl, errorTable );
|
||||
grid->lodRadius = lodRadius;
|
||||
VectorCopy(lodOrigin, grid->lodOrigin);
|
||||
return grid;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
R_GridInsertRow
|
||||
===============
|
||||
*/
|
||||
srfGridMesh_t *R_GridInsertRow( srfGridMesh_t *grid, int row, int column, vec3_t point, float loderror ) {
|
||||
int i, j;
|
||||
int width, height, oldheight;
|
||||
drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE];
|
||||
float errorTable[2][MAX_GRID_SIZE];
|
||||
float lodRadius;
|
||||
vec3_t lodOrigin;
|
||||
|
||||
oldheight = 0;
|
||||
width = grid->width;
|
||||
height = grid->height + 1;
|
||||
if (height > MAX_GRID_SIZE)
|
||||
return NULL;
|
||||
for (i = 0; i < height; i++) {
|
||||
if (i == row) {
|
||||
//insert new row
|
||||
for (j = 0; j < grid->width; j++) {
|
||||
LerpDrawVert( &grid->verts[(i-1) * grid->width + j], &grid->verts[i * grid->width + j], &ctrl[i][j] );
|
||||
if (j == column)
|
||||
VectorCopy(point, ctrl[i][j].xyz);
|
||||
}
|
||||
errorTable[1][i] = loderror;
|
||||
continue;
|
||||
}
|
||||
errorTable[1][i] = grid->heightLodError[oldheight];
|
||||
for (j = 0; j < grid->width; j++) {
|
||||
ctrl[i][j] = grid->verts[oldheight * grid->width + j];
|
||||
}
|
||||
oldheight++;
|
||||
}
|
||||
for (j = 0; j < grid->width; j++) {
|
||||
errorTable[0][j] = grid->widthLodError[j];
|
||||
}
|
||||
// put all the aproximating points on the curve
|
||||
//PutPointsOnCurve( ctrl, width, height );
|
||||
// calculate normals
|
||||
MakeMeshNormals( width, height, ctrl );
|
||||
|
||||
VectorCopy(grid->lodOrigin, lodOrigin);
|
||||
lodRadius = grid->lodRadius;
|
||||
// free the old grid
|
||||
R_FreeSurfaceGridMesh(grid);
|
||||
// create a new grid
|
||||
grid = R_CreateSurfaceGridMesh( width, height, ctrl, errorTable );
|
||||
grid->lodRadius = lodRadius;
|
||||
VectorCopy(lodOrigin, grid->lodOrigin);
|
||||
return grid;
|
||||
}
|
528
code/renderergl1/tr_flares.c
Normal file
528
code/renderergl1/tr_flares.c
Normal file
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// tr_flares.c
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
LIGHT FLARES
|
||||
|
||||
A light flare is an effect that takes place inside the eye when bright light
|
||||
sources are visible. The size of the flare relative to the screen is nearly
|
||||
constant, irrespective of distance, but the intensity should be proportional to the
|
||||
projected area of the light source.
|
||||
|
||||
A surface that has been flagged as having a light flare will calculate the depth
|
||||
buffer value that its midpoint should have when the surface is added.
|
||||
|
||||
After all opaque surfaces have been rendered, the depth buffer is read back for
|
||||
each flare in view. If the point has not been obscured by a closer surface, the
|
||||
flare should be drawn.
|
||||
|
||||
Surfaces that have a repeated texture should never be flagged as flaring, because
|
||||
there will only be a single flare added at the midpoint of the polygon.
|
||||
|
||||
To prevent abrupt popping, the intensity of the flare is interpolated up and
|
||||
down as it changes visibility. This involves scene to scene state, unlike almost
|
||||
all other aspects of the renderer, and is complicated by the fact that a single
|
||||
frame may have multiple scenes.
|
||||
|
||||
RB_RenderFlares() will be called once per view (twice in a mirrored scene, potentially
|
||||
up to five or more times in a frame with 3D status bar icons).
|
||||
|
||||
=============================================================================
|
||||
*/
|
||||
|
||||
|
||||
// flare states maintain visibility over multiple frames for fading
|
||||
// layers: view, mirror, menu
|
||||
typedef struct flare_s {
|
||||
struct flare_s *next; // for active chain
|
||||
|
||||
int addedFrame;
|
||||
|
||||
qboolean inPortal; // true if in a portal view of the scene
|
||||
int frameSceneNum;
|
||||
void *surface;
|
||||
int fogNum;
|
||||
|
||||
int fadeTime;
|
||||
|
||||
qboolean visible; // state of last test
|
||||
float drawIntensity; // may be non 0 even if !visible due to fading
|
||||
|
||||
int windowX, windowY;
|
||||
float eyeZ;
|
||||
|
||||
vec3_t origin;
|
||||
vec3_t color;
|
||||
} flare_t;
|
||||
|
||||
#define MAX_FLARES 256
|
||||
|
||||
flare_t r_flareStructs[MAX_FLARES];
|
||||
flare_t *r_activeFlares, *r_inactiveFlares;
|
||||
|
||||
int flareCoeff;
|
||||
|
||||
/*
|
||||
==================
|
||||
R_ClearFlares
|
||||
==================
|
||||
*/
|
||||
void R_ClearFlares( void ) {
|
||||
int i;
|
||||
|
||||
Com_Memset( r_flareStructs, 0, sizeof( r_flareStructs ) );
|
||||
r_activeFlares = NULL;
|
||||
r_inactiveFlares = NULL;
|
||||
|
||||
for ( i = 0 ; i < MAX_FLARES ; i++ ) {
|
||||
r_flareStructs[i].next = r_inactiveFlares;
|
||||
r_inactiveFlares = &r_flareStructs[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_AddFlare
|
||||
|
||||
This is called at surface tesselation time
|
||||
==================
|
||||
*/
|
||||
void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal ) {
|
||||
int i;
|
||||
flare_t *f;
|
||||
vec3_t local;
|
||||
float d = 1;
|
||||
vec4_t eye, clip, normalized, window;
|
||||
|
||||
backEnd.pc.c_flareAdds++;
|
||||
|
||||
if(normal && (normal[0] || normal[1] || normal[2]))
|
||||
{
|
||||
VectorSubtract( backEnd.viewParms.or.origin, point, local );
|
||||
VectorNormalizeFast(local);
|
||||
d = DotProduct(local, normal);
|
||||
|
||||
// If the viewer is behind the flare don't add it.
|
||||
if(d < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// if the point is off the screen, don't bother adding it
|
||||
// calculate screen coordinates and depth
|
||||
R_TransformModelToClip( point, backEnd.or.modelMatrix,
|
||||
backEnd.viewParms.projectionMatrix, eye, clip );
|
||||
|
||||
// check to see if the point is completely off screen
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
if ( clip[i] >= clip[3] || clip[i] <= -clip[3] ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
R_TransformClipToWindow( clip, &backEnd.viewParms, normalized, window );
|
||||
|
||||
if ( window[0] < 0 || window[0] >= backEnd.viewParms.viewportWidth
|
||||
|| window[1] < 0 || window[1] >= backEnd.viewParms.viewportHeight ) {
|
||||
return; // shouldn't happen, since we check the clip[] above, except for FP rounding
|
||||
}
|
||||
|
||||
// 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 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// allocate a new one
|
||||
if (!f ) {
|
||||
if ( !r_inactiveFlares ) {
|
||||
// the list is completely full
|
||||
return;
|
||||
}
|
||||
f = r_inactiveFlares;
|
||||
r_inactiveFlares = r_inactiveFlares->next;
|
||||
f->next = r_activeFlares;
|
||||
r_activeFlares = f;
|
||||
|
||||
f->surface = surface;
|
||||
f->frameSceneNum = backEnd.viewParms.frameSceneNum;
|
||||
f->inPortal = backEnd.viewParms.isPortal;
|
||||
f->addedFrame = -1;
|
||||
}
|
||||
|
||||
if ( f->addedFrame != backEnd.viewParms.frameCount - 1 ) {
|
||||
f->visible = qfalse;
|
||||
f->fadeTime = backEnd.refdef.time - 2000;
|
||||
}
|
||||
|
||||
f->addedFrame = backEnd.viewParms.frameCount;
|
||||
f->fogNum = fogNum;
|
||||
|
||||
VectorCopy(point, f->origin);
|
||||
VectorCopy( color, f->color );
|
||||
|
||||
// fade the intensity of the flare down as the
|
||||
// light surface turns away from the viewer
|
||||
VectorScale( f->color, d, f->color );
|
||||
|
||||
// save info needed to test
|
||||
f->windowX = backEnd.viewParms.viewportX + window[0];
|
||||
f->windowY = backEnd.viewParms.viewportY + window[1];
|
||||
|
||||
f->eyeZ = eye[2];
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_AddDlightFlares
|
||||
==================
|
||||
*/
|
||||
void RB_AddDlightFlares( void ) {
|
||||
dlight_t *l;
|
||||
int i, j, k;
|
||||
fog_t *fog = NULL;
|
||||
|
||||
if ( !r_flares->integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
l = backEnd.refdef.dlights;
|
||||
|
||||
if(tr.world)
|
||||
fog = tr.world->fogs;
|
||||
|
||||
for (i=0 ; i<backEnd.refdef.num_dlights ; i++, l++) {
|
||||
|
||||
if(fog)
|
||||
{
|
||||
// find which fog volume the light is in
|
||||
for ( j = 1 ; j < tr.world->numfogs ; j++ ) {
|
||||
fog = &tr.world->fogs[j];
|
||||
for ( k = 0 ; k < 3 ; k++ ) {
|
||||
if ( l->origin[k] < fog->bounds[0][k] || l->origin[k] > fog->bounds[1][k] ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( k == 3 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( j == tr.world->numfogs ) {
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
j = 0;
|
||||
|
||||
RB_AddFlare( (void *)l, j, l->origin, l->color, NULL );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
FLARE BACK END
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_TestFlare
|
||||
==================
|
||||
*/
|
||||
void RB_TestFlare( flare_t *f ) {
|
||||
float depth;
|
||||
qboolean visible;
|
||||
float fade;
|
||||
float screenZ;
|
||||
|
||||
backEnd.pc.c_flareTests++;
|
||||
|
||||
// doing a readpixels is as good as doing a glFinish(), so
|
||||
// don't bother with another sync
|
||||
glState.finishCalled = qfalse;
|
||||
|
||||
// read back the z buffer contents
|
||||
qglReadPixels( f->windowX, f->windowY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth );
|
||||
|
||||
screenZ = backEnd.viewParms.projectionMatrix[14] /
|
||||
( ( 2*depth - 1 ) * backEnd.viewParms.projectionMatrix[11] - backEnd.viewParms.projectionMatrix[10] );
|
||||
|
||||
visible = ( -f->eyeZ - -screenZ ) < 24;
|
||||
|
||||
if ( visible ) {
|
||||
if ( !f->visible ) {
|
||||
f->visible = qtrue;
|
||||
f->fadeTime = backEnd.refdef.time - 1;
|
||||
}
|
||||
fade = ( ( backEnd.refdef.time - f->fadeTime ) /1000.0f ) * r_flareFade->value;
|
||||
} else {
|
||||
if ( f->visible ) {
|
||||
f->visible = qfalse;
|
||||
f->fadeTime = backEnd.refdef.time - 1;
|
||||
}
|
||||
fade = 1.0f - ( ( backEnd.refdef.time - f->fadeTime ) / 1000.0f ) * r_flareFade->value;
|
||||
}
|
||||
|
||||
if ( fade < 0 ) {
|
||||
fade = 0;
|
||||
}
|
||||
if ( fade > 1 ) {
|
||||
fade = 1;
|
||||
}
|
||||
|
||||
f->drawIntensity = fade;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_RenderFlare
|
||||
==================
|
||||
*/
|
||||
void RB_RenderFlare( flare_t *f ) {
|
||||
float size;
|
||||
vec3_t color;
|
||||
int iColor[3];
|
||||
float distance, intensity, factor;
|
||||
byte fogFactors[3] = {255, 255, 255};
|
||||
|
||||
backEnd.pc.c_flareRenders++;
|
||||
|
||||
// We don't want too big values anyways when dividing by distance.
|
||||
if(f->eyeZ > -1.0f)
|
||||
distance = 1.0f;
|
||||
else
|
||||
distance = -f->eyeZ;
|
||||
|
||||
// calculate the flare size..
|
||||
size = backEnd.viewParms.viewportWidth * ( r_flareSize->value/640.0f + 8 / distance );
|
||||
|
||||
/*
|
||||
* This is an alternative to intensity scaling. It changes the size of the flare on screen instead
|
||||
* with growing distance. See in the description at the top why this is not the way to go.
|
||||
// size will change ~ 1/r.
|
||||
size = backEnd.viewParms.viewportWidth * (r_flareSize->value / (distance * -2.0f));
|
||||
*/
|
||||
|
||||
/*
|
||||
* As flare sizes stay nearly constant with increasing distance we must decrease the intensity
|
||||
* to achieve a reasonable visual result. The intensity is ~ (size^2 / distance^2) which can be
|
||||
* got by considering the ratio of
|
||||
* (flaresurface on screen) : (Surface of sphere defined by flare origin and distance from flare)
|
||||
* An important requirement is:
|
||||
* intensity <= 1 for all distances.
|
||||
*
|
||||
* The formula used here to compute the intensity is as follows:
|
||||
* intensity = flareCoeff * size^2 / (distance + size*sqrt(flareCoeff))^2
|
||||
* As you can see, the intensity will have a max. of 1 when the distance is 0.
|
||||
* The coefficient flareCoeff will determine the falloff speed with increasing distance.
|
||||
*/
|
||||
|
||||
factor = distance + size * sqrt(flareCoeff);
|
||||
|
||||
intensity = flareCoeff * size * size / (factor * factor);
|
||||
|
||||
VectorScale(f->color, f->drawIntensity * intensity, color);
|
||||
|
||||
// Calculations for fogging
|
||||
if(tr.world && f->fogNum < tr.world->numfogs)
|
||||
{
|
||||
tess.numVertexes = 1;
|
||||
VectorCopy(f->origin, tess.xyz[0]);
|
||||
tess.fogNum = f->fogNum;
|
||||
|
||||
RB_CalcModulateColorsByFog(fogFactors);
|
||||
|
||||
// We don't need to render the flare if colors are 0 anyways.
|
||||
if(!(fogFactors[0] || fogFactors[1] || fogFactors[2]))
|
||||
return;
|
||||
}
|
||||
|
||||
iColor[0] = color[0] * fogFactors[0];
|
||||
iColor[1] = color[1] * fogFactors[1];
|
||||
iColor[2] = color[2] * fogFactors[2];
|
||||
|
||||
RB_BeginSurface( tr.flareShader, 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;
|
||||
|
||||
RB_EndSurface();
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_RenderFlares
|
||||
|
||||
Because flares are simulating an occular effect, they should be drawn after
|
||||
everything (all views) in the entire frame has been drawn.
|
||||
|
||||
Because of the way portals use the depth buffer to mark off areas, the
|
||||
needed information would be lost after each view, so we are forced to draw
|
||||
flares after each view.
|
||||
|
||||
The resulting artifact is that flares in mirrors or portals don't dim properly
|
||||
when occluded by something in the main view, and portal flares that should
|
||||
extend past the portal edge will be overwritten.
|
||||
==================
|
||||
*/
|
||||
void RB_RenderFlares (void) {
|
||||
flare_t *f;
|
||||
flare_t **prev;
|
||||
qboolean draw;
|
||||
|
||||
if ( !r_flares->integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(r_flareCoeff->modified)
|
||||
{
|
||||
if(r_flareCoeff->value == 0.0f)
|
||||
flareCoeff = atof(FLARE_STDCOEFF);
|
||||
else
|
||||
flareCoeff = r_flareCoeff->value;
|
||||
|
||||
r_flareCoeff->modified = qfalse;
|
||||
}
|
||||
|
||||
// Reset currentEntity to world so that any previously referenced entities
|
||||
// don't have influence on the rendering of these flares (i.e. RF_ renderer flags).
|
||||
backEnd.currentEntity = &tr.worldEntity;
|
||||
backEnd.or = backEnd.viewParms.world;
|
||||
|
||||
// RB_AddDlightFlares();
|
||||
|
||||
// perform z buffer readback on each flare in this view
|
||||
draw = qfalse;
|
||||
prev = &r_activeFlares;
|
||||
while ( ( f = *prev ) != NULL ) {
|
||||
// throw out any flares that weren't added last frame
|
||||
if ( f->addedFrame < backEnd.viewParms.frameCount - 1 ) {
|
||||
*prev = f->next;
|
||||
f->next = r_inactiveFlares;
|
||||
r_inactiveFlares = f;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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 ) {
|
||||
RB_TestFlare( f );
|
||||
if ( f->drawIntensity ) {
|
||||
draw = qtrue;
|
||||
} else {
|
||||
// this flare has completely faded out, so remove it from the chain
|
||||
*prev = f->next;
|
||||
f->next = r_inactiveFlares;
|
||||
r_inactiveFlares = f;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
prev = &f->next;
|
||||
}
|
||||
|
||||
if ( !draw ) {
|
||||
return; // none visible
|
||||
}
|
||||
|
||||
if ( backEnd.viewParms.isPortal ) {
|
||||
qglDisable (GL_CLIP_PLANE0);
|
||||
}
|
||||
|
||||
qglPushMatrix();
|
||||
qglLoadIdentity();
|
||||
qglMatrixMode( GL_PROJECTION );
|
||||
qglPushMatrix();
|
||||
qglLoadIdentity();
|
||||
qglOrtho( backEnd.viewParms.viewportX, backEnd.viewParms.viewportX + backEnd.viewParms.viewportWidth,
|
||||
backEnd.viewParms.viewportY, backEnd.viewParms.viewportY + backEnd.viewParms.viewportHeight,
|
||||
-99999, 99999 );
|
||||
|
||||
for ( f = r_activeFlares ; f ; f = f->next ) {
|
||||
if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum
|
||||
&& f->inPortal == backEnd.viewParms.isPortal
|
||||
&& f->drawIntensity ) {
|
||||
RB_RenderFlare( f );
|
||||
}
|
||||
}
|
||||
|
||||
qglPopMatrix();
|
||||
qglMatrixMode( GL_MODELVIEW );
|
||||
qglPopMatrix();
|
||||
}
|
||||
|
554
code/renderergl1/tr_font.c
Normal file
554
code/renderergl1/tr_font.c
Normal file
|
@ -0,0 +1,554 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// tr_font.c
|
||||
//
|
||||
//
|
||||
// The font system uses FreeType 2.x to render TrueType fonts for use within the game.
|
||||
// As of this writing ( Nov, 2000 ) Team Arena uses these fonts for all of the ui and
|
||||
// about 90% of the cgame presentation. A few areas of the CGAME were left uses the old
|
||||
// fonts since the code is shared with standard Q3A.
|
||||
//
|
||||
// If you include this font rendering code in a commercial product you MUST include the
|
||||
// following somewhere with your product, see www.freetype.org for specifics or changes.
|
||||
// The Freetype code also uses some hinting techniques that MIGHT infringe on patents
|
||||
// held by apple so be aware of that also.
|
||||
//
|
||||
// As of Q3A 1.25+ and Team Arena, we are shipping the game with the font rendering code
|
||||
// disabled. This removes any potential patent issues and it keeps us from having to
|
||||
// distribute an actual TrueTrype font which is 1. expensive to do and 2. seems to require
|
||||
// an act of god to accomplish.
|
||||
//
|
||||
// What we did was pre-render the fonts using FreeType ( which is why we leave the FreeType
|
||||
// credit in the credits ) and then saved off the glyph data and then hand touched up the
|
||||
// font bitmaps so they scale a bit better in GL.
|
||||
//
|
||||
// There are limitations in the way fonts are saved and reloaded in that it is based on
|
||||
// point size and not name. So if you pre-render Helvetica in 18 point and Impact in 18 point
|
||||
// you will end up with a single 18 point data file and image set. Typically you will want to
|
||||
// choose 3 sizes to best approximate the scaling you will be doing in the ui scripting system
|
||||
//
|
||||
// In the UI Scripting code, a scale of 1.0 is equal to a 48 point font. In Team Arena, we
|
||||
// use three or four scales, most of them exactly equaling the specific rendered size. We
|
||||
// rendered three sizes in Team Arena, 12, 16, and 20.
|
||||
//
|
||||
// To generate new font data you need to go through the following steps.
|
||||
// 1. delete the fontImage_x_xx.tga files and fontImage_xx.dat files from the fonts path.
|
||||
// 2. in a ui script, specificy a font, smallFont, and bigFont keyword with font name and
|
||||
// point size. the original TrueType fonts must exist in fonts at this point.
|
||||
// 3. run the game, you should see things normally.
|
||||
// 4. Exit the game and there will be three dat files and at least three tga files. The
|
||||
// tga's are in 256x256 pages so if it takes three images to render a 24 point font you
|
||||
// will end up with fontImage_0_24.tga through fontImage_2_24.tga
|
||||
// 5. In future runs of the game, the system looks for these images and data files when a s
|
||||
// specific point sized font is rendered and loads them for use.
|
||||
// 6. Because of the original beta nature of the FreeType code you will probably want to hand
|
||||
// touch the font bitmaps.
|
||||
//
|
||||
// Currently a define in the project turns on or off the FreeType code which is currently
|
||||
// defined out. To pre-render new fonts you need enable the define ( BUILD_FREETYPE ) and
|
||||
// uncheck the exclude from build check box in the FreeType2 area of the Renderer project.
|
||||
|
||||
|
||||
#include "tr_local.h"
|
||||
#include "../qcommon/qcommon.h"
|
||||
|
||||
#ifdef BUILD_FREETYPE
|
||||
#include <ft2build.h>
|
||||
#include FT_ERRORS_H
|
||||
#include FT_SYSTEM_H
|
||||
#include FT_IMAGE_H
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_OUTLINE_H
|
||||
|
||||
#define _FLOOR(x) ((x) & -64)
|
||||
#define _CEIL(x) (((x)+63) & -64)
|
||||
#define _TRUNC(x) ((x) >> 6)
|
||||
|
||||
FT_Library ftLibrary = NULL;
|
||||
#endif
|
||||
|
||||
#define MAX_FONTS 6
|
||||
static int registeredFontCount = 0;
|
||||
static fontInfo_t registeredFont[MAX_FONTS];
|
||||
|
||||
#ifdef BUILD_FREETYPE
|
||||
void R_GetGlyphInfo(FT_GlyphSlot glyph, int *left, int *right, int *width, int *top, int *bottom, int *height, int *pitch) {
|
||||
*left = _FLOOR( glyph->metrics.horiBearingX );
|
||||
*right = _CEIL( glyph->metrics.horiBearingX + glyph->metrics.width );
|
||||
*width = _TRUNC(*right - *left);
|
||||
|
||||
*top = _CEIL( glyph->metrics.horiBearingY );
|
||||
*bottom = _FLOOR( glyph->metrics.horiBearingY - glyph->metrics.height );
|
||||
*height = _TRUNC( *top - *bottom );
|
||||
*pitch = ( qtrue ? (*width+3) & -4 : (*width+7) >> 3 );
|
||||
}
|
||||
|
||||
|
||||
FT_Bitmap *R_RenderGlyph(FT_GlyphSlot glyph, glyphInfo_t* glyphOut) {
|
||||
FT_Bitmap *bit2;
|
||||
int left, right, width, top, bottom, height, pitch, size;
|
||||
|
||||
R_GetGlyphInfo(glyph, &left, &right, &width, &top, &bottom, &height, &pitch);
|
||||
|
||||
if ( glyph->format == ft_glyph_format_outline ) {
|
||||
size = pitch*height;
|
||||
|
||||
bit2 = ri.Malloc(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->num_grays = 256;
|
||||
|
||||
Com_Memset( bit2->buffer, 0, size );
|
||||
|
||||
FT_Outline_Translate( &glyph->outline, -left, -bottom );
|
||||
|
||||
FT_Outline_Get_Bitmap( ftLibrary, &glyph->outline, bit2 );
|
||||
|
||||
glyphOut->height = height;
|
||||
glyphOut->pitch = pitch;
|
||||
glyphOut->top = (glyph->metrics.horiBearingY >> 6) + 1;
|
||||
glyphOut->bottom = bottom;
|
||||
|
||||
return bit2;
|
||||
} else {
|
||||
ri.Printf(PRINT_ALL, "Non-outline fonts are not supported\n");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void WriteTGA (char *filename, byte *data, int width, int height) {
|
||||
byte *buffer;
|
||||
int i, c;
|
||||
int row;
|
||||
unsigned char *flip;
|
||||
unsigned char *src, *dst;
|
||||
|
||||
buffer = ri.Malloc(width*height*4 + 18);
|
||||
Com_Memset (buffer, 0, 18);
|
||||
buffer[2] = 2; // uncompressed type
|
||||
buffer[12] = width&255;
|
||||
buffer[13] = width>>8;
|
||||
buffer[14] = height&255;
|
||||
buffer[15] = height>>8;
|
||||
buffer[16] = 32; // pixel size
|
||||
|
||||
// swap rgb to bgr
|
||||
c = 18 + width * height * 4;
|
||||
for (i=18 ; i<c ; i+=4)
|
||||
{
|
||||
buffer[i] = data[i-18+2]; // blue
|
||||
buffer[i+1] = data[i-18+1]; // green
|
||||
buffer[i+2] = data[i-18+0]; // red
|
||||
buffer[i+3] = data[i-18+3]; // alpha
|
||||
}
|
||||
|
||||
// flip upside down
|
||||
flip = (unsigned char *)ri.Malloc(width*4);
|
||||
for(row = 0; row < height/2; row++)
|
||||
{
|
||||
src = buffer + 18 + row * 4 * width;
|
||||
dst = buffer + 18 + (height - row - 1) * 4 * width;
|
||||
|
||||
Com_Memcpy(flip, src, width*4);
|
||||
Com_Memcpy(src, dst, width*4);
|
||||
Com_Memcpy(dst, flip, width*4);
|
||||
}
|
||||
ri.Free(flip);
|
||||
|
||||
ri.FS_WriteFile(filename, buffer, c);
|
||||
|
||||
//f = fopen (filename, "wb");
|
||||
//fwrite (buffer, 1, c, f);
|
||||
//fclose (f);
|
||||
|
||||
ri.Free (buffer);
|
||||
}
|
||||
|
||||
static glyphInfo_t *RE_ConstructGlyphInfo(unsigned char *imageOut, int *xOut, int *yOut, int *maxHeight, FT_Face face, const unsigned char c, qboolean calcHeight) {
|
||||
int i;
|
||||
static glyphInfo_t glyph;
|
||||
unsigned char *src, *dst;
|
||||
float scaled_width, scaled_height;
|
||||
FT_Bitmap *bitmap = NULL;
|
||||
|
||||
Com_Memset(&glyph, 0, sizeof(glyphInfo_t));
|
||||
// make sure everything is here
|
||||
if (face != NULL) {
|
||||
FT_Load_Glyph(face, FT_Get_Char_Index( face, c), FT_LOAD_DEFAULT );
|
||||
bitmap = R_RenderGlyph(face->glyph, &glyph);
|
||||
if (bitmap) {
|
||||
glyph.xSkip = (face->glyph->metrics.horiAdvance >> 6) + 1;
|
||||
} else {
|
||||
return &glyph;
|
||||
}
|
||||
|
||||
if (glyph.height > *maxHeight) {
|
||||
*maxHeight = glyph.height;
|
||||
}
|
||||
|
||||
if (calcHeight) {
|
||||
ri.Free(bitmap->buffer);
|
||||
ri.Free(bitmap);
|
||||
return &glyph;
|
||||
}
|
||||
|
||||
/*
|
||||
// need to convert to power of 2 sizes so we do not get
|
||||
// any scaling from the gl upload
|
||||
for (scaled_width = 1 ; scaled_width < glyph.pitch ; scaled_width<<=1)
|
||||
;
|
||||
for (scaled_height = 1 ; scaled_height < glyph.height ; scaled_height<<=1)
|
||||
;
|
||||
*/
|
||||
|
||||
scaled_width = glyph.pitch;
|
||||
scaled_height = glyph.height;
|
||||
|
||||
// we need to make sure we fit
|
||||
if (*xOut + scaled_width + 1 >= 255) {
|
||||
*xOut = 0;
|
||||
*yOut += *maxHeight + 1;
|
||||
}
|
||||
|
||||
if (*yOut + *maxHeight + 1 >= 255) {
|
||||
*yOut = -1;
|
||||
*xOut = -1;
|
||||
ri.Free(bitmap->buffer);
|
||||
ri.Free(bitmap);
|
||||
return &glyph;
|
||||
}
|
||||
|
||||
|
||||
src = bitmap->buffer;
|
||||
dst = imageOut + (*yOut * 256) + *xOut;
|
||||
|
||||
if (bitmap->pixel_mode == ft_pixel_mode_mono) {
|
||||
for (i = 0; i < glyph.height; i++) {
|
||||
int j;
|
||||
unsigned char *_src = src;
|
||||
unsigned char *_dst = dst;
|
||||
unsigned char mask = 0x80;
|
||||
unsigned char val = *_src;
|
||||
for (j = 0; j < glyph.pitch; j++) {
|
||||
if (mask == 0x80) {
|
||||
val = *_src++;
|
||||
}
|
||||
if (val & mask) {
|
||||
*_dst = 0xff;
|
||||
}
|
||||
mask >>= 1;
|
||||
|
||||
if ( mask == 0 ) {
|
||||
mask = 0x80;
|
||||
}
|
||||
_dst++;
|
||||
}
|
||||
|
||||
src += glyph.pitch;
|
||||
dst += 256;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < glyph.height; i++) {
|
||||
Com_Memcpy(dst, src, glyph.pitch);
|
||||
src += glyph.pitch;
|
||||
dst += 256;
|
||||
}
|
||||
}
|
||||
|
||||
// we now have an 8 bit per pixel grey scale bitmap
|
||||
// that is width wide and pf->ftSize->metrics.y_ppem tall
|
||||
|
||||
glyph.imageHeight = scaled_height;
|
||||
glyph.imageWidth = scaled_width;
|
||||
glyph.s = (float)*xOut / 256;
|
||||
glyph.t = (float)*yOut / 256;
|
||||
glyph.s2 = glyph.s + (float)scaled_width / 256;
|
||||
glyph.t2 = glyph.t + (float)scaled_height / 256;
|
||||
|
||||
*xOut += scaled_width + 1;
|
||||
|
||||
ri.Free(bitmap->buffer);
|
||||
ri.Free(bitmap);
|
||||
}
|
||||
|
||||
return &glyph;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int fdOffset;
|
||||
static byte *fdFile;
|
||||
|
||||
int readInt( void ) {
|
||||
int i = fdFile[fdOffset]+(fdFile[fdOffset+1]<<8)+(fdFile[fdOffset+2]<<16)+(fdFile[fdOffset+3]<<24);
|
||||
fdOffset += 4;
|
||||
return i;
|
||||
}
|
||||
|
||||
typedef union {
|
||||
byte fred[4];
|
||||
float ffred;
|
||||
} poor;
|
||||
|
||||
float readFloat( void ) {
|
||||
poor me;
|
||||
#if defined Q3_BIG_ENDIAN
|
||||
me.fred[0] = fdFile[fdOffset+3];
|
||||
me.fred[1] = fdFile[fdOffset+2];
|
||||
me.fred[2] = fdFile[fdOffset+1];
|
||||
me.fred[3] = fdFile[fdOffset+0];
|
||||
#elif defined Q3_LITTLE_ENDIAN
|
||||
me.fred[0] = fdFile[fdOffset+0];
|
||||
me.fred[1] = fdFile[fdOffset+1];
|
||||
me.fred[2] = fdFile[fdOffset+2];
|
||||
me.fred[3] = fdFile[fdOffset+3];
|
||||
#endif
|
||||
fdOffset += 4;
|
||||
return me.ffred;
|
||||
}
|
||||
|
||||
void RE_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font) {
|
||||
#ifdef BUILD_FREETYPE
|
||||
FT_Face face;
|
||||
int j, k, xOut, yOut, lastStart, imageNumber;
|
||||
int scaledSize, newSize, maxHeight, left;
|
||||
unsigned char *out, *imageBuff;
|
||||
glyphInfo_t *glyph;
|
||||
image_t *image;
|
||||
qhandle_t h;
|
||||
float max;
|
||||
float dpi = 72;
|
||||
float glyphScale;
|
||||
#endif
|
||||
void *faceData;
|
||||
int i, len;
|
||||
char name[1024];
|
||||
|
||||
if (!fontName) {
|
||||
ri.Printf(PRINT_ALL, "RE_RegisterFont: called with empty name\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pointSize <= 0) {
|
||||
pointSize = 12;
|
||||
}
|
||||
|
||||
R_IssuePendingRenderCommands();
|
||||
|
||||
if (registeredFontCount >= MAX_FONTS) {
|
||||
ri.Printf(PRINT_WARNING, "RE_RegisterFont: Too many fonts registered already.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Com_sprintf(name, sizeof(name), "fonts/fontImage_%i.dat",pointSize);
|
||||
for (i = 0; i < registeredFontCount; i++) {
|
||||
if (Q_stricmp(name, registeredFont[i].name) == 0) {
|
||||
Com_Memcpy(font, ®isteredFont[i], sizeof(fontInfo_t));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
len = ri.FS_ReadFile(name, NULL);
|
||||
if (len == sizeof(fontInfo_t)) {
|
||||
ri.FS_ReadFile(name, &faceData);
|
||||
fdOffset = 0;
|
||||
fdFile = faceData;
|
||||
for(i=0; i<GLYPHS_PER_FONT; i++) {
|
||||
font->glyphs[i].height = readInt();
|
||||
font->glyphs[i].top = readInt();
|
||||
font->glyphs[i].bottom = readInt();
|
||||
font->glyphs[i].pitch = readInt();
|
||||
font->glyphs[i].xSkip = readInt();
|
||||
font->glyphs[i].imageWidth = readInt();
|
||||
font->glyphs[i].imageHeight = readInt();
|
||||
font->glyphs[i].s = readFloat();
|
||||
font->glyphs[i].t = readFloat();
|
||||
font->glyphs[i].s2 = readFloat();
|
||||
font->glyphs[i].t2 = readFloat();
|
||||
font->glyphs[i].glyph = readInt();
|
||||
Q_strncpyz(font->glyphs[i].shaderName, (const char *)&fdFile[fdOffset], sizeof(font->glyphs[i].shaderName));
|
||||
fdOffset += sizeof(font->glyphs[i].shaderName);
|
||||
}
|
||||
font->glyphScale = readFloat();
|
||||
Com_Memcpy(font->name, &fdFile[fdOffset], MAX_QPATH);
|
||||
|
||||
// Com_Memcpy(font, faceData, sizeof(fontInfo_t));
|
||||
Q_strncpyz(font->name, name, sizeof(font->name));
|
||||
for (i = GLYPH_START; i < GLYPH_END; i++) {
|
||||
font->glyphs[i].glyph = RE_RegisterShaderNoMip(font->glyphs[i].shaderName);
|
||||
}
|
||||
Com_Memcpy(®isteredFont[registeredFontCount++], font, sizeof(fontInfo_t));
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef BUILD_FREETYPE
|
||||
ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType code not available\n");
|
||||
#else
|
||||
if (ftLibrary == NULL) {
|
||||
ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType not initialized.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
len = ri.FS_ReadFile(fontName, &faceData);
|
||||
if (len <= 0) {
|
||||
ri.Printf(PRINT_WARNING, "RE_RegisterFont: Unable to read font file '%s'\n", fontName);
|
||||
return;
|
||||
}
|
||||
|
||||
// allocate on the stack first in case we fail
|
||||
if (FT_New_Memory_Face( ftLibrary, faceData, len, 0, &face )) {
|
||||
ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType, unable to allocate new face.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (FT_Set_Char_Size( face, pointSize << 6, pointSize << 6, dpi, dpi)) {
|
||||
ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType, unable to set face char size.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
//*font = ®isteredFonts[registeredFontCount++];
|
||||
|
||||
// make a 256x256 image buffer, once it is full, register it, clean it and keep going
|
||||
// until all glyphs are rendered
|
||||
|
||||
out = ri.Malloc(1024*1024);
|
||||
if (out == NULL) {
|
||||
ri.Printf(PRINT_WARNING, "RE_RegisterFont: ri.Malloc failure during output image creation.\n");
|
||||
return;
|
||||
}
|
||||
Com_Memset(out, 0, 1024*1024);
|
||||
|
||||
maxHeight = 0;
|
||||
|
||||
for (i = GLYPH_START; i < GLYPH_END; i++) {
|
||||
RE_ConstructGlyphInfo(out, &xOut, &yOut, &maxHeight, face, (unsigned char)i, qtrue);
|
||||
}
|
||||
|
||||
xOut = 0;
|
||||
yOut = 0;
|
||||
i = GLYPH_START;
|
||||
lastStart = i;
|
||||
imageNumber = 0;
|
||||
|
||||
while ( i <= GLYPH_END ) {
|
||||
|
||||
glyph = RE_ConstructGlyphInfo(out, &xOut, &yOut, &maxHeight, face, (unsigned char)i, qfalse);
|
||||
|
||||
if (xOut == -1 || yOut == -1 || i == GLYPH_END) {
|
||||
// ran out of room
|
||||
// we need to create an image from the bitmap, set all the handles in the glyphs to this point
|
||||
//
|
||||
|
||||
scaledSize = 256*256;
|
||||
newSize = scaledSize * 4;
|
||||
imageBuff = ri.Malloc(newSize);
|
||||
left = 0;
|
||||
max = 0;
|
||||
for ( k = 0; k < (scaledSize) ; k++ ) {
|
||||
if (max < out[k]) {
|
||||
max = out[k];
|
||||
}
|
||||
}
|
||||
|
||||
if (max > 0) {
|
||||
max = 255/max;
|
||||
}
|
||||
|
||||
for ( k = 0; k < (scaledSize) ; k++ ) {
|
||||
imageBuff[left++] = 255;
|
||||
imageBuff[left++] = 255;
|
||||
imageBuff[left++] = 255;
|
||||
|
||||
imageBuff[left++] = ((float)out[k] * max);
|
||||
}
|
||||
|
||||
Com_sprintf (name, sizeof(name), "fonts/fontImage_%i_%i.tga", imageNumber++, pointSize);
|
||||
if (r_saveFontData->integer) {
|
||||
WriteTGA(name, imageBuff, 256, 256);
|
||||
}
|
||||
|
||||
//Com_sprintf (name, sizeof(name), "fonts/fontImage_%i_%i", imageNumber++, pointSize);
|
||||
image = R_CreateImage(name, imageBuff, 256, 256, qfalse, qfalse, GL_CLAMP_TO_EDGE);
|
||||
h = RE_RegisterShaderFromImage(name, LIGHTMAP_2D, image, qfalse);
|
||||
for (j = lastStart; j < i; j++) {
|
||||
font->glyphs[j].glyph = h;
|
||||
Q_strncpyz(font->glyphs[j].shaderName, name, sizeof(font->glyphs[j].shaderName));
|
||||
}
|
||||
lastStart = i;
|
||||
Com_Memset(out, 0, 1024*1024);
|
||||
xOut = 0;
|
||||
yOut = 0;
|
||||
ri.Free(imageBuff);
|
||||
i++;
|
||||
} else {
|
||||
Com_Memcpy(&font->glyphs[i], glyph, sizeof(glyphInfo_t));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// change the scale to be relative to 1 based on 72 dpi ( so dpi of 144 means a scale of .5 )
|
||||
glyphScale = 72.0f / dpi;
|
||||
|
||||
// we also need to adjust the scale based on point size relative to 48 points as the ui scaling is based on a 48 point font
|
||||
glyphScale *= 48.0f / pointSize;
|
||||
|
||||
registeredFont[registeredFontCount].glyphScale = glyphScale;
|
||||
font->glyphScale = glyphScale;
|
||||
Com_Memcpy(®isteredFont[registeredFontCount++], font, sizeof(fontInfo_t));
|
||||
|
||||
if (r_saveFontData->integer) {
|
||||
ri.FS_WriteFile(va("fonts/fontImage_%i.dat", pointSize), font, sizeof(fontInfo_t));
|
||||
}
|
||||
|
||||
ri.Free(out);
|
||||
|
||||
ri.FS_FreeFile(faceData);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void R_InitFreeType(void) {
|
||||
#ifdef BUILD_FREETYPE
|
||||
if (FT_Init_FreeType( &ftLibrary )) {
|
||||
ri.Printf(PRINT_WARNING, "R_InitFreeType: Unable to initialize FreeType.\n");
|
||||
}
|
||||
#endif
|
||||
registeredFontCount = 0;
|
||||
}
|
||||
|
||||
|
||||
void R_DoneFreeType(void) {
|
||||
#ifdef BUILD_FREETYPE
|
||||
if (ftLibrary) {
|
||||
FT_Done_FreeType( ftLibrary );
|
||||
ftLibrary = NULL;
|
||||
}
|
||||
#endif
|
||||
registeredFontCount = 0;
|
||||
}
|
||||
|
1607
code/renderergl1/tr_image.c
Normal file
1607
code/renderergl1/tr_image.c
Normal file
File diff suppressed because it is too large
Load diff
239
code/renderergl1/tr_image_bmp.c
Normal file
239
code/renderergl1/tr_image_bmp.c
Normal file
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char id[2];
|
||||
unsigned fileSize;
|
||||
unsigned reserved0;
|
||||
unsigned bitmapDataOffset;
|
||||
unsigned bitmapHeaderSize;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned short planes;
|
||||
unsigned short bitsPerPixel;
|
||||
unsigned compression;
|
||||
unsigned bitmapDataSize;
|
||||
unsigned hRes;
|
||||
unsigned vRes;
|
||||
unsigned colors;
|
||||
unsigned importantColors;
|
||||
unsigned char palette[256][4];
|
||||
} BMPHeader_t;
|
||||
|
||||
void R_LoadBMP( const char *name, byte **pic, int *width, int *height )
|
||||
{
|
||||
int columns, rows;
|
||||
unsigned numPixels;
|
||||
byte *pixbuf;
|
||||
int row, column;
|
||||
byte *buf_p;
|
||||
byte *end;
|
||||
union {
|
||||
byte *b;
|
||||
void *v;
|
||||
} buffer;
|
||||
int length;
|
||||
BMPHeader_t bmpHeader;
|
||||
byte *bmpRGBA;
|
||||
|
||||
*pic = NULL;
|
||||
|
||||
if(width)
|
||||
*width = 0;
|
||||
|
||||
if(height)
|
||||
*height = 0;
|
||||
|
||||
//
|
||||
// load the file
|
||||
//
|
||||
length = ri.FS_ReadFile( ( char * ) name, &buffer.v);
|
||||
if (!buffer.b || length < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (length < 54)
|
||||
{
|
||||
ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name );
|
||||
}
|
||||
|
||||
buf_p = buffer.b;
|
||||
end = buffer.b + length;
|
||||
|
||||
bmpHeader.id[0] = *buf_p++;
|
||||
bmpHeader.id[1] = *buf_p++;
|
||||
bmpHeader.fileSize = LittleLong( * ( int * ) buf_p );
|
||||
buf_p += 4;
|
||||
bmpHeader.reserved0 = LittleLong( * ( int * ) buf_p );
|
||||
buf_p += 4;
|
||||
bmpHeader.bitmapDataOffset = LittleLong( * ( int * ) buf_p );
|
||||
buf_p += 4;
|
||||
bmpHeader.bitmapHeaderSize = LittleLong( * ( int * ) buf_p );
|
||||
buf_p += 4;
|
||||
bmpHeader.width = LittleLong( * ( int * ) buf_p );
|
||||
buf_p += 4;
|
||||
bmpHeader.height = LittleLong( * ( int * ) buf_p );
|
||||
buf_p += 4;
|
||||
bmpHeader.planes = LittleShort( * ( short * ) buf_p );
|
||||
buf_p += 2;
|
||||
bmpHeader.bitsPerPixel = LittleShort( * ( short * ) buf_p );
|
||||
buf_p += 2;
|
||||
bmpHeader.compression = LittleLong( * ( int * ) buf_p );
|
||||
buf_p += 4;
|
||||
bmpHeader.bitmapDataSize = LittleLong( * ( int * ) buf_p );
|
||||
buf_p += 4;
|
||||
bmpHeader.hRes = LittleLong( * ( int * ) buf_p );
|
||||
buf_p += 4;
|
||||
bmpHeader.vRes = LittleLong( * ( int * ) buf_p );
|
||||
buf_p += 4;
|
||||
bmpHeader.colors = LittleLong( * ( int * ) buf_p );
|
||||
buf_p += 4;
|
||||
bmpHeader.importantColors = LittleLong( * ( int * ) buf_p );
|
||||
buf_p += 4;
|
||||
|
||||
if ( bmpHeader.bitsPerPixel == 8 )
|
||||
{
|
||||
if (buf_p + sizeof(bmpHeader.palette) > end)
|
||||
ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name );
|
||||
|
||||
Com_Memcpy( bmpHeader.palette, buf_p, sizeof( bmpHeader.palette ) );
|
||||
buf_p += sizeof(bmpHeader.palette);
|
||||
}
|
||||
|
||||
if (buffer.b + bmpHeader.bitmapDataOffset > end)
|
||||
{
|
||||
ri.Error( ERR_DROP, "LoadBMP: invalid offset value in header (%s)", name );
|
||||
}
|
||||
|
||||
buf_p = buffer.b + bmpHeader.bitmapDataOffset;
|
||||
|
||||
if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' )
|
||||
{
|
||||
ri.Error( ERR_DROP, "LoadBMP: only Windows-style BMP files supported (%s)", name );
|
||||
}
|
||||
if ( bmpHeader.fileSize != length )
|
||||
{
|
||||
ri.Error( ERR_DROP, "LoadBMP: header size does not match file size (%u vs. %u) (%s)", bmpHeader.fileSize, length, name );
|
||||
}
|
||||
if ( bmpHeader.compression != 0 )
|
||||
{
|
||||
ri.Error( ERR_DROP, "LoadBMP: only uncompressed BMP files supported (%s)", name );
|
||||
}
|
||||
if ( bmpHeader.bitsPerPixel < 8 )
|
||||
{
|
||||
ri.Error( ERR_DROP, "LoadBMP: monochrome and 4-bit BMP files not supported (%s)", name );
|
||||
}
|
||||
|
||||
switch ( bmpHeader.bitsPerPixel )
|
||||
{
|
||||
case 8:
|
||||
case 16:
|
||||
case 24:
|
||||
case 32:
|
||||
break;
|
||||
default:
|
||||
ri.Error( ERR_DROP, "LoadBMP: illegal pixel_size '%hu' in file '%s'", bmpHeader.bitsPerPixel, name );
|
||||
break;
|
||||
}
|
||||
|
||||
columns = bmpHeader.width;
|
||||
rows = bmpHeader.height;
|
||||
if ( rows < 0 )
|
||||
rows = -rows;
|
||||
numPixels = columns * rows;
|
||||
|
||||
if(columns <= 0 || !rows || numPixels > 0x1FFFFFFF // 4*1FFFFFFF == 0x7FFFFFFC < 0x7FFFFFFF
|
||||
|| ((numPixels * 4) / columns) / 4 != rows)
|
||||
{
|
||||
ri.Error (ERR_DROP, "LoadBMP: %s has an invalid image size", name);
|
||||
}
|
||||
if(buf_p + numPixels*bmpHeader.bitsPerPixel/8 > end)
|
||||
{
|
||||
ri.Error (ERR_DROP, "LoadBMP: file truncated (%s)", name);
|
||||
}
|
||||
|
||||
if ( width )
|
||||
*width = columns;
|
||||
if ( height )
|
||||
*height = rows;
|
||||
|
||||
bmpRGBA = ri.Malloc( numPixels * 4 );
|
||||
*pic = bmpRGBA;
|
||||
|
||||
|
||||
for ( row = rows-1; row >= 0; row-- )
|
||||
{
|
||||
pixbuf = bmpRGBA + row*columns*4;
|
||||
|
||||
for ( column = 0; column < columns; column++ )
|
||||
{
|
||||
unsigned char red, green, blue, alpha;
|
||||
int palIndex;
|
||||
unsigned short shortPixel;
|
||||
|
||||
switch ( bmpHeader.bitsPerPixel )
|
||||
{
|
||||
case 8:
|
||||
palIndex = *buf_p++;
|
||||
*pixbuf++ = bmpHeader.palette[palIndex][2];
|
||||
*pixbuf++ = bmpHeader.palette[palIndex][1];
|
||||
*pixbuf++ = bmpHeader.palette[palIndex][0];
|
||||
*pixbuf++ = 0xff;
|
||||
break;
|
||||
case 16:
|
||||
shortPixel = * ( unsigned short * ) pixbuf;
|
||||
pixbuf += 2;
|
||||
*pixbuf++ = ( shortPixel & ( 31 << 10 ) ) >> 7;
|
||||
*pixbuf++ = ( shortPixel & ( 31 << 5 ) ) >> 2;
|
||||
*pixbuf++ = ( shortPixel & ( 31 ) ) << 3;
|
||||
*pixbuf++ = 0xff;
|
||||
break;
|
||||
|
||||
case 24:
|
||||
blue = *buf_p++;
|
||||
green = *buf_p++;
|
||||
red = *buf_p++;
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
*pixbuf++ = 255;
|
||||
break;
|
||||
case 32:
|
||||
blue = *buf_p++;
|
||||
green = *buf_p++;
|
||||
red = *buf_p++;
|
||||
alpha = *buf_p++;
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
*pixbuf++ = alpha;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ri.FS_FreeFile( buffer.v );
|
||||
|
||||
}
|
437
code/renderergl1/tr_image_jpg.c
Normal file
437
code/renderergl1/tr_image_jpg.c
Normal file
|
@ -0,0 +1,437 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
/*
|
||||
* Include file for users of JPEG library.
|
||||
* You will need to have included system headers that define at least
|
||||
* the typedefs FILE and size_t before you can include jpeglib.h.
|
||||
* (stdio.h is sufficient on ANSI-conforming systems.)
|
||||
* You may also wish to include "jerror.h".
|
||||
*/
|
||||
|
||||
#ifdef USE_INTERNAL_JPEG
|
||||
# define JPEG_INTERNALS
|
||||
#endif
|
||||
|
||||
#include <jpeglib.h>
|
||||
|
||||
#ifndef USE_INTERNAL_JPEG
|
||||
# if JPEG_LIB_VERSION < 80
|
||||
# error Need system libjpeg >= 80
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static void R_JPGErrorExit(j_common_ptr cinfo)
|
||||
{
|
||||
char buffer[JMSG_LENGTH_MAX];
|
||||
|
||||
(*cinfo->err->format_message) (cinfo, buffer);
|
||||
|
||||
/* Let the memory manager delete any temp files before we die */
|
||||
jpeg_destroy(cinfo);
|
||||
|
||||
ri.Error(ERR_FATAL, "%s", buffer);
|
||||
}
|
||||
|
||||
static void R_JPGOutputMessage(j_common_ptr cinfo)
|
||||
{
|
||||
char buffer[JMSG_LENGTH_MAX];
|
||||
|
||||
/* Create the message */
|
||||
(*cinfo->err->format_message) (cinfo, buffer);
|
||||
|
||||
/* Send it to stderr, adding a newline */
|
||||
ri.Printf(PRINT_ALL, "%s\n", buffer);
|
||||
}
|
||||
|
||||
void R_LoadJPG(const char *filename, unsigned char **pic, int *width, int *height)
|
||||
{
|
||||
/* This struct contains the JPEG decompression parameters and pointers to
|
||||
* working space (which is allocated as needed by the JPEG library).
|
||||
*/
|
||||
struct jpeg_decompress_struct cinfo = {NULL};
|
||||
/* We use our private extension JPEG error handler.
|
||||
* Note that this struct must live as long as the main JPEG parameter
|
||||
* struct, to avoid dangling-pointer problems.
|
||||
*/
|
||||
/* This struct represents a JPEG error handler. It is declared separately
|
||||
* because applications often want to supply a specialized error handler
|
||||
* (see the second half of this file for an example). But here we just
|
||||
* take the easy way out and use the standard error handler, which will
|
||||
* print a message on stderr and call exit() if compression fails.
|
||||
* Note that this struct must live as long as the main JPEG parameter
|
||||
* struct, to avoid dangling-pointer problems.
|
||||
*/
|
||||
struct jpeg_error_mgr jerr;
|
||||
/* More stuff */
|
||||
JSAMPARRAY buffer; /* Output row buffer */
|
||||
unsigned int row_stride; /* physical row width in output buffer */
|
||||
unsigned int pixelcount, memcount;
|
||||
unsigned int sindex, dindex;
|
||||
byte *out;
|
||||
int len;
|
||||
union {
|
||||
byte *b;
|
||||
void *v;
|
||||
} fbuffer;
|
||||
byte *buf;
|
||||
|
||||
/* In this example we want to open the input file before doing anything else,
|
||||
* so that the setjmp() error recovery below can assume the file is open.
|
||||
* VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
|
||||
* requires it in order to read binary files.
|
||||
*/
|
||||
|
||||
len = ri.FS_ReadFile ( ( char * ) filename, &fbuffer.v);
|
||||
if (!fbuffer.b || len < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Step 1: allocate and initialize JPEG decompression object */
|
||||
|
||||
/* We have to set up the error handler first, in case the initialization
|
||||
* step fails. (Unlikely, but it could happen if you are out of memory.)
|
||||
* This routine fills in the contents of struct jerr, and returns jerr's
|
||||
* address which we place into the link field in cinfo.
|
||||
*/
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
cinfo.err->error_exit = R_JPGErrorExit;
|
||||
cinfo.err->output_message = R_JPGOutputMessage;
|
||||
|
||||
/* Now we can initialize the JPEG decompression object. */
|
||||
jpeg_create_decompress(&cinfo);
|
||||
|
||||
/* Step 2: specify data source (eg, a file) */
|
||||
|
||||
jpeg_mem_src(&cinfo, fbuffer.b, len);
|
||||
|
||||
/* Step 3: read file parameters with jpeg_read_header() */
|
||||
|
||||
(void) jpeg_read_header(&cinfo, TRUE);
|
||||
/* We can ignore the return value from jpeg_read_header since
|
||||
* (a) suspension is not possible with the stdio data source, and
|
||||
* (b) we passed TRUE to reject a tables-only JPEG file as an error.
|
||||
* See libjpeg.doc for more info.
|
||||
*/
|
||||
|
||||
/* Step 4: set parameters for decompression */
|
||||
|
||||
/*
|
||||
* Make sure it always converts images to RGB color space. This will
|
||||
* automatically convert 8-bit greyscale images to RGB as well.
|
||||
*/
|
||||
cinfo.out_color_space = JCS_RGB;
|
||||
|
||||
/* Step 5: Start decompressor */
|
||||
|
||||
(void) jpeg_start_decompress(&cinfo);
|
||||
/* We can ignore the return value since suspension is not possible
|
||||
* with the stdio data source.
|
||||
*/
|
||||
|
||||
/* We may need to do some setup of our own at this point before reading
|
||||
* the data. After jpeg_start_decompress() we have the correct scaled
|
||||
* output image dimensions available, as well as the output colormap
|
||||
* if we asked for color quantization.
|
||||
* In this example, we need to make an output work buffer of the right size.
|
||||
*/
|
||||
/* JSAMPLEs per row in output buffer */
|
||||
|
||||
pixelcount = cinfo.output_width * cinfo.output_height;
|
||||
|
||||
if(!cinfo.output_width || !cinfo.output_height
|
||||
|| ((pixelcount * 4) / cinfo.output_width) / 4 != cinfo.output_height
|
||||
|| pixelcount > 0x1FFFFFFF || cinfo.output_components != 3
|
||||
)
|
||||
{
|
||||
// Free the memory to make sure we don't leak memory
|
||||
ri.FS_FreeFile (fbuffer.v);
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
|
||||
ri.Error(ERR_DROP, "LoadJPG: %s has an invalid image format: %dx%d*4=%d, components: %d", filename,
|
||||
cinfo.output_width, cinfo.output_height, pixelcount * 4, cinfo.output_components);
|
||||
}
|
||||
|
||||
memcount = pixelcount * 4;
|
||||
row_stride = cinfo.output_width * cinfo.output_components;
|
||||
|
||||
out = ri.Malloc(memcount);
|
||||
|
||||
*width = cinfo.output_width;
|
||||
*height = cinfo.output_height;
|
||||
|
||||
/* Step 6: while (scan lines remain to be read) */
|
||||
/* jpeg_read_scanlines(...); */
|
||||
|
||||
/* Here we use the library's state variable cinfo.output_scanline as the
|
||||
* loop counter, so that we don't have to keep track ourselves.
|
||||
*/
|
||||
while (cinfo.output_scanline < cinfo.output_height) {
|
||||
/* jpeg_read_scanlines expects an array of pointers to scanlines.
|
||||
* Here the array is only one element long, but you could ask for
|
||||
* more than one scanline at a time if that's more convenient.
|
||||
*/
|
||||
buf = ((out+(row_stride*cinfo.output_scanline)));
|
||||
buffer = &buf;
|
||||
(void) jpeg_read_scanlines(&cinfo, buffer, 1);
|
||||
}
|
||||
|
||||
buf = out;
|
||||
|
||||
// Expand from RGB to RGBA
|
||||
sindex = pixelcount * cinfo.output_components;
|
||||
dindex = memcount;
|
||||
|
||||
do
|
||||
{
|
||||
buf[--dindex] = 255;
|
||||
buf[--dindex] = buf[--sindex];
|
||||
buf[--dindex] = buf[--sindex];
|
||||
buf[--dindex] = buf[--sindex];
|
||||
} while(sindex);
|
||||
|
||||
*pic = out;
|
||||
|
||||
/* Step 7: Finish decompression */
|
||||
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
/* We can ignore the return value since suspension is not possible
|
||||
* with the stdio data source.
|
||||
*/
|
||||
|
||||
/* Step 8: Release JPEG decompression object */
|
||||
|
||||
/* This is an important step since it will release a good deal of memory. */
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
|
||||
/* After finish_decompress, we can close the input file.
|
||||
* Here we postpone it until after no more JPEG errors are possible,
|
||||
* so as to simplify the setjmp error logic above. (Actually, I don't
|
||||
* think that jpeg_destroy can do an error exit, but why assume anything...)
|
||||
*/
|
||||
ri.FS_FreeFile (fbuffer.v);
|
||||
|
||||
/* At this point you may want to check to see whether any corrupt-data
|
||||
* warnings occurred (test whether jerr.pub.num_warnings is nonzero).
|
||||
*/
|
||||
|
||||
/* And we're done! */
|
||||
}
|
||||
|
||||
|
||||
/* Expanded data destination object for stdio output */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_destination_mgr pub; /* public fields */
|
||||
|
||||
byte* outfile; /* target stream */
|
||||
int size;
|
||||
} my_destination_mgr;
|
||||
|
||||
typedef my_destination_mgr * my_dest_ptr;
|
||||
|
||||
|
||||
/*
|
||||
* Initialize destination --- called by jpeg_start_compress
|
||||
* before any data is actually written.
|
||||
*/
|
||||
|
||||
static void
|
||||
init_destination (j_compress_ptr cinfo)
|
||||
{
|
||||
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
|
||||
|
||||
dest->pub.next_output_byte = dest->outfile;
|
||||
dest->pub.free_in_buffer = dest->size;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Empty the output buffer --- called whenever buffer fills up.
|
||||
*
|
||||
* In typical applications, this should write the entire output buffer
|
||||
* (ignoring the current state of next_output_byte & free_in_buffer),
|
||||
* reset the pointer & count to the start of the buffer, and return TRUE
|
||||
* indicating that the buffer has been dumped.
|
||||
*
|
||||
* In applications that need to be able to suspend compression due to output
|
||||
* overrun, a FALSE return indicates that the buffer cannot be emptied now.
|
||||
* In this situation, the compressor will return to its caller (possibly with
|
||||
* an indication that it has not accepted all the supplied scanlines). The
|
||||
* application should resume compression after it has made more room in the
|
||||
* output buffer. Note that there are substantial restrictions on the use of
|
||||
* suspension --- see the documentation.
|
||||
*
|
||||
* When suspending, the compressor will back up to a convenient restart point
|
||||
* (typically the start of the current MCU). next_output_byte & free_in_buffer
|
||||
* indicate where the restart point will be if the current call returns FALSE.
|
||||
* Data beyond this point will be regenerated after resumption, so do not
|
||||
* write it out when emptying the buffer externally.
|
||||
*/
|
||||
|
||||
static boolean
|
||||
empty_output_buffer (j_compress_ptr cinfo)
|
||||
{
|
||||
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
|
||||
|
||||
jpeg_destroy_compress(cinfo);
|
||||
|
||||
// Make crash fatal or we would probably leak memory.
|
||||
ri.Error(ERR_FATAL, "Output buffer for encoded JPEG image has insufficient size of %d bytes",
|
||||
dest->size);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Terminate destination --- called by jpeg_finish_compress
|
||||
* after all data has been written. Usually needs to flush buffer.
|
||||
*
|
||||
* NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
|
||||
* application must deal with any cleanup that should happen even
|
||||
* for error exit.
|
||||
*/
|
||||
|
||||
static void term_destination(j_compress_ptr cinfo)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Prepare for output to a stdio stream.
|
||||
* The caller must have already opened the stream, and is responsible
|
||||
* for closing it after finishing compression.
|
||||
*/
|
||||
|
||||
static void
|
||||
jpegDest (j_compress_ptr cinfo, byte* outfile, int size)
|
||||
{
|
||||
my_dest_ptr dest;
|
||||
|
||||
/* The destination object is made permanent so that multiple JPEG images
|
||||
* can be written to the same file without re-executing jpeg_stdio_dest.
|
||||
* This makes it dangerous to use this manager and a different destination
|
||||
* manager serially with the same JPEG object, because their private object
|
||||
* sizes may be different. Caveat programmer.
|
||||
*/
|
||||
if (cinfo->dest == NULL) { /* first time for this JPEG object? */
|
||||
cinfo->dest = (struct jpeg_destination_mgr *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
|
||||
sizeof(my_destination_mgr));
|
||||
}
|
||||
|
||||
dest = (my_dest_ptr) cinfo->dest;
|
||||
dest->pub.init_destination = init_destination;
|
||||
dest->pub.empty_output_buffer = empty_output_buffer;
|
||||
dest->pub.term_destination = term_destination;
|
||||
dest->outfile = outfile;
|
||||
dest->size = size;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SaveJPGToBuffer
|
||||
|
||||
Encodes JPEG from image in image_buffer and writes to buffer.
|
||||
Expects RGB input data
|
||||
=================
|
||||
*/
|
||||
size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality,
|
||||
int image_width, int image_height, byte *image_buffer, int padding)
|
||||
{
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
|
||||
my_dest_ptr dest;
|
||||
int row_stride; /* physical row width in image buffer */
|
||||
size_t outcount;
|
||||
|
||||
/* Step 1: allocate and initialize JPEG compression object */
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
cinfo.err->error_exit = R_JPGErrorExit;
|
||||
cinfo.err->output_message = R_JPGOutputMessage;
|
||||
|
||||
/* Now we can initialize the JPEG compression object. */
|
||||
jpeg_create_compress(&cinfo);
|
||||
|
||||
/* Step 2: specify data destination (eg, a file) */
|
||||
/* Note: steps 2 and 3 can be done in either order. */
|
||||
jpegDest(&cinfo, buffer, bufSize);
|
||||
|
||||
/* Step 3: set parameters for compression */
|
||||
cinfo.image_width = image_width; /* image width and height, in pixels */
|
||||
cinfo.image_height = image_height;
|
||||
cinfo.input_components = 3; /* # of color components per pixel */
|
||||
cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
|
||||
|
||||
jpeg_set_defaults(&cinfo);
|
||||
jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
|
||||
/* If quality is set high, disable chroma subsampling */
|
||||
if (quality >= 85) {
|
||||
cinfo.comp_info[0].h_samp_factor = 1;
|
||||
cinfo.comp_info[0].v_samp_factor = 1;
|
||||
}
|
||||
|
||||
/* Step 4: Start compressor */
|
||||
jpeg_start_compress(&cinfo, TRUE);
|
||||
|
||||
/* Step 5: while (scan lines remain to be written) */
|
||||
/* jpeg_write_scanlines(...); */
|
||||
row_stride = image_width * cinfo.input_components + padding; /* JSAMPLEs per row in image_buffer */
|
||||
|
||||
while (cinfo.next_scanline < cinfo.image_height) {
|
||||
/* jpeg_write_scanlines expects an array of pointers to scanlines.
|
||||
* Here the array is only one element long, but you could pass
|
||||
* more than one scanline at a time if that's more convenient.
|
||||
*/
|
||||
row_pointer[0] = &image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride];
|
||||
(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
|
||||
}
|
||||
|
||||
/* Step 6: Finish compression */
|
||||
jpeg_finish_compress(&cinfo);
|
||||
|
||||
dest = (my_dest_ptr) cinfo.dest;
|
||||
outcount = dest->size - dest->pub.free_in_buffer;
|
||||
|
||||
/* Step 7: release JPEG compression object */
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
/* And we're done! */
|
||||
return outcount;
|
||||
}
|
||||
|
||||
void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, byte *image_buffer, int padding)
|
||||
{
|
||||
byte *out;
|
||||
size_t bufSize;
|
||||
|
||||
bufSize = image_width * image_height * 3;
|
||||
out = ri.Hunk_AllocateTempMemory(bufSize);
|
||||
|
||||
bufSize = RE_SaveJPGToBuffer(out, bufSize, quality, image_width, image_height, image_buffer, padding);
|
||||
ri.FS_WriteFile(filename, out, bufSize);
|
||||
|
||||
ri.Hunk_FreeTempMemory(out);
|
||||
}
|
175
code/renderergl1/tr_image_pcx.c
Normal file
175
code/renderergl1/tr_image_pcx.c
Normal file
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
2008 Ludwig Nussel
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
/*
|
||||
========================================================================
|
||||
|
||||
PCX files are used for 8 bit images
|
||||
|
||||
========================================================================
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
char manufacturer;
|
||||
char version;
|
||||
char encoding;
|
||||
char bits_per_pixel;
|
||||
unsigned short xmin,ymin,xmax,ymax;
|
||||
unsigned short hres,vres;
|
||||
unsigned char palette[48];
|
||||
char reserved;
|
||||
char color_planes;
|
||||
unsigned short bytes_per_line;
|
||||
unsigned short palette_type;
|
||||
unsigned short hscreensize, vscreensize;
|
||||
char filler[54];
|
||||
unsigned char data[];
|
||||
} pcx_t;
|
||||
|
||||
void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height)
|
||||
{
|
||||
union {
|
||||
byte *b;
|
||||
void *v;
|
||||
} raw;
|
||||
byte *end;
|
||||
pcx_t *pcx;
|
||||
int len;
|
||||
unsigned char dataByte = 0, runLength = 0;
|
||||
byte *out, *pix;
|
||||
unsigned short w, h;
|
||||
byte *pic8;
|
||||
byte *palette;
|
||||
int i;
|
||||
unsigned size = 0;
|
||||
|
||||
if (width)
|
||||
*width = 0;
|
||||
if (height)
|
||||
*height = 0;
|
||||
*pic = NULL;
|
||||
|
||||
//
|
||||
// load the file
|
||||
//
|
||||
len = ri.FS_ReadFile( ( char * ) filename, &raw.v);
|
||||
if (!raw.b || len < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if((unsigned)len < sizeof(pcx_t))
|
||||
{
|
||||
ri.Printf (PRINT_ALL, "PCX truncated: %s\n", filename);
|
||||
ri.FS_FreeFile (raw.v);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// parse the PCX file
|
||||
//
|
||||
pcx = (pcx_t *)raw.b;
|
||||
end = raw.b+len;
|
||||
|
||||
w = LittleShort(pcx->xmax)+1;
|
||||
h = LittleShort(pcx->ymax)+1;
|
||||
size = w*h;
|
||||
|
||||
if (pcx->manufacturer != 0x0a
|
||||
|| pcx->version != 5
|
||||
|| pcx->encoding != 1
|
||||
|| pcx->color_planes != 1
|
||||
|| pcx->bits_per_pixel != 8
|
||||
|| w >= 1024
|
||||
|| h >= 1024)
|
||||
{
|
||||
ri.Printf (PRINT_ALL, "Bad or unsupported pcx file %s (%dx%d@%d)\n", filename, w, h, pcx->bits_per_pixel);
|
||||
return;
|
||||
}
|
||||
|
||||
pix = pic8 = ri.Malloc ( size );
|
||||
|
||||
raw.b = pcx->data;
|
||||
// FIXME: should use bytes_per_line but original q3 didn't do that either
|
||||
while(pix < pic8+size)
|
||||
{
|
||||
if(runLength > 0) {
|
||||
*pix++ = dataByte;
|
||||
--runLength;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(raw.b+1 > end)
|
||||
break;
|
||||
dataByte = *raw.b++;
|
||||
|
||||
if((dataByte & 0xC0) == 0xC0)
|
||||
{
|
||||
if(raw.b+1 > end)
|
||||
break;
|
||||
runLength = dataByte & 0x3F;
|
||||
dataByte = *raw.b++;
|
||||
}
|
||||
else
|
||||
runLength = 1;
|
||||
}
|
||||
|
||||
if(pix < pic8+size)
|
||||
{
|
||||
ri.Printf (PRINT_ALL, "PCX file truncated: %s\n", filename);
|
||||
ri.FS_FreeFile (pcx);
|
||||
ri.Free (pic8);
|
||||
}
|
||||
|
||||
if (raw.b-(byte*)pcx >= end - (byte*)769 || end[-769] != 0x0c)
|
||||
{
|
||||
ri.Printf (PRINT_ALL, "PCX missing palette: %s\n", filename);
|
||||
ri.FS_FreeFile (pcx);
|
||||
ri.Free (pic8);
|
||||
return;
|
||||
}
|
||||
|
||||
palette = end-768;
|
||||
|
||||
pix = out = ri.Malloc(4 * size );
|
||||
for (i = 0 ; i < size ; i++)
|
||||
{
|
||||
unsigned char p = pic8[i];
|
||||
pix[0] = palette[p*3];
|
||||
pix[1] = palette[p*3 + 1];
|
||||
pix[2] = palette[p*3 + 2];
|
||||
pix[3] = 255;
|
||||
pix += 4;
|
||||
}
|
||||
|
||||
if (width)
|
||||
*width = w;
|
||||
if (height)
|
||||
*height = h;
|
||||
|
||||
*pic = out;
|
||||
|
||||
ri.FS_FreeFile (pcx);
|
||||
ri.Free (pic8);
|
||||
}
|
2486
code/renderergl1/tr_image_png.c
Normal file
2486
code/renderergl1/tr_image_png.c
Normal file
File diff suppressed because it is too large
Load diff
320
code/renderergl1/tr_image_tga.c
Normal file
320
code/renderergl1/tr_image_tga.c
Normal file
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
/*
|
||||
========================================================================
|
||||
|
||||
TGA files are used for 24/32 bit images
|
||||
|
||||
========================================================================
|
||||
*/
|
||||
|
||||
typedef struct _TargaHeader {
|
||||
unsigned char id_length, colormap_type, image_type;
|
||||
unsigned short colormap_index, colormap_length;
|
||||
unsigned char colormap_size;
|
||||
unsigned short x_origin, y_origin, width, height;
|
||||
unsigned char pixel_size, attributes;
|
||||
} TargaHeader;
|
||||
|
||||
void R_LoadTGA ( const char *name, byte **pic, int *width, int *height)
|
||||
{
|
||||
unsigned columns, rows, numPixels;
|
||||
byte *pixbuf;
|
||||
int row, column;
|
||||
byte *buf_p;
|
||||
byte *end;
|
||||
union {
|
||||
byte *b;
|
||||
void *v;
|
||||
} buffer;
|
||||
TargaHeader targa_header;
|
||||
byte *targa_rgba;
|
||||
int length;
|
||||
|
||||
*pic = NULL;
|
||||
|
||||
if(width)
|
||||
*width = 0;
|
||||
if(height)
|
||||
*height = 0;
|
||||
|
||||
//
|
||||
// load the file
|
||||
//
|
||||
length = ri.FS_ReadFile ( ( char * ) name, &buffer.v);
|
||||
if (!buffer.b || length < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(length < 18)
|
||||
{
|
||||
ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name );
|
||||
}
|
||||
|
||||
buf_p = buffer.b;
|
||||
end = buffer.b + length;
|
||||
|
||||
targa_header.id_length = buf_p[0];
|
||||
targa_header.colormap_type = buf_p[1];
|
||||
targa_header.image_type = buf_p[2];
|
||||
|
||||
memcpy(&targa_header.colormap_index, &buf_p[3], 2);
|
||||
memcpy(&targa_header.colormap_length, &buf_p[5], 2);
|
||||
targa_header.colormap_size = buf_p[7];
|
||||
memcpy(&targa_header.x_origin, &buf_p[8], 2);
|
||||
memcpy(&targa_header.y_origin, &buf_p[10], 2);
|
||||
memcpy(&targa_header.width, &buf_p[12], 2);
|
||||
memcpy(&targa_header.height, &buf_p[14], 2);
|
||||
targa_header.pixel_size = buf_p[16];
|
||||
targa_header.attributes = buf_p[17];
|
||||
|
||||
targa_header.colormap_index = LittleShort(targa_header.colormap_index);
|
||||
targa_header.colormap_length = LittleShort(targa_header.colormap_length);
|
||||
targa_header.x_origin = LittleShort(targa_header.x_origin);
|
||||
targa_header.y_origin = LittleShort(targa_header.y_origin);
|
||||
targa_header.width = LittleShort(targa_header.width);
|
||||
targa_header.height = LittleShort(targa_header.height);
|
||||
|
||||
buf_p += 18;
|
||||
|
||||
if (targa_header.image_type!=2
|
||||
&& targa_header.image_type!=10
|
||||
&& targa_header.image_type != 3 )
|
||||
{
|
||||
ri.Error (ERR_DROP, "LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported");
|
||||
}
|
||||
|
||||
if ( targa_header.colormap_type != 0 )
|
||||
{
|
||||
ri.Error( ERR_DROP, "LoadTGA: colormaps not supported" );
|
||||
}
|
||||
|
||||
if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 )
|
||||
{
|
||||
ri.Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)");
|
||||
}
|
||||
|
||||
columns = targa_header.width;
|
||||
rows = targa_header.height;
|
||||
numPixels = columns * rows * 4;
|
||||
|
||||
if(!columns || !rows || numPixels > 0x7FFFFFFF || numPixels / columns / 4 != rows)
|
||||
{
|
||||
ri.Error (ERR_DROP, "LoadTGA: %s has an invalid image size", name);
|
||||
}
|
||||
|
||||
|
||||
targa_rgba = ri.Malloc (numPixels);
|
||||
|
||||
if (targa_header.id_length != 0)
|
||||
{
|
||||
if (buf_p + targa_header.id_length > end)
|
||||
ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name );
|
||||
|
||||
buf_p += targa_header.id_length; // skip TARGA image comment
|
||||
}
|
||||
|
||||
if ( targa_header.image_type==2 || targa_header.image_type == 3 )
|
||||
{
|
||||
if(buf_p + columns*rows*targa_header.pixel_size/8 > end)
|
||||
{
|
||||
ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
|
||||
}
|
||||
|
||||
// Uncompressed RGB or gray scale image
|
||||
for(row=rows-1; row>=0; row--)
|
||||
{
|
||||
pixbuf = targa_rgba + row*columns*4;
|
||||
for(column=0; column<columns; column++)
|
||||
{
|
||||
unsigned char red,green,blue,alphabyte;
|
||||
switch (targa_header.pixel_size)
|
||||
{
|
||||
|
||||
case 8:
|
||||
blue = *buf_p++;
|
||||
green = blue;
|
||||
red = blue;
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
*pixbuf++ = 255;
|
||||
break;
|
||||
|
||||
case 24:
|
||||
blue = *buf_p++;
|
||||
green = *buf_p++;
|
||||
red = *buf_p++;
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
*pixbuf++ = 255;
|
||||
break;
|
||||
case 32:
|
||||
blue = *buf_p++;
|
||||
green = *buf_p++;
|
||||
red = *buf_p++;
|
||||
alphabyte = *buf_p++;
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
*pixbuf++ = alphabyte;
|
||||
break;
|
||||
default:
|
||||
ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (targa_header.image_type==10) { // Runlength encoded RGB images
|
||||
unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
|
||||
|
||||
red = 0;
|
||||
green = 0;
|
||||
blue = 0;
|
||||
alphabyte = 0xff;
|
||||
|
||||
for(row=rows-1; row>=0; row--) {
|
||||
pixbuf = targa_rgba + row*columns*4;
|
||||
for(column=0; column<columns; ) {
|
||||
if(buf_p + 1 > end)
|
||||
ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
|
||||
packetHeader= *buf_p++;
|
||||
packetSize = 1 + (packetHeader & 0x7f);
|
||||
if (packetHeader & 0x80) { // run-length packet
|
||||
if(buf_p + targa_header.pixel_size/8 > end)
|
||||
ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
|
||||
switch (targa_header.pixel_size) {
|
||||
case 24:
|
||||
blue = *buf_p++;
|
||||
green = *buf_p++;
|
||||
red = *buf_p++;
|
||||
alphabyte = 255;
|
||||
break;
|
||||
case 32:
|
||||
blue = *buf_p++;
|
||||
green = *buf_p++;
|
||||
red = *buf_p++;
|
||||
alphabyte = *buf_p++;
|
||||
break;
|
||||
default:
|
||||
ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name );
|
||||
break;
|
||||
}
|
||||
|
||||
for(j=0;j<packetSize;j++) {
|
||||
*pixbuf++=red;
|
||||
*pixbuf++=green;
|
||||
*pixbuf++=blue;
|
||||
*pixbuf++=alphabyte;
|
||||
column++;
|
||||
if (column==columns) { // run spans across rows
|
||||
column=0;
|
||||
if (row>0)
|
||||
row--;
|
||||
else
|
||||
goto breakOut;
|
||||
pixbuf = targa_rgba + row*columns*4;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // non run-length packet
|
||||
|
||||
if(buf_p + targa_header.pixel_size/8*packetSize > end)
|
||||
ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
|
||||
for(j=0;j<packetSize;j++) {
|
||||
switch (targa_header.pixel_size) {
|
||||
case 24:
|
||||
blue = *buf_p++;
|
||||
green = *buf_p++;
|
||||
red = *buf_p++;
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
*pixbuf++ = 255;
|
||||
break;
|
||||
case 32:
|
||||
blue = *buf_p++;
|
||||
green = *buf_p++;
|
||||
red = *buf_p++;
|
||||
alphabyte = *buf_p++;
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
*pixbuf++ = alphabyte;
|
||||
break;
|
||||
default:
|
||||
ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name );
|
||||
break;
|
||||
}
|
||||
column++;
|
||||
if (column==columns) { // pixel packet run spans across rows
|
||||
column=0;
|
||||
if (row>0)
|
||||
row--;
|
||||
else
|
||||
goto breakOut;
|
||||
pixbuf = targa_rgba + row*columns*4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
breakOut:;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// TTimo: this is the chunk of code to ensure a behavior that meets TGA specs
|
||||
// bit 5 set => top-down
|
||||
if (targa_header.attributes & 0x20) {
|
||||
unsigned char *flip = (unsigned char*)malloc (columns*4);
|
||||
unsigned char *src, *dst;
|
||||
|
||||
for (row = 0; row < rows/2; row++) {
|
||||
src = targa_rgba + row * 4 * columns;
|
||||
dst = targa_rgba + (rows - row - 1) * 4 * columns;
|
||||
|
||||
memcpy (flip, src, columns*4);
|
||||
memcpy (src, dst, columns*4);
|
||||
memcpy (dst, flip, columns*4);
|
||||
}
|
||||
free (flip);
|
||||
}
|
||||
#endif
|
||||
// instead we just print a warning
|
||||
if (targa_header.attributes & 0x20) {
|
||||
ri.Printf( PRINT_WARNING, "WARNING: '%s' TGA file header declares top-down image, ignoring\n", name);
|
||||
}
|
||||
|
||||
if (width)
|
||||
*width = columns;
|
||||
if (height)
|
||||
*height = rows;
|
||||
|
||||
*pic = targa_rgba;
|
||||
|
||||
ri.FS_FreeFile (buffer.v);
|
||||
}
|
1366
code/renderergl1/tr_init.c
Normal file
1366
code/renderergl1/tr_init.c
Normal file
File diff suppressed because it is too large
Load diff
394
code/renderergl1/tr_light.c
Normal file
394
code/renderergl1/tr_light.c
Normal file
|
@ -0,0 +1,394 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// tr_light.c
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
#define DLIGHT_AT_RADIUS 16
|
||||
// at the edge of a dlight's influence, this amount of light will be added
|
||||
|
||||
#define DLIGHT_MINIMUM_RADIUS 16
|
||||
// never calculate a range less than this to prevent huge light numbers
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
R_TransformDlights
|
||||
|
||||
Transforms the origins of an array of dlights.
|
||||
Used by both the front end (for DlightBmodel) and
|
||||
the back end (before doing the lighting calculation)
|
||||
===============
|
||||
*/
|
||||
void R_TransformDlights( int count, dlight_t *dl, orientationr_t *or) {
|
||||
int i;
|
||||
vec3_t temp;
|
||||
|
||||
for ( i = 0 ; i < count ; i++, dl++ ) {
|
||||
VectorSubtract( dl->origin, or->origin, temp );
|
||||
dl->transformed[0] = DotProduct( temp, or->axis[0] );
|
||||
dl->transformed[1] = DotProduct( temp, or->axis[1] );
|
||||
dl->transformed[2] = DotProduct( temp, or->axis[2] );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
R_DlightBmodel
|
||||
|
||||
Determine which dynamic lights may effect this bmodel
|
||||
=============
|
||||
*/
|
||||
void R_DlightBmodel( bmodel_t *bmodel ) {
|
||||
int i, j;
|
||||
dlight_t *dl;
|
||||
int mask;
|
||||
msurface_t *surf;
|
||||
|
||||
// transform all the lights
|
||||
R_TransformDlights( tr.refdef.num_dlights, tr.refdef.dlights, &tr.or );
|
||||
|
||||
mask = 0;
|
||||
for ( i=0 ; i<tr.refdef.num_dlights ; i++ ) {
|
||||
dl = &tr.refdef.dlights[i];
|
||||
|
||||
// see if the point is close enough to the bounds to matter
|
||||
for ( j = 0 ; j < 3 ; j++ ) {
|
||||
if ( dl->transformed[j] - bmodel->bounds[1][j] > dl->radius ) {
|
||||
break;
|
||||
}
|
||||
if ( bmodel->bounds[0][j] - dl->transformed[j] > dl->radius ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( j < 3 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// we need to check this light
|
||||
mask |= 1 << i;
|
||||
}
|
||||
|
||||
tr.currentEntity->needDlights = (mask != 0);
|
||||
|
||||
// set the dlight bits in all the surfaces
|
||||
for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) {
|
||||
surf = bmodel->firstSurface + i;
|
||||
|
||||
if ( *surf->data == SF_FACE ) {
|
||||
((srfSurfaceFace_t *)surf->data)->dlightBits = mask;
|
||||
} else if ( *surf->data == SF_GRID ) {
|
||||
((srfGridMesh_t *)surf->data)->dlightBits = mask;
|
||||
} else if ( *surf->data == SF_TRIANGLES ) {
|
||||
((srfTriangles_t *)surf->data)->dlightBits = mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
LIGHT SAMPLING
|
||||
|
||||
=============================================================================
|
||||
*/
|
||||
|
||||
extern cvar_t *r_ambientScale;
|
||||
extern cvar_t *r_directedScale;
|
||||
extern cvar_t *r_debugLight;
|
||||
|
||||
/*
|
||||
=================
|
||||
R_SetupEntityLightingGrid
|
||||
|
||||
=================
|
||||
*/
|
||||
static void R_SetupEntityLightingGrid( trRefEntity_t *ent ) {
|
||||
vec3_t lightOrigin;
|
||||
int pos[3];
|
||||
int i, j;
|
||||
byte *gridData;
|
||||
float frac[3];
|
||||
int gridStep[3];
|
||||
vec3_t direction;
|
||||
float totalFactor;
|
||||
|
||||
if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) {
|
||||
// seperate lightOrigins are needed so an object that is
|
||||
// sinking into the ground can still be lit, and so
|
||||
// multi-part models can be lit identically
|
||||
VectorCopy( ent->e.lightingOrigin, lightOrigin );
|
||||
} else {
|
||||
VectorCopy( ent->e.origin, lightOrigin );
|
||||
}
|
||||
|
||||
VectorSubtract( lightOrigin, tr.world->lightGridOrigin, lightOrigin );
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
float v;
|
||||
|
||||
v = lightOrigin[i]*tr.world->lightGridInverseSize[i];
|
||||
pos[i] = floor( v );
|
||||
frac[i] = v - pos[i];
|
||||
if ( pos[i] < 0 ) {
|
||||
pos[i] = 0;
|
||||
} else if ( pos[i] >= tr.world->lightGridBounds[i] - 1 ) {
|
||||
pos[i] = tr.world->lightGridBounds[i] - 1;
|
||||
}
|
||||
}
|
||||
|
||||
VectorClear( ent->ambientLight );
|
||||
VectorClear( ent->directedLight );
|
||||
VectorClear( direction );
|
||||
|
||||
assert( tr.world->lightGridData ); // NULL with -nolight maps
|
||||
|
||||
// trilerp the light value
|
||||
gridStep[0] = 8;
|
||||
gridStep[1] = 8 * tr.world->lightGridBounds[0];
|
||||
gridStep[2] = 8 * tr.world->lightGridBounds[0] * tr.world->lightGridBounds[1];
|
||||
gridData = tr.world->lightGridData + pos[0] * gridStep[0]
|
||||
+ pos[1] * gridStep[1] + pos[2] * gridStep[2];
|
||||
|
||||
totalFactor = 0;
|
||||
for ( i = 0 ; i < 8 ; i++ ) {
|
||||
float factor;
|
||||
byte *data;
|
||||
int lat, lng;
|
||||
vec3_t normal;
|
||||
#if idppc
|
||||
float d0, d1, d2, d3, d4, d5;
|
||||
#endif
|
||||
factor = 1.0;
|
||||
data = gridData;
|
||||
for ( j = 0 ; j < 3 ; j++ ) {
|
||||
if ( i & (1<<j) ) {
|
||||
factor *= frac[j];
|
||||
data += gridStep[j];
|
||||
} else {
|
||||
factor *= (1.0f - frac[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if ( !(data[0]+data[1]+data[2]) ) {
|
||||
continue; // ignore samples in walls
|
||||
}
|
||||
totalFactor += factor;
|
||||
#if idppc
|
||||
d0 = data[0]; d1 = data[1]; d2 = data[2];
|
||||
d3 = data[3]; d4 = data[4]; d5 = data[5];
|
||||
|
||||
ent->ambientLight[0] += factor * d0;
|
||||
ent->ambientLight[1] += factor * d1;
|
||||
ent->ambientLight[2] += factor * d2;
|
||||
|
||||
ent->directedLight[0] += factor * d3;
|
||||
ent->directedLight[1] += factor * d4;
|
||||
ent->directedLight[2] += factor * d5;
|
||||
#else
|
||||
ent->ambientLight[0] += factor * data[0];
|
||||
ent->ambientLight[1] += factor * data[1];
|
||||
ent->ambientLight[2] += factor * data[2];
|
||||
|
||||
ent->directedLight[0] += factor * data[3];
|
||||
ent->directedLight[1] += factor * data[4];
|
||||
ent->directedLight[2] += factor * data[5];
|
||||
#endif
|
||||
lat = data[7];
|
||||
lng = data[6];
|
||||
lat *= (FUNCTABLE_SIZE/256);
|
||||
lng *= (FUNCTABLE_SIZE/256);
|
||||
|
||||
// decode X as cos( lat ) * sin( long )
|
||||
// decode Y as sin( lat ) * sin( long )
|
||||
// decode Z as cos( long )
|
||||
|
||||
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];
|
||||
|
||||
VectorMA( direction, factor, normal, direction );
|
||||
}
|
||||
|
||||
if ( totalFactor > 0 && totalFactor < 0.99 ) {
|
||||
totalFactor = 1.0f / totalFactor;
|
||||
VectorScale( ent->ambientLight, totalFactor, ent->ambientLight );
|
||||
VectorScale( ent->directedLight, totalFactor, ent->directedLight );
|
||||
}
|
||||
|
||||
VectorScale( ent->ambientLight, r_ambientScale->value, ent->ambientLight );
|
||||
VectorScale( ent->directedLight, r_directedScale->value, ent->directedLight );
|
||||
|
||||
VectorNormalize2( direction, ent->lightDir );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
LogLight
|
||||
===============
|
||||
*/
|
||||
static void LogLight( trRefEntity_t *ent ) {
|
||||
int max1, max2;
|
||||
|
||||
if ( !(ent->e.renderfx & RF_FIRST_PERSON ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
max1 = ent->ambientLight[0];
|
||||
if ( ent->ambientLight[1] > max1 ) {
|
||||
max1 = ent->ambientLight[1];
|
||||
} else if ( ent->ambientLight[2] > max1 ) {
|
||||
max1 = ent->ambientLight[2];
|
||||
}
|
||||
|
||||
max2 = ent->directedLight[0];
|
||||
if ( ent->directedLight[1] > max2 ) {
|
||||
max2 = ent->directedLight[1];
|
||||
} else if ( ent->directedLight[2] > max2 ) {
|
||||
max2 = ent->directedLight[2];
|
||||
}
|
||||
|
||||
ri.Printf( PRINT_ALL, "amb:%i dir:%i\n", max1, max2 );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_SetupEntityLighting
|
||||
|
||||
Calculates all the lighting values that will be used
|
||||
by the Calc_* functions
|
||||
=================
|
||||
*/
|
||||
void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ) {
|
||||
int i;
|
||||
dlight_t *dl;
|
||||
float power;
|
||||
vec3_t dir;
|
||||
float d;
|
||||
vec3_t lightDir;
|
||||
vec3_t lightOrigin;
|
||||
|
||||
// lighting calculations
|
||||
if ( ent->lightingCalculated ) {
|
||||
return;
|
||||
}
|
||||
ent->lightingCalculated = qtrue;
|
||||
|
||||
//
|
||||
// trace a sample point down to find ambient light
|
||||
//
|
||||
if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) {
|
||||
// seperate lightOrigins are needed so an object that is
|
||||
// sinking into the ground can still be lit, and so
|
||||
// multi-part models can be lit identically
|
||||
VectorCopy( ent->e.lightingOrigin, lightOrigin );
|
||||
} else {
|
||||
VectorCopy( ent->e.origin, lightOrigin );
|
||||
}
|
||||
|
||||
// if NOWORLDMODEL, only use dynamic lights (menu system, etc)
|
||||
if ( !(refdef->rdflags & RDF_NOWORLDMODEL )
|
||||
&& tr.world->lightGridData ) {
|
||||
R_SetupEntityLightingGrid( ent );
|
||||
} else {
|
||||
ent->ambientLight[0] = ent->ambientLight[1] =
|
||||
ent->ambientLight[2] = tr.identityLight * 150;
|
||||
ent->directedLight[0] = ent->directedLight[1] =
|
||||
ent->directedLight[2] = tr.identityLight * 150;
|
||||
VectorCopy( tr.sunDirection, ent->lightDir );
|
||||
}
|
||||
|
||||
// bonus items and view weapons have a fixed minimum add
|
||||
if ( 1 /* ent->e.renderfx & RF_MINLIGHT */ ) {
|
||||
// give everything a minimum light add
|
||||
ent->ambientLight[0] += tr.identityLight * 32;
|
||||
ent->ambientLight[1] += tr.identityLight * 32;
|
||||
ent->ambientLight[2] += tr.identityLight * 32;
|
||||
}
|
||||
|
||||
//
|
||||
// modify the light by dynamic lights
|
||||
//
|
||||
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 );
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
// clamp ambient
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
if ( ent->ambientLight[i] > tr.identityLightByte ) {
|
||||
ent->ambientLight[i] = tr.identityLightByte;
|
||||
}
|
||||
}
|
||||
|
||||
if ( r_debugLight->integer ) {
|
||||
LogLight( ent );
|
||||
}
|
||||
|
||||
// save out the byte packet version
|
||||
((byte *)&ent->ambientLightInt)[0] = ri.ftol(ent->ambientLight[0]);
|
||||
((byte *)&ent->ambientLightInt)[1] = ri.ftol(ent->ambientLight[1]);
|
||||
((byte *)&ent->ambientLightInt)[2] = ri.ftol(ent->ambientLight[2]);
|
||||
((byte *)&ent->ambientLightInt)[3] = 0xff;
|
||||
|
||||
// transform the direction to local space
|
||||
VectorNormalize( lightDir );
|
||||
ent->lightDir[0] = DotProduct( lightDir, ent->e.axis[0] );
|
||||
ent->lightDir[1] = DotProduct( lightDir, ent->e.axis[1] );
|
||||
ent->lightDir[2] = DotProduct( lightDir, ent->e.axis[2] );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_LightForPoint
|
||||
=================
|
||||
*/
|
||||
int R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir )
|
||||
{
|
||||
trRefEntity_t ent;
|
||||
|
||||
if ( tr.world->lightGridData == NULL )
|
||||
return qfalse;
|
||||
|
||||
Com_Memset(&ent, 0, sizeof(ent));
|
||||
VectorCopy( point, ent.e.origin );
|
||||
R_SetupEntityLightingGrid( &ent );
|
||||
VectorCopy(ent.ambientLight, ambientLight);
|
||||
VectorCopy(ent.directedLight, directedLight);
|
||||
VectorCopy(ent.lightDir, lightDir);
|
||||
|
||||
return qtrue;
|
||||
}
|
1728
code/renderergl1/tr_local.h
Normal file
1728
code/renderergl1/tr_local.h
Normal file
File diff suppressed because it is too large
Load diff
1398
code/renderergl1/tr_main.c
Normal file
1398
code/renderergl1/tr_main.c
Normal file
File diff suppressed because it is too large
Load diff
458
code/renderergl1/tr_marks.c
Normal file
458
code/renderergl1/tr_marks.c
Normal file
|
@ -0,0 +1,458 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// tr_marks.c -- polygon projection on the world polygons
|
||||
|
||||
#include "tr_local.h"
|
||||
//#include "assert.h"
|
||||
|
||||
#define MAX_VERTS_ON_POLY 64
|
||||
|
||||
#define MARKER_OFFSET 0 // 1
|
||||
|
||||
/*
|
||||
=============
|
||||
R_ChopPolyBehindPlane
|
||||
|
||||
Out must have space for two more vertexes than in
|
||||
=============
|
||||
*/
|
||||
#define SIDE_FRONT 0
|
||||
#define SIDE_BACK 1
|
||||
#define SIDE_ON 2
|
||||
static void R_ChopPolyBehindPlane( int numInPoints, vec3_t inPoints[MAX_VERTS_ON_POLY],
|
||||
int *numOutPoints, vec3_t outPoints[MAX_VERTS_ON_POLY],
|
||||
vec3_t normal, vec_t dist, vec_t epsilon) {
|
||||
float dists[MAX_VERTS_ON_POLY+4];
|
||||
int sides[MAX_VERTS_ON_POLY+4];
|
||||
int counts[3];
|
||||
float dot;
|
||||
int i, j;
|
||||
float *p1, *p2, *clip;
|
||||
float d;
|
||||
|
||||
// don't clip if it might overflow
|
||||
if ( numInPoints >= MAX_VERTS_ON_POLY - 2 ) {
|
||||
*numOutPoints = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
counts[0] = counts[1] = counts[2] = 0;
|
||||
|
||||
// determine sides for each point
|
||||
for ( i = 0 ; i < numInPoints ; i++ ) {
|
||||
dot = DotProduct( inPoints[i], normal );
|
||||
dot -= dist;
|
||||
dists[i] = dot;
|
||||
if ( dot > epsilon ) {
|
||||
sides[i] = SIDE_FRONT;
|
||||
} else if ( dot < -epsilon ) {
|
||||
sides[i] = SIDE_BACK;
|
||||
} else {
|
||||
sides[i] = SIDE_ON;
|
||||
}
|
||||
counts[sides[i]]++;
|
||||
}
|
||||
sides[i] = sides[0];
|
||||
dists[i] = dists[0];
|
||||
|
||||
*numOutPoints = 0;
|
||||
|
||||
if ( !counts[0] ) {
|
||||
return;
|
||||
}
|
||||
if ( !counts[1] ) {
|
||||
*numOutPoints = numInPoints;
|
||||
Com_Memcpy( outPoints, inPoints, numInPoints * sizeof(vec3_t) );
|
||||
return;
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < numInPoints ; i++ ) {
|
||||
p1 = inPoints[i];
|
||||
clip = outPoints[ *numOutPoints ];
|
||||
|
||||
if ( sides[i] == SIDE_ON ) {
|
||||
VectorCopy( p1, clip );
|
||||
(*numOutPoints)++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( sides[i] == SIDE_FRONT ) {
|
||||
VectorCopy( p1, clip );
|
||||
(*numOutPoints)++;
|
||||
clip = outPoints[ *numOutPoints ];
|
||||
}
|
||||
|
||||
if ( sides[i+1] == SIDE_ON || sides[i+1] == sides[i] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// generate a split point
|
||||
p2 = inPoints[ (i+1) % numInPoints ];
|
||||
|
||||
d = dists[i] - dists[i+1];
|
||||
if ( d == 0 ) {
|
||||
dot = 0;
|
||||
} else {
|
||||
dot = dists[i] / d;
|
||||
}
|
||||
|
||||
// clip xyz
|
||||
|
||||
for (j=0 ; j<3 ; j++) {
|
||||
clip[j] = p1[j] + dot * ( p2[j] - p1[j] );
|
||||
}
|
||||
|
||||
(*numOutPoints)++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_BoxSurfaces_r
|
||||
|
||||
=================
|
||||
*/
|
||||
void R_BoxSurfaces_r(mnode_t *node, vec3_t mins, vec3_t maxs, surfaceType_t **list, int listsize, int *listlength, vec3_t dir) {
|
||||
|
||||
int s, c;
|
||||
msurface_t *surf, **mark;
|
||||
|
||||
// do the tail recursion in a loop
|
||||
while ( node->contents == -1 ) {
|
||||
s = BoxOnPlaneSide( mins, maxs, node->plane );
|
||||
if (s == 1) {
|
||||
node = node->children[0];
|
||||
} else if (s == 2) {
|
||||
node = node->children[1];
|
||||
} else {
|
||||
R_BoxSurfaces_r(node->children[0], mins, maxs, list, listsize, listlength, dir);
|
||||
node = node->children[1];
|
||||
}
|
||||
}
|
||||
|
||||
// add the individual surfaces
|
||||
mark = node->firstmarksurface;
|
||||
c = node->nummarksurfaces;
|
||||
while (c--) {
|
||||
//
|
||||
if (*listlength >= listsize) break;
|
||||
//
|
||||
surf = *mark;
|
||||
// check if the surface has NOIMPACT or NOMARKS set
|
||||
if ( ( surf->shader->surfaceFlags & ( SURF_NOIMPACT | SURF_NOMARKS ) )
|
||||
|| ( surf->shader->contentFlags & CONTENTS_FOG ) ) {
|
||||
surf->viewCount = tr.viewCount;
|
||||
}
|
||||
// extra check for surfaces to avoid list overflows
|
||||
else if (*(surf->data) == SF_FACE) {
|
||||
// the face plane should go through the box
|
||||
s = BoxOnPlaneSide( mins, maxs, &(( srfSurfaceFace_t * ) surf->data)->plane );
|
||||
if (s == 1 || s == 2) {
|
||||
surf->viewCount = tr.viewCount;
|
||||
} else if (DotProduct((( srfSurfaceFace_t * ) surf->data)->plane.normal, dir) > -0.5) {
|
||||
// don't add faces that make sharp angles with the projection direction
|
||||
surf->viewCount = tr.viewCount;
|
||||
}
|
||||
}
|
||||
else if (*(surfaceType_t *) (surf->data) != SF_GRID &&
|
||||
*(surfaceType_t *) (surf->data) != SF_TRIANGLES)
|
||||
surf->viewCount = tr.viewCount;
|
||||
// check the viewCount because the surface may have
|
||||
// already been added if it spans multiple leafs
|
||||
if (surf->viewCount != tr.viewCount) {
|
||||
surf->viewCount = tr.viewCount;
|
||||
list[*listlength] = (surfaceType_t *) surf->data;
|
||||
(*listlength)++;
|
||||
}
|
||||
mark++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_AddMarkFragments
|
||||
|
||||
=================
|
||||
*/
|
||||
void R_AddMarkFragments(int numClipPoints, vec3_t clipPoints[2][MAX_VERTS_ON_POLY],
|
||||
int numPlanes, vec3_t *normals, float *dists,
|
||||
int maxPoints, vec3_t pointBuffer,
|
||||
int maxFragments, markFragment_t *fragmentBuffer,
|
||||
int *returnedPoints, int *returnedFragments,
|
||||
vec3_t mins, vec3_t maxs) {
|
||||
int pingPong, i;
|
||||
markFragment_t *mf;
|
||||
|
||||
// chop the surface by all the bounding planes of the to be projected polygon
|
||||
pingPong = 0;
|
||||
|
||||
for ( i = 0 ; i < numPlanes ; i++ ) {
|
||||
|
||||
R_ChopPolyBehindPlane( numClipPoints, clipPoints[pingPong],
|
||||
&numClipPoints, clipPoints[!pingPong],
|
||||
normals[i], dists[i], 0.5 );
|
||||
pingPong ^= 1;
|
||||
if ( numClipPoints == 0 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// completely clipped away?
|
||||
if ( numClipPoints == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add this fragment to the returned list
|
||||
if ( numClipPoints + (*returnedPoints) > maxPoints ) {
|
||||
return; // not enough space for this polygon
|
||||
}
|
||||
/*
|
||||
// all the clip points should be within the bounding box
|
||||
for ( i = 0 ; i < numClipPoints ; i++ ) {
|
||||
int j;
|
||||
for ( j = 0 ; j < 3 ; j++ ) {
|
||||
if (clipPoints[pingPong][i][j] < mins[j] - 0.5) break;
|
||||
if (clipPoints[pingPong][i][j] > maxs[j] + 0.5) break;
|
||||
}
|
||||
if (j < 3) break;
|
||||
}
|
||||
if (i < numClipPoints) return;
|
||||
*/
|
||||
|
||||
mf = fragmentBuffer + (*returnedFragments);
|
||||
mf->firstPoint = (*returnedPoints);
|
||||
mf->numPoints = numClipPoints;
|
||||
Com_Memcpy( pointBuffer + (*returnedPoints) * 3, clipPoints[pingPong], numClipPoints * sizeof(vec3_t) );
|
||||
|
||||
(*returnedPoints) += numClipPoints;
|
||||
(*returnedFragments)++;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_MarkFragments
|
||||
|
||||
=================
|
||||
*/
|
||||
int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection,
|
||||
int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) {
|
||||
int numsurfaces, numPlanes;
|
||||
int i, j, k, m, n;
|
||||
surfaceType_t *surfaces[64];
|
||||
vec3_t mins, maxs;
|
||||
int returnedFragments;
|
||||
int returnedPoints;
|
||||
vec3_t normals[MAX_VERTS_ON_POLY+2];
|
||||
float dists[MAX_VERTS_ON_POLY+2];
|
||||
vec3_t clipPoints[2][MAX_VERTS_ON_POLY];
|
||||
int numClipPoints;
|
||||
float *v;
|
||||
srfGridMesh_t *cv;
|
||||
drawVert_t *dv;
|
||||
vec3_t normal;
|
||||
vec3_t projectionDir;
|
||||
vec3_t v1, v2;
|
||||
int *indexes;
|
||||
|
||||
if (numPoints <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//increment view count for double check prevention
|
||||
tr.viewCount++;
|
||||
|
||||
//
|
||||
VectorNormalize2( projection, projectionDir );
|
||||
// find all the brushes that are to be considered
|
||||
ClearBounds( mins, maxs );
|
||||
for ( i = 0 ; i < numPoints ; i++ ) {
|
||||
vec3_t temp;
|
||||
|
||||
AddPointToBounds( points[i], mins, maxs );
|
||||
VectorAdd( points[i], projection, temp );
|
||||
AddPointToBounds( temp, mins, maxs );
|
||||
// make sure we get all the leafs (also the one(s) in front of the hit surface)
|
||||
VectorMA( points[i], -20, projectionDir, temp );
|
||||
AddPointToBounds( temp, mins, maxs );
|
||||
}
|
||||
|
||||
if (numPoints > MAX_VERTS_ON_POLY) numPoints = MAX_VERTS_ON_POLY;
|
||||
// create the bounding planes for the to be projected polygon
|
||||
for ( i = 0 ; i < numPoints ; i++ ) {
|
||||
VectorSubtract(points[(i+1)%numPoints], points[i], v1);
|
||||
VectorAdd(points[i], projection, v2);
|
||||
VectorSubtract(points[i], v2, v2);
|
||||
CrossProduct(v1, v2, normals[i]);
|
||||
VectorNormalizeFast(normals[i]);
|
||||
dists[i] = DotProduct(normals[i], points[i]);
|
||||
}
|
||||
// add near and far clipping planes for projection
|
||||
VectorCopy(projectionDir, normals[numPoints]);
|
||||
dists[numPoints] = DotProduct(normals[numPoints], points[0]) - 32;
|
||||
VectorCopy(projectionDir, normals[numPoints+1]);
|
||||
VectorInverse(normals[numPoints+1]);
|
||||
dists[numPoints+1] = DotProduct(normals[numPoints+1], points[0]) - 20;
|
||||
numPlanes = numPoints + 2;
|
||||
|
||||
numsurfaces = 0;
|
||||
R_BoxSurfaces_r(tr.world->nodes, mins, maxs, surfaces, 64, &numsurfaces, projectionDir);
|
||||
//assert(numsurfaces <= 64);
|
||||
//assert(numsurfaces != 64);
|
||||
|
||||
returnedPoints = 0;
|
||||
returnedFragments = 0;
|
||||
|
||||
for ( i = 0 ; i < numsurfaces ; i++ ) {
|
||||
|
||||
if (*surfaces[i] == SF_GRID) {
|
||||
|
||||
cv = (srfGridMesh_t *) surfaces[i];
|
||||
for ( m = 0 ; m < cv->height - 1 ; m++ ) {
|
||||
for ( n = 0 ; n < cv->width - 1 ; n++ ) {
|
||||
// We triangulate the grid and chop all triangles within
|
||||
// the bounding planes of the to be projected polygon.
|
||||
// LOD is not taken into account, not such a big deal though.
|
||||
//
|
||||
// It's probably much nicer to chop the grid itself and deal
|
||||
// with this grid as a normal SF_GRID surface so LOD will
|
||||
// be applied. However the LOD of that chopped grid must
|
||||
// be synced with the LOD of the original curve.
|
||||
// One way to do this; the chopped grid shares vertices with
|
||||
// the original curve. When LOD is applied to the original
|
||||
// curve the unused vertices are flagged. Now the chopped curve
|
||||
// should skip the flagged vertices. This still leaves the
|
||||
// problems with the vertices at the chopped grid edges.
|
||||
//
|
||||
// To avoid issues when LOD applied to "hollow curves" (like
|
||||
// the ones around many jump pads) we now just add a 2 unit
|
||||
// offset to the triangle vertices.
|
||||
// The offset is added in the vertex normal vector direction
|
||||
// so all triangles will still fit together.
|
||||
// The 2 unit offset should avoid pretty much all LOD problems.
|
||||
|
||||
numClipPoints = 3;
|
||||
|
||||
dv = cv->verts + m * cv->width + n;
|
||||
|
||||
VectorCopy(dv[0].xyz, clipPoints[0][0]);
|
||||
VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[0].normal, clipPoints[0][0]);
|
||||
VectorCopy(dv[cv->width].xyz, clipPoints[0][1]);
|
||||
VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]);
|
||||
VectorCopy(dv[1].xyz, clipPoints[0][2]);
|
||||
VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[1].normal, clipPoints[0][2]);
|
||||
// check the normal of this triangle
|
||||
VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1);
|
||||
VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2);
|
||||
CrossProduct(v1, v2, normal);
|
||||
VectorNormalizeFast(normal);
|
||||
if (DotProduct(normal, projectionDir) < -0.1) {
|
||||
// add the fragments of this triangle
|
||||
R_AddMarkFragments(numClipPoints, clipPoints,
|
||||
numPlanes, normals, dists,
|
||||
maxPoints, pointBuffer,
|
||||
maxFragments, fragmentBuffer,
|
||||
&returnedPoints, &returnedFragments, mins, maxs);
|
||||
|
||||
if ( returnedFragments == maxFragments ) {
|
||||
return returnedFragments; // not enough space for more fragments
|
||||
}
|
||||
}
|
||||
|
||||
VectorCopy(dv[1].xyz, clipPoints[0][0]);
|
||||
VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[1].normal, clipPoints[0][0]);
|
||||
VectorCopy(dv[cv->width].xyz, clipPoints[0][1]);
|
||||
VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]);
|
||||
VectorCopy(dv[cv->width+1].xyz, clipPoints[0][2]);
|
||||
VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[cv->width+1].normal, clipPoints[0][2]);
|
||||
// check the normal of this triangle
|
||||
VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1);
|
||||
VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2);
|
||||
CrossProduct(v1, v2, normal);
|
||||
VectorNormalizeFast(normal);
|
||||
if (DotProduct(normal, projectionDir) < -0.05) {
|
||||
// add the fragments of this triangle
|
||||
R_AddMarkFragments(numClipPoints, clipPoints,
|
||||
numPlanes, normals, dists,
|
||||
maxPoints, pointBuffer,
|
||||
maxFragments, fragmentBuffer,
|
||||
&returnedPoints, &returnedFragments, mins, maxs);
|
||||
|
||||
if ( returnedFragments == maxFragments ) {
|
||||
return returnedFragments; // not enough space for more fragments
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (*surfaces[i] == SF_FACE) {
|
||||
|
||||
srfSurfaceFace_t *surf = ( srfSurfaceFace_t * ) surfaces[i];
|
||||
|
||||
// check the normal of this face
|
||||
if (DotProduct(surf->plane.normal, projectionDir) > -0.5) {
|
||||
continue;
|
||||
}
|
||||
|
||||
indexes = (int *)( (byte *)surf + surf->ofsIndices );
|
||||
for ( k = 0 ; k < surf->numIndices ; k += 3 ) {
|
||||
for ( j = 0 ; j < 3 ; j++ ) {
|
||||
v = surf->points[0] + VERTEXSIZE * indexes[k+j];;
|
||||
VectorMA( v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] );
|
||||
}
|
||||
|
||||
// add the fragments of this face
|
||||
R_AddMarkFragments( 3 , clipPoints,
|
||||
numPlanes, normals, dists,
|
||||
maxPoints, pointBuffer,
|
||||
maxFragments, fragmentBuffer,
|
||||
&returnedPoints, &returnedFragments, mins, maxs);
|
||||
if ( returnedFragments == maxFragments ) {
|
||||
return returnedFragments; // not enough space for more fragments
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(*surfaces[i] == SF_TRIANGLES && r_marksOnTriangleMeshes->integer) {
|
||||
|
||||
srfTriangles_t *surf = (srfTriangles_t *) surfaces[i];
|
||||
|
||||
for (k = 0; k < surf->numIndexes; k += 3)
|
||||
{
|
||||
for(j = 0; j < 3; j++)
|
||||
{
|
||||
v = surf->verts[surf->indexes[k + j]].xyz;
|
||||
VectorMA(v, MARKER_OFFSET, surf->verts[surf->indexes[k + j]].normal, clipPoints[0][j]);
|
||||
}
|
||||
|
||||
// add the fragments of this face
|
||||
R_AddMarkFragments(3, clipPoints,
|
||||
numPlanes, normals, dists,
|
||||
maxPoints, pointBuffer,
|
||||
maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs);
|
||||
if(returnedFragments == maxFragments)
|
||||
{
|
||||
return returnedFragments; // not enough space for more fragments
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnedFragments;
|
||||
}
|
||||
|
416
code/renderergl1/tr_mesh.c
Normal file
416
code/renderergl1/tr_mesh.c
Normal file
|
@ -0,0 +1,416 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// tr_mesh.c: triangle model functions
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
static float ProjectRadius( float r, vec3_t location )
|
||||
{
|
||||
float pr;
|
||||
float dist;
|
||||
float c;
|
||||
vec3_t p;
|
||||
float projected[4];
|
||||
|
||||
c = DotProduct( tr.viewParms.or.axis[0], tr.viewParms.or.origin );
|
||||
dist = DotProduct( tr.viewParms.or.axis[0], location ) - c;
|
||||
|
||||
if ( dist <= 0 )
|
||||
return 0;
|
||||
|
||||
p[0] = 0;
|
||||
p[1] = fabs( r );
|
||||
p[2] = -dist;
|
||||
|
||||
projected[0] = p[0] * tr.viewParms.projectionMatrix[0] +
|
||||
p[1] * tr.viewParms.projectionMatrix[4] +
|
||||
p[2] * tr.viewParms.projectionMatrix[8] +
|
||||
tr.viewParms.projectionMatrix[12];
|
||||
|
||||
projected[1] = p[0] * tr.viewParms.projectionMatrix[1] +
|
||||
p[1] * tr.viewParms.projectionMatrix[5] +
|
||||
p[2] * tr.viewParms.projectionMatrix[9] +
|
||||
tr.viewParms.projectionMatrix[13];
|
||||
|
||||
projected[2] = p[0] * tr.viewParms.projectionMatrix[2] +
|
||||
p[1] * tr.viewParms.projectionMatrix[6] +
|
||||
p[2] * tr.viewParms.projectionMatrix[10] +
|
||||
tr.viewParms.projectionMatrix[14];
|
||||
|
||||
projected[3] = p[0] * tr.viewParms.projectionMatrix[3] +
|
||||
p[1] * tr.viewParms.projectionMatrix[7] +
|
||||
p[2] * tr.viewParms.projectionMatrix[11] +
|
||||
tr.viewParms.projectionMatrix[15];
|
||||
|
||||
|
||||
pr = projected[1] / projected[3];
|
||||
|
||||
if ( pr > 1.0f )
|
||||
pr = 1.0f;
|
||||
|
||||
return pr;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
R_CullModel
|
||||
=============
|
||||
*/
|
||||
static int R_CullModel( md3Header_t *header, trRefEntity_t *ent ) {
|
||||
vec3_t bounds[2];
|
||||
md3Frame_t *oldFrame, *newFrame;
|
||||
int i;
|
||||
|
||||
// compute frame pointers
|
||||
newFrame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.frame;
|
||||
oldFrame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.oldframe;
|
||||
|
||||
// cull bounding sphere ONLY if this is not an upscaled entity
|
||||
if ( !ent->e.nonNormalizedAxes )
|
||||
{
|
||||
if ( ent->e.frame == ent->e.oldframe )
|
||||
{
|
||||
switch ( R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ) )
|
||||
{
|
||||
case CULL_OUT:
|
||||
tr.pc.c_sphere_cull_md3_out++;
|
||||
return CULL_OUT;
|
||||
|
||||
case CULL_IN:
|
||||
tr.pc.c_sphere_cull_md3_in++;
|
||||
return CULL_IN;
|
||||
|
||||
case CULL_CLIP:
|
||||
tr.pc.c_sphere_cull_md3_clip++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int sphereCull, sphereCullB;
|
||||
|
||||
sphereCull = R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius );
|
||||
if ( newFrame == oldFrame ) {
|
||||
sphereCullB = sphereCull;
|
||||
} else {
|
||||
sphereCullB = R_CullLocalPointAndRadius( oldFrame->localOrigin, oldFrame->radius );
|
||||
}
|
||||
|
||||
if ( sphereCull == sphereCullB )
|
||||
{
|
||||
if ( sphereCull == CULL_OUT )
|
||||
{
|
||||
tr.pc.c_sphere_cull_md3_out++;
|
||||
return CULL_OUT;
|
||||
}
|
||||
else if ( sphereCull == CULL_IN )
|
||||
{
|
||||
tr.pc.c_sphere_cull_md3_in++;
|
||||
return CULL_IN;
|
||||
}
|
||||
else
|
||||
{
|
||||
tr.pc.c_sphere_cull_md3_clip++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// calculate a bounding box in the current coordinate system
|
||||
for (i = 0 ; i < 3 ; i++) {
|
||||
bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i];
|
||||
bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i];
|
||||
}
|
||||
|
||||
switch ( R_CullLocalBox( bounds ) )
|
||||
{
|
||||
case CULL_IN:
|
||||
tr.pc.c_box_cull_md3_in++;
|
||||
return CULL_IN;
|
||||
case CULL_CLIP:
|
||||
tr.pc.c_box_cull_md3_clip++;
|
||||
return CULL_CLIP;
|
||||
case CULL_OUT:
|
||||
default:
|
||||
tr.pc.c_box_cull_md3_out++;
|
||||
return CULL_OUT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
R_ComputeLOD
|
||||
|
||||
=================
|
||||
*/
|
||||
int R_ComputeLOD( trRefEntity_t *ent ) {
|
||||
float radius;
|
||||
float flod, lodscale;
|
||||
float projectedRadius;
|
||||
md3Frame_t *frame;
|
||||
#ifdef RAVENMD4
|
||||
mdrHeader_t *mdr;
|
||||
mdrFrame_t *mdrframe;
|
||||
#endif
|
||||
int lod;
|
||||
|
||||
if ( tr.currentModel->numLods < 2 )
|
||||
{
|
||||
// model has only 1 LOD level, skip computations and bias
|
||||
lod = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// multiple LODs exist, so compute projected bounding sphere
|
||||
// and use that as a criteria for selecting LOD
|
||||
|
||||
#ifdef RAVENMD4
|
||||
if(tr.currentModel->type == MOD_MDR)
|
||||
{
|
||||
int frameSize;
|
||||
mdr = (mdrHeader_t *) tr.currentModel->modelData;
|
||||
frameSize = (size_t) (&((mdrFrame_t *)0)->bones[mdr->numBones]);
|
||||
|
||||
mdrframe = (mdrFrame_t *) ((byte *) mdr + mdr->ofsFrames + frameSize * ent->e.frame);
|
||||
|
||||
radius = RadiusFromBounds(mdrframe->bounds[0], mdrframe->bounds[1]);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
frame = ( md3Frame_t * ) ( ( ( unsigned char * ) tr.currentModel->md3[0] ) + tr.currentModel->md3[0]->ofsFrames );
|
||||
|
||||
frame += ent->e.frame;
|
||||
|
||||
radius = RadiusFromBounds( frame->bounds[0], frame->bounds[1] );
|
||||
}
|
||||
|
||||
if ( ( projectedRadius = ProjectRadius( radius, ent->e.origin ) ) != 0 )
|
||||
{
|
||||
lodscale = r_lodscale->value;
|
||||
if (lodscale > 20) lodscale = 20;
|
||||
flod = 1.0f - projectedRadius * lodscale;
|
||||
}
|
||||
else
|
||||
{
|
||||
// object intersects near view plane, e.g. view weapon
|
||||
flod = 0;
|
||||
}
|
||||
|
||||
flod *= tr.currentModel->numLods;
|
||||
lod = ri.ftol(flod);
|
||||
|
||||
if ( lod < 0 )
|
||||
{
|
||||
lod = 0;
|
||||
}
|
||||
else if ( lod >= tr.currentModel->numLods )
|
||||
{
|
||||
lod = tr.currentModel->numLods - 1;
|
||||
}
|
||||
}
|
||||
|
||||
lod += r_lodbias->integer;
|
||||
|
||||
if ( lod >= tr.currentModel->numLods )
|
||||
lod = tr.currentModel->numLods - 1;
|
||||
if ( lod < 0 )
|
||||
lod = 0;
|
||||
|
||||
return lod;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_ComputeFogNum
|
||||
|
||||
=================
|
||||
*/
|
||||
int R_ComputeFogNum( md3Header_t *header, trRefEntity_t *ent ) {
|
||||
int i, j;
|
||||
fog_t *fog;
|
||||
md3Frame_t *md3Frame;
|
||||
vec3_t localOrigin;
|
||||
|
||||
if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// FIXME: non-normalized axis issues
|
||||
md3Frame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.frame;
|
||||
VectorAdd( ent->e.origin, md3Frame->localOrigin, localOrigin );
|
||||
for ( i = 1 ; i < tr.world->numfogs ; i++ ) {
|
||||
fog = &tr.world->fogs[i];
|
||||
for ( j = 0 ; j < 3 ; j++ ) {
|
||||
if ( localOrigin[j] - md3Frame->radius >= fog->bounds[1][j] ) {
|
||||
break;
|
||||
}
|
||||
if ( localOrigin[j] + md3Frame->radius <= fog->bounds[0][j] ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( j == 3 ) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_AddMD3Surfaces
|
||||
|
||||
=================
|
||||
*/
|
||||
void R_AddMD3Surfaces( trRefEntity_t *ent ) {
|
||||
int i;
|
||||
md3Header_t *header = NULL;
|
||||
md3Surface_t *surface = NULL;
|
||||
md3Shader_t *md3Shader = NULL;
|
||||
shader_t *shader = NULL;
|
||||
int cull;
|
||||
int lod;
|
||||
int fogNum;
|
||||
qboolean personalModel;
|
||||
|
||||
// don't add third_person objects if not in a portal
|
||||
personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal;
|
||||
|
||||
if ( ent->e.renderfx & RF_WRAP_FRAMES ) {
|
||||
ent->e.frame %= tr.currentModel->md3[0]->numFrames;
|
||||
ent->e.oldframe %= tr.currentModel->md3[0]->numFrames;
|
||||
}
|
||||
|
||||
//
|
||||
// Validate the frames so there is no chance of a crash.
|
||||
// This will write directly into the entity structure, so
|
||||
// when the surfaces are rendered, they don't need to be
|
||||
// range checked again.
|
||||
//
|
||||
if ( (ent->e.frame >= tr.currentModel->md3[0]->numFrames)
|
||||
|| (ent->e.frame < 0)
|
||||
|| (ent->e.oldframe >= tr.currentModel->md3[0]->numFrames)
|
||||
|| (ent->e.oldframe < 0) ) {
|
||||
ri.Printf( PRINT_DEVELOPER, "R_AddMD3Surfaces: no such frame %d to %d for '%s'\n",
|
||||
ent->e.oldframe, ent->e.frame,
|
||||
tr.currentModel->name );
|
||||
ent->e.frame = 0;
|
||||
ent->e.oldframe = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// compute LOD
|
||||
//
|
||||
lod = R_ComputeLOD( ent );
|
||||
|
||||
header = tr.currentModel->md3[lod];
|
||||
|
||||
//
|
||||
// cull the entire model if merged bounding box of both frames
|
||||
// is outside the view frustum.
|
||||
//
|
||||
cull = R_CullModel ( header, ent );
|
||||
if ( cull == CULL_OUT ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// set up lighting now that we know we aren't culled
|
||||
//
|
||||
if ( !personalModel || r_shadows->integer > 1 ) {
|
||||
R_SetupEntityLighting( &tr.refdef, ent );
|
||||
}
|
||||
|
||||
//
|
||||
// see if we are in a fog volume
|
||||
//
|
||||
fogNum = R_ComputeFogNum( header, ent );
|
||||
|
||||
//
|
||||
// draw all surfaces
|
||||
//
|
||||
surface = (md3Surface_t *)( (byte *)header + header->ofsSurfaces );
|
||||
for ( i = 0 ; i < header->numSurfaces ; i++ ) {
|
||||
|
||||
if ( ent->e.customShader ) {
|
||||
shader = R_GetShaderByHandle( ent->e.customShader );
|
||||
} else if ( ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins ) {
|
||||
skin_t *skin;
|
||||
int j;
|
||||
|
||||
skin = R_GetSkinByHandle( ent->e.customSkin );
|
||||
|
||||
// match the surface name to something in the skin file
|
||||
shader = tr.defaultShader;
|
||||
for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
|
||||
// the names have both been lowercased
|
||||
if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) {
|
||||
shader = skin->surfaces[j]->shader;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (shader == tr.defaultShader) {
|
||||
ri.Printf( PRINT_DEVELOPER, "WARNING: no shader for surface %s in skin %s\n", surface->name, skin->name);
|
||||
}
|
||||
else if (shader->defaultShader) {
|
||||
ri.Printf( PRINT_DEVELOPER, "WARNING: shader %s in skin %s not found\n", shader->name, skin->name);
|
||||
}
|
||||
} else if ( surface->numShaders <= 0 ) {
|
||||
shader = tr.defaultShader;
|
||||
} else {
|
||||
md3Shader = (md3Shader_t *) ( (byte *)surface + surface->ofsShaders );
|
||||
md3Shader += ent->e.skinNum % surface->numShaders;
|
||||
shader = tr.shaders[ md3Shader->shaderIndex ];
|
||||
}
|
||||
|
||||
|
||||
// we will add shadows even if the main object isn't visible in the view
|
||||
|
||||
// stencil shadows can't do personal models unless I polyhedron clip
|
||||
if ( !personalModel
|
||||
&& r_shadows->integer == 2
|
||||
&& fogNum == 0
|
||||
&& !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) )
|
||||
&& shader->sort == SS_OPAQUE ) {
|
||||
R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse );
|
||||
}
|
||||
|
||||
// projection shadows work fine with personal models
|
||||
if ( r_shadows->integer == 3
|
||||
&& fogNum == 0
|
||||
&& (ent->e.renderfx & RF_SHADOW_PLANE )
|
||||
&& shader->sort == SS_OPAQUE ) {
|
||||
R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse );
|
||||
}
|
||||
|
||||
// don't add third_person objects if not viewing through a portal
|
||||
if ( !personalModel ) {
|
||||
R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse );
|
||||
}
|
||||
|
||||
surface = (md3Surface_t *)( (byte *)surface + surface->ofsEnd );
|
||||
}
|
||||
|
||||
}
|
||||
|
1317
code/renderergl1/tr_model.c
Normal file
1317
code/renderergl1/tr_model.c
Normal file
File diff suppressed because it is too large
Load diff
1058
code/renderergl1/tr_model_iqm.c
Normal file
1058
code/renderergl1/tr_model_iqm.c
Normal file
File diff suppressed because it is too large
Load diff
91
code/renderergl1/tr_noise.c
Normal file
91
code/renderergl1/tr_noise.c
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// tr_noise.c
|
||||
#include "tr_local.h"
|
||||
|
||||
#define NOISE_SIZE 256
|
||||
#define NOISE_MASK ( NOISE_SIZE - 1 )
|
||||
|
||||
#define VAL( a ) s_noise_perm[ ( a ) & ( NOISE_MASK )]
|
||||
#define INDEX( x, y, z, t ) VAL( x + VAL( y + VAL( z + VAL( t ) ) ) )
|
||||
|
||||
static float s_noise_table[NOISE_SIZE];
|
||||
static int s_noise_perm[NOISE_SIZE];
|
||||
|
||||
static float GetNoiseValue( int x, int y, int z, int t )
|
||||
{
|
||||
int index = INDEX( ( int ) x, ( int ) y, ( int ) z, ( int ) t );
|
||||
|
||||
return s_noise_table[index];
|
||||
}
|
||||
|
||||
void R_NoiseInit( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < NOISE_SIZE; i++ )
|
||||
{
|
||||
s_noise_table[i] = ( float ) ( ( ( rand() / ( float ) RAND_MAX ) * 2.0 - 1.0 ) );
|
||||
s_noise_perm[i] = ( unsigned char ) ( rand() / ( float ) RAND_MAX * 255 );
|
||||
}
|
||||
}
|
||||
|
||||
float R_NoiseGet4f( float x, float y, float z, float t )
|
||||
{
|
||||
int i;
|
||||
int ix, iy, iz, it;
|
||||
float fx, fy, fz, ft;
|
||||
float front[4];
|
||||
float back[4];
|
||||
float fvalue, bvalue, value[2], finalvalue;
|
||||
|
||||
ix = ( int ) floor( x );
|
||||
fx = x - ix;
|
||||
iy = ( int ) floor( y );
|
||||
fy = y - iy;
|
||||
iz = ( int ) floor( z );
|
||||
fz = z - iz;
|
||||
it = ( int ) floor( t );
|
||||
ft = t - it;
|
||||
|
||||
for ( i = 0; i < 2; i++ )
|
||||
{
|
||||
front[0] = GetNoiseValue( ix, iy, iz, it + i );
|
||||
front[1] = GetNoiseValue( ix+1, iy, iz, it + i );
|
||||
front[2] = GetNoiseValue( ix, iy+1, iz, it + i );
|
||||
front[3] = GetNoiseValue( ix+1, iy+1, iz, it + i );
|
||||
|
||||
back[0] = GetNoiseValue( ix, iy, iz + 1, it + i );
|
||||
back[1] = GetNoiseValue( ix+1, iy, iz + 1, it + i );
|
||||
back[2] = GetNoiseValue( ix, iy+1, iz + 1, it + i );
|
||||
back[3] = GetNoiseValue( ix+1, iy+1, iz + 1, it + i );
|
||||
|
||||
fvalue = LERP( LERP( front[0], front[1], fx ), LERP( front[2], front[3], fx ), fy );
|
||||
bvalue = LERP( LERP( back[0], back[1], fx ), LERP( back[2], back[3], fx ), fy );
|
||||
|
||||
value[i] = LERP( fvalue, bvalue, fz );
|
||||
}
|
||||
|
||||
finalvalue = LERP( value[0], value[1], ft );
|
||||
|
||||
return finalvalue;
|
||||
}
|
411
code/renderergl1/tr_scene.c
Normal file
411
code/renderergl1/tr_scene.c
Normal file
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
int r_firstSceneDrawSurf;
|
||||
|
||||
int r_numdlights;
|
||||
int r_firstSceneDlight;
|
||||
|
||||
int r_numentities;
|
||||
int r_firstSceneEntity;
|
||||
|
||||
int r_numpolys;
|
||||
int r_firstScenePoly;
|
||||
|
||||
int r_numpolyverts;
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
R_InitNextFrame
|
||||
|
||||
====================
|
||||
*/
|
||||
void R_InitNextFrame( void ) {
|
||||
backEndData->commands.used = 0;
|
||||
|
||||
r_firstSceneDrawSurf = 0;
|
||||
|
||||
r_numdlights = 0;
|
||||
r_firstSceneDlight = 0;
|
||||
|
||||
r_numentities = 0;
|
||||
r_firstSceneEntity = 0;
|
||||
|
||||
r_numpolys = 0;
|
||||
r_firstScenePoly = 0;
|
||||
|
||||
r_numpolyverts = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
RE_ClearScene
|
||||
|
||||
====================
|
||||
*/
|
||||
void RE_ClearScene( void ) {
|
||||
r_firstSceneDlight = r_numdlights;
|
||||
r_firstSceneEntity = r_numentities;
|
||||
r_firstScenePoly = r_numpolys;
|
||||
}
|
||||
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
DISCRETE POLYS
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
=====================
|
||||
R_AddPolygonSurfaces
|
||||
|
||||
Adds all the scene's polys into this view's drawsurf list
|
||||
=====================
|
||||
*/
|
||||
void R_AddPolygonSurfaces( void ) {
|
||||
int i;
|
||||
shader_t *sh;
|
||||
srfPoly_t *poly;
|
||||
|
||||
tr.currentEntityNum = REFENTITYNUM_WORLD;
|
||||
tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT;
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
RE_AddPolyToScene
|
||||
|
||||
=====================
|
||||
*/
|
||||
void RE_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) {
|
||||
srfPoly_t *poly;
|
||||
int i, j;
|
||||
int fogIndex;
|
||||
fog_t *fog;
|
||||
vec3_t bounds[2];
|
||||
|
||||
if ( !tr.registered ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !hShader ) {
|
||||
ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolyToScene: NULL poly shader\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for ( j = 0; j < numPolys; j++ ) {
|
||||
if ( r_numpolyverts + numVerts > max_polyverts || r_numpolys >= max_polys ) {
|
||||
/*
|
||||
NOTE TTimo this was initially a PRINT_WARNING
|
||||
but it happens a lot with high fighting scenes and particles
|
||||
since we don't plan on changing the const and making for room for those effects
|
||||
simply cut this message to developer only
|
||||
*/
|
||||
ri.Printf( PRINT_DEVELOPER, "WARNING: RE_AddPolyToScene: r_max_polys or r_max_polyverts reached\n");
|
||||
return;
|
||||
}
|
||||
|
||||
poly = &backEndData->polys[r_numpolys];
|
||||
poly->surfaceType = SF_POLY;
|
||||
poly->hShader = hShader;
|
||||
poly->numVerts = numVerts;
|
||||
poly->verts = &backEndData->polyVerts[r_numpolyverts];
|
||||
|
||||
Com_Memcpy( poly->verts, &verts[numVerts*j], numVerts * sizeof( *verts ) );
|
||||
|
||||
if ( glConfig.hardwareType == GLHW_RAGEPRO ) {
|
||||
poly->verts->modulate[0] = 255;
|
||||
poly->verts->modulate[1] = 255;
|
||||
poly->verts->modulate[2] = 255;
|
||||
poly->verts->modulate[3] = 255;
|
||||
}
|
||||
// done.
|
||||
r_numpolys++;
|
||||
r_numpolyverts += numVerts;
|
||||
|
||||
// if no world is loaded
|
||||
if ( tr.world == NULL ) {
|
||||
fogIndex = 0;
|
||||
}
|
||||
// see if it is in a fog volume
|
||||
else if ( tr.world->numfogs == 1 ) {
|
||||
fogIndex = 0;
|
||||
} else {
|
||||
// find which fog volume the poly is in
|
||||
VectorCopy( poly->verts[0].xyz, bounds[0] );
|
||||
VectorCopy( poly->verts[0].xyz, bounds[1] );
|
||||
for ( i = 1 ; i < poly->numVerts ; i++ ) {
|
||||
AddPointToBounds( poly->verts[i].xyz, bounds[0], bounds[1] );
|
||||
}
|
||||
for ( fogIndex = 1 ; fogIndex < tr.world->numfogs ; fogIndex++ ) {
|
||||
fog = &tr.world->fogs[fogIndex];
|
||||
if ( bounds[1][0] >= fog->bounds[0][0]
|
||||
&& bounds[1][1] >= fog->bounds[0][1]
|
||||
&& bounds[1][2] >= fog->bounds[0][2]
|
||||
&& bounds[0][0] <= fog->bounds[1][0]
|
||||
&& bounds[0][1] <= fog->bounds[1][1]
|
||||
&& bounds[0][2] <= fog->bounds[1][2] ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( fogIndex == tr.world->numfogs ) {
|
||||
fogIndex = 0;
|
||||
}
|
||||
}
|
||||
poly->fogIndex = fogIndex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=================================================================================
|
||||
|
||||
|
||||
/*
|
||||
=====================
|
||||
RE_AddRefEntityToScene
|
||||
|
||||
=====================
|
||||
*/
|
||||
void RE_AddRefEntityToScene( const refEntity_t *ent ) {
|
||||
if ( !tr.registered ) {
|
||||
return;
|
||||
}
|
||||
if ( r_numentities >= MAX_REFENTITIES ) {
|
||||
ri.Printf(PRINT_DEVELOPER, "RE_AddRefEntityToScene: Dropping refEntity, reached MAX_REFENTITIES\n");
|
||||
return;
|
||||
}
|
||||
if ( Q_isnan(ent->origin[0]) || Q_isnan(ent->origin[1]) || Q_isnan(ent->origin[2]) ) {
|
||||
static qboolean firstTime = qtrue;
|
||||
if (firstTime) {
|
||||
firstTime = qfalse;
|
||||
ri.Printf( PRINT_WARNING, "RE_AddRefEntityToScene passed a refEntity which has an origin with a NaN component\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ( (int)ent->reType < 0 || ent->reType >= RT_MAX_REF_ENTITY_TYPE ) {
|
||||
ri.Error( ERR_DROP, "RE_AddRefEntityToScene: bad reType %i", ent->reType );
|
||||
}
|
||||
|
||||
backEndData->entities[r_numentities].e = *ent;
|
||||
backEndData->entities[r_numentities].lightingCalculated = qfalse;
|
||||
|
||||
r_numentities++;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=====================
|
||||
RE_AddDynamicLightToScene
|
||||
|
||||
=====================
|
||||
*/
|
||||
void RE_AddDynamicLightToScene( const vec3_t org, float intensity, float r, float g, float b, int additive ) {
|
||||
dlight_t *dl;
|
||||
|
||||
if ( !tr.registered ) {
|
||||
return;
|
||||
}
|
||||
if ( r_numdlights >= MAX_DLIGHTS ) {
|
||||
return;
|
||||
}
|
||||
if ( intensity <= 0 ) {
|
||||
return;
|
||||
}
|
||||
// these cards don't have the correct blend mode
|
||||
if ( glConfig.hardwareType == GLHW_RIVA128 || glConfig.hardwareType == GLHW_PERMEDIA2 ) {
|
||||
return;
|
||||
}
|
||||
dl = &backEndData->dlights[r_numdlights++];
|
||||
VectorCopy (org, dl->origin);
|
||||
dl->radius = intensity;
|
||||
dl->color[0] = r;
|
||||
dl->color[1] = g;
|
||||
dl->color[2] = b;
|
||||
dl->additive = additive;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
RE_AddLightToScene
|
||||
|
||||
=====================
|
||||
*/
|
||||
void RE_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ) {
|
||||
RE_AddDynamicLightToScene( org, intensity, r, g, b, qfalse );
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
RE_AddAdditiveLightToScene
|
||||
|
||||
=====================
|
||||
*/
|
||||
void RE_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ) {
|
||||
RE_AddDynamicLightToScene( org, intensity, r, g, b, qtrue );
|
||||
}
|
||||
|
||||
/*
|
||||
@@@@@@@@@@@@@@@@@@@@@
|
||||
RE_RenderScene
|
||||
|
||||
Draw a 3D view into a part of the window, then return
|
||||
to 2D drawing.
|
||||
|
||||
Rendering a scene may require multiple views to be rendered
|
||||
to handle mirrors,
|
||||
@@@@@@@@@@@@@@@@@@@@@
|
||||
*/
|
||||
void RE_RenderScene( const refdef_t *fd ) {
|
||||
viewParms_t parms;
|
||||
int startTime;
|
||||
|
||||
if ( !tr.registered ) {
|
||||
return;
|
||||
}
|
||||
GLimp_LogComment( "====== RE_RenderScene =====\n" );
|
||||
|
||||
if ( r_norefresh->integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
startTime = ri.Milliseconds();
|
||||
|
||||
if (!tr.world && !( fd->rdflags & RDF_NOWORLDMODEL ) ) {
|
||||
ri.Error (ERR_DROP, "R_RenderScene: NULL worldmodel");
|
||||
}
|
||||
|
||||
Com_Memcpy( tr.refdef.text, fd->text, sizeof( tr.refdef.text ) );
|
||||
|
||||
tr.refdef.x = fd->x;
|
||||
tr.refdef.y = fd->y;
|
||||
tr.refdef.width = fd->width;
|
||||
tr.refdef.height = fd->height;
|
||||
tr.refdef.fov_x = fd->fov_x;
|
||||
tr.refdef.fov_y = fd->fov_y;
|
||||
|
||||
VectorCopy( fd->vieworg, tr.refdef.vieworg );
|
||||
VectorCopy( fd->viewaxis[0], tr.refdef.viewaxis[0] );
|
||||
VectorCopy( fd->viewaxis[1], tr.refdef.viewaxis[1] );
|
||||
VectorCopy( fd->viewaxis[2], tr.refdef.viewaxis[2] );
|
||||
|
||||
tr.refdef.time = fd->time;
|
||||
tr.refdef.rdflags = fd->rdflags;
|
||||
|
||||
// copy the areamask data over and note if it has changed, which
|
||||
// will force a reset of the visible leafs even if the view hasn't moved
|
||||
tr.refdef.areamaskModified = qfalse;
|
||||
if ( ! (tr.refdef.rdflags & RDF_NOWORLDMODEL) ) {
|
||||
int areaDiff;
|
||||
int i;
|
||||
|
||||
// compare the area bits
|
||||
areaDiff = 0;
|
||||
for (i = 0 ; i < MAX_MAP_AREA_BYTES/4 ; i++) {
|
||||
areaDiff |= ((int *)tr.refdef.areamask)[i] ^ ((int *)fd->areamask)[i];
|
||||
((int *)tr.refdef.areamask)[i] = ((int *)fd->areamask)[i];
|
||||
}
|
||||
|
||||
if ( areaDiff ) {
|
||||
// a door just opened or something
|
||||
tr.refdef.areamaskModified = qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// derived info
|
||||
|
||||
tr.refdef.floatTime = tr.refdef.time * 0.001f;
|
||||
|
||||
tr.refdef.numDrawSurfs = r_firstSceneDrawSurf;
|
||||
tr.refdef.drawSurfs = backEndData->drawSurfs;
|
||||
|
||||
tr.refdef.num_entities = r_numentities - r_firstSceneEntity;
|
||||
tr.refdef.entities = &backEndData->entities[r_firstSceneEntity];
|
||||
|
||||
tr.refdef.num_dlights = r_numdlights - r_firstSceneDlight;
|
||||
tr.refdef.dlights = &backEndData->dlights[r_firstSceneDlight];
|
||||
|
||||
tr.refdef.numPolys = r_numpolys - r_firstScenePoly;
|
||||
tr.refdef.polys = &backEndData->polys[r_firstScenePoly];
|
||||
|
||||
// turn off dynamic lighting globally by clearing all the
|
||||
// dlights if it needs to be disabled or if vertex lighting is enabled
|
||||
if ( r_dynamiclight->integer == 0 ||
|
||||
r_vertexLight->integer == 1 ||
|
||||
glConfig.hardwareType == GLHW_PERMEDIA2 ) {
|
||||
tr.refdef.num_dlights = 0;
|
||||
}
|
||||
|
||||
// a single frame may have multiple scenes draw inside it --
|
||||
// a 3D game view, 3D status bar renderings, 3D menus, etc.
|
||||
// They need to be distinguished by the light flare code, because
|
||||
// the visibility state for a given surface may be different in
|
||||
// each scene / view.
|
||||
tr.frameSceneNum++;
|
||||
tr.sceneCount++;
|
||||
|
||||
// setup view parms for the initial view
|
||||
//
|
||||
// set up viewport
|
||||
// The refdef takes 0-at-the-top y coordinates, so
|
||||
// convert to GL's 0-at-the-bottom space
|
||||
//
|
||||
Com_Memset( &parms, 0, sizeof( parms ) );
|
||||
parms.viewportX = tr.refdef.x;
|
||||
parms.viewportY = glConfig.vidHeight - ( tr.refdef.y + tr.refdef.height );
|
||||
parms.viewportWidth = tr.refdef.width;
|
||||
parms.viewportHeight = tr.refdef.height;
|
||||
parms.isPortal = qfalse;
|
||||
|
||||
parms.fovX = tr.refdef.fov_x;
|
||||
parms.fovY = tr.refdef.fov_y;
|
||||
|
||||
parms.stereoFrame = tr.refdef.stereoFrame;
|
||||
|
||||
VectorCopy( fd->vieworg, parms.or.origin );
|
||||
VectorCopy( fd->viewaxis[0], parms.or.axis[0] );
|
||||
VectorCopy( fd->viewaxis[1], parms.or.axis[1] );
|
||||
VectorCopy( fd->viewaxis[2], parms.or.axis[2] );
|
||||
|
||||
VectorCopy( fd->vieworg, parms.pvsOrigin );
|
||||
|
||||
R_RenderView( &parms );
|
||||
|
||||
// the next scene rendered in this frame will tack on after this one
|
||||
r_firstSceneDrawSurf = tr.refdef.numDrawSurfs;
|
||||
r_firstSceneEntity = r_numentities;
|
||||
r_firstSceneDlight = r_numdlights;
|
||||
r_firstScenePoly = r_numpolys;
|
||||
|
||||
tr.frontEndMsec += ri.Milliseconds() - startTime;
|
||||
}
|
1527
code/renderergl1/tr_shade.c
Normal file
1527
code/renderergl1/tr_shade.c
Normal file
File diff suppressed because it is too large
Load diff
1216
code/renderergl1/tr_shade_calc.c
Normal file
1216
code/renderergl1/tr_shade_calc.c
Normal file
File diff suppressed because it is too large
Load diff
3067
code/renderergl1/tr_shader.c
Normal file
3067
code/renderergl1/tr_shader.c
Normal file
File diff suppressed because it is too large
Load diff
343
code/renderergl1/tr_shadows.c
Normal file
343
code/renderergl1/tr_shadows.c
Normal file
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
#include "tr_local.h"
|
||||
|
||||
|
||||
/*
|
||||
|
||||
for a projection shadow:
|
||||
|
||||
point[x] += light vector * ( z - shadow plane )
|
||||
point[y] +=
|
||||
point[z] = shadow plane
|
||||
|
||||
1 0 light[x] / light[z]
|
||||
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
int i2;
|
||||
int facing;
|
||||
} edgeDef_t;
|
||||
|
||||
#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];
|
||||
|
||||
void R_AddEdgeDef( int i1, int i2, int facing ) {
|
||||
int c;
|
||||
|
||||
c = numEdgeDefs[ i1 ];
|
||||
if ( c == MAX_EDGE_DEFS ) {
|
||||
return; // overflow
|
||||
}
|
||||
edgeDefs[ i1 ][ c ].i2 = i2;
|
||||
edgeDefs[ i1 ][ c ].facing = facing;
|
||||
|
||||
numEdgeDefs[ i1 ]++;
|
||||
}
|
||||
|
||||
void R_RenderShadowEdges( void ) {
|
||||
int i;
|
||||
|
||||
#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;
|
||||
|
||||
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();
|
||||
}
|
||||
#else
|
||||
int c, c2;
|
||||
int j, k;
|
||||
int i2;
|
||||
int c_edges, c_rejected;
|
||||
int hit[2];
|
||||
|
||||
// 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.
|
||||
// A well behaved polyhedron would have exactly two faces for each edge,
|
||||
// but lots of models have dangling edges or overfanned edges
|
||||
c_edges = 0;
|
||||
c_rejected = 0;
|
||||
|
||||
for ( i = 0 ; i < tess.numVertexes ; i++ ) {
|
||||
c = numEdgeDefs[ i ];
|
||||
for ( j = 0 ; j < c ; j++ ) {
|
||||
if ( !edgeDefs[ i ][ j ].facing ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hit[0] = 0;
|
||||
hit[1] = 0;
|
||||
|
||||
i2 = edgeDefs[ i ][ j ].i2;
|
||||
c2 = numEdgeDefs[ i2 ];
|
||||
for ( k = 0 ; k < c2 ; k++ ) {
|
||||
if ( edgeDefs[ i2 ][ k ].i2 == i ) {
|
||||
hit[ edgeDefs[ i2 ][ 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();
|
||||
c_edges++;
|
||||
} else {
|
||||
c_rejected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
RB_ShadowTessEnd
|
||||
|
||||
triangleFromEdge[ v1 ][ v2 ]
|
||||
|
||||
|
||||
set triangle from edge( v1, v2, tri )
|
||||
if ( facing[ triangleFromEdge[ v1 ][ v2 ] ] && !facing[ triangleFromEdge[ v2 ][ v1 ] ) {
|
||||
}
|
||||
=================
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
if ( glConfig.stencilBits < 4 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy( backEnd.currentEntity->lightDir, lightDir );
|
||||
|
||||
// project vertexes away from light direction
|
||||
for ( i = 0 ; i < tess.numVertexes ; i++ ) {
|
||||
VectorMA( tess.xyz[i], -512, lightDir, tess.xyz[i+tess.numVertexes] );
|
||||
}
|
||||
|
||||
// decide which triangles face the light
|
||||
Com_Memset( numEdgeDefs, 0, 4 * tess.numVertexes );
|
||||
|
||||
numTris = tess.numIndexes / 3;
|
||||
for ( i = 0 ; i < numTris ; i++ ) {
|
||||
int i1, i2, i3;
|
||||
vec3_t d1, d2, normal;
|
||||
float *v1, *v2, *v3;
|
||||
float d;
|
||||
|
||||
i1 = tess.indexes[ i*3 + 0 ];
|
||||
i2 = tess.indexes[ i*3 + 1 ];
|
||||
i3 = tess.indexes[ i*3 + 2 ];
|
||||
|
||||
v1 = tess.xyz[ i1 ];
|
||||
v2 = tess.xyz[ i2 ];
|
||||
v3 = tess.xyz[ i3 ];
|
||||
|
||||
VectorSubtract( v2, v1, d1 );
|
||||
VectorSubtract( v3, v1, d2 );
|
||||
CrossProduct( d1, d2, normal );
|
||||
|
||||
d = DotProduct( normal, lightDir );
|
||||
if ( d > 0 ) {
|
||||
facing[ i ] = 1;
|
||||
} else {
|
||||
facing[ i ] = 0;
|
||||
}
|
||||
|
||||
// create the edges
|
||||
R_AddEdgeDef( i1, i2, facing[ i ] );
|
||||
R_AddEdgeDef( i2, i3, facing[ i ] );
|
||||
R_AddEdgeDef( i3, i1, facing[ i ] );
|
||||
}
|
||||
|
||||
// draw the silhouette edges
|
||||
|
||||
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 );
|
||||
|
||||
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();
|
||||
} else {
|
||||
qglCullFace( GL_BACK );
|
||||
qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
|
||||
|
||||
R_RenderShadowEdges();
|
||||
|
||||
qglCullFace( GL_FRONT );
|
||||
qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR );
|
||||
|
||||
R_RenderShadowEdges();
|
||||
}
|
||||
|
||||
|
||||
// reenable writing to the color buffer
|
||||
qglColorMask(rgba[0], rgba[1], rgba[2], rgba[3]);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
RB_ShadowFinish
|
||||
|
||||
Darken everything that is is a shadow volume.
|
||||
We have to delay this until everything has been shadowed,
|
||||
because otherwise shadows from different body parts would
|
||||
overlap and double darken.
|
||||
=================
|
||||
*/
|
||||
void RB_ShadowFinish( void ) {
|
||||
if ( r_shadows->integer != 2 ) {
|
||||
return;
|
||||
}
|
||||
if ( glConfig.stencilBits < 4 ) {
|
||||
return;
|
||||
}
|
||||
qglEnable( GL_STENCIL_TEST );
|
||||
qglStencilFunc( GL_NOTEQUAL, 0, 255 );
|
||||
|
||||
qglDisable (GL_CLIP_PLANE0);
|
||||
qglDisable (GL_CULL_FACE);
|
||||
|
||||
GL_Bind( tr.whiteImage );
|
||||
|
||||
qglLoadIdentity ();
|
||||
|
||||
qglColor3f( 0.6f, 0.6f, 0.6f );
|
||||
GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO );
|
||||
|
||||
// qglColor3f( 1, 0, 0 );
|
||||
// GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO );
|
||||
|
||||
qglBegin( GL_QUADS );
|
||||
qglVertex3f( -100, 100, -10 );
|
||||
qglVertex3f( 100, 100, -10 );
|
||||
qglVertex3f( 100, -100, -10 );
|
||||
qglVertex3f( -100, -100, -10 );
|
||||
qglEnd ();
|
||||
|
||||
qglColor4f(1,1,1,1);
|
||||
qglDisable( GL_STENCIL_TEST );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
RB_ProjectionShadowDeform
|
||||
|
||||
=================
|
||||
*/
|
||||
void RB_ProjectionShadowDeform( void ) {
|
||||
float *xyz;
|
||||
int i;
|
||||
float h;
|
||||
vec3_t ground;
|
||||
vec3_t light;
|
||||
float groundDist;
|
||||
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];
|
||||
|
||||
groundDist = backEnd.or.origin[2] - backEnd.currentEntity->e.shadowPlane;
|
||||
|
||||
VectorCopy( backEnd.currentEntity->lightDir, lightDir );
|
||||
d = DotProduct( lightDir, ground );
|
||||
// don't let the shadows get too long or go negative
|
||||
if ( d < 0.5 ) {
|
||||
VectorMA( lightDir, (0.5 - d), ground, lightDir );
|
||||
d = DotProduct( lightDir, ground );
|
||||
}
|
||||
d = 1.0 / d;
|
||||
|
||||
light[0] = lightDir[0] * d;
|
||||
light[1] = lightDir[1] * d;
|
||||
light[2] = lightDir[2] * d;
|
||||
|
||||
for ( i = 0; i < tess.numVertexes; i++, xyz += 4 ) {
|
||||
h = DotProduct( xyz, ground ) + groundDist;
|
||||
|
||||
xyz[0] -= light[0] * h;
|
||||
xyz[1] -= light[1] * h;
|
||||
xyz[2] -= light[2] * h;
|
||||
}
|
||||
}
|
845
code/renderergl1/tr_sky.c
Normal file
845
code/renderergl1/tr_sky.c
Normal file
|
@ -0,0 +1,845 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// tr_sky.c
|
||||
#include "tr_local.h"
|
||||
|
||||
#define SKY_SUBDIVISIONS 8
|
||||
#define HALF_SKY_SUBDIVISIONS (SKY_SUBDIVISIONS/2)
|
||||
|
||||
static float s_cloudTexCoords[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2];
|
||||
static float s_cloudTexP[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1];
|
||||
|
||||
/*
|
||||
===================================================================================
|
||||
|
||||
POLYGON TO BOX SIDE PROJECTION
|
||||
|
||||
===================================================================================
|
||||
*/
|
||||
|
||||
static vec3_t sky_clip[6] =
|
||||
{
|
||||
{1,1,0},
|
||||
{1,-1,0},
|
||||
{0,-1,1},
|
||||
{0,1,1},
|
||||
{1,0,1},
|
||||
{-1,0,1}
|
||||
};
|
||||
|
||||
static float sky_mins[2][6], sky_maxs[2][6];
|
||||
static float sky_min, sky_max;
|
||||
|
||||
/*
|
||||
================
|
||||
AddSkyPolygon
|
||||
================
|
||||
*/
|
||||
static void AddSkyPolygon (int nump, vec3_t vecs)
|
||||
{
|
||||
int i,j;
|
||||
vec3_t v, av;
|
||||
float s, t, dv;
|
||||
int axis;
|
||||
float *vp;
|
||||
// s = [0]/[2], t = [1]/[2]
|
||||
static int vec_to_st[6][3] =
|
||||
{
|
||||
{-2,3,1},
|
||||
{2,3,-1},
|
||||
|
||||
{1,3,2},
|
||||
{-1,3,-2},
|
||||
|
||||
{-2,-1,3},
|
||||
{-2,1,-3}
|
||||
|
||||
// {-1,2,3},
|
||||
// {1,2,-3}
|
||||
};
|
||||
|
||||
// decide which face it maps to
|
||||
VectorCopy (vec3_origin, v);
|
||||
for (i=0, vp=vecs ; i<nump ; i++, vp+=3)
|
||||
{
|
||||
VectorAdd (vp, v, v);
|
||||
}
|
||||
av[0] = fabs(v[0]);
|
||||
av[1] = fabs(v[1]);
|
||||
av[2] = fabs(v[2]);
|
||||
if (av[0] > av[1] && av[0] > av[2])
|
||||
{
|
||||
if (v[0] < 0)
|
||||
axis = 1;
|
||||
else
|
||||
axis = 0;
|
||||
}
|
||||
else if (av[1] > av[2] && av[1] > av[0])
|
||||
{
|
||||
if (v[1] < 0)
|
||||
axis = 3;
|
||||
else
|
||||
axis = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v[2] < 0)
|
||||
axis = 5;
|
||||
else
|
||||
axis = 4;
|
||||
}
|
||||
|
||||
// project new texture coords
|
||||
for (i=0 ; i<nump ; i++, vecs+=3)
|
||||
{
|
||||
j = vec_to_st[axis][2];
|
||||
if (j > 0)
|
||||
dv = vecs[j - 1];
|
||||
else
|
||||
dv = -vecs[-j - 1];
|
||||
if (dv < 0.001)
|
||||
continue; // don't divide by zero
|
||||
j = vec_to_st[axis][0];
|
||||
if (j < 0)
|
||||
s = -vecs[-j -1] / dv;
|
||||
else
|
||||
s = vecs[j-1] / dv;
|
||||
j = vec_to_st[axis][1];
|
||||
if (j < 0)
|
||||
t = -vecs[-j -1] / dv;
|
||||
else
|
||||
t = vecs[j-1] / dv;
|
||||
|
||||
if (s < sky_mins[0][axis])
|
||||
sky_mins[0][axis] = s;
|
||||
if (t < sky_mins[1][axis])
|
||||
sky_mins[1][axis] = t;
|
||||
if (s > sky_maxs[0][axis])
|
||||
sky_maxs[0][axis] = s;
|
||||
if (t > sky_maxs[1][axis])
|
||||
sky_maxs[1][axis] = t;
|
||||
}
|
||||
}
|
||||
|
||||
#define ON_EPSILON 0.1f // point on plane side epsilon
|
||||
#define MAX_CLIP_VERTS 64
|
||||
/*
|
||||
================
|
||||
ClipSkyPolygon
|
||||
================
|
||||
*/
|
||||
static void ClipSkyPolygon (int nump, vec3_t vecs, int stage)
|
||||
{
|
||||
float *norm;
|
||||
float *v;
|
||||
qboolean front, back;
|
||||
float d, e;
|
||||
float dists[MAX_CLIP_VERTS];
|
||||
int sides[MAX_CLIP_VERTS];
|
||||
vec3_t newv[2][MAX_CLIP_VERTS];
|
||||
int newc[2];
|
||||
int i, j;
|
||||
|
||||
if (nump > MAX_CLIP_VERTS-2)
|
||||
ri.Error (ERR_DROP, "ClipSkyPolygon: MAX_CLIP_VERTS");
|
||||
if (stage == 6)
|
||||
{ // fully clipped, so draw it
|
||||
AddSkyPolygon (nump, vecs);
|
||||
return;
|
||||
}
|
||||
|
||||
front = back = qfalse;
|
||||
norm = sky_clip[stage];
|
||||
for (i=0, v = vecs ; i<nump ; i++, v+=3)
|
||||
{
|
||||
d = DotProduct (v, norm);
|
||||
if (d > ON_EPSILON)
|
||||
{
|
||||
front = qtrue;
|
||||
sides[i] = SIDE_FRONT;
|
||||
}
|
||||
else if (d < -ON_EPSILON)
|
||||
{
|
||||
back = qtrue;
|
||||
sides[i] = SIDE_BACK;
|
||||
}
|
||||
else
|
||||
sides[i] = SIDE_ON;
|
||||
dists[i] = d;
|
||||
}
|
||||
|
||||
if (!front || !back)
|
||||
{ // not clipped
|
||||
ClipSkyPolygon (nump, vecs, stage+1);
|
||||
return;
|
||||
}
|
||||
|
||||
// clip it
|
||||
sides[i] = sides[0];
|
||||
dists[i] = dists[0];
|
||||
VectorCopy (vecs, (vecs+(i*3)) );
|
||||
newc[0] = newc[1] = 0;
|
||||
|
||||
for (i=0, v = vecs ; i<nump ; i++, v+=3)
|
||||
{
|
||||
switch (sides[i])
|
||||
{
|
||||
case SIDE_FRONT:
|
||||
VectorCopy (v, newv[0][newc[0]]);
|
||||
newc[0]++;
|
||||
break;
|
||||
case SIDE_BACK:
|
||||
VectorCopy (v, newv[1][newc[1]]);
|
||||
newc[1]++;
|
||||
break;
|
||||
case SIDE_ON:
|
||||
VectorCopy (v, newv[0][newc[0]]);
|
||||
newc[0]++;
|
||||
VectorCopy (v, newv[1][newc[1]]);
|
||||
newc[1]++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sides[i] == SIDE_ON || sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
|
||||
continue;
|
||||
|
||||
d = dists[i] / (dists[i] - dists[i+1]);
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{
|
||||
e = v[j] + d*(v[j+3] - v[j]);
|
||||
newv[0][newc[0]][j] = e;
|
||||
newv[1][newc[1]][j] = e;
|
||||
}
|
||||
newc[0]++;
|
||||
newc[1]++;
|
||||
}
|
||||
|
||||
// continue
|
||||
ClipSkyPolygon (newc[0], newv[0][0], stage+1);
|
||||
ClipSkyPolygon (newc[1], newv[1][0], stage+1);
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
ClearSkyBox
|
||||
==============
|
||||
*/
|
||||
static void ClearSkyBox (void) {
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<6 ; i++) {
|
||||
sky_mins[0][i] = sky_mins[1][i] = 9999;
|
||||
sky_maxs[0][i] = sky_maxs[1][i] = -9999;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
RB_ClipSkyPolygons
|
||||
================
|
||||
*/
|
||||
void RB_ClipSkyPolygons( shaderCommands_t *input )
|
||||
{
|
||||
vec3_t p[5]; // need one extra point for clipping
|
||||
int i, j;
|
||||
|
||||
ClearSkyBox();
|
||||
|
||||
for ( i = 0; i < input->numIndexes; i += 3 )
|
||||
{
|
||||
for (j = 0 ; j < 3 ; j++)
|
||||
{
|
||||
VectorSubtract( input->xyz[input->indexes[i+j]],
|
||||
backEnd.viewParms.or.origin,
|
||||
p[j] );
|
||||
}
|
||||
ClipSkyPolygon( 3, p[0], 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================================================================================
|
||||
|
||||
CLOUD VERTEX GENERATION
|
||||
|
||||
===================================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** MakeSkyVec
|
||||
**
|
||||
** Parms: s, t range from -1 to 1
|
||||
*/
|
||||
static void MakeSkyVec( float s, float t, int axis, float outSt[2], vec3_t outXYZ )
|
||||
{
|
||||
// 1 = s, 2 = t, 3 = 2048
|
||||
static int st_to_vec[6][3] =
|
||||
{
|
||||
{3,-1,2},
|
||||
{-3,1,2},
|
||||
|
||||
{1,3,2},
|
||||
{-1,-3,2},
|
||||
|
||||
{-2,-1,3}, // 0 degrees yaw, look straight up
|
||||
{2,-1,-3} // look straight down
|
||||
};
|
||||
|
||||
vec3_t b;
|
||||
int j, k;
|
||||
float boxSize;
|
||||
|
||||
boxSize = backEnd.viewParms.zFar / 1.75; // div sqrt(3)
|
||||
b[0] = s*boxSize;
|
||||
b[1] = t*boxSize;
|
||||
b[2] = boxSize;
|
||||
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{
|
||||
k = st_to_vec[axis][j];
|
||||
if (k < 0)
|
||||
{
|
||||
outXYZ[j] = -b[-k - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
outXYZ[j] = b[k - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// avoid bilerp seam
|
||||
s = (s+1)*0.5;
|
||||
t = (t+1)*0.5;
|
||||
if (s < sky_min)
|
||||
{
|
||||
s = sky_min;
|
||||
}
|
||||
else if (s > sky_max)
|
||||
{
|
||||
s = sky_max;
|
||||
}
|
||||
|
||||
if (t < sky_min)
|
||||
{
|
||||
t = sky_min;
|
||||
}
|
||||
else if (t > sky_max)
|
||||
{
|
||||
t = sky_max;
|
||||
}
|
||||
|
||||
t = 1.0 - t;
|
||||
|
||||
|
||||
if ( outSt )
|
||||
{
|
||||
outSt[0] = s;
|
||||
outSt[1] = t;
|
||||
}
|
||||
}
|
||||
|
||||
static int sky_texorder[6] = {0,2,1,3,4,5};
|
||||
static vec3_t s_skyPoints[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1];
|
||||
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;
|
||||
|
||||
GL_Bind( image );
|
||||
|
||||
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++ )
|
||||
{
|
||||
qglTexCoord2fv( s_skyTexCoords[t][s] );
|
||||
qglVertex3fv( s_skyPoints[t][s] );
|
||||
|
||||
qglTexCoord2fv( s_skyTexCoords[t+1][s] );
|
||||
qglVertex3fv( s_skyPoints[t+1][s] );
|
||||
}
|
||||
|
||||
qglEnd();
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawSkyBox( shader_t *shader )
|
||||
{
|
||||
int i;
|
||||
|
||||
sky_min = 0;
|
||||
sky_max = 1;
|
||||
|
||||
Com_Memset( s_skyTexCoords, 0, sizeof( s_skyTexCoords ) );
|
||||
|
||||
for (i=0 ; i<6 ; i++)
|
||||
{
|
||||
int sky_mins_subd[2], sky_maxs_subd[2];
|
||||
int s, t;
|
||||
|
||||
sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
|
||||
sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
|
||||
sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
|
||||
sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
|
||||
|
||||
if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) ||
|
||||
( sky_mins[1][i] >= sky_maxs[1][i] ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sky_mins_subd[0] = sky_mins[0][i] * HALF_SKY_SUBDIVISIONS;
|
||||
sky_mins_subd[1] = sky_mins[1][i] * HALF_SKY_SUBDIVISIONS;
|
||||
sky_maxs_subd[0] = sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS;
|
||||
sky_maxs_subd[1] = sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS;
|
||||
|
||||
if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS )
|
||||
sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS;
|
||||
else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS )
|
||||
sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS;
|
||||
if ( sky_mins_subd[1] < -HALF_SKY_SUBDIVISIONS )
|
||||
sky_mins_subd[1] = -HALF_SKY_SUBDIVISIONS;
|
||||
else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS )
|
||||
sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS;
|
||||
|
||||
if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS )
|
||||
sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS;
|
||||
else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS )
|
||||
sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS;
|
||||
if ( sky_maxs_subd[1] < -HALF_SKY_SUBDIVISIONS )
|
||||
sky_maxs_subd[1] = -HALF_SKY_SUBDIVISIONS;
|
||||
else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS )
|
||||
sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS;
|
||||
|
||||
//
|
||||
// iterate through the subdivisions
|
||||
//
|
||||
for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ )
|
||||
{
|
||||
for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ )
|
||||
{
|
||||
MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
|
||||
( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
|
||||
i,
|
||||
s_skyTexCoords[t][s],
|
||||
s_skyPoints[t][s] );
|
||||
}
|
||||
}
|
||||
|
||||
DrawSkySide( shader->sky.outerbox[sky_texorder[i]],
|
||||
sky_mins_subd,
|
||||
sky_maxs_subd );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void FillCloudySkySide( const int mins[2], const int maxs[2], qboolean addIndexes )
|
||||
{
|
||||
int s, t;
|
||||
int vertexStart = tess.numVertexes;
|
||||
int tHeight, sWidth;
|
||||
|
||||
tHeight = maxs[1] - mins[1] + 1;
|
||||
sWidth = maxs[0] - mins[0] + 1;
|
||||
|
||||
for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ )
|
||||
{
|
||||
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];
|
||||
|
||||
tess.numVertexes++;
|
||||
|
||||
if ( tess.numVertexes >= SHADER_MAX_VERTEXES )
|
||||
{
|
||||
ri.Error( ERR_DROP, "SHADER_MAX_VERTEXES hit in FillCloudySkySide()" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only add indexes for one pass, otherwise it would draw multiple times for each pass
|
||||
if ( addIndexes ) {
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void FillCloudBox( const shader_t *shader, int stage )
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i =0; i < 6; i++ )
|
||||
{
|
||||
int sky_mins_subd[2], sky_maxs_subd[2];
|
||||
int s, t;
|
||||
float MIN_T;
|
||||
|
||||
if ( 1 ) // FIXME? shader->sky.fullClouds )
|
||||
{
|
||||
MIN_T = -HALF_SKY_SUBDIVISIONS;
|
||||
|
||||
// still don't want to draw the bottom, even if fullClouds
|
||||
if ( i == 5 )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch( i )
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
MIN_T = -1;
|
||||
break;
|
||||
case 5:
|
||||
// don't draw clouds beneath you
|
||||
continue;
|
||||
case 4: // top
|
||||
default:
|
||||
MIN_T = -HALF_SKY_SUBDIVISIONS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
|
||||
sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
|
||||
sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
|
||||
sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
|
||||
|
||||
if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) ||
|
||||
( sky_mins[1][i] >= sky_maxs[1][i] ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sky_mins_subd[0] = ri.ftol(sky_mins[0][i] * HALF_SKY_SUBDIVISIONS);
|
||||
sky_mins_subd[1] = ri.ftol(sky_mins[1][i] * HALF_SKY_SUBDIVISIONS);
|
||||
sky_maxs_subd[0] = ri.ftol(sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS);
|
||||
sky_maxs_subd[1] = ri.ftol(sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS);
|
||||
|
||||
if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS )
|
||||
sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS;
|
||||
else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS )
|
||||
sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS;
|
||||
if ( sky_mins_subd[1] < MIN_T )
|
||||
sky_mins_subd[1] = MIN_T;
|
||||
else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS )
|
||||
sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS;
|
||||
|
||||
if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS )
|
||||
sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS;
|
||||
else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS )
|
||||
sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS;
|
||||
if ( sky_maxs_subd[1] < MIN_T )
|
||||
sky_maxs_subd[1] = MIN_T;
|
||||
else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS )
|
||||
sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS;
|
||||
|
||||
//
|
||||
// iterate through the subdivisions
|
||||
//
|
||||
for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ )
|
||||
{
|
||||
for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ )
|
||||
{
|
||||
MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
|
||||
( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
|
||||
i,
|
||||
NULL,
|
||||
s_skyPoints[t][s] );
|
||||
|
||||
s_skyTexCoords[t][s][0] = s_cloudTexCoords[i][t][s][0];
|
||||
s_skyTexCoords[t][s][1] = s_cloudTexCoords[i][t][s][1];
|
||||
}
|
||||
}
|
||||
|
||||
// only add indexes for first stage
|
||||
FillCloudySkySide( sky_mins_subd, sky_maxs_subd, ( stage == 0 ) );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** R_BuildCloudData
|
||||
*/
|
||||
void R_BuildCloudData( shaderCommands_t *input )
|
||||
{
|
||||
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;
|
||||
|
||||
if ( shader->sky.cloudHeight )
|
||||
{
|
||||
for ( i = 0; i < MAX_SHADER_STAGES; i++ )
|
||||
{
|
||||
if ( !tess.xstages[i] ) {
|
||||
break;
|
||||
}
|
||||
FillCloudBox( shader, i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** R_InitSkyTexCoords
|
||||
** Called when a sky shader is parsed
|
||||
*/
|
||||
#define SQR( a ) ((a)*(a))
|
||||
void R_InitSkyTexCoords( float heightCloud )
|
||||
{
|
||||
int i, s, t;
|
||||
float radiusWorld = 4096;
|
||||
float p;
|
||||
float sRad, tRad;
|
||||
vec3_t skyVec;
|
||||
vec3_t v;
|
||||
|
||||
// init zfar so MakeSkyVec works even though
|
||||
// a world hasn't been bounded
|
||||
backEnd.viewParms.zFar = 1024;
|
||||
|
||||
for ( i = 0; i < 6; i++ )
|
||||
{
|
||||
for ( t = 0; t <= SKY_SUBDIVISIONS; t++ )
|
||||
{
|
||||
for ( s = 0; s <= SKY_SUBDIVISIONS; s++ )
|
||||
{
|
||||
// compute vector from view origin to sky side integral point
|
||||
MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
|
||||
( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
|
||||
i,
|
||||
NULL,
|
||||
skyVec );
|
||||
|
||||
// compute parametric value 'p' that intersects with cloud layer
|
||||
p = ( 1.0f / ( 2 * DotProduct( skyVec, skyVec ) ) ) *
|
||||
( -2 * skyVec[2] * radiusWorld +
|
||||
2 * sqrt( SQR( skyVec[2] ) * SQR( radiusWorld ) +
|
||||
2 * SQR( skyVec[0] ) * radiusWorld * heightCloud +
|
||||
SQR( skyVec[0] ) * SQR( heightCloud ) +
|
||||
2 * SQR( skyVec[1] ) * radiusWorld * heightCloud +
|
||||
SQR( skyVec[1] ) * SQR( heightCloud ) +
|
||||
2 * SQR( skyVec[2] ) * radiusWorld * heightCloud +
|
||||
SQR( skyVec[2] ) * SQR( heightCloud ) ) );
|
||||
|
||||
s_cloudTexP[i][t][s] = p;
|
||||
|
||||
// compute intersection point based on p
|
||||
VectorScale( skyVec, p, v );
|
||||
v[2] += radiusWorld;
|
||||
|
||||
// compute vector from world origin to intersection point 'v'
|
||||
VectorNormalize( v );
|
||||
|
||||
sRad = Q_acos( v[0] );
|
||||
tRad = Q_acos( v[1] );
|
||||
|
||||
s_cloudTexCoords[i][t][s][0] = sRad;
|
||||
s_cloudTexCoords[i][t][s][1] = tRad;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================================
|
||||
|
||||
/*
|
||||
** RB_DrawSun
|
||||
*/
|
||||
void RB_DrawSun( void ) {
|
||||
float size;
|
||||
float dist;
|
||||
vec3_t origin, vec1, vec2;
|
||||
vec3_t temp;
|
||||
|
||||
if ( !backEnd.skyRenderedThisView ) {
|
||||
return;
|
||||
}
|
||||
if ( !r_drawSun->integer ) {
|
||||
return;
|
||||
}
|
||||
qglLoadMatrixf( backEnd.viewParms.world.modelMatrix );
|
||||
qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]);
|
||||
|
||||
dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3)
|
||||
size = dist * 0.4;
|
||||
|
||||
VectorScale( tr.sunDirection, dist, origin );
|
||||
PerpendicularVector( vec1, tr.sunDirection );
|
||||
CrossProduct( tr.sunDirection, vec1, vec2 );
|
||||
|
||||
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_EndSurface();
|
||||
|
||||
// back to normal depth range
|
||||
qglDepthRange( 0.0, 1.0 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
RB_StageIteratorSky
|
||||
|
||||
All of the visible sky triangles are in tess
|
||||
|
||||
Other things could be stuck in here, like birds in the sky, etc
|
||||
================
|
||||
*/
|
||||
void RB_StageIteratorSky( void ) {
|
||||
if ( r_fastsky->integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// go through all the polygons and project them onto
|
||||
// the sky box to see which blocks on each side need
|
||||
// to be drawn
|
||||
RB_ClipSkyPolygons( &tess );
|
||||
|
||||
// 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 );
|
||||
}
|
||||
|
||||
// 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 );
|
||||
|
||||
qglPopMatrix();
|
||||
}
|
||||
|
||||
// generate the vertexes for all the clouds, which will be drawn
|
||||
// by the generic shader routine
|
||||
R_BuildCloudData( &tess );
|
||||
|
||||
RB_StageIteratorGeneric();
|
||||
|
||||
// draw the inner skybox
|
||||
|
||||
|
||||
// back to normal depth range
|
||||
qglDepthRange( 0.0, 1.0 );
|
||||
|
||||
// note that sky was drawn so we will draw a sun later
|
||||
backEnd.skyRenderedThisView = qtrue;
|
||||
}
|
||||
|
48
code/renderergl1/tr_subs.c
Normal file
48
code/renderergl1/tr_subs.c
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2010 James Canete (use.less01@gmail.com)
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// tr_subs.c - common function replacements for modular renderer
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
void QDECL Com_Printf( const char *msg, ... )
|
||||
{
|
||||
va_list argptr;
|
||||
char text[1024];
|
||||
|
||||
va_start(argptr, msg);
|
||||
Q_vsnprintf(text, sizeof(text), msg, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
ri.Printf(PRINT_ALL, "%s", text);
|
||||
}
|
||||
|
||||
void QDECL Com_Error( int level, const char *error, ... )
|
||||
{
|
||||
va_list argptr;
|
||||
char text[1024];
|
||||
|
||||
va_start(argptr, error);
|
||||
Q_vsnprintf(text, sizeof(text), error, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
ri.Error(level, "%s", text);
|
||||
}
|
1244
code/renderergl1/tr_surface.c
Normal file
1244
code/renderergl1/tr_surface.c
Normal file
File diff suppressed because it is too large
Load diff
668
code/renderergl1/tr_world.c
Normal file
668
code/renderergl1/tr_world.c
Normal file
|
@ -0,0 +1,668 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Quake III Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
#include "tr_local.h"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
R_CullTriSurf
|
||||
|
||||
Returns true if the grid is completely culled away.
|
||||
Also sets the clipped hint bit in tess
|
||||
=================
|
||||
*/
|
||||
static qboolean R_CullTriSurf( srfTriangles_t *cv ) {
|
||||
int boxCull;
|
||||
|
||||
boxCull = R_CullLocalBox( cv->bounds );
|
||||
|
||||
if ( boxCull == CULL_OUT ) {
|
||||
return qtrue;
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_CullGrid
|
||||
|
||||
Returns true if the grid is completely culled away.
|
||||
Also sets the clipped hint bit in tess
|
||||
=================
|
||||
*/
|
||||
static qboolean R_CullGrid( srfGridMesh_t *cv ) {
|
||||
int boxCull;
|
||||
int sphereCull;
|
||||
|
||||
if ( r_nocurves->integer ) {
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if ( tr.currentEntityNum != REFENTITYNUM_WORLD ) {
|
||||
sphereCull = R_CullLocalPointAndRadius( cv->localOrigin, cv->meshRadius );
|
||||
} else {
|
||||
sphereCull = R_CullPointAndRadius( cv->localOrigin, cv->meshRadius );
|
||||
}
|
||||
|
||||
// check for trivial reject
|
||||
if ( sphereCull == CULL_OUT )
|
||||
{
|
||||
tr.pc.c_sphere_cull_patch_out++;
|
||||
return qtrue;
|
||||
}
|
||||
// check bounding box if necessary
|
||||
else if ( sphereCull == CULL_CLIP )
|
||||
{
|
||||
tr.pc.c_sphere_cull_patch_clip++;
|
||||
|
||||
boxCull = R_CullLocalBox( cv->meshBounds );
|
||||
|
||||
if ( boxCull == CULL_OUT )
|
||||
{
|
||||
tr.pc.c_box_cull_patch_out++;
|
||||
return qtrue;
|
||||
}
|
||||
else if ( boxCull == CULL_IN )
|
||||
{
|
||||
tr.pc.c_box_cull_patch_in++;
|
||||
}
|
||||
else
|
||||
{
|
||||
tr.pc.c_box_cull_patch_clip++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tr.pc.c_sphere_cull_patch_in++;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
R_CullSurface
|
||||
|
||||
Tries to back face cull surfaces before they are lighted or
|
||||
added to the sorting list.
|
||||
|
||||
This will also allow mirrors on both sides of a model without recursion.
|
||||
================
|
||||
*/
|
||||
static qboolean R_CullSurface( surfaceType_t *surface, shader_t *shader ) {
|
||||
srfSurfaceFace_t *sface;
|
||||
float d;
|
||||
|
||||
if ( r_nocull->integer ) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if ( *surface == SF_GRID ) {
|
||||
return R_CullGrid( (srfGridMesh_t *)surface );
|
||||
}
|
||||
|
||||
if ( *surface == SF_TRIANGLES ) {
|
||||
return R_CullTriSurf( (srfTriangles_t *)surface );
|
||||
}
|
||||
|
||||
if ( *surface != SF_FACE ) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if ( shader->cullType == CT_TWO_SIDED ) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
// face culling
|
||||
if ( !r_facePlaneCull->integer ) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
sface = ( srfSurfaceFace_t * ) surface;
|
||||
d = DotProduct (tr.or.viewOrigin, sface->plane.normal);
|
||||
|
||||
// don't cull exactly on the plane, because there are levels of rounding
|
||||
// through the BSP, ICD, and hardware that may cause pixel gaps if an
|
||||
// epsilon isn't allowed here
|
||||
if ( shader->cullType == CT_FRONT_SIDED ) {
|
||||
if ( d < sface->plane.dist - 8 ) {
|
||||
return qtrue;
|
||||
}
|
||||
} else {
|
||||
if ( d > sface->plane.dist + 8 ) {
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
|
||||
static int R_DlightFace( srfSurfaceFace_t *face, int dlightBits ) {
|
||||
float d;
|
||||
int i;
|
||||
dlight_t *dl;
|
||||
|
||||
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
|
||||
if ( ! ( dlightBits & ( 1 << i ) ) ) {
|
||||
continue;
|
||||
}
|
||||
dl = &tr.refdef.dlights[i];
|
||||
d = DotProduct( dl->origin, face->plane.normal ) - face->plane.dist;
|
||||
if ( d < -dl->radius || d > dl->radius ) {
|
||||
// dlight doesn't reach the plane
|
||||
dlightBits &= ~( 1 << i );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !dlightBits ) {
|
||||
tr.pc.c_dlightSurfacesCulled++;
|
||||
}
|
||||
|
||||
face->dlightBits = dlightBits;
|
||||
return dlightBits;
|
||||
}
|
||||
|
||||
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 & ( 1 << i ) ) ) {
|
||||
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] ) {
|
||||
// dlight doesn't reach the bounds
|
||||
dlightBits &= ~( 1 << i );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !dlightBits ) {
|
||||
tr.pc.c_dlightSurfacesCulled++;
|
||||
}
|
||||
|
||||
grid->dlightBits = dlightBits;
|
||||
return dlightBits;
|
||||
}
|
||||
|
||||
|
||||
static int R_DlightTrisurf( srfTriangles_t *surf, int dlightBits ) {
|
||||
// FIXME: more dlight culling to trisurfs...
|
||||
surf->dlightBits = dlightBits;
|
||||
return dlightBits;
|
||||
#if 0
|
||||
int i;
|
||||
dlight_t *dl;
|
||||
|
||||
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
|
||||
if ( ! ( dlightBits & ( 1 << i ) ) ) {
|
||||
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] ) {
|
||||
// dlight doesn't reach the bounds
|
||||
dlightBits &= ~( 1 << i );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !dlightBits ) {
|
||||
tr.pc.c_dlightSurfacesCulled++;
|
||||
}
|
||||
|
||||
grid->dlightBits = dlightBits;
|
||||
return dlightBits;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
R_DlightSurface
|
||||
|
||||
The given surface is going to be drawn, and it touches a leaf
|
||||
that is touched by one or more dlights, so try to throw out
|
||||
more dlights if possible.
|
||||
====================
|
||||
*/
|
||||
static int R_DlightSurface( msurface_t *surf, int dlightBits ) {
|
||||
if ( *surf->data == SF_FACE ) {
|
||||
dlightBits = R_DlightFace( (srfSurfaceFace_t *)surf->data, dlightBits );
|
||||
} else if ( *surf->data == SF_GRID ) {
|
||||
dlightBits = R_DlightGrid( (srfGridMesh_t *)surf->data, dlightBits );
|
||||
} else if ( *surf->data == SF_TRIANGLES ) {
|
||||
dlightBits = R_DlightTrisurf( (srfTriangles_t *)surf->data, dlightBits );
|
||||
} else {
|
||||
dlightBits = 0;
|
||||
}
|
||||
|
||||
if ( dlightBits ) {
|
||||
tr.pc.c_dlightSurfaces++;
|
||||
}
|
||||
|
||||
return dlightBits;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
======================
|
||||
R_AddWorldSurface
|
||||
======================
|
||||
*/
|
||||
static void R_AddWorldSurface( msurface_t *surf, int dlightBits ) {
|
||||
if ( surf->viewCount == tr.viewCount ) {
|
||||
return; // already in this view
|
||||
}
|
||||
|
||||
surf->viewCount = tr.viewCount;
|
||||
// FIXME: bmodel fog?
|
||||
|
||||
// try to cull before dlighting or adding
|
||||
if ( R_CullSurface( surf->data, surf->shader ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check for dlighting
|
||||
if ( dlightBits ) {
|
||||
dlightBits = R_DlightSurface( surf, dlightBits );
|
||||
dlightBits = ( dlightBits != 0 );
|
||||
}
|
||||
|
||||
R_AddDrawSurf( surf->data, surf->shader, surf->fogIndex, dlightBits );
|
||||
}
|
||||
|
||||
/*
|
||||
=============================================================
|
||||
|
||||
BRUSH MODELS
|
||||
|
||||
=============================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
=================
|
||||
R_AddBrushModelSurfaces
|
||||
=================
|
||||
*/
|
||||
void R_AddBrushModelSurfaces ( trRefEntity_t *ent ) {
|
||||
bmodel_t *bmodel;
|
||||
int clip;
|
||||
model_t *pModel;
|
||||
int i;
|
||||
|
||||
pModel = R_GetModelByHandle( ent->e.hModel );
|
||||
|
||||
bmodel = pModel->bmodel;
|
||||
|
||||
clip = R_CullLocalBox( bmodel->bounds );
|
||||
if ( clip == CULL_OUT ) {
|
||||
return;
|
||||
}
|
||||
|
||||
R_SetupEntityLighting( &tr.refdef, ent );
|
||||
R_DlightBmodel( bmodel );
|
||||
|
||||
for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) {
|
||||
R_AddWorldSurface( bmodel->firstSurface + i, tr.currentEntity->needDlights );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============================================================
|
||||
|
||||
WORLD MODEL
|
||||
|
||||
=============================================================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
R_RecursiveWorldNode
|
||||
================
|
||||
*/
|
||||
static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits ) {
|
||||
|
||||
do {
|
||||
int newDlights[2];
|
||||
|
||||
// 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 ( !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 & 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 ( 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 ) {
|
||||
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
|
||||
|
||||
// determine which dlights are needed
|
||||
newDlights[0] = 0;
|
||||
newDlights[1] = 0;
|
||||
if ( dlightBits ) {
|
||||
int i;
|
||||
|
||||
for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) {
|
||||
dlight_t *dl;
|
||||
float dist;
|
||||
|
||||
if ( dlightBits & ( 1 << i ) ) {
|
||||
dl = &tr.refdef.dlights[i];
|
||||
dist = DotProduct( dl->origin, node->plane->normal ) - node->plane->dist;
|
||||
|
||||
if ( dist > -dl->radius ) {
|
||||
newDlights[0] |= ( 1 << i );
|
||||
}
|
||||
if ( dist < dl->radius ) {
|
||||
newDlights[1] |= ( 1 << i );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recurse down the children, front side first
|
||||
R_RecursiveWorldNode (node->children[0], planeBits, newDlights[0] );
|
||||
|
||||
// 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];
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
R_PointInLeaf
|
||||
===============
|
||||
*/
|
||||
static mnode_t *R_PointInLeaf( const vec3_t p ) {
|
||||
mnode_t *node;
|
||||
float d;
|
||||
cplane_t *plane;
|
||||
|
||||
if ( !tr.world ) {
|
||||
ri.Error (ERR_DROP, "R_PointInLeaf: bad model");
|
||||
}
|
||||
|
||||
node = tr.world->nodes;
|
||||
while( 1 ) {
|
||||
if (node->contents != -1) {
|
||||
break;
|
||||
}
|
||||
plane = node->plane;
|
||||
d = DotProduct (p,plane->normal) - plane->dist;
|
||||
if (d > 0) {
|
||||
node = node->children[0];
|
||||
} else {
|
||||
node = node->children[1];
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
R_ClusterPVS
|
||||
==============
|
||||
*/
|
||||
static const byte *R_ClusterPVS (int cluster) {
|
||||
if (!tr.world || !tr.world->vis || cluster < 0 || cluster >= tr.world->numClusters ) {
|
||||
return tr.world->novis;
|
||||
}
|
||||
|
||||
return tr.world->vis + cluster * tr.world->clusterBytes;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_inPVS
|
||||
=================
|
||||
*/
|
||||
qboolean R_inPVS( const vec3_t p1, const vec3_t p2 ) {
|
||||
mnode_t *leaf;
|
||||
byte *vis;
|
||||
|
||||
leaf = R_PointInLeaf( p1 );
|
||||
vis = ri.CM_ClusterPVS( leaf->cluster ); // why not R_ClusterPVS ??
|
||||
leaf = R_PointInLeaf( p2 );
|
||||
|
||||
if ( !(vis[leaf->cluster>>3] & (1<<(leaf->cluster&7))) ) {
|
||||
return qfalse;
|
||||
}
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
R_MarkLeaves
|
||||
|
||||
Mark the leaves and nodes that are in the PVS for the current
|
||||
cluster
|
||||
===============
|
||||
*/
|
||||
static void R_MarkLeaves (void) {
|
||||
const byte *vis;
|
||||
mnode_t *leaf, *parent;
|
||||
int i;
|
||||
int cluster;
|
||||
|
||||
// lockpvs lets designers walk around to determine the
|
||||
// extent of the current pvs
|
||||
if ( r_lockpvs->integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// current viewcluster
|
||||
leaf = R_PointInLeaf( tr.viewParms.pvsOrigin );
|
||||
cluster = leaf->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
|
||||
&& !r_showcluster->modified ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( r_showcluster->modified || r_showcluster->integer ) {
|
||||
r_showcluster->modified = qfalse;
|
||||
if ( r_showcluster->integer ) {
|
||||
ri.Printf( PRINT_ALL, "cluster:%i area:%i\n", cluster, leaf->area );
|
||||
}
|
||||
}
|
||||
|
||||
tr.visCount++;
|
||||
tr.viewCluster = cluster;
|
||||
|
||||
if ( r_novis->integer || tr.viewCluster == -1 ) {
|
||||
for (i=0 ; i<tr.world->numnodes ; i++) {
|
||||
if (tr.world->nodes[i].contents != CONTENTS_SOLID) {
|
||||
tr.world->nodes[i].visframe = tr.visCount;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
vis = R_ClusterPVS (tr.viewCluster);
|
||||
|
||||
for (i=0,leaf=tr.world->nodes ; i<tr.world->numnodes ; i++, leaf++) {
|
||||
cluster = leaf->cluster;
|
||||
if ( cluster < 0 || cluster >= tr.world->numClusters ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check general pvs
|
||||
if ( !(vis[cluster>>3] & (1<<(cluster&7))) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for door connection
|
||||
if ( (tr.refdef.areamask[leaf->area>>3] & (1<<(leaf->area&7)) ) ) {
|
||||
continue; // not visible
|
||||
}
|
||||
|
||||
parent = leaf;
|
||||
do {
|
||||
if (parent->visframe == tr.visCount)
|
||||
break;
|
||||
parent->visframe = tr.visCount;
|
||||
parent = parent->parent;
|
||||
} while (parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
R_AddWorldSurfaces
|
||||
=============
|
||||
*/
|
||||
void R_AddWorldSurfaces (void) {
|
||||
if ( !r_drawworld->integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
tr.currentEntityNum = REFENTITYNUM_WORLD;
|
||||
tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT;
|
||||
|
||||
// determine which leaves are in the PVS / areamask
|
||||
R_MarkLeaves ();
|
||||
|
||||
// clear out the visible min/max
|
||||
ClearBounds( tr.viewParms.visBounds[0], tr.viewParms.visBounds[1] );
|
||||
|
||||
// perform frustum culling and add all the potentially visible surfaces
|
||||
if ( tr.refdef.num_dlights > 32 ) {
|
||||
tr.refdef.num_dlights = 32 ;
|
||||
}
|
||||
R_RecursiveWorldNode( tr.world->nodes, 15, ( 1 << tr.refdef.num_dlights ) - 1 );
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue