From e11dac742d12a7fc9478ea9d07b5fece4a6270ba Mon Sep 17 00:00:00 2001 From: g-cont Date: Wed, 8 Jan 2014 00:00:00 +0400 Subject: [PATCH] 08 Jan 2014 --- common/com_model.h | 8 ++-- common/features.h | 1 + engine/client/cl_frame.c | 36 +++++++++++---- engine/client/gl_decals.c | 10 ++-- engine/client/gl_image.c | 20 +++++--- engine/client/gl_studio.c | 21 +++++++-- engine/common/model.c | 94 +++++++++++++++++++++++++------------- engine/engine.dsp | 2 +- engine/studio.h | 4 ++ game_launch/game.ncb | Bin 33792 -> 41984 bytes game_launch/game.opt | Bin 52736 -> 52736 bytes 11 files changed, 139 insertions(+), 57 deletions(-) diff --git a/common/com_model.h b/common/com_model.h index f60c284c..939209e9 100644 --- a/common/com_model.h +++ b/common/com_model.h @@ -200,10 +200,10 @@ typedef struct msurfmesh_s vec3_t *vertices; // vertexes array vec2_t *stcoords; // s\t coords array - vec2_t *lmcoords; // l\m coords array - vec3_t *normals; // normals array - vec3_t tangent; // shared for mesh - vec3_t binormal; // shared for mesh + vec2_t *lmcoords; // l\m coords array (unused for studio models) + vec3_t *normals; // normals array (identical for bsp polys, unique for studio models) + vec3_t *tangent; // tangent array (identical for bsp polys, unique for studio models) + vec3_t *binormal; // binormal array (identical for bsp polys, unique for studio models) byte *colors; // colors array for vertex lighting (filling 0xFF by default) unsigned short *indices; // indices diff --git a/common/features.h b/common/features.h index 8893570c..1b585a63 100644 --- a/common/features.h +++ b/common/features.h @@ -24,5 +24,6 @@ GNU General Public License for more details. #define ENGINE_LARGE_LIGHTMAPS (1<<4) // change lightmap sizes from 128x128 to 256x256 #define ENGINE_COMPENSATE_QUAKE_BUG (1<<5) // compensate stupid quake bug (inverse pitch) for mods where this bug is fixed #define ENGINE_DISABLE_HDTEXTURES (1<<6) // disable support of HD-textures in case custom renderer have separate way to load them +#define ENGINE_COMPUTE_STUDIO_LERP (1<<7) // enable MOVETYPE_STEP lerping back in engine #endif//FEATURES_H \ No newline at end of file diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 850c7817..f05a57a8 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -155,16 +155,14 @@ void CL_UpdateEntityFields( cl_entity_t *ent ) { qboolean applyVel, applyAvel; - d = -1.0f; - applyVel = !VectorCompare( m_pGround->curstate.origin, m_pGround->prevstate.origin ); applyAvel = !VectorCompare( m_pGround->curstate.angles, m_pGround->prevstate.angles ); if( applyVel || applyAvel ) { - ent->origin[0] += ( m_pGround->curstate.origin[0] - m_pGround->prevstate.origin[0] ) * d; - ent->origin[1] += ( m_pGround->curstate.origin[1] - m_pGround->prevstate.origin[1] ) * d; -// ent->origin[2] += ( m_pGround->curstate.origin[2] - m_pGround->prevstate.origin[2] ) * d; + ent->origin[0] += ( m_pGround->curstate.origin[0] - m_pGround->prevstate.origin[0] ) * -1.0f; + ent->origin[1] += ( m_pGround->curstate.origin[1] - m_pGround->prevstate.origin[1] ) * -1.0f; +// ent->origin[2] += ( m_pGround->curstate.origin[2] - m_pGround->prevstate.origin[2] ) * -1.0f; ent->latched.prevorigin[2] = ent->origin[2]; } @@ -176,13 +174,33 @@ void CL_UpdateEntityFields( cl_entity_t *ent ) ang1 = m_pGround->curstate.angles[i]; ang2 = m_pGround->prevstate.angles[i]; - f = ang1 - ang2; - if( d > 180.0f ) f -= 360.0f; - else if( d < -180.0f ) f += 360.0f; - ent->angles[i] += d * f; + d = ang1 - ang2; + if( d > 180.0f ) d -= 360.0f; + else if( d < -180.0f ) d += 360.0f; + ent->angles[i] += d * -1.0f; } } } + + // move code from StudioSetupTransform here + if( host.features & ENGINE_COMPUTE_STUDIO_LERP ) + { + ent->origin[0] += ( ent->curstate.origin[0] - ent->latched.prevorigin[0] ) * f; + ent->origin[1] += ( ent->curstate.origin[1] - ent->latched.prevorigin[1] ) * f; + ent->origin[2] += ( ent->curstate.origin[2] - ent->latched.prevorigin[2] ) * f; + + for( i = 0; i < 3; i++ ) + { + float ang1, ang2; + + ang1 = ent->angles[i]; + ang2 = ent->latched.prevangles[i]; + d = ang1 - ang2; + if( d > 180.0f ) d -= 360.0f; + else if( d < -180.0f ) d += 360.0f; + ent->angles[i] += d * f; + } + } } } } diff --git a/engine/client/gl_decals.c b/engine/client/gl_decals.c index a7825736..4f33b926 100644 --- a/engine/client/gl_decals.c +++ b/engine/client/gl_decals.c @@ -538,6 +538,7 @@ msurfmesh_t *R_DecalCreateMesh( decalinfo_t *decalinfo, decal_t *pdecal, msurfac // mesh + ( vertex, normal, (st + lmst) ) * numVerts + elem * numElems; bufSize = sizeof( msurfmesh_t ) + numVerts * ( sizeof( vec3_t ) + sizeof( vec3_t ) + sizeof( vec4_t )) + numElems * sizeof( word ); + bufSize += numVerts * ( sizeof( vec3_t ) + sizeof( vec3_t )); // tangent and binormal bufSize += numVerts * sizeof( rgba_t ); // color array buffer = Mem_Alloc( cls.mempool, bufSize ); @@ -556,6 +557,10 @@ msurfmesh_t *R_DecalCreateMesh( decalinfo_t *decalinfo, decal_t *pdecal, msurfac buffer += numVerts * sizeof( vec2_t ); mesh->normals = (vec3_t *)buffer; buffer += numVerts * sizeof( vec3_t ); + mesh->tangent = (vec3_t *)buffer; + buffer += numVerts * sizeof( vec3_t ); + mesh->binormal = (vec3_t *)buffer; + buffer += numVerts * sizeof( vec3_t ); mesh->colors = (byte *)buffer; buffer += numVerts * sizeof( rgba_t ); @@ -572,9 +577,6 @@ msurfmesh_t *R_DecalCreateMesh( decalinfo_t *decalinfo, decal_t *pdecal, msurfac mesh->indices[i*3+2] = i + 2; } - VectorCopy( decalinfo->m_Basis[0], mesh->tangent ); - VectorCopy( decalinfo->m_Basis[1], mesh->binormal ); - // clear colors (it can be used for vertex lighting) Q_memset( mesh->colors, 0xFF, numVerts * sizeof( rgba_t )); @@ -582,6 +584,8 @@ msurfmesh_t *R_DecalCreateMesh( decalinfo_t *decalinfo, decal_t *pdecal, msurfac for( i = 0; i < numVerts; i++, v += VERTEXSIZE ) { VectorCopy( v, mesh->vertices[i] ); + VectorCopy( decalinfo->m_Basis[0], mesh->tangent[i] ); + VectorCopy( decalinfo->m_Basis[1], mesh->binormal[i] ); VectorCopy( decalinfo->m_Basis[2], mesh->normals[i] ); mesh->stcoords[i][0] = v[3]; diff --git a/engine/client/gl_image.c b/engine/client/gl_image.c index 11adc248..4c739a41 100644 --- a/engine/client/gl_image.c +++ b/engine/client/gl_image.c @@ -468,7 +468,9 @@ void R_TextureList_f( void ) break; } - if( image->flags & TF_NOMIPMAP ) + if( image->flags & TF_NORMALMAP ) + Msg( "normal " ); + else if( image->flags & TF_NOMIPMAP ) Msg( "linear " ); if( image->flags & TF_NEAREST ) Msg( "nearest" ); @@ -802,7 +804,7 @@ byte *GL_ResampleTexture( const byte *source, int inWidth, int inHeight, int out /* ================= -GL_ResampleTexture +GL_ApplyGamma Assume input buffer is RGBA ================= @@ -901,6 +903,9 @@ void GL_GenerateMipmaps( byte *buffer, rgbdata_t *pic, gltexture_t *tex, GLenum return; } + // screen texture? + if( !buffer ) return; + mipLevel = 0; w = tex->width; h = tex->height; @@ -978,7 +983,8 @@ static void GL_UploadTexture( rgbdata_t *pic, gltexture_t *tex, qboolean subImag tex->fogParams[2] = pic->fogParams[2]; tex->fogParams[3] = pic->fogParams[3]; - GL_RoundImageDimensions( &tex->width, &tex->height, tex->flags, false ); + // NOTE: normalmaps must be power of two or software mip generator will stop working + GL_RoundImageDimensions( &tex->width, &tex->height, tex->flags, ( tex->flags & TF_NORMALMAP )); if( s&3 ) { @@ -1102,7 +1108,7 @@ static void GL_UploadTexture( rgbdata_t *pic, gltexture_t *tex, qboolean subImag Host_Error( "GL_UploadTexture: %s image buffer overflow\n", tex->name ); // copy or resample the texture - if(( tex->width == tex->srcWidth && tex->height == tex->srcHeight ) || ( tex->flags & TF_TEXTURE_3D )) + if(( tex->width == tex->srcWidth && tex->height == tex->srcHeight ) || ( tex->flags & ( TF_TEXTURE_1D|TF_TEXTURE_3D ))) { data = buf; } @@ -1124,10 +1130,12 @@ static void GL_UploadTexture( rgbdata_t *pic, gltexture_t *tex, qboolean subImag } else if( glTarget == GL_TEXTURE_CUBE_MAP_ARB ) { - if( GL_Support( GL_SGIS_MIPMAPS_EXT )) GL_GenerateMipmaps( data, pic, tex, glTarget, inFormat, i, subImage ); + if( GL_Support( GL_SGIS_MIPMAPS_EXT ) && !( tex->flags & TF_NORMALMAP )) + GL_GenerateMipmaps( data, pic, tex, glTarget, inFormat, i, subImage ); if( subImage ) pglTexSubImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + i, 0, 0, 0, tex->width, tex->height, inFormat, dataType, data ); else pglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + i, 0, outFormat, tex->width, tex->height, 0, inFormat, dataType, data ); - if( !GL_Support( GL_SGIS_MIPMAPS_EXT )) GL_GenerateMipmaps( data, pic, tex, glTarget, inFormat, i, subImage ); + if( !GL_Support( GL_SGIS_MIPMAPS_EXT ) || ( tex->flags & TF_NORMALMAP )) + GL_GenerateMipmaps( data, pic, tex, glTarget, inFormat, i, subImage ); } else if( glTarget == GL_TEXTURE_3D ) { diff --git a/engine/client/gl_studio.c b/engine/client/gl_studio.c index ededffe5..fe116ee2 100644 --- a/engine/client/gl_studio.c +++ b/engine/client/gl_studio.c @@ -517,8 +517,8 @@ void R_StudioSetUpTransform( cl_entity_t *e ) VectorCopy( e->origin, origin ); VectorCopy( e->angles, angles ); - // interpolate monsters position - if( e->curstate.movetype == MOVETYPE_STEP ) + // interpolate monsters position (moved into UpdateEntityFields by user request) + if( e->curstate.movetype == MOVETYPE_STEP && !( host.features & ENGINE_COMPUTE_STUDIO_LERP )) { float d, f = 0.0f; int i; @@ -1699,7 +1699,7 @@ void R_StudioLighting( float *lv, int bone, int flags, vec3_t normal ) r = r_studio_lambert->value; if( r < 1.0f ) r = 1.0f; lightcos = (lightcos + ( r - 1.0f )) / r; // do modified hemispherical lighting - if( lightcos > 0.0f ) VectorMA( illum, -lightcos, plight->lightcolor, illum ); + if( lightcos > 0.0f ) VectorMA( illum, lightcos, plight->lightcolor, illum ); if( illum[0] <= 0.0f ) illum[0] = 0.0f; if( illum[1] <= 0.0f ) illum[1] = 0.0f; @@ -3371,6 +3371,9 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture if( ptexture->flags & STUDIO_NF_TRANSPARENT ) flags |= (TF_CLAMP|TF_NOMIPMAP); + if( ptexture->flags & STUDIO_NF_NORMALMAP ) + flags |= (TF_NORMALMAP); + // store some textures for remapping if( !Q_strnicmp( ptexture->name, "DM_Base", 7 ) || !Q_strnicmp( ptexture->name, "remap", 5 )) { @@ -3579,6 +3582,18 @@ void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded ) loadmodel->radius = RadiusFromBounds( loadmodel->mins, loadmodel->maxs ); loadmodel->flags = phdr->flags; // copy header flags + // check for static model + if( phdr->numseqgroups == 1 && phdr->numseq == 1 && phdr->numbones == 1 ) + { + mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)phdr + phdr->seqindex); + + // HACKHACK: MilkShape created a default animations with 30 frames + // FIXME: analyze real frames for more predicatable results + // TODO: analyze all the sequences + if( pseqdesc->numframes == 1 || pseqdesc->numframes == 30 ) + pseqdesc->flags |= STUDIO_STATIC; + } + if( loaded ) *loaded = true; } diff --git a/engine/common/model.c b/engine/common/model.c index 58a772c1..beffca66 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -1282,10 +1282,10 @@ static void Mod_BuildPolygon( mextrasurf_t *info, msurface_t *surf, int numVerts { float s, t; uint bufSize; + vec3_t normal, tangent, binormal; mtexinfo_t *texinfo = surf->texinfo; int i, numElems; byte *buffer; - vec3_t normal; msurfmesh_t *mesh; // allocate mesh @@ -1293,6 +1293,7 @@ static void Mod_BuildPolygon( mextrasurf_t *info, msurface_t *surf, int numVerts // mesh + ( vertex, normal, (st + lmst) ) * numVerts + elem * numElems; bufSize = sizeof( msurfmesh_t ) + numVerts * ( sizeof( vec3_t ) + sizeof( vec3_t ) + sizeof( vec4_t )) + numElems * sizeof( word ); + bufSize += numVerts * ( sizeof( vec3_t ) + sizeof( vec3_t )); // tangent and binormal bufSize += numVerts * sizeof( rgba_t ); // color array buffer = Mem_Alloc( loadmodel->mempool, bufSize ); @@ -1302,11 +1303,16 @@ static void Mod_BuildPolygon( mextrasurf_t *info, msurface_t *surf, int numVerts mesh->numVerts = numVerts; mesh->numElems = numElems; - // calc tangent and binormal - VectorCopy( surf->texinfo->vecs[0], mesh->tangent ); - VectorCopy( surf->texinfo->vecs[1], mesh->binormal ); - VectorNormalize( mesh->tangent ); - VectorNormalize( mesh->binormal ); + // calc tangent space + if( surf->flags & SURF_PLANEBACK ) + VectorNegate( surf->plane->normal, normal ); + else VectorCopy( surf->plane->normal, normal ); + VectorCopy( surf->texinfo->vecs[0], tangent ); + VectorNegate( surf->texinfo->vecs[1], binormal ); + + VectorNormalize( normal ); // g-cont. this is even needed? + VectorNormalize( tangent ); + VectorNormalize( binormal ); // setup pointers mesh->vertices = (vec3_t *)buffer; @@ -1317,6 +1323,10 @@ static void Mod_BuildPolygon( mextrasurf_t *info, msurface_t *surf, int numVerts buffer += numVerts * sizeof( vec2_t ); mesh->normals = (vec3_t *)buffer; buffer += numVerts * sizeof( vec3_t ); + mesh->tangent = (vec3_t *)buffer; + buffer += numVerts * sizeof( vec3_t ); + mesh->binormal = (vec3_t *)buffer; + buffer += numVerts * sizeof( vec3_t ); mesh->colors = (byte *)buffer; buffer += numVerts * sizeof( rgba_t ); @@ -1335,13 +1345,6 @@ static void Mod_BuildPolygon( mextrasurf_t *info, msurface_t *surf, int numVerts mesh->indices[i*3+2] = i + 2; } - // setup normal - if( surf->flags & SURF_PLANEBACK ) - VectorNegate( surf->plane->normal, normal ); - else VectorCopy( surf->plane->normal, normal ); - - VectorNormalize( normal ); // g-cont. this is even needed? - // clear colors (it can be used for vertex lighting) Q_memset( mesh->colors, 0xFF, numVerts * sizeof( rgba_t )); @@ -1349,6 +1352,8 @@ static void Mod_BuildPolygon( mextrasurf_t *info, msurface_t *surf, int numVerts { // vertex VectorCopy( verts, mesh->vertices[i] ); + VectorCopy( tangent, mesh->tangent[i] ); + VectorCopy( binormal, mesh->binormal[i] ); VectorCopy( normal, mesh->normals[i] ); // texture coordinates @@ -1384,11 +1389,12 @@ Mod_SubdividePolygon */ static void Mod_SubdividePolygon( mextrasurf_t *info, msurface_t *surf, int numVerts, float *verts, float tessSize ) { - vec3_t vTotal, nTotal, mins, maxs; - mtexinfo_t *texinfo = surf->texinfo; + vec3_t vTotal, nTotal, tTotal, bTotal; vec3_t front[MAX_SIDE_VERTS], back[MAX_SIDE_VERTS]; float *v, m, oneDivVerts, dist, dists[MAX_SIDE_VERTS]; qboolean lightmap = (surf->flags & SURF_DRAWTILED) ? false : true; + vec3_t normal, tangent, binormal, mins, maxs; + mtexinfo_t *texinfo = surf->texinfo; vec2_t totalST, totalLM; float s, t, scale; int i, j, f, b; @@ -1453,6 +1459,7 @@ static void Mod_SubdividePolygon( mextrasurf_t *info, msurface_t *surf, int numV // mesh + ( vertex, normal, (st + lmst) ) * ( numVerts + 2 ); bufSize = sizeof( msurfmesh_t ) + (( numVerts + 2 ) * (( sizeof( vec3_t ) + sizeof( vec3_t ) + sizeof( vec4_t )))); + bufSize += ( numVerts + 2 ) * ( sizeof( vec3_t ) + sizeof( vec3_t )); // tangent and binormal bufSize += ( numVerts + 2 ) * sizeof( rgba_t ); // color array buffer = Mem_Alloc( loadmodel->mempool, bufSize ); @@ -1464,11 +1471,16 @@ static void Mod_SubdividePolygon( mextrasurf_t *info, msurface_t *surf, int numV mesh->numVerts = numVerts + 2; mesh->numElems = numVerts * 3; - // calc tangent and binormal - VectorCopy( surf->texinfo->vecs[0], mesh->tangent ); - VectorNegate( surf->texinfo->vecs[1], mesh->binormal ); - VectorNormalize( mesh->tangent ); - VectorNormalize( mesh->binormal ); + // calc tangent space + if( surf->flags & SURF_PLANEBACK ) + VectorNegate( surf->plane->normal, normal ); + else VectorCopy( surf->plane->normal, normal ); + VectorCopy( surf->texinfo->vecs[0], tangent ); + VectorNegate( surf->texinfo->vecs[1], binormal ); + + VectorNormalize( normal ); // g-cont. this is even needed? + VectorNormalize( tangent ); + VectorNormalize( binormal ); // setup pointers mesh->vertices = (vec3_t *)buffer; @@ -1479,11 +1491,17 @@ static void Mod_SubdividePolygon( mextrasurf_t *info, msurface_t *surf, int numV buffer += mesh->numVerts * sizeof( vec2_t ); mesh->normals = (vec3_t *)buffer; buffer += mesh->numVerts * sizeof( vec3_t ); + mesh->tangent = (vec3_t *)buffer; + buffer += mesh->numVerts * sizeof( vec3_t ); + mesh->binormal = (vec3_t *)buffer; + buffer += mesh->numVerts * sizeof( vec3_t ); mesh->colors = (byte *)buffer; buffer += mesh->numVerts * sizeof( rgba_t ); VectorClear( vTotal ); VectorClear( nTotal ); + VectorClear( bTotal ); + VectorClear( tTotal ); totalST[0] = totalST[1] = 0; totalLM[0] = totalLM[1] = 0; @@ -1497,14 +1515,14 @@ static void Mod_SubdividePolygon( mextrasurf_t *info, msurface_t *surf, int numV { // vertex VectorCopy( verts, mesh->vertices[i+1] ); - - // setup normal - if( surf->flags & SURF_PLANEBACK ) - VectorNegate( surf->plane->normal, mesh->normals[i+1] ); - else VectorCopy( surf->plane->normal, mesh->normals[i+1] ); + VectorCopy( normal, mesh->normals[i+1] ); + VectorCopy( tangent, mesh->tangent[i+1] ); + VectorCopy( binormal, mesh->binormal[i+1] ); VectorAdd( vTotal, mesh->vertices[i+1], vTotal ); VectorAdd( nTotal, mesh->normals[i+1], nTotal ); + VectorAdd( tTotal, mesh->tangent[i+1], tTotal ); + VectorAdd( bTotal, mesh->binormal[i+1], bTotal ); if( lightmap ) { @@ -1558,7 +1576,12 @@ static void Mod_SubdividePolygon( mextrasurf_t *info, msurface_t *surf, int numV VectorScale( vTotal, oneDivVerts, mesh->vertices[0] ); VectorScale( nTotal, oneDivVerts, mesh->normals[0] ); + VectorScale( tTotal, oneDivVerts, mesh->tangent[0] ); + VectorScale( bTotal, oneDivVerts, mesh->binormal[0] ); + VectorNormalize( mesh->normals[0] ); + VectorNormalize( mesh->tangent[0] ); + VectorNormalize( mesh->binormal[0] ); // texture coordinates mesh->stcoords[0][0] = totalST[0] * oneDivVerts; @@ -1571,6 +1594,8 @@ static void Mod_SubdividePolygon( mextrasurf_t *info, msurface_t *surf, int numV // copy first vertex to last VectorCopy( mesh->vertices[1], mesh->vertices[i+1] ); VectorCopy( mesh->normals[1], mesh->normals[i+1] ); + VectorCopy( mesh->tangent[1], mesh->tangent[i+1] ); + VectorCopy( mesh->binormal[1], mesh->binormal[i+1] ); Vector2Copy( mesh->stcoords[1], mesh->stcoords[i+1] ); Vector2Copy( mesh->lmcoords[1], mesh->lmcoords[i+1] ); @@ -1590,6 +1615,7 @@ static void Mod_ConvertSurface( mextrasurf_t *info, msurface_t *surf ) { msurfmesh_t *poly, *next, *mesh; float *outSTcoords, *outLMcoords; + float *outTangent, *outBinorm; float *outVerts, *outNorms; int numElems, numVerts; word *outIndexes; @@ -1608,6 +1634,7 @@ static void Mod_ConvertSurface( mextrasurf_t *info, msurface_t *surf ) // mesh + ( vertex, normal, (st + lmst) ) * numVerts + elem * numElems; bufSize = sizeof( msurfmesh_t ) + numVerts * ( sizeof( vec3_t ) + sizeof( vec3_t ) + sizeof( vec4_t )) + numElems * sizeof( word ); + bufSize += numVerts * ( sizeof( vec3_t ) + sizeof( vec3_t )); // tangent and binormal bufSize += numVerts * sizeof( rgba_t ); // color array // unsigned short limit @@ -1622,12 +1649,6 @@ static void Mod_ConvertSurface( mextrasurf_t *info, msurface_t *surf ) mesh->numVerts = numVerts; mesh->numElems = numElems; - // calc tangent and binormal - VectorCopy( surf->texinfo->vecs[0], mesh->tangent ); - VectorNegate( surf->texinfo->vecs[1], mesh->binormal ); - VectorNormalize( mesh->tangent ); - VectorNormalize( mesh->binormal ); - // setup pointers mesh->vertices = (vec3_t *)buffer; buffer += numVerts * sizeof( vec3_t ); @@ -1637,14 +1658,21 @@ static void Mod_ConvertSurface( mextrasurf_t *info, msurface_t *surf ) buffer += numVerts * sizeof( vec2_t ); mesh->normals = (vec3_t *)buffer; buffer += numVerts * sizeof( vec3_t ); + mesh->tangent = (vec3_t *)buffer; + buffer += numVerts * sizeof( vec3_t ); + mesh->binormal = (vec3_t *)buffer; + buffer += numVerts * sizeof( vec3_t ); mesh->colors = (byte *)buffer; buffer += numVerts * sizeof( rgba_t ); + mesh->indices = (word *)buffer; buffer += numElems * sizeof( word ); // setup moving pointers outVerts = (float *)mesh->vertices; outNorms = (float *)mesh->normals; + outTangent = (float *)mesh->tangent; + outBinorm = (float *)mesh->binormal; outSTcoords = (float *)mesh->stcoords; outLMcoords = (float *)mesh->lmcoords; outIndexes = (word *)mesh->indices; @@ -1673,6 +1701,8 @@ static void Mod_ConvertSurface( mextrasurf_t *info, msurface_t *surf ) // vertices VectorCopy( poly->vertices[i], outVerts ); VectorCopy( poly->normals[i], outNorms ); + VectorCopy( poly->tangent[i], outTangent ); + VectorCopy( poly->binormal[i], outBinorm ); outSTcoords[0] = poly->stcoords[i][0]; outSTcoords[1] = poly->stcoords[i][1]; @@ -1681,6 +1711,8 @@ static void Mod_ConvertSurface( mextrasurf_t *info, msurface_t *surf ) outVerts += 3; outNorms += 3; + outBinorm += 3; + outTangent += 3; outSTcoords += 2; outLMcoords += 2; } diff --git a/engine/engine.dsp b/engine/engine.dsp index d28b205c..bf1dc5da 100644 --- a/engine/engine.dsp +++ b/engine/engine.dsp @@ -91,7 +91,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 -# ADD LINK32 user32.lib gdi32.lib shell32.lib advapi32.lib winmm.lib mpeg.lib ../utils/vgui/lib/win32_vc6/vgui.lib /nologo /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"libc.lib" /out:"..\temp\engine\!debug/xash.dll" /pdbtype:sept /libpath:"./common/soundlib" +# ADD LINK32 msvcrtd.lib user32.lib gdi32.lib shell32.lib advapi32.lib winmm.lib mpeg.lib ../utils/vgui/lib/win32_vc6/vgui.lib /nologo /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"libc.lib" /out:"..\temp\engine\!debug/xash.dll" /pdbtype:sept /libpath:"./common/soundlib" # SUBTRACT LINK32 /incremental:no /map /nodefaultlib # Begin Custom Build TargetDir=\Xash3D\src_main\temp\engine\!debug diff --git a/engine/studio.h b/engine/studio.h index 0e0895e8..631c59f2 100644 --- a/engine/studio.h +++ b/engine/studio.h @@ -68,6 +68,9 @@ Studio models are position independent, so the cache manager can move them. #define STUDIO_NF_ALPHA 0x0010 // rendering as transparent texture #define STUDIO_NF_ADDITIVE 0x0020 // rendering with additive mode #define STUDIO_NF_TRANSPARENT 0x0040 // use texture with alpha channel +#define STUDIO_NF_NORMALMAP 0x0080 // indexed normalmap +#define STUDIO_NF_HEIGHTMAP 0x0100 // heightmap that can be used for parallax or normalmap +#define STUDIO_NF_GLOSSMAP 0x0200 // glossmap // motion flags #define STUDIO_X 0x0001 @@ -93,6 +96,7 @@ Studio models are position independent, so the cache manager can move them. // sequence flags #define STUDIO_LOOPING 0x0001 +#define STUDIO_STATIC 0x8000 // studiomodel is static // bone flags #define STUDIO_HAS_NORMALS 0x0001 diff --git a/game_launch/game.ncb b/game_launch/game.ncb index dee3a28e15acf91bcaf810b6f1fdd9bc5d3f4aaa..321c09bd2903da6b4db66612f65d4d7ead723b0a 100644 GIT binary patch delta 452 zcmZqZU}`wQG{J~llc9ovf#HPRLq_GziXKOqng20=*euBMUVgFy3l9rW>IaO+yUB%v zg^_79XU=ctWSAylMh1ozK+Qmf3^Sl~2aw**%)n3qq(LTR0O>hEIs!;b0BH{({RT)| zfcQY28bF#2ByRo)tp0`VLq-_}q0Q+9Gnp3!$V_q(WsI3D=Aths#h}2zz{Vg0q)LGL z7$ql%xyTzbfJAv2To^nVnt`G$b|5|VC5m}c0Gp5= zqY4uP0~3QDV+N3B1k#%&3SKa83Yo&eQ6YUIh=K80@kYlK=0yn{lRP+}Uav>?`UjTR nKr5*0bzuf2pws0Ty)kON`1 delta 149 zcmZoT!PL;fG{J~lk)eWtf#F2<0Y;h4iUCKNnRp&-7G!xZKUsl=mzja#6O6;R$%TW3 zk#Y0a;P1@pEI=(F4AQ0oq*nlGAjL2PO1}Wof($}H18*`f3XqxP!2#6C>G7LcL<6cz S7^ohmYqLbb3+BZWdISI^TPWWE diff --git a/game_launch/game.opt b/game_launch/game.opt index fb61a9cd690e883240d910a7175a18a0642355ec..83526171b48f6a7b1f44e1c82a9d26fbf1cdcd5e 100644 GIT binary patch literal 52736 zcmeHQ4|G)3nZI{thy#t7xS;X>6A^=A2m~cYgd{V8L=rM&h7jZ_yv#f@Ba@kN<|QPE zk}7IcY-3Gp6m?@u*R)0rf;P7HG_7nmwp7!uJxf=+O}lQ39#2b^_T+5ab${P`_svW~ zfC=?%x5syLzTEG<_uYHH``y3qoA0}C41n7COvEz*X93U!ST5jf zz&`+H0cHb;?p(xqfb#(711z&2EYcuO@NI6qPrRKEr3mc%>V_^ z2=D^}02M%V|5u@oMe(PhEh<<9ah$b-gUrJ@=rJa*wo@eQJ+}Kt8>YX>seUHudXye# zTUmm|APYb0FEKCpPyi&0Qw53H^gc^{m-sjpJw4a?q|S;^j?$xS8_HLKO+Dl=8@D4F z2V`1)HWeevuRyDZP*;+|>4cdmpT;YdO(By!&LJWiYzQ3EBi77 ziNa#CsO6<`HLNNL)u?W?@zyz0N)T5Qb7t_P_4jd)_ddP=aAf^`Tt@5$j5gfIgNW(b z;SKkZBpE#$x#>RMyRVKrSP|RKv+dgZ5Am)e59!ZendVsg1ZcV*U&F8FOW78dZI5N| zw2ioG_2=E&>iJ$gr@UBOJs%KwKJLFN@Zha&d=&V*fRWvI@DC6l1r*+LCm%yhNk=!` z$sIoMQFkAoiJ3hI_((-JZ_|4?W3M^ieW4 zFUEbhz=t+I$%6u?bwvX{T=ozz#EQ@Xe9UHNLx=Y8-N32d-H3Ytm5C1Cii!R za4I(!t4D|6<6ZIG?a=rEfe!#568K@e#t#WR_VPY)s??H7E6K}YxAy?7s6CUDOs`&gyG+i>3} zaOpesyp3>_*MWA}1)S==8*vw46lL`wegY5!>;?1z#&CZCF_lw|_*J@h#dF}p&Au%7 zI3}L|d4D}WDDdEcdOjlXF5G`d;C&zNLzJ{P!I z??T|XJuXn2l1y^368>E9u|v=;nDYp)7I+Wv27wP<`UnpQ{Kz-9^G@JnKW*V%h`Rx8 ze{A7TAnp}-AL4$1bM0Dw0P&!}hY%ApBWvpTA>czw9s0)>c1ZByz&NDm?|$uh{+6Ki zeEoSoD)2Vk|3Kh{+r2yo^b+Wc@9Ae30ypcOFY>`7wnX4m@6xpU^vn$OEEj9c$AS+T zW1XJ$Ow+h5(iC6V!-E3feC1tS6L{?D4ZK6(Z9mw_cMCi?@CDu@aOowCe}NDEU^gEy zX@1tqUln-U8v~5+X{?j|7JQifmiAlefq13R%O1jBF5B*WyOQ_5cbCqu@#Js+f7rJl z@|?xEkmtLQC&`kr{j4AJE6I}ZLEt1y#$IFxfs-t$B}RagEE(Hrdj~kll6D10fRij4 zJHUrtQ6?~h>)ULxjp=f&%IslboU zJiw|2-t}A!ZxFcWk2O3X@KO15JSOnIfrt4HfsbtL;GF`meD-<-Bw0a`f|D zb{XW+2fp+0jCjEAmcI&|c%XU@11BCB+huzTIPpOB9tBQ35d8HT zi3h@SfD;ddUkIFdAe{Qn9Q@Tm9-Wxa`i_#kv982Bxf^tZH=KDV4+78ou$;#bYk=X8 z%J~k&9RTORO5TZhH=y&ymAo5q55emzc`xFT07Kmsd=N3^9s5#+9@Bl2 zM-AjLh&trsns{LBetrlz@j&%H3S2_EQN(`+P-1FR=SCwxT(VgLr@BdL_ZOt$#RAU} zaUAtG*Rfpe?skZNx=TFQnpbh3z(;{^7Pu35Sm13xuI639slW9gege=7*bC?b^aBO} z2LOWr;&lk|L4nhCGjrN$9izR-Y_=VHOs9QtZyRsx#+rltiN**Hc60^x3nK)6%huAv zx4*>L@FnXxUdGmDA7E?G5YI_QBeRTtk(LRb-ZB0%jb5PRB9f^&{~S|l`K!@AXt&R^ z$A!GyPq*p$<)5MPU9wTfBNz4XE`hVF?&41fT>8oe-Y4+2AzPJ4L8?K~Yu-`>b8J$LcLFJaGeGx{-L zqwH3`AM*7)TgPMn)?qW#nDq31Oo3!K`syS6VZ3F(0{Y==e7y3DcO=U&>2>1H zA3pYv+rG`auVS7DLH`Ws^Y5wV!xx~R2);~uXHJK{Zg(DD!H)p$0sV8J-@Nv2K5)T4 zHj!S>AO9S9%kP7{cY^*|(8s3PS>c1n(>HYNXL)z;XJ-EGp#L%Yc`4{emez2y9nE~r zb>woqU%UglUfNGRP&aA+Fw^`kVp>OrMSnYl{ziQb+5vv-7VO*6*L1DJCiFe@HJTr@ zfm2^2Uh{xcU&E?kp9`G&8l^1+PJNANmI0@}hTQ?L1WtX8Xneq_uaTBw4CQA*%kig^ z^ta$&%6ad{<=m8i6;_>JqP>Q&?s%SFeO!N_vMD&QAN&CMyUXQ_i3h6tAms%fsC^C+4S1lmBfyCVqIrkt!2`5B{0MO3foP5aH+TrMC3^eJ zVM|ZaKC!Q?<~d(oos{2fmd|d0ya)Y{@to(6mp7G7!NJ;CG4ekP^1dGQNAa%J^}Bt? zwa?*amh<3~Ym)hsX7*BQf6xykj&FZQ)s1@n9PDA}&9c!;vY<1YWzX?<@K~guna>L^ zyI-dxUQFbzdw-p9U|j41U-{Dgd<;O(DbTp4Im`|%jrrcyKi7CZ{CXeWU+Xc0V?PAG z8XOyVM8-RJ(c&$kwt^u0=nc&8XW1S^zLeL9vrNx-^qfcdpumHB_Ol&?&$*v(equjc zhL~ub7<&hCKaZ|?8Q+YU@G;<>;(o8)NJnXU9A&&4_bY*SO)KL*#DsTdmGQC9?%@iP zY-8Y)&Afx>|A+A&-TB|o@*SX|@;cT($2$eyJA5zi7Wk2$KErzje)y@!dA~`2Pd6VF zc*Cj=eo)|}zj%?22t4nia{i9Ma~}LWFRak8&g8Q9AP0=g`AF8f#T z-L~-n8+zcLWV@K@(&_2mY-n76j($sPh)#p{?)?4=9fo$+a_M}1sn;KoUW~xxWnx}* zZ9dZzh<~%b+u_N_-ij_KdrtSM-%$F}`|n2|S;nc45lF!`I(|O1xYwdTS7Mzgexa3{ z`Ap=2^w6jK3`yB$)Za``*A&wAmX(mJ8BF04pII0Wb0A0aS>9c>+=+gF5HNgqE$_R@ zNH+tyEkr%+&=xNc{rs40JZspx3w5uBE_=fG2!tX{7#`FgY#3MPB3<^|{m>KL1p0Ms zHm)zcv!5OL!FV8E9MCZ5;`!d+wD93qeB;1)elvOi-s2j#^CJblJnvf%CLsm_JB|7* zEYgPhnN-jXkVhBpl3L#k3xq64)kri1*FYFElL9d)O*%&!otd9OVf-IPgx#gyCCvOz zMn}b;ijH!orp$znXem!q%W3KVAPh_YXX*be{h!$1TKYdr|7Yp{Ed8I*r_uh^(*HpS z2(*8;^naHA&(i-{`ahZvEdAeI+|vKyw<1gbr>jXU{U6N=r?zPofsj^G;#D=5 z5?3`(G?DM}L_-lxnird&=PHrCVWm}(K`gJ*+B7wwQC81Dfjb}B@ol*v(baC6)-LF1ae=54QwVxol;FySloept4XTvZL5}$EPXtRj1PUs$I1; z1(gaJMkCmonlem57SI&zMMXtdQQhLvTUEc7kbP=Glf9wlSXgz3n^G7qs7BW!4OwDm z1@(|=oyho^W+Mlsnmo+0PT*G|#FVI9NRk(kAa83? zwyH&PBH9x7t1>J(6LL)1N|4Z6wrrsUuj>p#kV}><$x!yKW*JSVBYAsM)0EPY%{c{I zKs~)qY#IwV)JRM7;>ns}4eVZPln5FvE`~x`I7~MaB%#k5GP0@V_Cb_!8<eKuC#b<0OAI8kdatFRQ4ltCOS~q;fQg8uvwA z;gA~9@|!oAQ^EY}uc%5Ndo}IJ?ml5c=%$|v5T-?7XdJkFvdY#d@kCGwdlGU*M2lmh zAO{3;M3|0-F~U@vm3rxPn!u!>8dtBUQqAG0!3sILRbCBi;c%!4)snqNXC(4yJRz4w z%V8Z%S0rm6a-v@yvc19RHdho3M#Blc&CCo_!%d8#I#mh#;;Jgw`4+pYFb7gTTg2pN z$Gk0`fx&oKS^IzgetTXhvbIX>g3WR2uvqjpp=ybAi zXqeCn(?uG?G~TLD<0)ArF?ZBY^)II;L0Bvnk@2g{gpTGiuCeAwF>QXPUF|CR^$H<- zm9kycG*yaV6_YSPVzER>QxRz@tlR@?tL*XMiVW2Y{7071uWaaW&Cx(;OS`1_{c0j{ z%4Gj?iCz{vH>O~{QZ+Rm+OA68wbfEpB%#JnUG7KcG5ppGZfhe|k&uRk57zNhZ4ctX z=~BXe7wq$wqD_>rx;3G;AHTAuW^>u&D~~I%_jfCrLQQi!GNIPVD$~OLXmf^hE_wP; zm|5Nu3I`~!l_518D9(_H7%em!Ei~IA`X(e_^zh3ROgzn-V%6{hNNh|^IR&d!jqc^o zzxnzTZa=;X8@9P%>H=4C!NoFLQgXU7CYK}$Fw-4M#KKBD?TcFC>YNRjQ0i)LtZ@0f z^7`79OXQj=PfeLeb{COT1qtq@v0BnKqRJ#$)4&z0(Y18S7jP^4W zvev}{il!E2*dNSJNwS+E=87e0^FaE1K@v2UBwy5+Da@@YW{VT{jkTB_$h~alstVsG zS9O`!yJ<~XO+|*?{R+KOs8-8y)k;l&7(KL$P4(^MSfgrPglbXErfms@!-;gkWSYah zNKHA|1K}x871YMlY2UAJ;@1T9C6V^p(#lX+O(ZvRQf(vcTSKjw&5Vt8txN3e+=efX z?5$fBS7JdiCa7w4Slx)R+&VQ9P~+lB9rDRws8oyHTUKKn&OquZr`}(nDy1ka%jlw0 z?d(X&Y+7`x$*6iWTs3R-$vPaq4C{(m?~O>hwxE#Qs$_q2BoNm3%RwZ$6yU^5N<|wM z$cwOPTG_IFd%Ij8hQvYa@)@Z~h=0N4MJv&CeL{~$qwBX%A^fiVWv0)qE`qOjS%Y zS=bjXT9jcy=Hxg7(i7u@M)3rZ>Mw_eI3WXbaLjhqxEt2CsPXoa5_c5RZF0w>F?wZ5 zZXwN#+}?`nP@`cakuTQ=16*k631x|$mo;?q&bey8#kl;wG+xY zZqFuPRZT^S@s`5A zG{g#dw(Uguh__CW!p5@UC9)j!rq73|ilFqJ%b(KoCLJ5uVN!6w%fJkxB3OJAFF*N* zH*~*+hFz_keD!JbPI=5`x04;EEoqO6`+eRv{!UpbFFpdB>r2U|+X$4#Oh<=_59$h-AQ}Y%6+%%_?fH=mX)(?#b>?1Vvp|O>2esZ zxmo9t@-XY{a3tAge{h!?7kkdD9zlChU5`AqjpIc!8P34`UIRHyWBY8?CutulgF**? z*|wTx+bZ)na_8;=$i>E5kUqb-loQ;Hme|IeHnw3oANj*gytsa!k?cAEnfNHBAJV`#t0&4WnJabZ`i;@H<4Z_g%|^T4n^wP^$O`>o{=D6KJ2>yr_|ePBA0)kgjuY0e z?AASLxY-D}t_nz8awy|$K0Q+c#eLbz;&t`u17TRfS<8nUqGxXWgQtrHsd~)Df z^GVGo>}&AmFv7`rr+!0nr1{viZ|_;$rOzj`;pg!h_)g7xHkQ90KKU5CpB$eId>Z}p zRD8z1x@R1pmA~Jv^O+03-~XZcA7RHTBd;UB@8AJ}_g-AfI|WWrfgFCA@`HhX7y+(&@ ztODGQHVSF^TXyhOh3)E0-ocQ-dR5>)nVoNROPgm$WY6m9we z&NKLg`qMCEm2rQX?&ilpZ}z97`P+2d1~t|-kWVM((EQgnU|h_De^R#1jd`;7%k})5 z=n0F^F3wm#bB>mcTfbsQI`r`|ejQH^cjPPD~;cqv}>hvMJK`nC(l+C0C1_h+$_dPVYVPSL?s;h6_yF3W<2&i{r}ER9BaJbWCp+fZ{YV$P!^6`d zjnqOd30M;Nlu2N+^VilxXAprK9i5AIsxXJyIPJBJ)5kJ9e{4QwN}CUaD*Pl>h12kw zu&-~<=)^Z8o%#Q4l<&j^G*cO$zg9$vCzMaOPA8DRr&;$*&tJQN3KHeJSQs@=;IAH1 zS0Ql;X9rG6{!=c0Ra*IWXfX7%-=@35N+LnOAWmCWQCqh>{eLzc0w?*2Kus7ACc9Oq z8fH6_XEIaI1ll&;nnGkV=}jm7;FQYbVp7B>Vv1jtuFy}ZE0GpSF~hndKQTX@(TqrE z=#1buHnT294ig2CvKh1NzfuNx>1G1SpMfmyCX?tnef2A3*TJ~R< zPm(|0S@vI(+nrhVUzYusW&f43HWx;T^rOCI|8)WjGRyvpEOssXuM=4rTJ~S3^0Xl> z>Ziv33w5>5f3wbiBXtl3`fZOO^_v0RO$GYh$$XZ&3sshN{@dT}`EQ%wH-0&qr7Oj* zme$j0jZ&TQ1q3JEG(Py?WG>+$H7Vt!H>oeZKTV%ef&EFxsNZBcXL-Hlb@;{VGtA{v z;!UbXUQmAV?!5UFJCU3?_~LF!QwhKS_0u zVEKRQKF#!gV;UP;{-63r*7E;MIZs>upX6wq=J#d!f1d2%0Ge1W|IgI%6PEucw)mF+ z=cGrISpJ`u|EFmRZ25mqJb7o*AO0=>PxhU@zx9^>FL?mpq}u??{?D@iBehKGFedB#x72}hNlQ2Lm-;yIHf!1ckqNo~q$kK(_J5ZB-^u!jW&dZ{|Ixwc;t+G_d@TK+rT^2F zf0q7F7sIDb|Ht9~OBQ+`f2>>Mxx$H+;eZ#13hPe6Boa&iXX*be|KF(`kZSq=CM^A* zrTHn9FhvbJ3Cp*LnZ}*n|&(i;qmWP66|M%&$|8wKa!A91C^B0?d#f^?Ih5tBzamwcp z)=~cA{6jZRne?N4I{PugG@OGtwJ_^6%9qp1pIT{?Q&08$$H{X!{kwln`7@GV)i6)5 zq?3oU)+?GnxX45nn+Q(MG#)K6xh35(JfA1^-M0L{;V5qTe_P-Go$5!dE&uOvAN{uc zzb*go@gJJD{J%3iIE0pI%m3T*|E5z#tndG7^tJF4ezVc?|F-t>z{i+sR7vV>wWm9Ts`|d%eMF6n??lwFejZ)(GQJrx;bXu%#rzILdf8?pFfunpVbrhzak^D&u3H-NO|o*~Y*pn`!BPNxl>;{qLtw|C^;-e^y|O zS^B>do+xSge_Q_F^u2lGn?t8Ou`~Jeb3najvd`4lxK3IX3>VwF zRdFR2bcYhLu+mO_uMRK#^d6sEN9U)<#gjVZGiL(|s;j-R!sYYI>uXmok!z|vHDw;z zUDS*N)+uqdI3YZ%F&^EPuEtdbcrT<&OJHNkz5ACew()nw3{VF<+rT>ErK5hQr{|iahmihny literal 52736 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