diff --git a/include/r_internal.h b/include/r_internal.h index dfcab0056..af3e1d645 100644 --- a/include/r_internal.h +++ b/include/r_internal.h @@ -93,4 +93,7 @@ void R_MarkLeaves (void); void GL_SetPalette (void *data, const byte *palette); void GLSL_SetPalette (void *data, const byte *palette); +int R_BillboardFrame (entity_t *ent, int orientation, const vec3_t cameravec, + vec3_t bbup, vec3_t bbright, vec3_t bbpn); + #endif//__r_internal_h diff --git a/libs/video/renderer/Makemodule.am b/libs/video/renderer/Makemodule.am index c5ff6407a..11f8e343a 100644 --- a/libs/video/renderer/Makemodule.am +++ b/libs/video/renderer/Makemodule.am @@ -29,6 +29,7 @@ video_renderer_common_sources = \ libs/video/renderer/font8x8.c \ libs/video/renderer/noisetextures.c \ libs/video/renderer/r_alias.c \ + libs/video/renderer/r_billboard.c \ libs/video/renderer/r_bsp.c \ libs/video/renderer/r_dyn_textures.c \ libs/video/renderer/r_efrag.c \ diff --git a/libs/video/renderer/gl/gl_mod_sprite.c b/libs/video/renderer/gl/gl_mod_sprite.c index d25bec868..abd858321 100644 --- a/libs/video/renderer/gl/gl_mod_sprite.c +++ b/libs/video/renderer/gl/gl_mod_sprite.c @@ -105,24 +105,23 @@ static void R_DrawSpriteModel_f (entity_t *e) { float modelalpha, color[4]; - vec4f_t up = {}, right = {}; + vec4f_t cameravec = {}; + vec4f_t up = {}, right = {}, pn = {}; vec4f_t origin, point; - msprite_t *psprite; + msprite_t *sprite = e->renderer.model->cache.data; mspriteframe_t *frame; + origin = Transform_GetWorldPosition (e->transform); + VectorCopy (r_origin, cameravec); + cameravec -= origin; + // don't bother culling, it's just a single polygon without a surface cache frame = R_GetSpriteFrame (e); - psprite = e->renderer.model->cache.data; - if (psprite->type == SPR_ORIENTED) { // bullet marks on walls - up = Transform_Up (e->transform); - right = Transform_Right (e->transform); - } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) { - up = (vec4f_t) { 0, 0, 1, 0 }; - VectorCopy (vright, right); - } else { // normal sprite - VectorCopy (vup, up); - VectorCopy (vright, right); + if (!R_BillboardFrame (e, sprite->type, &cameravec[0], + &up[0], &right[0], &pn[0])) { + // the orientation is undefined so can't draw the sprite + return; } VectorCopy (e->renderer.colormod, color); @@ -136,7 +135,6 @@ R_DrawSpriteModel_f (entity_t *e) qfglColor4fv (color); - origin = Transform_GetWorldPosition (e->transform); point = origin + frame->down * up + frame->left * right; qfglTexCoord2f (0, 1); diff --git a/libs/video/renderer/glsl/glsl_sprite.c b/libs/video/renderer/glsl/glsl_sprite.c index a56a2fd83..56fe571f7 100644 --- a/libs/video/renderer/glsl/glsl_sprite.c +++ b/libs/video/renderer/glsl/glsl_sprite.c @@ -214,9 +214,9 @@ glsl_R_DrawSprite (entity_t *ent) { msprite_t *sprite = (msprite_t *) ent->renderer.model->cache.data; mspriteframe_t *frame1, *frame2; - float blend, sr, cr, dot; - vec3_t tvec; - vec4f_t svpn = {}, svright = {}, svup = {}, rot; + float blend; + vec4f_t cameravec = {}; + vec4f_t svpn = {}, svright = {}, svup = {}; static quat_t color = { 1, 1, 1, 1}; float vertsa[6][3], vertsb[6][3]; static float uvab[6][4] = { @@ -228,76 +228,14 @@ glsl_R_DrawSprite (entity_t *ent) { 0, 1, 0, 1 }, }; - switch (sprite->type) { - case SPR_FACING_UPRIGHT: - // generate the sprite's exes with svup straight up in worldspace - // and svright perpendicular to r_origin. This will not work if the - // view direction is very close to straight up or down because the - // cross product will be between two nearly parallel vectors and - // starts to approach an undefined staate, so we don't draw if the - // two vectors are less than 1 degree apart - VectorNegate (r_origin, tvec); - VectorNormalize (tvec); - dot = tvec[2]; // same as DotProcut (tvec, svup) because - // svup is 0, 0, 1 - if ((dot > 0.999848) || (dot < -0.99848)) // cos (1 degree) - return; - VectorSet (0, 0, 1, svup); - // CrossProduct (svup, -r_origin, svright) - VectorSet (tvec[1], -tvec[0], 0, svright); - svright = normalf (svright); - // CrossProduct (svright, svup, svpn); - VectorSet (-svright[1], svright[0], 0, svpn); - break; - case SPR_VP_PARALLEL: - // generate the prite's axes completely parallel to the viewplane. - // There are no problem situations, because the prite is always in - // the same position relative to the viewer. - VectorCopy (vup, svup); - VectorCopy (vright, svright); - VectorCopy (vpn, svpn); - break; - case SPR_VP_PARALLEL_UPRIGHT: - // generate the sprite's axes with svup straight up in worldspace, - // and svright parallel to the viewplane. This will not work if the - // view diretion iss very close to straight up or down because the - // cross prodcut will be between two nearly parallel vectors and - // starts to approach an undefined state, so we don't draw if the - // two vectros are less that 1 degree apart - dot = vpn[2]; - if ((dot > 0.999848) || (dot < -0.99848)) // cos (1 degree) - return; - VectorSet (0, 0, 1, svup); - // CrossProduct (svup, -r_origin, svright) - VectorSet (vpn[1], -vpn[0], 0, svright); - svright = normalf (svright); - // CrossProduct (svright, svup, svpn); - VectorSet (-svright[1], svright[0], 0, svpn); - break; - case SPR_ORIENTED: - // generate the prite's axes according to the sprite's world - // orientation - svup = Transform_Up (ent->transform); - svright = Transform_Right (ent->transform); - svpn = Transform_Forward (ent->transform); - break; - case SPR_VP_PARALLEL_ORIENTED: - // generate the sprite's axes parallel to the viewplane, but - // rotated in that plane round the center according to the sprite - // entity's roll angle. Thus svpn stays the same, but svright and - // svup rotate - rot = Transform_GetLocalRotation (ent->transform); - //FIXME assumes the entity is only rolled - sr = 2 * rot[0] * rot[3]; - cr = rot[3] * rot[3] - rot[0] * rot[0]; - VectorCopy (vpn, svpn); - VectorScale (vright, cr, svright); - VectorMultAdd (svright, sr, vup, svright); - VectorScale (vup, cr, svup); - VectorMultAdd (svup, -sr, vright, svup); - break; - default: - Sys_Error ("R_DrawSprite: Bad sprite type %d", sprite->type); + vec4f_t origin = Transform_GetWorldPosition (ent->transform); + VectorCopy (r_origin, cameravec); + cameravec -= origin; + + if (!R_BillboardFrame (ent, sprite->type, &cameravec[0], + &svup[0], &svright[0], &svpn[0])) { + // the orientation is undefined so can't draw the sprite + return; } R_GetSpriteFrames (ent, sprite, &frame1, &frame2, &blend); @@ -312,7 +250,6 @@ glsl_R_DrawSprite (entity_t *ent) qfeglVertexAttrib4fv (quake_sprite.colorb.location, color); qfeglVertexAttrib1f (quake_sprite.blend.location, blend); - vec4f_t origin = Transform_GetWorldPosition (ent->transform); make_quad (frame1, origin, svpn, svright, svup, vertsa); make_quad (frame2, origin, svpn, svright, svup, vertsb); diff --git a/libs/video/renderer/r_billboard.c b/libs/video/renderer/r_billboard.c new file mode 100644 index 000000000..9df63be8a --- /dev/null +++ b/libs/video/renderer/r_billboard.c @@ -0,0 +1,134 @@ +/* + r_billboard.c + + Billboard frame setup + + Copyright (C) 1996-1997 Id Software, Inc. + + This program 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. + + This program 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 this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "QF/render.h" +#include "QF/sys.h" + +#include "QF/scene/entity.h" + +#include "r_internal.h" + + +int +R_BillboardFrame (entity_t *ent, int orientation, const vec3_t cameravec, + vec3_t bbup, vec3_t bbright, vec3_t bbpn) +{ + vec3_t tvec; + float dot; + + switch (orientation) { + case SPR_FACING_UPRIGHT: + // generate the sprite's axes, with vup straight up in worldspace, + // and bbright perpendicular to cameravec. This will not work if + // the camera origin is directly above the entity origin + // (cameravec is straight up or down), because the cross product + // will be between two nearly parallel vectors and starts to + // approach an undefined state, so we don't draw if the two + // vectors are less than 1 degree apart + VectorNegate (cameravec, tvec); + VectorNormalize (tvec); + dot = tvec[2]; // same as DotProduct (tvec, bbup) because + // bbup is 0, 0, 1 + if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) + return 0; + VectorSet (0, 0, 1, bbup); + //CrossProduct(bbup, -cameravec, bbright) + VectorSet (tvec[1], -tvec[0], 0, bbright); + VectorNormalize (bbright); + //CrossProduct (bbright, bbup, bbpn) + VectorSet (-bbright[1], bbright[0], 0, bbpn); + break; + case SPR_VP_PARALLEL: + // generate the sprite's axes, completely parallel to the + // viewplane. There are no problem situations, because the + // sprite is always in the same orientation relative to the viewer + VectorCopy (vup, bbup); + VectorCopy (vright, bbright); + VectorCopy (vpn, bbpn); + break; + case SPR_VP_PARALLEL_UPRIGHT: + // generate the sprite's axes, with vup straight up in worldspace, + // and bbright parallel to the viewplane. + // This will not work if the view direction is very close to + // straight up or down, because the cross product will be between + // two nearly parallel vectors and starts to approach an undefined + // state, so we don't draw if the two vectors are less than 1 + // degree apart + dot = vpn[2]; // same as DotProduct (vpn, bbup) because + // bbup is 0, 0, 1 + if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = + return 0; + VectorSet (0, 0, 1, bbup); + //CrossProduct(bbup, vpn, bbright) + VectorSet (vpn[1], -vpn[0], 0, bbright); + VectorNormalize (bbright); + //CrossProduct (bbright, bbup, bbpn) + VectorSet (-bbright[1], bbright[0], 0, bbpn); + break; + case SPR_ORIENTED: + { + // generate the sprite's axes, according to the sprite's world + // orientation + mat4f_t mat; + Transform_GetWorldMatrix (ent->transform, mat); + VectorCopy (mat[0], bbpn); + VectorNegate (mat[1], bbright); + VectorCopy (mat[2], bbup); + } + break; + case SPR_VP_PARALLEL_ORIENTED: + { + // generate the sprite's axes, parallel to the viewplane, but + // rotated in that plane around the center according to the + // sprite entity's roll angle. So vpn stays the same, but + // vright and vup rotate + vec4f_t rot = Transform_GetLocalRotation (ent->transform); + // FIXME needs proper testing (need to find, make, or fake a + // parallel oriented sprite) + rot = qmulf (r_refdef.viewrotation, rot); + QuatMultVec (&rot[0], vpn, bbpn); + QuatMultVec (&rot[0], vright, bbright); + QuatMultVec (&rot[0], vup, bbup); + } + break; + default: + Sys_Error ("R_BillboardFrame: Bad orientation %d", orientation); + } + return 1; +} diff --git a/libs/video/renderer/sw/sw_rsprite.c b/libs/video/renderer/sw/sw_rsprite.c index 82c7b0d1b..610b078dd 100644 --- a/libs/video/renderer/sw/sw_rsprite.c +++ b/libs/video/renderer/sw/sw_rsprite.c @@ -284,109 +284,22 @@ R_GetSpriteframe (msprite_t *psprite) void R_DrawSprite (void) { - int i; - msprite_t *psprite; - vec3_t tvec; - float dot, sr, cr; + msprite_t *sprite = currententity->renderer.model->cache.data; - psprite = currententity->renderer.model->cache.data; - - r_spritedesc.pspriteframe = R_GetSpriteframe (psprite); + r_spritedesc.pspriteframe = R_GetSpriteframe (sprite); sprite_width = r_spritedesc.pspriteframe->width; sprite_height = r_spritedesc.pspriteframe->height; - // TODO: make this caller-selectable - if (psprite->type == SPR_FACING_UPRIGHT) { - // generate the sprite's axes, with vup straight up in worldspace, and - // r_spritedesc.vright perpendicular to modelorg. - // This will not work if the view direction is very close to straight - // up or down, because the cross product will be between two nearly - // parallel vectors and starts to approach an undefined state, so we - // don't draw if the two vectors are less than 1 degree apart - tvec[0] = -modelorg[0]; - tvec[1] = -modelorg[1]; - tvec[2] = -modelorg[2]; - VectorNormalize (tvec); - dot = tvec[2]; // same as DotProduct (tvec, - // r_spritedesc.vup) because - // r_spritedesc.vup is 0, 0, 1 - if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = - // 0.999848 - return; - r_spritedesc.vup[0] = 0; - r_spritedesc.vup[1] = 0; - r_spritedesc.vup[2] = 1; - r_spritedesc.vright[0] = tvec[1]; - //CrossProduct(r_spritedesc.vup, -modelorg, r_spritedesc.vright) - r_spritedesc.vright[1] = -tvec[0]; - r_spritedesc.vright[2] = 0; - VectorNormalize (r_spritedesc.vright); - r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; - r_spritedesc.vpn[1] = r_spritedesc.vright[0]; - r_spritedesc.vpn[2] = 0; - //CrossProduct (r_spritedesc.vright, r_spritedesc.vup, r_spritedesc.vpn) - } else if (psprite->type == SPR_VP_PARALLEL) { - // generate the sprite's axes, completely parallel to the viewplane. - // There are no problem situations, because the sprite is always in the - // same position relative to the viewer - for (i = 0; i < 3; i++) { - r_spritedesc.vup[i] = vup[i]; - r_spritedesc.vright[i] = vright[i]; - r_spritedesc.vpn[i] = vpn[i]; - } - } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) { - // generate the sprite's axes, with vup straight up in worldspace, and - // r_spritedesc.vright parallel to the viewplane. - // This will not work if the view direction is very close to straight - // up or down, because the cross product will be between two nearly - // parallel vectors and starts to approach an undefined state, so we - // don't draw if the two vectors are less than 1 degree apart - dot = vpn[2]; // same as DotProduct (vpn, - // r_spritedesc.vup) because - // r_spritedesc.vup is 0, 0, 1 - if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = - // 0.999848 - return; - r_spritedesc.vup[0] = 0; - r_spritedesc.vup[1] = 0; - r_spritedesc.vup[2] = 1; - r_spritedesc.vright[0] = vpn[1]; - //CrossProduct (r_spritedesc.vup, vpn, - r_spritedesc.vright[1] = -vpn[0]; // r_spritedesc.vright) - r_spritedesc.vright[2] = 0; - VectorNormalize (r_spritedesc.vright); - r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; - r_spritedesc.vpn[1] = r_spritedesc.vright[0]; - r_spritedesc.vpn[2] = 0; - //CrossProduct (r_spritedesc.vright, r_spritedesc.vup, r_spritedesc.vpn) - } else if (psprite->type == SPR_ORIENTED) { - // generate the sprite's axes, according to the sprite's world - // orientation - mat4f_t mat; - Transform_GetWorldMatrix (currententity->transform, mat); - VectorCopy (mat[0], r_spritedesc.vpn); - VectorNegate (mat[1], r_spritedesc.vright); - VectorCopy (mat[2], r_spritedesc.vup); - } else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) { - // generate the sprite's axes, parallel to the viewplane, but rotated - // in that plane around the center according to the sprite entity's - // roll angle. So vpn stays the same, but vright and vup rotate - vec4f_t rot = Transform_GetLocalRotation (currententity->transform); - //FIXME assumes the entity is only rolled - sr = 2 * rot[0] * rot[3]; - cr = rot[3] * rot[3] - rot[0] * rot[0]; - - for (i = 0; i < 3; i++) { - r_spritedesc.vpn[i] = vpn[i]; - r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr; - r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr; - } - } else { - Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite->type); + if (!R_BillboardFrame (currententity, sprite->type, modelorg, + r_spritedesc.vup, + r_spritedesc.vright, + r_spritedesc.vpn)) { + // the orientation is undefined so can't draw the sprite + return; } - R_RotateSprite (psprite->beamlength); + R_RotateSprite (sprite->beamlength); R_SetupAndDrawSprite (); } diff --git a/libs/video/renderer/sw32/sw32_rsprite.c b/libs/video/renderer/sw32/sw32_rsprite.c index 153fba1fb..da4d5687a 100644 --- a/libs/video/renderer/sw32/sw32_rsprite.c +++ b/libs/video/renderer/sw32/sw32_rsprite.c @@ -280,109 +280,22 @@ R_GetSpriteframe (msprite_t *psprite) void sw32_R_DrawSprite (void) { - int i; - msprite_t *psprite; - vec3_t tvec; - float dot, sr, cr; + msprite_t *sprite = currententity->renderer.model->cache.data; - psprite = currententity->renderer.model->cache.data; - - sw32_r_spritedesc.pspriteframe = R_GetSpriteframe (psprite); + sw32_r_spritedesc.pspriteframe = R_GetSpriteframe (sprite); sprite_width = sw32_r_spritedesc.pspriteframe->width; sprite_height = sw32_r_spritedesc.pspriteframe->height; - // TODO: make this caller-selectable - if (psprite->type == SPR_FACING_UPRIGHT) { - // generate the sprite's axes, with vup straight up in worldspace, and - // sw32_r_spritedesc.vright perpendicular to modelorg. - // This will not work if the view direction is very close to straight - // up or down, because the cross product will be between two nearly - // parallel vectors and starts to approach an undefined state, so we - // don't draw if the two vectors are less than 1 degree apart - tvec[0] = -modelorg[0]; - tvec[1] = -modelorg[1]; - tvec[2] = -modelorg[2]; - VectorNormalize (tvec); - dot = tvec[2]; // same as DotProduct (tvec, - // sw32_r_spritedesc.vup) because - // sw32_r_spritedesc.vup is 0, 0, 1 - if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = - // 0.999848 - return; - sw32_r_spritedesc.vup[0] = 0; - sw32_r_spritedesc.vup[1] = 0; - sw32_r_spritedesc.vup[2] = 1; - sw32_r_spritedesc.vright[0] = tvec[1]; - //CrossProduct(sw32_r_spritedesc.vup, -modelorg, sw32_r_spritedesc.vright) - sw32_r_spritedesc.vright[1] = -tvec[0]; - sw32_r_spritedesc.vright[2] = 0; - VectorNormalize (sw32_r_spritedesc.vright); - sw32_r_spritedesc.vpn[0] = -sw32_r_spritedesc.vright[1]; - sw32_r_spritedesc.vpn[1] = sw32_r_spritedesc.vright[0]; - sw32_r_spritedesc.vpn[2] = 0; - //CrossProduct (sw32_r_spritedesc.vright, sw32_r_spritedesc.vup, sw32_r_spritedesc.vpn) - } else if (psprite->type == SPR_VP_PARALLEL) { - // generate the sprite's axes, completely parallel to the viewplane. - // There are no problem situations, because the sprite is always in the - // same position relative to the viewer - for (i = 0; i < 3; i++) { - sw32_r_spritedesc.vup[i] = vup[i]; - sw32_r_spritedesc.vright[i] = vright[i]; - sw32_r_spritedesc.vpn[i] = vpn[i]; - } - } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) { - // generate the sprite's axes, with vup straight up in worldspace, and - // sw32_r_spritedesc.vright parallel to the viewplane. - // This will not work if the view direction is very close to straight - // up or down, because the cross product will be between two nearly - // parallel vectors and starts to approach an undefined state, so we - // don't draw if the two vectors are less than 1 degree apart - dot = vpn[2]; // same as DotProduct (vpn, - // sw32_r_spritedesc.vup) because - // sw32_r_spritedesc.vup is 0, 0, 1 - if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = - // 0.999848 - return; - sw32_r_spritedesc.vup[0] = 0; - sw32_r_spritedesc.vup[1] = 0; - sw32_r_spritedesc.vup[2] = 1; - sw32_r_spritedesc.vright[0] = vpn[1]; - //CrossProduct (sw32_r_spritedesc.vup, vpn, - sw32_r_spritedesc.vright[1] = -vpn[0]; // sw32_r_spritedesc.vright) - sw32_r_spritedesc.vright[2] = 0; - VectorNormalize (sw32_r_spritedesc.vright); - sw32_r_spritedesc.vpn[0] = -sw32_r_spritedesc.vright[1]; - sw32_r_spritedesc.vpn[1] = sw32_r_spritedesc.vright[0]; - sw32_r_spritedesc.vpn[2] = 0; - //CrossProduct (sw32_r_spritedesc.vright, sw32_r_spritedesc.vup, sw32_r_spritedesc.vpn) - } else if (psprite->type == SPR_ORIENTED) { - // generate the sprite's axes, according to the sprite's world - // orientation - mat4f_t mat; - Transform_GetWorldMatrix (currententity->transform, mat); - VectorCopy (mat[0], r_spritedesc.vpn); - VectorNegate (mat[1], r_spritedesc.vright); - VectorCopy (mat[2], r_spritedesc.vup); - } else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) { - // generate the sprite's axes, parallel to the viewplane, but rotated - // in that plane around the center according to the sprite entity's - // roll angle. So vpn stays the same, but vright and vup rotate - vec4f_t rot = Transform_GetLocalRotation (currententity->transform); - //FIXME assumes the entity is only rolled - sr = 2 * rot[0] * rot[3]; - cr = rot[3] * rot[3] - rot[0] * rot[0]; - - for (i = 0; i < 3; i++) { - sw32_r_spritedesc.vpn[i] = vpn[i]; - sw32_r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr; - sw32_r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr; - } - } else { - Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite->type); + if (!R_BillboardFrame (currententity, sprite->type, modelorg, + r_spritedesc.vup, + r_spritedesc.vright, + r_spritedesc.vpn)) { + // the orientation is undefined so can't draw the sprite + return; } - R_RotateSprite (psprite->beamlength); + R_RotateSprite (sprite->beamlength); R_SetupAndDrawSprite (); }