//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $Date: $ // $NoKeywords: $ //=============================================================================// #ifndef GL_MODEL_PRIVATE_H #define GL_MODEL_PRIVATE_H #ifdef _WIN32 #pragma once #endif #include "mathlib/vector4d.h" #include "tier0/dbg.h" #include "tier1/utlsymbol.h" #include "idispinfo.h" #include "shadowmgr.h" #include "vcollide.h" #include "studio.h" #include "qlimits.h" #include "host.h" #include "gl_model.h" #include "cmodel.h" #include "bspfile.h" #include "Overlay.h" //#include "datamap.h" #include "surfacehandle.h" #include "mathlib/compressed_light_cube.h" #include "datacache/imdlcache.h" #include "bitmap/cubemap.h" //----------------------------------------------------------------------------- // forward declarations //----------------------------------------------------------------------------- struct studiomeshdata_t; struct decal_t; struct msurface1_t; struct msurfacelighting_t; struct msurfacenormal_t; class ITexture; class CEngineSprite; // !!! if this is changed, it must be changed in asm_draw.h too !!! struct mvertex_t { Vector position; }; // !!! if this is changed, it must be changed in asm_draw.h too !!! struct medge_t { unsigned short v[2]; // unsigned int cachededgeoffset; }; class IMaterial; class IMaterialVar; // This is here for b/w compatibility with world surfaces that use // WorldVertexTransition. We can get rid of it when we rev the engine. #define TEXINFO_USING_BASETEXTURE2 0x0001 struct mtexinfo_t { Vector4D textureVecsTexelsPerWorldUnits[2]; // [s/t] unit vectors in world space. // [i][3] is the s/t offset relative to the origin. Vector4D lightmapVecsLuxelsPerWorldUnits[2]; float luxelsPerWorldUnit; float worldUnitsPerLuxel; unsigned short flags; // SURF_ flags. unsigned short texinfoFlags;// TEXINFO_ flags. IMaterial *material; mtexinfo_t( mtexinfo_t const& src ) { // copy constructor needed since Vector4D has no copy constructor memcpy( this, &src, sizeof(mtexinfo_t) ); } }; struct mnode_t { // common with leaf int contents; // <0 to differentiate from leafs // -1 means check the node for visibility // -2 means don't check the node for visibility int visframe; // node needs to be traversed if current mnode_t *parent; short area; // If all leaves below this node are in the same area, then // this is the area index. If not, this is -1. short flags; VectorAligned m_vecCenter; VectorAligned m_vecHalfDiagonal; // node specific cplane_t *plane; mnode_t *children[2]; unsigned short firstsurface; unsigned short numsurfaces; }; struct mleaf_t { public: // common with node int contents; // contents mask int visframe; // node needs to be traversed if current mnode_t *parent; short area; short flags; VectorAligned m_vecCenter; VectorAligned m_vecHalfDiagonal; // leaf specific short cluster; short leafWaterDataID; unsigned short firstmarksurface; unsigned short nummarksurfaces; short nummarknodesurfaces; short unused; unsigned short dispListStart; // index into displist of first displacement unsigned short dispCount; // number of displacements in the list for this leaf }; struct mleafwaterdata_t { float surfaceZ; float minZ; short surfaceTexInfoID; short firstLeafIndex; }; struct mcubemapsample_t { Vector origin; ITexture *pTexture; unsigned char size; // default (mat_envmaptgasize) if 0, 1<<(size-1) otherwise. }; typedef struct mportal_s { unsigned short *vertList; int numportalverts; cplane_t *plane; unsigned short cluster[2]; // int visframe; } mportal_t; typedef struct mcluster_s { unsigned short *portalList; int numportals; } mcluster_t; struct mmodel_t { Vector mins, maxs; Vector origin; // for sounds or lights float radius; int headnode; int firstface, numfaces; }; struct mprimitive_t { int type; unsigned short firstIndex; unsigned short indexCount; unsigned short firstVert; unsigned short vertCount; }; struct mprimvert_t { Vector pos; float texCoord[2]; float lightCoord[2]; }; typedef dleafambientindex_t mleafambientindex_t; typedef dleafambientlighting_t mleafambientlighting_t; struct LightShadowZBufferSample_t { float m_flTraceDistance; // how far we traced. 0 = invalid float m_flHitDistance; // where we hit }; #define SHADOW_ZBUF_RES 8 // 6 * 64 * 2 * 4 = 3k bytes per light typedef CCubeMap< LightShadowZBufferSample_t, SHADOW_ZBUF_RES> lightzbuffer_t; #include "model_types.h" #define MODELFLAG_MATERIALPROXY 0x0001 // we've got a material proxy #define MODELFLAG_TRANSLUCENT 0x0002 // we've got a translucent model #define MODELFLAG_VERTEXLIT 0x0004 // we've got a vertex-lit model #define MODELFLAG_TRANSLUCENT_TWOPASS 0x0008 // render opaque part in opaque pass #define MODELFLAG_FRAMEBUFFER_TEXTURE 0x0010 // we need the framebuffer as a texture #define MODELFLAG_HAS_DLIGHT 0x0020 // need to check dlights #define MODELFLAG_STUDIOHDR_USES_FB_TEXTURE 0x0100 // persisted from studiohdr #define MODELFLAG_STUDIOHDR_USES_BUMPMAPPING 0x0200 // persisted from studiohdr #define MODELFLAG_STUDIOHDR_USES_ENV_CUBEMAP 0x0400 // persisted from studiohdr #define MODELFLAG_STUDIOHDR_AMBIENT_BOOST 0x0800 // persisted from studiohdr #define MODELFLAG_STUDIOHDR_DO_NOT_CAST_SHADOWS 0x1000 // persisted from studiohdr struct worldbrushdata_t { int numsubmodels; int numplanes; cplane_t *planes; int numleafs; // number of visible leafs, not counting 0 mleaf_t *leafs; int numleafwaterdata; mleafwaterdata_t *leafwaterdata; int numvertexes; mvertex_t *vertexes; int numoccluders; doccluderdata_t *occluders; int numoccluderpolys; doccluderpolydata_t *occluderpolys; int numoccludervertindices; int *occludervertindices; int numvertnormalindices; // These index vertnormals. unsigned short *vertnormalindices; int numvertnormals; Vector *vertnormals; int numnodes; mnode_t *nodes; unsigned short *m_LeafMinDistToWater; int numtexinfo; mtexinfo_t *texinfo; int numtexdata; csurface_t *texdata; int numDispInfos; HDISPINFOARRAY hDispInfos; // Use DispInfo_Index to get IDispInfos.. /* int numOrigSurfaces; msurface_t *pOrigSurfaces; */ int numsurfaces; msurface1_t *surfaces1; msurface2_t *surfaces2; msurfacelighting_t *surfacelighting; msurfacenormal_t *surfacenormals; bool unloadedlightmaps; int numvertindices; unsigned short *vertindices; int nummarksurfaces; SurfaceHandle_t *marksurfaces; ColorRGBExp32 *lightdata; int numworldlights; dworldlight_t *worldlights; lightzbuffer_t *shadowzbuffers; // non-polygon primitives (strips and lists) int numprimitives; mprimitive_t *primitives; int numprimverts; mprimvert_t *primverts; int numprimindices; unsigned short *primindices; int m_nAreas; darea_t *m_pAreas; int m_nAreaPortals; dareaportal_t *m_pAreaPortals; int m_nClipPortalVerts; Vector *m_pClipPortalVerts; mcubemapsample_t *m_pCubemapSamples; int m_nCubemapSamples; int m_nDispInfoReferences; unsigned short *m_pDispInfoReferences; mleafambientindex_t *m_pLeafAmbient; mleafambientlighting_t *m_pAmbientSamples; #if 0 int numportals; mportal_t *portals; int numclusters; mcluster_t *clusters; int numportalverts; unsigned short *portalverts; int numclusterportals; unsigned short *clusterportals; #endif }; // only models with type "mod_brush" have this data struct brushdata_t { worldbrushdata_t *pShared; int firstmodelsurface, nummodelsurfaces; unsigned short renderHandle; unsigned short firstnode; }; // only models with type "mod_sprite" have this data struct spritedata_t { int numframes; int width; int height; CEngineSprite *sprite; }; struct model_t { FileNameHandle_t fnHandle; CUtlString strName; int nLoadFlags; // mark loaded/not loaded int nServerCount; // marked at load IMaterial **ppMaterials; // null-terminated runtime material cache; ((intptr_t*)(ppMaterials))[-1] == nMaterials modtype_t type; int flags; // MODELFLAG_??? // volume occupied by the model graphics Vector mins, maxs; float radius; union { brushdata_t brush; MDLHandle_t studio; spritedata_t sprite; }; }; //----------------------------------------------------------------------------- // Decals //----------------------------------------------------------------------------- struct decallist_t { DECLARE_SIMPLE_DATADESC(); Vector position; char name[ 128 ]; short entityIndex; byte depth; byte flags; // This is the surface plane that we hit so that we can move certain decals across // transitions if they hit similar geometry Vector impactPlaneNormal; }; inline class IDispInfo *MLeaf_Disaplcement( mleaf_t *pLeaf, int index, worldbrushdata_t *pData = host_state.worldbrush ) { Assert(indexdispCount); int dispIndex = pData->m_pDispInfoReferences[pLeaf->dispListStart+index]; return DispInfo_IndexArray( pData->hDispInfos, dispIndex ); } #define MAXLIGHTMAPS 4 // drawing surface flags #define SURFDRAW_NOLIGHT 0x00000001 // no lightmap #define SURFDRAW_NODE 0x00000002 // This surface is on a node #define SURFDRAW_SKY 0x00000004 // portal to sky #define SURFDRAW_BUMPLIGHT 0x00000008 // Has multiple lightmaps for bump-mapping #define SURFDRAW_NODRAW 0x00000010 // don't draw this surface, not really visible #define SURFDRAW_TRANS 0x00000020 // sort this surface from back to front #define SURFDRAW_PLANEBACK 0x00000040 // faces away from plane of the node that stores this face #define SURFDRAW_DYNAMIC 0x00000080 // Don't use a static buffer for this face #define SURFDRAW_TANGENTSPACE 0x00000100 // This surface needs a tangent space #define SURFDRAW_NOCULL 0x00000200 // Don't bother backface culling these #define SURFDRAW_HASLIGHTSYTLES 0x00000400 // has a lightstyle other than 0 #define SURFDRAW_HAS_DISP 0x00000800 // has a dispinfo #define SURFDRAW_ALPHATEST 0x00001000 // Is alphstested #define SURFDRAW_NOSHADOWS 0x00002000 // No shadows baby #define SURFDRAW_NODECALS 0x00004000 // No decals baby #define SURFDRAW_HAS_PRIMS 0x00008000 // has a list of prims #define SURFDRAW_WATERSURFACE 0x00010000 // This is a water surface #define SURFDRAW_UNDERWATER 0x00020000 #define SURFDRAW_ABOVEWATER 0x00040000 #define SURFDRAW_HASDLIGHT 0x00080000 // Has some kind of dynamic light that must be checked #define SURFDRAW_DLIGHTPASS 0x00100000 // Must be drawn in the dlight pass #define SURFDRAW_UNUSED2 0x00200000 // unused #define SURFDRAW_VERTCOUNT_MASK 0xFF000000 // 8 bits of vertex count #define SURFDRAW_SORTGROUP_MASK 0x00C00000 // 2 bits of sortgroup #define SURFDRAW_VERTCOUNT_SHIFT 24 #define SURFDRAW_SORTGROUP_SHIFT 22 // NOTE: 16-bytes, preserve size/alignment - we index this alot struct msurface1_t { // garymct - are these needed? - used by decal projection code int textureMins[2]; // smallest unnormalized s/t position on the surface. short textureExtents[2]; // ?? s/t texture size, 1..512 for all non-sky surfaces struct { unsigned short numPrims; unsigned short firstPrimID; // index into primitive list if numPrims > 0 } prims; }; #pragma pack(1) struct msurfacenormal_t { unsigned int firstvertnormal; // unsigned short firstvertnormal; // FIXME: Should I just point to the leaf here since it has this data????????????? // short fogVolumeID; // -1 if not in fog }; #pragma pack() // This is a single cache line (32 bytes) struct msurfacelighting_t { // You read that minus sign right. See the comment below. ColorRGBExp32 *AvgLightColor( int nLightStyleIndex ) { return m_pSamples - (nLightStyleIndex + 1); } // Lightmap info short m_LightmapMins[2]; short m_LightmapExtents[2]; short m_OffsetIntoLightmapPage[2]; int m_nLastComputedFrame; // last frame the surface's lightmap was recomputed int m_fDLightBits; // Indicates which dlights illuminates this surface. int m_nDLightFrame; // Indicates the last frame in which dlights illuminated this surface unsigned char m_nStyles[MAXLIGHTMAPS]; // index into d_lightstylevalue[] for animated lights // no one surface can be effected by more than 4 // animated lights. // NOTE: This is tricky. To get this to fit in a single cache line, // and to save the extra memory of not having to store average light colors for // lightstyles that are not used, I store between 0 and 4 average light colors +before+ // the samples, depending on how many lightstyles there are. Naturally, accessing // an average color for an undefined lightstyle on the surface results in undefined results. // 0->4 avg light colors, *in reverse order from m_nStyles* + [numstyles*surfsize] ColorRGBExp32 *m_pSamples; }; const int WORLD_DECAL_HANDLE_INVALID = 0xFFFF; typedef unsigned short WorldDecalHandle_t; #pragma pack(1) // NOTE: 32-bytes. Aligned/indexed often struct msurface2_t { unsigned int flags; // see SURFDRAW_ #defines (only 22-bits right now) // These are packed in to flags now //unsigned char vertCount; // number of verts for this surface //unsigned char sortGroup; // only uses 2 bits, subdivide? cplane_t *plane; // pointer to shared plane int firstvertindex; // look up in model->vertindices[] (only uses 17-18 bits?) WorldDecalHandle_t decals; ShadowDecalHandle_t m_ShadowDecals; // unsigned short OverlayFragmentHandle_t m_nFirstOverlayFragment; // First overlay fragment on the surface (short) short materialSortID; unsigned short vertBufferIndex; unsigned short m_bDynamicShadowsEnabled : 1; // Can this surface receive dynamic shadows? unsigned short texinfo : 15; IDispInfo *pDispInfo; // displacement map information int visframe; // should be drawn when node is crossed }; #pragma pack() inline unsigned short MSurf_AreDynamicShadowsEnabled( SurfaceHandle_t surfID ) { return surfID->m_bDynamicShadowsEnabled; } inline int MSurf_Index( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { int surfaceIndex = surfID - pData->surfaces2; Assert(surfaceIndex >= 0 && surfaceIndex < pData->numsurfaces); return surfaceIndex; } inline const SurfaceHandle_t SurfaceHandleFromIndex( int surfaceIndex, const worldbrushdata_t *pData ) { return &pData->surfaces2[surfaceIndex]; } inline SurfaceHandle_t SurfaceHandleFromIndex( int surfaceIndex, worldbrushdata_t *pData = host_state.worldbrush ) { return &pData->surfaces2[surfaceIndex]; } #if _DEBUG #define ASSERT_SURF_VALID(surfID) MSurf_Index(surfID) #else #define ASSERT_SURF_VALID(surfID) #endif inline unsigned int& MSurf_Flags( SurfaceHandle_t surfID ) { return surfID->flags; } inline bool SurfaceHasDispInfo( SurfaceHandle_t surfID ) { return ( MSurf_Flags( surfID ) & SURFDRAW_HAS_DISP ) ? true : false; } inline bool SurfaceHasPrims( SurfaceHandle_t surfID ) { return ( MSurf_Flags( surfID ) & SURFDRAW_HAS_PRIMS ) ? true : false; } inline int& MSurf_VisFrame( SurfaceHandle_t surfID ) { return surfID->visframe; } inline int MSurf_SortGroup( SurfaceHandle_t surfID ) { return (surfID->flags & SURFDRAW_SORTGROUP_MASK) >> SURFDRAW_SORTGROUP_SHIFT; } inline void MSurf_SetSortGroup( SurfaceHandle_t surfID, int sortGroup ) { unsigned int flags = (sortGroup << SURFDRAW_SORTGROUP_SHIFT) & SURFDRAW_SORTGROUP_MASK; surfID->flags |= flags; } /* inline int& MSurf_DLightFrame( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { // ASSERT_SURF_VALID( surfID ); Assert( pData ); return pData->surfacelighting[surfID].dlightframe; } */ inline int& MSurf_DLightBits( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { int surfaceIndex = MSurf_Index(surfID,pData); // ASSERT_SURF_VALID( surfID ); Assert( pData ); return pData->surfacelighting[surfaceIndex].m_fDLightBits; } inline cplane_t& MSurf_Plane( SurfaceHandle_t surfID ) { return *surfID->plane; } inline int& MSurf_FirstVertIndex( SurfaceHandle_t surfID ) { return surfID->firstvertindex; } inline int MSurf_VertCount( SurfaceHandle_t surfID ) { return (surfID->flags >> SURFDRAW_VERTCOUNT_SHIFT) & 0xFF; } inline void MSurf_SetVertCount( SurfaceHandle_t surfID, int vertCount ) { int flags = (vertCount << SURFDRAW_VERTCOUNT_SHIFT) & SURFDRAW_VERTCOUNT_MASK; surfID->flags |= flags; } inline int *MSurf_TextureMins( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { int surfaceIndex = MSurf_Index(surfID,pData); // ASSERT_SURF_VALID( surfID ); Assert( pData ); return pData->surfaces1[surfaceIndex].textureMins; } inline short *MSurf_TextureExtents( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { int surfaceIndex = MSurf_Index(surfID,pData); // ASSERT_SURF_VALID( surfID ); Assert( pData ); return pData->surfaces1[surfaceIndex].textureExtents; } inline short *MSurf_LightmapMins( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { int surfaceIndex = MSurf_Index(surfID,pData); // ASSERT_SURF_VALID( surfID ); Assert( pData ); return pData->surfacelighting[surfaceIndex].m_LightmapMins; } inline short *MSurf_LightmapExtents( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { int surfaceIndex = MSurf_Index(surfID,pData); // ASSERT_SURF_VALID( surfID ); Assert( pData ); return pData->surfacelighting[surfaceIndex].m_LightmapExtents; } inline short MSurf_MaxLightmapSizeWithBorder( SurfaceHandle_t surfID ) { // ASSERT_SURF_VALID( surfID ); return SurfaceHasDispInfo( surfID ) ? MAX_DISP_LIGHTMAP_DIM_INCLUDING_BORDER : MAX_BRUSH_LIGHTMAP_DIM_INCLUDING_BORDER; } inline short MSurf_MaxLightmapSizeWithoutBorder( SurfaceHandle_t surfID ) { // ASSERT_SURF_VALID( surfID ); return SurfaceHasDispInfo( surfID ) ? MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER : MAX_BRUSH_LIGHTMAP_DIM_WITHOUT_BORDER; } inline mtexinfo_t *MSurf_TexInfo( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { return &pData->texinfo[surfID->texinfo]; } inline WorldDecalHandle_t& MSurf_Decals( SurfaceHandle_t surfID ) { return surfID->decals; } inline bool SurfaceHasDecals( SurfaceHandle_t surfID ) { return ( MSurf_Decals( surfID ) != WORLD_DECAL_HANDLE_INVALID ) ? true : false; } inline ShadowDecalHandle_t& MSurf_ShadowDecals( SurfaceHandle_t surfID ) { return surfID->m_ShadowDecals; } inline ColorRGBExp32 *MSurf_AvgLightColor( SurfaceHandle_t surfID, int nIndex, worldbrushdata_t *pData = host_state.worldbrush ) { int surfaceIndex = MSurf_Index(surfID,pData); // ASSERT_SURF_VALID( surfID ); Assert( pData ); return pData->surfacelighting[surfaceIndex].AvgLightColor(nIndex); } inline byte *MSurf_Styles( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { int surfaceIndex = MSurf_Index(surfID,pData); // ASSERT_SURF_VALID( surfID ); Assert( pData ); return pData->surfacelighting[surfaceIndex].m_nStyles; } /* inline int *MSurf_CachedLight( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { // ASSERT_SURF_VALID( surfID ); Assert( pData ); return pData->surfacelighting[surfID].cached_light; } inline short& MSurf_CachedDLight( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { // ASSERT_SURF_VALID( surfID ); Assert( pData ); return pData->surfacelighting[surfID].cached_dlight; } */ inline unsigned short MSurf_NumPrims( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { if ( SurfaceHasDispInfo( surfID ) || !SurfaceHasPrims( surfID )) return 0; int surfaceIndex = MSurf_Index(surfID,pData); // ASSERT_SURF_VALID( surfID ); Assert( pData ); return pData->surfaces1[surfaceIndex].prims.numPrims; } inline unsigned short MSurf_FirstPrimID( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { if ( SurfaceHasDispInfo( surfID ) ) return 0; int surfaceIndex = MSurf_Index(surfID,pData); // ASSERT_SURF_VALID( surfID ); Assert( pData ); return pData->surfaces1[surfaceIndex].prims.firstPrimID; } inline ColorRGBExp32 *MSurf_Samples( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { int surfaceIndex = MSurf_Index(surfID,pData); // ASSERT_SURF_VALID( surfID ); Assert( pData ); return pData->surfacelighting[surfaceIndex].m_pSamples; } inline IDispInfo *MSurf_DispInfo( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { return surfID->pDispInfo; } //inline unsigned short &MSurf_FirstVertNormal( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) inline unsigned int &MSurf_FirstVertNormal( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { int surfaceIndex = MSurf_Index(surfID,pData); // ASSERT_SURF_VALID( surfID ); Assert( pData ); Assert( pData->surfacenormals[surfaceIndex].firstvertnormal < MAX_MAP_VERTNORMALINDICES ); return pData->surfacenormals[surfaceIndex].firstvertnormal; } inline unsigned short &MSurf_VertBufferIndex( SurfaceHandle_t surfID ) { return surfID->vertBufferIndex; } inline short& MSurf_MaterialSortID( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { return surfID->materialSortID; } inline short *MSurf_OffsetIntoLightmapPage( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { int surfaceIndex = MSurf_Index(surfID,pData); // ASSERT_SURF_VALID( surfID ); Assert( pData ); return pData->surfacelighting[surfaceIndex].m_OffsetIntoLightmapPage; } inline VPlane MSurf_GetForwardFacingPlane( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { // ASSERT_SURF_VALID( surfID ); Assert( pData ); return VPlane( MSurf_Plane( surfID).normal, MSurf_Plane( surfID ).dist ); } inline OverlayFragmentHandle_t &MSurf_OverlayFragmentList( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { return surfID->m_nFirstOverlayFragment; } inline msurfacelighting_t *SurfaceLighting( SurfaceHandle_t surfID, worldbrushdata_t *pData = host_state.worldbrush ) { int surfaceIndex = MSurf_Index(surfID,pData); Assert( pData ); return &pData->surfacelighting[surfaceIndex]; } #endif // GL_MODEL_PRIVATE_H