From c068f22329d5cc722622a2183bbb22eef2093df7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 24 Aug 2026 11:16:07 +0200 Subject: initial import --- src/parsec/ro_part.cpp | 582 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 582 insertions(+) create mode 100644 src/parsec/ro_part.cpp (limited to 'src/parsec/ro_part.cpp') diff --git a/src/parsec/ro_part.cpp b/src/parsec/ro_part.cpp new file mode 100644 index 0000000..ec40ff4 --- /dev/null +++ b/src/parsec/ro_part.cpp @@ -0,0 +1,582 @@ +/* + * PARSEC - Particle Rendering Encapsulation + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:25:33 $ + * + * Orginally written by: + * Copyright (c) Markus Hadwiger 1999-2001 + * + * 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 the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// C library +#include +#include +#include + +// compilation flags/debug support +#include "config.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// global externals +#include "globals.h" + +// subsystem headers +#include "sys_defs.h" +#include "vid_defs.h" + +// rendering subsystem +#include "r_part.h" + +// particle types +#include "parttype.h" + + +// local module header +#include "ro_part.h" + + +// proprietary module headers +#include "con_aux.h" +#include "e_callbk.h" +#include "part_sys.h" +#include "ro_api.h" +#include "ro_supp.h" + +#include "debug.h" + +// mathematics header +#include "utl_math.h" + + + +// flags +//#define USE_INDEXED_TRIANGLES +//#define REVERSED_DEPTH_RANGE // must be consistent with ogl setup + + +// cluster drawing info ------------------------------------------------------- +// +struct pcldrawinf_s { + + int width; + int height; + int scalewidth; + int scaleheight; + depth_t depthvalue; + + TextureMap* texmap; + imgtrafo_s* imgtrafo; + + dword itertype; + dword raststate; + dword rastmask; +}; + + +// dynamic array for vertices of entire particle cluster ---------------------- +// +static GLVertex4* gl_cluster_vtxs; +static int gl_cluster_vcount; +static GLTexInfo gl_cluster_texinfo; +static pcldrawinf_s gl_cluster_drawinf; + +#ifdef USE_INDEXED_TRIANGLES +static dword* gl_cluster_trindxs; +static int gl_cluster_icount; +#endif + + +// enter vertices of one particle into cumulative vertex array ---------------- +// +INLINE +void RO_ScheduleParticleVertices( SPoint *spt, pcldrawinf_s *pdi ) +{ + ASSERT( spt != NULL ); + + ASSERT( pdi->scalewidth > 0 ); + ASSERT( pdi->scaleheight > 0 ); + + ASSERT( gl_cluster_vtxs != NULL ); + +//TODO: +// pdi->imgtrafo + + depth_t depthvalue = pdi->depthvalue; +// LogZValues( DEPTH_TO_INTEGER( depthvalue ) ); + GLfloat depth = DEPTH_TO_FLOAT( depthvalue ) * OPENGL_DEPTH_RANGE; + + int sw2 = pdi->scalewidth / 2; + int sh2 = pdi->scaleheight / 2; + + // unclipped coordinates + int x1 = spt->X - sw2; + int y1 = spt->Y - sh2; +// int u1 = 0; +// int v1 = 0; + + int x2 = spt->X + sw2 - 1; + int y2 = spt->Y - sh2; + int u2 = pdi->width; +// int v2 = 0; + + int x3 = spt->X + sw2 - 1; + int y3 = spt->Y + sh2 - 1; + int u3 = pdi->width; + int v3 = pdi->height; + + int x4 = spt->X - sw2; + int y4 = spt->Y + sh2 - 1; +// int u4 = 0; + int v4 = pdi->height; + + //NOTE: + // clipping is done by OpenGL. + + float scale_u = gl_cluster_texinfo.coscale; + float scale_v = gl_cluster_texinfo.aratio * scale_u; + + // append to vertex array + int& bindx = gl_cluster_vcount; + + gl_cluster_vtxs[ bindx + 0 ].x = (GLfloat) x1; + gl_cluster_vtxs[ bindx + 0 ].y = (GLfloat) y1; + gl_cluster_vtxs[ bindx + 0 ].z = (GLfloat) depth; + gl_cluster_vtxs[ bindx + 0 ].w = (GLfloat) 1.0f; + gl_cluster_vtxs[ bindx + 0 ].s = (GLfloat) 0.0f; //u1 * scale_u; + gl_cluster_vtxs[ bindx + 0 ].t = (GLfloat) 0.0f; //v1 * scale_v; + + gl_cluster_vtxs[ bindx + 1 ].x = (GLfloat) x2; + gl_cluster_vtxs[ bindx + 1 ].y = (GLfloat) y2; + gl_cluster_vtxs[ bindx + 1 ].z = gl_cluster_vtxs[ bindx ].z; + gl_cluster_vtxs[ bindx + 1 ].w = (GLfloat) 1.0f; + gl_cluster_vtxs[ bindx + 1 ].s = (GLfloat) u2 * scale_u; + gl_cluster_vtxs[ bindx + 1 ].t = (GLfloat) 0.0f; //v2 * scale_v; + + gl_cluster_vtxs[ bindx + 2 ].x = (GLfloat) x3; + gl_cluster_vtxs[ bindx + 2 ].y = (GLfloat) y3; + gl_cluster_vtxs[ bindx + 2 ].z = gl_cluster_vtxs[ bindx ].z; + gl_cluster_vtxs[ bindx + 2 ].w = (GLfloat) 1.0f; + gl_cluster_vtxs[ bindx + 2 ].s = (GLfloat) u3 * scale_u; + gl_cluster_vtxs[ bindx + 2 ].t = (GLfloat) v3 * scale_v; + + gl_cluster_vtxs[ bindx + 3 ].x = (GLfloat) x4; + gl_cluster_vtxs[ bindx + 3 ].y = (GLfloat) y4; + gl_cluster_vtxs[ bindx + 3 ].z = gl_cluster_vtxs[ bindx ].z; + gl_cluster_vtxs[ bindx + 3 ].w = (GLfloat) 1.0f; + gl_cluster_vtxs[ bindx + 3 ].s = (GLfloat) 0.0f; //u4 * scale_u; + gl_cluster_vtxs[ bindx + 3 ].t = (GLfloat) v4 * scale_v; + +#ifdef USE_INDEXED_TRIANGLES + + gl_cluster_trindxs[ gl_cluster_icount + 0 ] = bindx + 0; + gl_cluster_trindxs[ gl_cluster_icount + 1 ] = bindx + 1; + gl_cluster_trindxs[ gl_cluster_icount + 2 ] = bindx + 3; + + gl_cluster_trindxs[ gl_cluster_icount + 3 ] = bindx + 1; + gl_cluster_trindxs[ gl_cluster_icount + 4 ] = bindx + 3; + gl_cluster_trindxs[ gl_cluster_icount + 5 ] = bindx + 2; + + gl_cluster_icount += 6; + +#endif + + bindx += 4; +} + + +// ---------------------------------------------------------------------------- +// +INLINE +int DepthCull( SPoint *spt, geomv_t zdist ) +{ + #define DEPTH_BIAS 2 + + if ( AUX_DISABLE_POINT_VISIBILITY_DETECTION ) { + return FALSE; + } + + gl_cluster_drawinf.depthvalue = DEPTHBUFF_OOZ( zdist ); + + // use depth-buffer to determine visibility of light-source + depth_t midpoint = 0; + +#ifdef FRACTIONAL_DEPTH_VALUES + + // compare with bias +#ifdef REVERSED_DEPTH_RANGE + if ( midpoint < gl_cluster_drawinf.depthvalue + FIXED_TO_GEOMV( DEPTH_BIAS ) ) { +#else // REVERSED_DEPTH_RANGE + if ( midpoint >= gl_cluster_drawinf.depthvalue + FIXED_TO_GEOMV( DEPTH_BIAS ) ) { +#endif // REVERSED_DEPTH_RANGE + return TRUE; + } + +#else // FRACTIONAL_DEPTH_VALUES + +#ifdef REVERSED_DEPTH_RANGE + if ( (word)midpoint < gl_cluster_drawinf.depthvalue + DEPTH_BIAS ) { +#else // REVERSED_DEPTH_RANGE + if ( (word)midpoint > gl_cluster_drawinf.depthvalue + DEPTH_BIAS ) { +#endif // REVERSED_DEPTH_RANGE + return TRUE; + } + +#endif // FRACTIONAL_DEPTH_VALUES + + return FALSE; +} + + +// schedule single particle for drawing later on ------------------------------ +// +INLINE +void RO_ScheduleParticle( particle_s *particle, SPoint *spt, geomv_t zdist ) +{ + ASSERT( particle != NULL ); + ASSERT( spt != NULL ); + + // field bitmap is actually rendering flags + dword rendflags = particle->bitmap; + + if ( rendflags & PART_REND_POINTVIS ) { + + // midpoint not on screen means particle invisible + if ( ( spt->X < 0 ) || ( spt->X >= Screen_Width ) || + ( spt->Y < 0 ) || ( spt->Y >= Screen_Height ) ) { + return; + } + + // midpoint occluded means entire particle invisible + if ( DepthCull( spt, zdist ) ) { + return; + } + } + + // calc texture size + if ( rendflags & PART_REND_NODEPTHSCALE ) { + + // skip particle entirely if too far away + if ( zdist > Far_View_Plane/2 ) { + return; + } + + //NOTE: + // for no-depthscale particles the ref_z directly + // determines the size of the particle with respect + // to its texture width and height. + + // fixed texture size + gl_cluster_drawinf.scalewidth = + ( (int) ( gl_cluster_drawinf.width * particle->ref_z ) ) & ~1; //TODO: cache? + gl_cluster_drawinf.scaleheight = + ( (int) ( gl_cluster_drawinf.height * particle->ref_z ) ) & ~1; //TODO: remove even? + + } else { + + // calc scale factor according to reference z + float scalefac = particle->ref_z / GEOMV_TO_FLOAT( zdist ); + + // scale texture size + gl_cluster_drawinf.scalewidth = + ( (int) ( gl_cluster_drawinf.width * scalefac ) ) & ~1; //TODO: remove even? + gl_cluster_drawinf.scaleheight = + ( (int) ( gl_cluster_drawinf.height * scalefac ) ) & ~1; + } +/* + // check lower size boundary and draw either as texture or square + if ( ( gl_cluster_drawinf.scalewidth <= particle->sizebound ) || + ( gl_cluster_drawinf.scaleheight <= particle->sizebound ) ) { + + visual_t drawcol = COLINDX_TO_VISUAL( particle->color ); + if ( ( rendflags & PART_REND_POINTVIS ) == 0 ) + gl_cluster_drawinf.depthvalue = DEPTHBUFF_OOZ( zdist ); + DrawParticleSquare( spt, &gl_cluster_drawinf, drawcol ); + + } else +*/ + if ( ( gl_cluster_drawinf.scalewidth > 0 ) && ( gl_cluster_drawinf.scaleheight > 0 ) ) { + + int swidth2 = gl_cluster_drawinf.scalewidth / 2; + int sheight2 = gl_cluster_drawinf.scaleheight / 2; + + if ( ( spt->X > -swidth2 ) && ( spt->X < Screen_Width + swidth2 ) && + ( spt->Y > -sheight2 ) && ( spt->Y < Screen_Height + sheight2 ) ) { + + if ( ( rendflags & PART_REND_POINTVIS ) == 0 ) + gl_cluster_drawinf.depthvalue = DEPTHBUFF_OOZ( zdist ); + RO_ScheduleParticleVertices( spt, &gl_cluster_drawinf ); + } + } +} + + +// determine texture entire particle cluster should be rendered with ---------- +// +INLINE +void RO_DetermineClusterTexture( pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + ASSERT( cluster->numel > 0 ); + + particle_s *particle = &cluster->rep[ 0 ]; + + // no legacy stuff here + ASSERT( particle->extinfo != NULL ); + ASSERT( ( particle->bitmap & iter_base_mask ) >= iter_texonly ); + + // fetch extinfo (current texture and trafo frame) + pextinfo_s *extinfo = particle->extinfo; + + pdef_s *pdef = extinfo->partdef; //TODO: partdef_dest + ASSERT( pdef != NULL ); + + ASSERT( pdef->tex_table != NULL ); + texfrm_s *curtexframe = &pdef->tex_table[ extinfo->tex_pos ]; + xfofrm_s *curxfoframe = pdef->xfo_table ? + &pdef->xfo_table[ extinfo->xfo_pos ] : NULL; + + TextureMap *curtex = curtexframe->texmap; + ASSERT( curtex != NULL ); + + // rasterizer configuration for entire cluster + dword raststate = rast_nozwrite | rast_texclamp | rast_chromakeyoff | rast_zcompare; + dword rastmask = rast_nomask; + + /* + if ( particle->bitmap & PART_REND_NODEPTHCMP ) { + raststate &= ~rast_mask_zcompare; + rastmask &= ~rast_mask_zcompare; + } + */ + + // fill drawing info + gl_cluster_drawinf.width = 1 << curtex->Width; + gl_cluster_drawinf.height = 1 << curtex->Height; + gl_cluster_drawinf.texmap = curtex; + gl_cluster_drawinf.imgtrafo = curxfoframe ? curxfoframe->imgtrafo : NULL; + gl_cluster_drawinf.itertype = particle->bitmap & PART_REND_MASK_ITER; + gl_cluster_drawinf.raststate = raststate; + gl_cluster_drawinf.rastmask = rastmask; + + // fill texinfo structure and determine scale factor for coordinates + RO_TextureMap2GLTexInfo( &gl_cluster_texinfo, curtex ); +} + + +// schedule active cluster particles for drawing later on --------------------- +// +INLINE +int RO_ScheduleClusterParticles( pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + + // scan contained particles + int numactive = 0; + for ( int curp = 0; curp < cluster->numel; curp++ ) { + + particle_s *particle = &cluster->rep[ curp ]; + + // skip inactive particles + if ( ( particle->flags & PARTICLE_ACTIVE ) == 0 ) + continue; + else + numactive++; + + Vertex3 tempvert; + Vertex3 posvec = particle->position; + + // if position is in (particle)object-space transform into world-space + if ( cluster->type & CT_PARTICLE_OBJ_MASK ) { + + Vertex3& origin = ((particleobj_pcluster_s*)cluster)->origin; + + posvec.X += origin.X; + posvec.Y += origin.Y; + posvec.Z += origin.Z; + + // if position is in object-space transform into world-space + } else if ( cluster->type & CT_GENOBJECTRELATIVE_OBJ_MASK ) { + + GenObject *baseobject = ((objectbase_pcluster_s*)cluster)->baseobject; + ASSERT( baseobject != NULL ); + MtxVctMUL( baseobject->ObjPosition, &posvec, &tempvert ); + posvec = tempvert; + } + + // transform position into view-space + MtxVctMUL( ViewCamera, &posvec, &tempvert ); + + // draw view-space particle + if ( ( tempvert.Z > Near_View_Plane ) && ( tempvert.Z < Far_View_Plane ) ) { + + // project particle to screen + SPoint screenpos; + PROJECT_TO_SCREEN( tempvert, screenpos ); + + // schedule for drawing + RO_ScheduleParticle( particle, &screenpos, tempvert.Z ); + } + } + + return numactive; +} + + +// blast out all scheduled particles using one vertex array ------------------- +// +INLINE +void RO_DrawScheduledParticles() +{ + // enforce texel source + RO_SelectTexelSource( &gl_cluster_texinfo ); + + // save zcmp and zwrite state + int zcmpstate = RO_DepthCmpEnabled(); + int zwritestate = RO_DepthWriteEnabled(); + + // configure rasterizer + RO_InitRasterizerState( gl_cluster_drawinf.itertype, + gl_cluster_drawinf.raststate, + gl_cluster_drawinf.rastmask ); + RO_TextureCombineState( texcomb_decal ); + + // set fixed color + glColor4ub( (GLubyte) 220, (GLubyte) 220, (GLubyte) 220, (GLubyte) 180 ); + + // specify vertex arrays + RO_ClientState( VTXARRAY_VERTICES | VTXARRAY_TEXCOORDS ); + if ( RO_ArrayMakeCurrent( VTXPTRS_RO_PART_1, gl_cluster_vtxs ) ) { + glVertexPointer( 4, GL_FLOAT, sizeof( GLVertex4 ), &gl_cluster_vtxs->x ); + glTexCoordPointer( 2, GL_FLOAT, sizeof( GLVertex4 ), &gl_cluster_vtxs->s ); + } + + // draw vertex array + glDrawArrays( GL_QUADS, 0, gl_cluster_vcount ); + + // disable vertex arrays +// RO_ClientState( VTXARRAY_NONE ); + + // set rasterizer state to default + RO_DefaultRasterizerState(); + + // restore zcmp and zwrite state + RO_RestoreDepthState( zcmpstate, zwritestate ); +} + + +// render particle cluster ---------------------------------------------------- +// +int R_DrawParticleCluster( pcluster_s *cluster, int *numactive ) +{ + ASSERT( cluster != NULL ); + ASSERT( numactive != NULL ); + + //NOTE: + // this function exists purely for performance improvements, it + // adds no functionality to what PART_SYS::DrawClusterParticles() + // is able to do. it however allows to trade off code duplication + // for performance improvements in the form of better (and direct) + // accommodation of the actual underlying rendering API (OpenGL). + + // determine whether cluster can be rendered here + if ( ( cluster->type & CT_HINT_PARTICLES_IDENTICAL ) == 0 ) + return FALSE; + if ( ( cluster->type & CT_HINT_PARTICLES_HAVE_EXTINFO ) == 0 ) + return FALSE; + if ( cluster->numel == 0 ) + return FALSE; + ASSERT( cluster->rep[ 0 ].extinfo != NULL ); + if ( ( cluster->rep[ 0 ].bitmap & iter_base_mask ) < iter_texonly ) + return FALSE; + + // determine texture for all particles in cluster + RO_DetermineClusterTexture( cluster ); + + // create empty vertex array + gl_cluster_vcount = 0; + gl_cluster_vtxs = (GLVertex4 *) ALLOCMEM( cluster->numel * sizeof( GLVertex4 ) * 4 ); + if ( gl_cluster_vtxs == NULL ) { + OUTOFMEM( "no mem for cluster vertex array." ); + } + +#ifdef USE_INDEXED_TRIANGLES + + // create index array + gl_cluster_icount = 0; + gl_cluster_trindxs = (dword *) ALLOCMEM( cluster->numel * sizeof( dword ) * 6 ); + if ( gl_cluster_trindxs == NULL ) { + OUTOFMEM( "no mem for vertex indexes." ); + } + +#endif // USE_INDEXED_TRIANGLES + + // schedule active particles + *numactive = RO_ScheduleClusterParticles( cluster ); + + // draw active and scheduled particles + if ( ( *numactive > 0 ) && ( gl_cluster_vcount > 0 ) ) { + RO_DrawScheduledParticles(); + } + + // delete vertex array + FREEMEM( gl_cluster_vtxs ); + gl_cluster_vtxs = NULL; + +#ifdef USE_INDEXED_TRIANGLES + + // delete index array + FREEMEM( gl_cluster_trindxs ); + gl_cluster_trindxs = NULL; + +#endif // USE_INDEXED_TRIANGLES + + return TRUE; +} + + +// render particles ----------------------------------------------------------- +// +void R_DrawParticles() +{ + //FIXME: + // fldcw is test only! + + RO_EnableDepthBuffer( true, true ); + + //NOTE: + // currently, only iterated particles are influenced + // by the z-buffer setting done here. and then only + // z-compare state. z-write is enabled for each particle. + // for bitmap particles the z-buffer (both write and + // compare) is enabled for each particle. + + // walk pre-callbacks + CALLBACK_WalkCallbacks( CBTYPE_DRAW_PRE_PARTICLES ); + + // call particle system main + PRTSYS_DrawParticles(); + + // walk post-callbacks + CALLBACK_WalkCallbacks( CBTYPE_DRAW_POST_PARTICLES ); + + RO_DisableDepthBuffer( true, true ); +} + + + -- cgit v1.2.3