/* * R_AddBrushModelToDrawList */ qboolean R_AddBrushModelToDrawList( const entity_t *e ) { unsigned int i; vec3_t origin; vec3_t bmins, bmaxs; qboolean rotated; model_t *model = e->model; mbrushmodel_t *bmodel = ( mbrushmodel_t * )model->extradata; msurface_t *surf; mfog_t *fog; float radius, distance; unsigned int bit, fullBits; unsigned int dlightBits, shadowBits; if( bmodel->nummodelsurfaces == 0 ) { return qfalse; } radius = R_BrushModelBBox( e, bmins, bmaxs, &rotated ); if( R_CullModelEntity( e, bmins, bmaxs, radius, rotated ) ) { return qfalse; } // never render weapon models or non-occluders into shadowmaps if( rn.renderFlags & RF_SHADOWMAPVIEW ) { if( rsc.entShadowGroups[R_ENT2NUM(e)] != rn.shadowGroup->id ) { return qtrue; } } VectorAdd( e->model->mins, e->model->maxs, origin ); VectorMA( e->origin, 0.5, origin, origin ); distance = Distance( origin, rn.refdef.vieworg ); fog = R_FogForBounds( bmins, bmaxs ); R_TransformPointToModelSpace( e, rotated, rn.refdef.vieworg, modelOrg ); // check dynamic lights that matter in the instance against the model dlightBits = 0; for( i = 0, fullBits = rn.dlightBits, bit = 1; fullBits; i++, fullBits &= ~bit, bit <<= 1 ) { if( !( fullBits & bit ) ) { continue; } if( !BoundsAndSphereIntersect( bmins, bmaxs, rsc.dlights[i].origin, rsc.dlights[i].intensity ) ) { continue; } dlightBits |= bit; } // check shadowmaps that matter in the instance against the model shadowBits = 0; for( i = 0, fullBits = rn.shadowBits; fullBits; i++, fullBits &= ~bit ) { shadowGroup_t *grp = rsc.shadowGroups + i; bit = grp->bit; if( !( fullBits & bit ) ) { continue; } if( !BoundsIntersect( bmins, bmaxs, grp->visMins, grp->visMaxs ) ) { continue; } shadowBits |= bit; } for( i = 0, surf = bmodel->firstmodelsurface; i < bmodel->nummodelsurfaces; i++, surf++ ) { if( !surf->drawSurf ) { continue; } if( surf->visFrame != rf.frameCount ) { surf->visFrame = rf.frameCount; R_AddSurfaceToDrawList( e, surf, fog, 0, dlightBits, shadowBits, distance ); } } return qtrue; }
/* * R_TraceLine */ static msurface_t *R_TransformedTraceLine( rtrace_t *tr, const vec3_t start, const vec3_t end, entity_t *test, int surfumask ) { model_t *model; r_traceframecount++; // for multi-check avoidance // fill in a default trace memset( tr, 0, sizeof( *tr ) ); trace_surface = NULL; trace_umask = surfumask; trace_fraction = 1; VectorCopy( end, trace_impact ); memset( &trace_plane, 0, sizeof( trace_plane ) ); ClearBounds( trace_absmins, trace_absmaxs ); AddPointToBounds( start, trace_absmins, trace_absmaxs ); AddPointToBounds( end, trace_absmins, trace_absmaxs ); model = test->model; if( model ) { if( model->type == mod_brush ) { mbrushmodel_t *bmodel = ( mbrushmodel_t * )model->extradata; vec3_t temp, start_l, end_l; mat3_t axis; bool rotated = !Matrix3_Compare( test->axis, axis_identity ); // transform VectorSubtract( start, test->origin, start_l ); VectorSubtract( end, test->origin, end_l ); if( rotated ) { VectorCopy( start_l, temp ); Matrix3_TransformVector( test->axis, temp, start_l ); VectorCopy( end_l, temp ); Matrix3_TransformVector( test->axis, temp, end_l ); } VectorCopy( start_l, trace_start ); VectorCopy( end_l, trace_end ); // world uses a recursive approach using BSP tree, submodels // just walk the list of surfaces linearly if( test->model == rsh.worldModel ) { R_RecursiveHullCheck( bmodel->nodes, start_l, end_l ); } else if( BoundsIntersect( model->mins, model->maxs, trace_absmins, trace_absmaxs ) ) { R_TraceAgainstBmodel( bmodel ); } // transform back if( rotated && trace_fraction != 1 ) { Matrix3_Transpose( test->axis, axis ); VectorCopy( tr->plane.normal, temp ); Matrix3_TransformVector( axis, temp, trace_plane.normal ); } } } // calculate the impact plane, if any if( trace_fraction < 1 && trace_surface != NULL ) { VectorNormalize( trace_plane.normal ); trace_plane.dist = DotProduct( trace_plane.normal, trace_impact ); CategorizePlane( &trace_plane ); tr->shader = trace_surface->shader; tr->plane = trace_plane; tr->surfFlags = trace_surface->flags; tr->ent = R_ENT2NUM( test ); } tr->fraction = trace_fraction; VectorCopy( trace_impact, tr->endpos ); return trace_surface; }
/* * R_DrawSkeletalSurf */ qboolean R_DrawSkeletalSurf( const entity_t *e, const shader_t *shader, const mfog_t *fog, drawSurfaceSkeletal_t *drawSurf ) { unsigned int i, j; int framenum = e->frame; int oldframenum = e->oldframe; float backlerp = e->backlerp; float frontlerp = 1.0 - backlerp; bonepose_t tempbonepose[256]; const bonepose_t *bp, *oldbp, *bonepose, *oldbonepose, *lerpedbonepose; bonepose_t *out, tp; mskbone_t *bone; mat4_t *bonePoseRelativeMat; dualquat_t *bonePoseRelativeDQ; size_t bonePoseRelativeMatSize, bonePoseRelativeDQSize; const model_t *mod = drawSurf->model; const mskmodel_t *skmodel = ( const mskmodel_t * )mod->extradata; const mskmesh_t *skmesh = drawSurf->mesh; qboolean hardwareTransform = skmesh->vbo != NULL && glConfig.maxGLSLBones > 0 ? qtrue : qfalse; vattribmask_t vattribs; bonePoseRelativeMat = NULL; bonePoseRelativeDQ = NULL; bp = e->boneposes; oldbp = e->oldboneposes; // not sure if it's really needed if( bp == skmodel->frames[0].boneposes ) { bp = NULL; framenum = oldframenum = 0; } // choose boneposes for lerping if( bp ) { if( !oldbp ) oldbp = bp; } else { if( ( framenum >= (int)skmodel->numframes ) || ( framenum < 0 ) ) { #ifndef PUBLIC_BUILD ri.Com_DPrintf( "R_DrawBonesFrameLerp %s: no such frame %d\n", mod->name, framenum ); #endif framenum = 0; } if( ( oldframenum >= (int)skmodel->numframes ) || ( oldframenum < 0 ) ) { #ifndef PUBLIC_BUILD ri.Com_DPrintf( "R_DrawBonesFrameLerp %s: no such oldframe %d\n", mod->name, oldframenum ); #endif oldframenum = 0; } bp = skmodel->frames[framenum].boneposes; oldbp = skmodel->frames[oldframenum].boneposes; } if( bp == oldbp && !framenum && skmesh->vbo != NULL ) { // fastpath: render static frame 0 as is RB_BindVBO( skmesh->vbo->index, GL_TRIANGLES ); RB_DrawElements( 0, skmesh->numverts, 0, skmesh->numtris * 3 ); return qfalse; } // see what vertex attribs backend needs vattribs = RB_GetVertexAttribs(); // cache size bonePoseRelativeMatSize = sizeof( mat4_t ) * (skmodel->numbones + skmodel->numblends); bonePoseRelativeDQSize = sizeof( dualquat_t ) * skmodel->numbones; // fetch bones tranforms from cache (both matrices and dual quaternions) bonePoseRelativeDQ = ( dualquat_t * )R_GetSketalCache( R_ENT2NUM( e ), mod->lodnum ); if( bonePoseRelativeDQ ) { bonePoseRelativeMat = ( mat4_t * )(( qbyte * )bonePoseRelativeDQ + bonePoseRelativeDQSize); } else { // lerp boneposes and store results in cache lerpedbonepose = tempbonepose; if( bp == oldbp || frontlerp == 1 ) { if( e->boneposes ) { // assume that parent transforms have already been applied lerpedbonepose = bp; } else { for( i = 0; i < skmodel->numbones; i++ ) { j = i; out = tempbonepose + j; bonepose = bp + j; bone = skmodel->bones + j; if( bone->parent >= 0 ) { DualQuat_Multiply( tempbonepose[bone->parent].dualquat, bonepose->dualquat, out->dualquat ); } else { DualQuat_Copy( bonepose->dualquat, out->dualquat ); } } } } else { if( e->boneposes ) { // lerp, assume that parent transforms have already been applied for( i = 0, out = tempbonepose, bonepose = bp, oldbonepose = oldbp, bone = skmodel->bones; i < skmodel->numbones; i++, out++, bonepose++, oldbonepose++, bone++ ) { DualQuat_Lerp( oldbonepose->dualquat, bonepose->dualquat, frontlerp, out->dualquat ); } } else { // lerp and transform for( i = 0; i < skmodel->numbones; i++ ) { j = i; out = tempbonepose + j; bonepose = bp + j; oldbonepose = oldbp + j; bone = skmodel->bones + j; DualQuat_Lerp( oldbonepose->dualquat, bonepose->dualquat, frontlerp, out->dualquat ); if( bone->parent >= 0 ) { DualQuat_Copy( out->dualquat, tp.dualquat ); DualQuat_Multiply( tempbonepose[bone->parent].dualquat, tp.dualquat, out->dualquat ); } } } } bonePoseRelativeDQ = ( dualquat_t * )R_AllocSkeletalDataCache( R_ENT2NUM( e ), mod->lodnum, bonePoseRelativeDQSize + bonePoseRelativeMatSize ); // generate dual quaternions for all bones for( i = 0; i < skmodel->numbones; i++ ) { DualQuat_Multiply( lerpedbonepose[i].dualquat, skmodel->invbaseposes[i].dualquat, bonePoseRelativeDQ[i] ); DualQuat_Normalize( bonePoseRelativeDQ[i] ); } // CPU transforms if( !hardwareTransform ) { bonePoseRelativeMat = ( mat4_t * )(( qbyte * )bonePoseRelativeDQ + bonePoseRelativeDQSize); // generate matrices for all bones for( i = 0; i < skmodel->numbones; i++ ) { Matrix4_FromDualQuaternion( bonePoseRelativeDQ[i], bonePoseRelativeMat[i] ); } // generate matrices for all blend combinations R_SkeletalBlendPoses( skmodel->numblends, skmodel->blends, skmodel->numbones, bonePoseRelativeMat ); } } if( hardwareTransform ) { RB_BindVBO( skmesh->vbo->index, GL_TRIANGLES ); RB_SetBonesData( skmodel->numbones, bonePoseRelativeDQ, skmesh->maxWeights ); RB_DrawElements( 0, skmesh->numverts, 0, skmesh->numtris * 3 ); } else { mesh_t *rb_mesh; RB_BindVBO( RB_VBO_STREAM, GL_TRIANGLES ); rb_mesh = RB_MapBatchMesh( skmesh->numverts, skmesh->numtris * 3 ); if( !rb_mesh ) { ri.Com_DPrintf( S_COLOR_YELLOW "R_DrawAliasSurf: RB_MapBatchMesh returned NULL for (%s)(%s)", drawSurf->model->name, skmesh->name ); return qfalse; } R_SkeletalTransformVerts( skmesh->numverts, skmesh->vertexBlends, bonePoseRelativeMat, ( vec_t * )skmesh->xyzArray[0], ( vec_t * )rb_mesh->xyzArray ); if( vattribs & VATTRIB_SVECTOR_BIT ) { R_SkeletalTransformNormalsAndSVecs( skmesh->numverts, skmesh->vertexBlends, bonePoseRelativeMat, ( vec_t * )skmesh->normalsArray[0], ( vec_t * )rb_mesh->normalsArray, ( vec_t * )skmesh->sVectorsArray[0], ( vec_t * )rb_mesh->sVectorsArray ); } else if( vattribs & VATTRIB_NORMAL_BIT ) { R_SkeletalTransformNormals( skmesh->numverts, skmesh->vertexBlends, bonePoseRelativeMat, ( vec_t * )skmesh->normalsArray[0], ( vec_t * )rb_mesh->normalsArray ); } rb_mesh->elems = skmesh->elems; rb_mesh->numElems = skmesh->numtris * 3; rb_mesh->numVerts = skmesh->numverts; rb_mesh->stArray = skmesh->stArray; RB_UploadMesh( rb_mesh ); RB_EndBatch(); } return qfalse; }
/* * R_AddSkeletalModelToDrawList */ qboolean R_AddSkeletalModelToDrawList( const entity_t *e ) { int i; const mfog_t *fog; const model_t *mod; const shader_t *shader; const mskmesh_t *mesh; const mskmodel_t *skmodel; vec3_t mins, maxs; float radius; float distance; int clipped; mod = R_SkeletalModelLOD( e ); if( !( skmodel = ( ( mskmodel_t * )mod->extradata ) ) || !skmodel->nummeshes ) return qfalse; radius = R_SkeletalModelLerpBBox( e, mod, mins, maxs ); clipped = R_CullModelEntity( e, mins, maxs, radius, qtrue ); if( clipped ) return qfalse; // never render weapon models or non-occluders into shadowmaps if( rn.renderFlags & RF_SHADOWMAPVIEW ) { if( e->renderfx & RF_WEAPONMODEL ) { return qtrue; } if( rsc.entShadowGroups[R_ENT2NUM(e)] != rn.shadowGroup->id ) { return qtrue; } } // make sure weapon model is always closest to the viewer if( e->renderfx & RF_WEAPONMODEL ) { distance = 0; } else { distance = Distance( e->origin, rn.viewOrigin ) + 1; } fog = R_FogForSphere( e->origin, radius ); #if 0 if( !( e->flags & RF_WEAPONMODEL ) && fog ) { R_SkeletalModelLerpBBox( e, mod ); if( R_CompletelyFogged( fog, e->origin, skm_radius ) ) return qfalse; } #endif for( i = 0, mesh = skmodel->meshes; i < (int)skmodel->nummeshes; i++, mesh++ ) { shader = NULL; if( e->customSkin ) { shader = R_FindShaderForSkinFile( e->customSkin, mesh->name ); } else if( e->customShader ) { shader = e->customShader; } else { shader = mesh->skin.shader; } if( shader ) { R_AddDSurfToDrawList( e, fog, shader, distance, 0, NULL, skmodel->drawSurfs + i ); } } return qtrue; }
/* * R_AddLightOccluder */ bool R_AddLightOccluder( const entity_t *ent ) { int i; float maxSide; vec3_t origin; unsigned int hash_key; shadowGroup_t *group; mleaf_t *leaf; vec3_t mins, maxs, bbox[8]; bool bmodelRotated = false; if( rn.refdef.rdflags & RDF_NOWORLDMODEL ) { return false; } if( !ent->model || ent->model->type == mod_brush ) { return false; } VectorCopy( ent->lightingOrigin, origin ); if( ent->model->type == mod_brush ) { vec3_t t; VectorAdd( ent->model->mins, ent->model->maxs, t ); VectorMA( ent->origin, 0.5, t, origin ); } if( VectorCompare( origin, vec3_origin ) ) { return false; } // find lighting group containing entities with same lightingOrigin as ours hash_key = (unsigned int)( origin[0] * 7 + origin[1] * 5 + origin[2] * 3 ); hash_key &= ( SHADOWGROUPS_HASH_SIZE - 1 ); for( group = r_shadowGroups_hash[hash_key]; group; group = group->hashNext ) { if( VectorCompare( group->origin, origin ) ) { goto add; // found an existing one, add } } if( rsc.numShadowGroups == MAX_SHADOWGROUPS ) { return false; // no free groups } leaf = Mod_PointInLeaf( origin, rsh.worldModel ); // start a new group group = &rsc.shadowGroups[rsc.numShadowGroups]; memset( group, 0, sizeof( *group ) ); group->id = group - rsc.shadowGroups + 1; group->bit = ( 1 << rsc.numShadowGroups ); group->vis = Mod_ClusterPVS( leaf->cluster, rsh.worldModel ); group->useOrtho = true; group->alpha = r_shadows_alpha->value; // clear group bounds VectorCopy( origin, group->origin ); ClearBounds( group->mins, group->maxs ); ClearBounds( group->visMins, group->visMaxs ); // add to hash table group->hashNext = r_shadowGroups_hash[hash_key]; r_shadowGroups_hash[hash_key] = group; rsc.numShadowGroups++; add: // get model bounds if( ent->model->type == mod_alias ) { R_AliasModelBBox( ent, mins, maxs ); } else if( ent->model->type == mod_skeletal ) { R_SkeletalModelBBox( ent, mins, maxs ); } else if( ent->model->type == mod_brush ) { R_BrushModelBBox( ent, mins, maxs, &bmodelRotated ); } else { ClearBounds( mins, maxs ); } maxSide = 0; for( i = 0; i < 3; i++ ) { if( mins[i] >= maxs[i] ) { return false; } maxSide = max( maxSide, maxs[i] - mins[i] ); } // ignore tiny objects if( maxSide < 10 ) { return false; } rsc.entShadowGroups[R_ENT2NUM( ent )] = group->id; if( ent->flags & RF_WEAPONMODEL ) { return true; } if( ent->model->type == mod_brush ) { VectorCopy( mins, group->mins ); VectorCopy( maxs, group->maxs ); } else { // rotate local bounding box and compute the full bounding box for this group R_TransformBounds( ent->origin, ent->axis, mins, maxs, bbox ); for( i = 0; i < 8; i++ ) { AddPointToBounds( bbox[i], group->mins, group->maxs ); } } // increase projection distance if needed VectorSubtract( group->mins, origin, mins ); VectorSubtract( group->maxs, origin, maxs ); group->radius = RadiusFromBounds( mins, maxs ); group->projDist = max( group->projDist, group->radius + min( r_shadows_projection_distance->value, 64.0f ) ); return true; }