void R_SetupDecalTextureSpaceBasis( decal_t *pDecal, msurface_t *surf, int texture, vec3_t textureSpaceBasis[3], float decalWorldScale[2] ) { float *sAxis = NULL; int width, height; if( pDecal->flags & FDECAL_USESAXIS ) sAxis = pDecal->saxis; // Compute the non-scaled decal basis R_DecalComputeBasis( surf, sAxis, textureSpaceBasis ); R_GetDecalDimensions( texture, &width, &height ); // world width of decal = ptexture->width / pDecal->scale // world height of decal = ptexture->height / pDecal->scale // scale is inverse, scales world space to decal u/v space [0,1] // OPTIMIZE: Get rid of these divides decalWorldScale[0] = (float)pDecal->scale / width; decalWorldScale[1] = (float)pDecal->scale / height; VectorScale( textureSpaceBasis[0], decalWorldScale[0], textureSpaceBasis[0] ); VectorScale( textureSpaceBasis[1], decalWorldScale[1], textureSpaceBasis[1] ); }
// Check for intersecting decals on this surface static decal_t *R_DecalIntersect( decalinfo_t *decalinfo, msurface_t *surf, int *pcount ) { int texture; decal_t *plast, *pDecal; vec3_t decalExtents[2]; float lastArea = 2; int mapSize[2]; plast = NULL; *pcount = 0; // (Same as R_SetupDecalClip). texture = decalinfo->m_iTexture; // precalculate the extents of decalinfo's decal in world space. R_GetDecalDimensions( texture, &mapSize[0], &mapSize[1] ); VectorScale( decalinfo->m_Basis[0], ((mapSize[0] / decalinfo->m_scale) * 0.5f), decalExtents[0] ); VectorScale( decalinfo->m_Basis[1], ((mapSize[1] / decalinfo->m_scale) * 0.5f), decalExtents[1] ); pDecal = surf->pdecals; while( pDecal ) { texture = pDecal->texture; // Don't steal bigger decals and replace them with smaller decals // Don't steal permanent decals if(!( pDecal->flags & FDECAL_PERMANENT )) { vec3_t testBasis[3]; vec3_t testPosition[2]; float testWorldScale[2]; vec2_t vDecalMin, vDecalMax; vec2_t vUnionMin, vUnionMax; R_SetupDecalTextureSpaceBasis( pDecal, surf, texture, testBasis, testWorldScale ); VectorSubtract( decalinfo->m_Position, decalExtents[0], testPosition[0] ); VectorSubtract( decalinfo->m_Position, decalExtents[1], testPosition[1] ); // Here, we project the min and max extents of the decal that got passed in into // this decal's (pDecal's) [0,0,1,1] clip space, just like we would if we were // clipping a triangle into pDecal's clip space. Vector2Set( vDecalMin, DotProduct( testPosition[0], testBasis[0] ) - pDecal->dx + 0.5f, DotProduct( testPosition[1], testBasis[1] ) - pDecal->dy + 0.5f ); VectorAdd( decalinfo->m_Position, decalExtents[0], testPosition[0] ); VectorAdd( decalinfo->m_Position, decalExtents[1], testPosition[1] ); Vector2Set( vDecalMax, DotProduct( testPosition[0], testBasis[0] ) - pDecal->dx + 0.5f, DotProduct( testPosition[1], testBasis[1] ) - pDecal->dy + 0.5f ); // Now figure out the part of the projection that intersects pDecal's // clip box [0,0,1,1]. Vector2Set( vUnionMin, max( vDecalMin[0], 0 ), max( vDecalMin[1], 0 )); Vector2Set( vUnionMax, min( vDecalMax[0], 1 ), min( vDecalMax[1], 1 )); if( vUnionMin[0] < 1 && vUnionMin[1] < 1 && vUnionMax[0] > 0 && vUnionMax[1] > 0 ) { // Figure out how much of this intersects the (0,0) - (1,1) bbox. float flArea = (vUnionMax[0] - vUnionMin[1]) * (vUnionMax[1] - vUnionMin[1]); if( flArea > 0.6f ) { *pcount += 1; if( !plast || flArea <= lastArea ) { plast = pDecal; lastArea = flArea; } } } } pDecal = pDecal->pnext; } return plast; }
// Shoots a decal onto the surface of the BSP. position is the center of the decal in world coords void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos, int flags, vec3_t saxis, float scale ) { decalinfo_t decalInfo; hull_t *hull; cl_entity_t *ent = NULL; model_t *model = NULL; int width, height; if( textureIndex <= 0 || textureIndex >= MAX_TEXTURES ) { MsgDev( D_ERROR, "Decal has invalid texture!\n" ); return; } if( entityIndex > 0 ) { ent = CL_GetEntityByIndex( entityIndex ); if( modelIndex > 0 ) model = Mod_Handle( modelIndex ); else if( ent != NULL ) model = Mod_Handle( ent->curstate.modelindex ); else return; } else if( modelIndex > 0 ) model = Mod_Handle( modelIndex ); else model = cl.worldmodel; if( !model ) return; if( model->type != mod_brush ) { MsgDev( D_ERROR, "Decals must hit mod_brush!\n" ); return; } decalInfo.m_pModel = model; hull = &model->hulls[0]; // always use #0 hull if( ent && !( flags & FDECAL_LOCAL_SPACE )) { vec3_t pos_l; // transform decal position in local bmodel space if( !VectorIsNull( ent->angles )) { matrix4x4 matrix; Matrix4x4_CreateFromEntity( matrix, ent->angles, ent->origin, 1.0f ); Matrix4x4_VectorITransform( matrix, pos, pos_l ); } else { VectorSubtract( pos, ent->origin, pos_l ); } VectorCopy( pos_l, decalInfo.m_Position ); flags |= FDECAL_LOCAL_SPACE; // decal position moved into local space } else { // pass position in global VectorCopy( pos, decalInfo.m_Position ); } // deal with the s axis if one was passed in if( saxis ) { flags |= FDECAL_USESAXIS; VectorCopy( saxis, decalInfo.m_SAxis ); } // this decal must use landmark for correct transition if(!( model->flags & MODEL_HAS_ORIGIN )) { flags |= FDECAL_USE_LANDMARK; } // more state used by R_DecalNode() decalInfo.m_iTexture = textureIndex; decalInfo.m_Entity = entityIndex; decalInfo.m_Flags = flags; R_GetDecalDimensions( textureIndex, &width, &height ); decalInfo.m_Size = width >> 1; if(( height >> 1 ) > decalInfo.m_Size ) decalInfo.m_Size = height >> 1; decalInfo.m_scale = bound( MIN_DECAL_SCALE, scale, MAX_DECAL_SCALE ); // compute the decal dimensions in world space decalInfo.m_decalWidth = width / decalInfo.m_scale; decalInfo.m_decalHeight = height / decalInfo.m_scale; R_DecalNode( model, &model->nodes[hull->firstclipnode], &decalInfo ); }