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/obj_ctrl.cpp | 1154 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1154 insertions(+) create mode 100644 src/parsec/obj_ctrl.cpp (limited to 'src/parsec/obj_ctrl.cpp') diff --git a/src/parsec/obj_ctrl.cpp b/src/parsec/obj_ctrl.cpp new file mode 100644 index 0000000..af0fc0c --- /dev/null +++ b/src/parsec/obj_ctrl.cpp @@ -0,0 +1,1154 @@ +/* + * PARSEC - Control Code + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:35 $ + * + * Orginally written by: + * Copyright (c) Markus Hadwiger 1996-1999 + * + * 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 +#include + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// global externals +#include "globals.h" + +// subsystem headers +#include "net_defs.h" + +// mathematics header +#include "utl_math.h" + +// model header +#include "utl_model.h" + +// local module header +#include "obj_ctrl.h" + +// proprietary module headers +#include "con_aux.h" +#include "con_com.h" +#include "con_main.h" +#include "g_supp.h" +#include "h_supp.h" +#include "obj_cust.h" +#include "obj_expl.h" +#include "obj_part.h" +#include "part_api.h" +#include "g_sfx.h" +#include "g_vapor.h" + + +// flags +#define ALLOW_OBJLIST_DISABLING +#define CULL_IN_WORLD_SPACE +#define CULL_BOUNDING_SPHERES +#define ALLOW_NEARPLANE_CLIPPING +//#define INSTANTIATE_FACE_STRUCTURES + + + +// to determine whether an object should never be clipped by the nearplane ---- +// +#ifdef ALLOW_NEARPLANE_CLIPPING +// #define PREVENT_OBJECT_NEARPLANE_CLIPPING(o) ( !OBJECT_TYPE_LASER(o) ) + #define PREVENT_OBJECT_NEARPLANE_CLIPPING(o) ( !OBJECT_TYPE_LASER(o) && !AUX_DISABLE_NEARPLANE_TOUCH_CULLING ) +// #define PREVENT_OBJECT_NEARPLANE_CLIPPING(o) ( FALSE ) +#else + #define PREVENT_OBJECT_NEARPLANE_CLIPPING(o) ( TRUE ) +#endif + +//NOTE: +//CAVEAT: +// if ALLOW_NEARPLANE_CLIPPING is specified certain objects will not +// be culled as soon as they touch the near view-plane. therefore, +// 3-D clipping MUST be performed to avoid projection problems. +// +// !! if ALLOW_NEARPLANE_CLIPPING is specified here, CLIP_OBJECT_POLYGONS !! +// !! must be specified in Rx_OBJ.C to enable the geometry clipper. !! + + +// init object maintenance structures ----------------------------------------- +// +void InitObjCtrl() +{ + //NOTE: + // this function gets called exactly once, + // from G_BOOT::GameBoot(). + + static int init_valid = TRUE; + + if ( init_valid ) { + init_valid = FALSE; + + // allocate dummy heads of object lists + PShipObjects = (ShipObject *) ALLOCMEM( sizeof( GenObject ) ); + LaserObjects = (LaserObject *) ALLOCMEM( sizeof( GenObject ) ); + MisslObjects = (MissileObject *) ALLOCMEM( sizeof( GenObject ) ); + ExtraObjects = (ExtraObject *) ALLOCMEM( sizeof( GenObject ) ); + CustmObjects = (CustomObject *) ALLOCMEM( sizeof( GenObject ) ); + VObjList = (GenObject *) ALLOCMEM( sizeof( GenObject ) ); + + // check if memory allocation failed + if ( ( PShipObjects == NULL ) || ( LaserObjects == NULL ) || + ( MisslObjects == NULL ) || ( ExtraObjects == NULL ) || + ( CustmObjects == NULL ) || ( VObjList == NULL ) ) { + + OUTOFMEM( 0 ); + } + + // initialize dummy heads of object lists + memset( PShipObjects, 0, sizeof( GenObject ) ); + memset( LaserObjects, 0, sizeof( GenObject ) ); + memset( MisslObjects, 0, sizeof( GenObject ) ); + memset( ExtraObjects, 0, sizeof( GenObject ) ); + memset( CustmObjects, 0, sizeof( GenObject ) ); + memset( VObjList, 0, sizeof( GenObject ) ); + + } else { + + // never happens + ASSERT( 0 ); + } +} + + +// invoke constructor of custom object ---------------------------------------- +// +INLINE +void InvokeCustomConstructor( GenObject *objectpo ) +{ + ASSERT( OBJECT_TYPE_CUSTOM( objectpo ) ); + + CustomObject *cobj = (CustomObject *) objectpo; + if ( cobj->callback_instant != NULL ) { + (*cobj->callback_instant)( cobj ); + } +} + + +// invoke destructor of custom object ----------------------------------------- +// +INLINE +void InvokeCustomDestructor( GenObject *objectpo ) +{ + ASSERT( OBJECT_TYPE_CUSTOM( objectpo ) ); + + CustomObject *cobj = (CustomObject *) objectpo; + if ( cobj->callback_destroy != NULL ) { + (*cobj->callback_destroy)( cobj ); + } +} + + +// free memory occupied by an object ------------------------------------------ +// +void FreeObjectMem( GenObject *objectpo ) +{ + //NOTE: + // this function should only be used + // from within OBJ_xx modules. + + ASSERT( objectpo != NULL ); + + // notify registered custom objects of impending object deletion + if ( ( objectpo->ObjectType & TYPELISTMASK ) != CUSTM_LIST_NO ) { + OBJ_NotifyCustomObjectsList( objectpo, CUSTOM_NOTIFY_GENOBJECT_DELETE ); + } + + switch ( objectpo->ObjectType & TYPELISTMASK ) { + + case PSHIP_LIST_NO: + // ensure duration weapons are properly killed + KillDurationWeapons( (ShipObject *) objectpo ); + break; + + case LASER_LIST_NO: + // no special handling + break; + + case MISSL_LIST_NO: + // no special handling + break; + + case EXTRA_LIST_NO: + // for extras maintain count + CurrentNumExtras--; + break; + + case CUSTM_LIST_NO: + // for custom objects invoke destructor + InvokeCustomDestructor( objectpo ); + break; + } + + // free attached particle clusters + PRT_FreeAttachedClusterList( objectpo ); + +#ifndef INSTANTIATE_FACE_STRUCTURES +/*//FIXME: + // non-virtual objects may have additional instance data + dword objclass = objectpo->ObjectClass; + if ( objclass != CLASS_ID_INVALID ) { + + // make sure we take the base lod + Face *basefaces = objectpo->FaceList; + if ( objectpo->NumLodObjects > 0 ) { + ASSERT( objectpo->LodObjects != NULL ); + basefaces = objectpo->LodObjects[ 0 ].LodObject->FaceList; + } + + // free instanced face data if attached separately + ASSERT( objclass < NumObjClasses ); + if ( basefaces != ObjClasses[ objclass ]->FaceList ) { + FREEMEM( basefaces ); + } + } +*/ +#endif + + FREEMEM( objectpo ); +} + + +// free all object memory blocks contained in specific list ------------------- +// +int FreeObjList( GenObject *listpo ) +{ + //NOTE: + // in addition to FreeObjects() this is also + // used by CON_ACT::AcDestroyObjectList(). + + ASSERT( listpo != NULL ); + ASSERT( listpo != MyShip ); + + GenObject *delpo = listpo; + while ( delpo->NextObj != NULL ) { + + ASSERT( delpo->NextObj ); + ASSERT( delpo->NextObj != MyShip ); + + GenObject *temppo = delpo->NextObj->NextObj; + FreeObjectMem( delpo->NextObj ); + delpo->NextObj = temppo; + } + + return 1; +} + + +// free all object memory blocks ---------------------------------------------- +// +int FreeObjects() +{ + //NOTE: + // this function is called every time the + // game loop is entered (G_BOOT::GameInit()) + // and by CON_ACT::AcPrepareDataRestore() to + // remove all objects. + + int rc = 0; + + rc += FreeObjList( PShipObjects ); + rc += FreeObjList( LaserObjects ); + rc += FreeObjList( MisslObjects ); + rc += FreeObjList( ExtraObjects ); + rc += FreeObjList( CustmObjects ); + + return ( rc == 5 ); +} + + +// correct pointers that have moved after object was instantiated ------------- +// +void OBJ_CorrectObjectInstance( GenObject *dstobj, GenObject *srcobj ) +{ + ASSERT( dstobj != NULL ); + ASSERT( srcobj != NULL ); + + ptrdiff_t pdiff = (char*)dstobj - (char*)srcobj; + + if ( dstobj->FaceAnimStates != NULL ) { + dstobj->FaceAnimStates = (FaceAnimState *) ( (char*)dstobj->FaceAnimStates + pdiff ); + } + if ( dstobj->VtxAnimStates != NULL ) { + dstobj->VtxAnimStates = (VtxAnimState *) ( (char*)dstobj->VtxAnimStates + pdiff ); + } +} + + +// internal flag to disable automatic stargate creation ----------------------- +// +static int disable_ship_creation_stargate = FALSE; + + +// create object instance and insert into global objectlist ------------------- +// +GenObject *CreateObject( int objclass, const Xmatrx startmatrx ) +{ + ASSERT( objclass >= 0 ); + ASSERT( objclass < NumObjClasses ); + + // check if class number is valid + if ( ( objclass < 0 ) || ( objclass >= NumObjClasses ) ) + return NULL; + + size_t allocinstancesize = ObjClasses[ objclass ]->InstanceSize; + +#ifdef INSTANTIATE_FACE_STRUCTURES + + // reserve space for copy of facelist + allocinstancesize += ObjClasses[ objclass ]->NumFaces * sizeof( Face ); + +#endif // INSTANTIATE_FACE_STRUCTURES + + // create new object header + GenObject *newinstance = (GenObject *) ALLOCMEM( allocinstancesize ); + if ( newinstance == NULL ) + OUTOFMEM( "no mem for object instance." ); + + // fill header with class data + memcpy( newinstance, ObjClasses[ objclass ], ObjClasses[ objclass ]->InstanceSize ); + OBJ_CorrectObjectInstance( newinstance, ObjClasses[ objclass ] ); + +#ifdef INSTANTIATE_FACE_STRUCTURES + + // make copy of facelist + Face *instfacelist = (Face *) ( (char*)newinstance + newinstance->InstanceSize ); + memcpy( instfacelist, newinstance->FaceList, + newinstance->NumFaces * sizeof( Face ) ); + newinstance->FaceList = instfacelist; + +#endif // INSTANTIATE_FACE_STRUCTURES + + // set object number + newinstance->ObjectNumber = NextObjNumber; + newinstance->HostObjNumber = CreateGlobalObjId( NextObjNumber ); + NextObjNumber++; + + // set start matrix + memcpy( newinstance->ObjPosition, startmatrx, sizeof( Xmatrx ) ); + + // link header into list of existing objects of this type + GenObject *listhead; + switch ( newinstance->ObjectType & TYPELISTMASK ) { + + case PSHIP_LIST_NO: + listhead = ( PShipObjects->NextObj == MyShip ) ? + PShipObjects->NextObj : PShipObjects; + // create stargate for newly created spaceship + if ( !disable_ship_creation_stargate ) { + SFX_CreateStargate( (ShipObject *) newinstance ); + } + break; + + case LASER_LIST_NO: + listhead = LaserObjects; + break; + + case MISSL_LIST_NO: + listhead = MisslObjects; + // create missile trail + CreateVaporTrail( newinstance ); + break; + + case EXTRA_LIST_NO: + listhead = ExtraObjects; + // create generation animation + SFX_CreateExtra( (ExtraObject *) newinstance ); + // for extras maintain count + CurrentNumExtras++; + break; + + case CUSTM_LIST_NO: + listhead = CustmObjects; + // for custom objects invoke constructor + InvokeCustomConstructor( newinstance ); + break; + + default: + PANIC( 0 ); + } + + // attach static particles that are + // part of the object instance + if ( AUX_ATTACH_OBJECT_PARTICLES ) { + OBJ_AttachClassParticles( newinstance ); + } + + // append object at head of list + newinstance->NextObj = listhead->NextObj; + listhead->NextObj = newinstance; + + return newinstance; +} + + +// create object instance and insert into global objectlist ------------------- +// +GenObject *SummonObject( int objclass, const Xmatrx startmatrx ) +{ + ASSERT( objclass >= 0 ); + ASSERT( objclass < NumObjClasses ); + + // disable stargate + disable_ship_creation_stargate = TRUE; + + GenObject *obj = CreateObject( objclass, startmatrx ); + + // enable stargate + disable_ship_creation_stargate = FALSE; + + return obj; +} + +// create object instance from a type and insert into global objectlist ------- +// +GenObject* SummonObjectFromType( int objtypeid, const Xmatrx startmatrx ) +{ + ASSERT( objtypeid >= 0 ); + //ASSERT( objtypeid < num_custom_types ); + + // check whether this type is summonable + int custtype_flags = OBJ_GetCustomTypeFlags( objtypeid ); + if ( !( custtype_flags & CUSTOM_TYPE_SUMMONABLE ) ) { + return NULL; + } + + CustomObject *obj = CreateVirtualObject( objtypeid ); + + // set start matrix + memcpy( obj->ObjPosition, startmatrx, sizeof( Xmatrx ) ); + + return (GenObject*)obj; +} + + +// create virtual (type-only) instance of custom object ----------------------- +// +CustomObject *CreateVirtualObject( dword objtypeid ) +{ + ASSERT( TYPEID_TYPE_CUSTOM( objtypeid ) ); + if ( !TYPEID_TYPE_CUSTOM( objtypeid ) ) + return NULL; + + // create new object header + size_t custsize = OBJ_FetchCustomTypeSize( objtypeid ); + CustomObject *newinstance = (CustomObject *) ALLOCMEM( custsize ); + if ( newinstance == NULL ) + OUTOFMEM( "no mem for custom object." ); + + // GenObject::NumVerts==0 ensures object + // will be skipped by BuildVisList() + memset( newinstance, 0, custsize ); + + // init fields that must be valid + newinstance->ObjectType = objtypeid; + newinstance->ObjectClass = CLASS_ID_INVALID; + newinstance->InstanceSize = custsize; + + // would normally have been done on class loading + OBJ_InitCustomType( newinstance ); + + // set object number + newinstance->ObjectNumber = NextObjNumber; + newinstance->HostObjNumber = CreateGlobalObjId( NextObjNumber ); + NextObjNumber++; + + // invoke constructor + InvokeCustomConstructor( newinstance ); + + // append object at head of list + newinstance->NextObj = CustmObjects->NextObj; + CustmObjects->NextObj = newinstance; + + return newinstance; +} + + +// fetch object contained in specific object list ----------------------------- +// +GenObject *FetchSpecificObject( dword objno, GenObject *listpo ) +{ + ASSERT( listpo != NULL ); + + GenObject *scan = listpo->NextObj; + while ( ( scan != NULL ) && ( scan->ObjectNumber != objno ) ) + scan = scan->NextObj; + + // prevent fetching of local ship + if ( scan == MyShip ) + scan = NULL; + + // return found object or NULL + return scan; +} + + +// fetch object contained in specific object list ----------------------------- +// +GenObject *FetchSpecificHostObject( dword hostobjno, GenObject *listpo ) +{ + ASSERT( listpo != NULL ); + + // NOTE: Since MyShip->HostObjNumber seems not to be set to + // ShipHostObjId( LocalPlayerId ), ( scan->HostObjNumber == 0 ) + // could be MyShip in ObjectCamera-Mode, additionally present to + // ship with real HostObjNumber of 0! So we try to check next + // object. + + GenObject *scan = listpo->NextObj; + while ( ( ( scan != NULL ) && ( scan->HostObjNumber != hostobjno ) ) || ( scan == MyShip ) ) + scan = scan->NextObj; + + ASSERT( scan != MyShip ); + + // obsolete + // prevent fetching of local ship +// if ( scan == MyShip ) +// scan = NULL; + + // return found object or NULL + return scan; +} + + +// fetch object via object id (search in all object lists) -------------------- +// +GenObject *FetchObject( dword objno ) +{ + GenObject *obj; + + obj = FetchSpecificObject( objno, PShipObjects ); + if ( obj != NULL ) + return obj; + obj = FetchSpecificObject( objno, LaserObjects ); + if ( obj != NULL ) + return obj; + obj = FetchSpecificObject( objno, MisslObjects ); + if ( obj != NULL ) + return obj; + obj = FetchSpecificObject( objno, ExtraObjects ); + if ( obj != NULL ) + return obj; + obj = FetchSpecificObject( objno, CustmObjects ); + + return obj; +} + + +// fetch object via host object id (search in all object lists) --------------- +// +GenObject *FetchHostObject( dword hostobjno ) +{ + GenObject *obj; + + obj = FetchSpecificHostObject( hostobjno, PShipObjects ); + if ( obj != NULL ) + return obj; + obj = FetchSpecificHostObject( hostobjno, LaserObjects ); + if ( obj != NULL ) + return obj; + obj = FetchSpecificHostObject( hostobjno, MisslObjects ); + if ( obj != NULL ) + return obj; + obj = FetchSpecificHostObject( hostobjno, ExtraObjects ); + if ( obj != NULL ) + return obj; + obj = FetchSpecificHostObject( hostobjno, CustmObjects ); + + return obj; +} + + +// kill object contained in specific object list (search only one list) ------- +// +int KillSpecificObject( dword objno, GenObject *listpo ) +{ + ASSERT( listpo != NULL ); + ASSERT( listpo != MyShip ); + + GenObject *delpo = listpo; + while ( ( delpo->NextObj != NULL ) && ( delpo->NextObj->ObjectNumber != objno ) ) + delpo = delpo->NextObj; + + if ( delpo->NextObj == NULL ) + return 0; + + ASSERT( delpo->NextObj ); + ASSERT( delpo->NextObj != MyShip ); + + GenObject *temppo = delpo->NextObj->NextObj; + FreeObjectMem( delpo->NextObj ); + delpo->NextObj = temppo; + + return 1; +} + + +// kill object contained in specific object list (search only one list) ------- +// +int KillSpecificHostObject( dword hostobjno, GenObject *listpo ) +{ + ASSERT( listpo != NULL ); + ASSERT( listpo != MyShip ); + + GenObject *delpo = listpo; + while ( ( delpo->NextObj != NULL ) && ( delpo->NextObj->HostObjNumber != hostobjno ) ) { + delpo = delpo->NextObj; + } + + if ( delpo->NextObj == NULL ) + return 0; + + ASSERT( delpo->NextObj ); + ASSERT( delpo->NextObj != MyShip ); + + GenObject *temppo = delpo->NextObj->NextObj; + FreeObjectMem( delpo->NextObj ); + delpo->NextObj = temppo; + + return 1; +} + + +// kill object instance (search in all object lists) -------------------------- +// +int KillObject( dword objno ) +{ + if ( KillSpecificObject( objno, PShipObjects ) ) + return 1; + else if ( KillSpecificObject( objno, LaserObjects ) ) + return 1; + else if ( KillSpecificObject( objno, MisslObjects ) ) + return 1; + else if ( KillSpecificObject( objno, ExtraObjects ) ) + return 1; + else if ( KillSpecificObject( objno, CustmObjects ) ) + return 1; + + return 0; +} + + +// kill object instance (search in all object lists) -------------------------- +// +int KillHostObject( dword hostobjno ) +{ + //NOTE: + // currently never used since KillSpecificHostObject() + // is always used directly. + +//FIXME: + ASSERT( 0 ); // remove this on first use + + if ( KillSpecificHostObject( hostobjno, PShipObjects ) ) + return 1; + else if ( KillSpecificHostObject( hostobjno, LaserObjects ) ) + return 1; + else if ( KillSpecificHostObject( hostobjno, MisslObjects ) ) + return 1; + else if ( KillSpecificHostObject( hostobjno, ExtraObjects ) ) + return 1; + else if ( KillSpecificHostObject( hostobjno, CustmObjects ) ) + return 1; + + return 0; +} + + +// kill all instances of passed in object class (search only one list) -------- +// +PRIVATE +int KillClassInstancesFromList( int objclass, GenObject *listpo ) +{ + ASSERT( listpo != NULL ); + ASSERT( listpo != MyShip ); + + GenObject *delpo = listpo; + for ( int killcount = 0; ; killcount++ ) { + + while ( ( delpo->NextObj != NULL ) && + ( delpo->NextObj->ObjectClass != (dword)objclass ) ) + delpo = delpo->NextObj; + + if ( delpo->NextObj == NULL ) + return killcount; + + ASSERT( delpo->NextObj ); + ASSERT( delpo->NextObj != MyShip ); + + GenObject *temppo = delpo->NextObj->NextObj; + FreeObjectMem( delpo->NextObj ); + delpo->NextObj = temppo; + } + + // never reached + ASSERT( 0 ); +} + + +// kill all instances of passed in object class (search in all object lists) -- +// +int KillClassInstances( int objclass ) +{ + //NOTE: + // this function is used by CON_LOAD::ConLoadObject() + // to remove objects prior to overloading them. + + int killcount = 0; + + killcount += KillClassInstancesFromList( objclass, PShipObjects ); + killcount += KillClassInstancesFromList( objclass, LaserObjects ); + killcount += KillClassInstancesFromList( objclass, MisslObjects ); + killcount += KillClassInstancesFromList( objclass, ExtraObjects ); + killcount += KillClassInstancesFromList( objclass, CustmObjects ); + + return killcount; +} + + +//FIXME: seperate between object management functions and other functions in this module + + + + +// check entire object against viewing frustum -------------------------------- +// +INLINE +int ObjectInFrustum( const Camera camera, GenObject *objectp ) +{ + ASSERT( objectp != NULL ); + + // cull against entire frustum by default + objectp->CullMask = 0x3f; + + // get type id of the custom planet + static dword planet_typeid = TYPE_ID_INVALID; + if ( planet_typeid == TYPE_ID_INVALID ) { + planet_typeid = OBJ_FetchCustomTypeId( "planet" ); + } + + // disable far-plane clipping for planets + if ( objectp->ObjectType == planet_typeid ) { + objectp->CullMask = 0x3d; + } + + +#ifdef CULL_IN_WORLD_SPACE + + // precalculated world-frustum for ViewCamera + ASSERT( camera == ViewCamera ); + + geomv_t radius = objectp->BoundingSphere; + + #ifdef CULL_BOUNDING_SPHERES + + Sphere3 sphere; + sphere.X = objectp->ObjPosition[ 0 ][ 3 ]; + sphere.Y = objectp->ObjPosition[ 1 ][ 3 ]; + sphere.Z = objectp->ObjPosition[ 2 ][ 3 ]; + sphere.R = radius; + + // ensure near view plane doesn't clip object for most object types + geomv_t nearplaneofs = PLANE_OFFSET( &World_ViewVolume[ 0 ] ); + if ( PREVENT_OBJECT_NEARPLANE_CLIPPING( objectp ) ) { + PLANE_OFFSET( &World_ViewVolume[ 0 ] ) += radius * 2; + } + + int cull = CULL_SphereAgainstVolume( &sphere, World_ViewVolume, &objectp->CullMask ); + + PLANE_OFFSET( &World_ViewVolume[ 0 ] ) = nearplaneofs; + + return !cull; + + #else // CULL_BOUNDING_SPHERES + + Vertex3 origin; + origin.X = objectp->ObjPosition[ 0 ][ 3 ]; + origin.Y = objectp->ObjPosition[ 1 ][ 3 ]; + origin.Z = objectp->ObjPosition[ 2 ][ 3 ]; + + CullBox3 cullbox; + + cullbox.minmax[ 0 ] = origin.X - radius; + cullbox.minmax[ 1 ] = origin.Y - radius; + cullbox.minmax[ 2 ] = origin.Z - radius; + + cullbox.minmax[ 3 ] = origin.X + radius; + cullbox.minmax[ 4 ] = origin.Y + radius; + cullbox.minmax[ 5 ] = origin.Z + radius; + + // ensure near view plane doesn't clip object for most object types + geomv_t nearplaneofs = PLANE_OFFSET( &World_CullVolume[ 0 ].plane ); + if ( PREVENT_OBJECT_NEARPLANE_CLIPPING( objectp ) ) { + PLANE_OFFSET( &World_CullVolume[ 0 ].plane ) += radius * 2; + } + + int cull = CULL_BoxAgainstCullVolume( &cullbox, World_CullVolume, &objectp->CullMask ); + + PLANE_OFFSET( &World_CullVolume[ 0 ].plane ) = nearplaneofs; + + return !cull; + + #endif // CULL_BOUNDING_SPHERES + +#else // CULL_IN_WORLD_SPACE + + // fetch object position + geomv_t objX = objectp->CurrentXmatrx[ 0 ][ 3 ]; + geomv_t objY = objectp->CurrentXmatrx[ 1 ][ 3 ]; + geomv_t objZ = objectp->CurrentXmatrx[ 2 ][ 3 ]; + + // fetch radius of bounding sphere + geomv_t bsphere = objectp->BoundingSphere; + + // calc potentially nearest z coordinate + geomv_t nearestZ = objZ - bsphere; + + // far too near? :) + if ( nearestZ < Near_View_Plane ) + return FALSE; + // too far away? + if ( nearestZ > Far_View_Plane ) + return FALSE; + + // calc potentially farthest z coordinate + geomv_t farthestZ = objZ + bsphere; + + geomv_t criterionX = GEOMV_MUL( farthestZ, Criterion_X ); + // too far to the left or right? + if ( ( objX - bsphere ) > criterionX ) + return FALSE; + if ( ( objX + bsphere ) < -criterionX ) + return FALSE; + + geomv_t criterionY = GEOMV_MUL( farthestZ, Criterion_Y ); + // too far up or down? + if ( ( objY - bsphere ) > criterionY ) + return FALSE; + if ( ( objY + bsphere ) < -criterionY ) + return FALSE; + + return TRUE; + +#endif // CULL_IN_WORLD_SPACE + +} + + +// calc position of object in viewspace --------------------------------------- +// +INLINE +void CalcViewSpacePosition( const Camera camera, GenObject *object, Vertex3& pos ) +{ + ASSERT( object != NULL ); + + Vertex3 objt; + objt.X = object->ObjPosition[ 0 ][ 3 ]; + objt.Y = object->ObjPosition[ 1 ][ 3 ]; + objt.Z = object->ObjPosition[ 2 ][ 3 ]; + + MtxVctMUL( camera, &objt, &pos ); +} + + +// check whether specified object is current target --------------------------- +// +INLINE +void ObjectIsTarget( const Camera camera, GenObject *object, Vertex3& pos ) +{ + ASSERT( object != NULL ); + + if ( ObjCameraActive ) + return; + + if ( OBJECT_TYPE_SHIP( object ) && ( object->HostObjNumber == TargetObjNumber ) ) { + + ShipObject *ship = (ShipObject *) object; + + if ( ( ship->ExplosionCount == 0 ) || + ( ship->ExplosionCount >= BM_EXPLNOTARGFRAME * EXPL_REF_SPEED ) ) { +/* +#ifdef CULL_IN_WORLD_SPACE + CalcViewSpacePosition( camera, object, pos ); +#endif +*/ // set flag that current target is visible + TargetVisible = TRUE; + + if ( ( object->HostObjNumber & 0xffff ) == 0 ) + TargetRemId = GetObjectOwner( object ); + else + TargetRemId = TARGETID_NO_TARGET; + + // calc position of target tracker + SPoint tloc; + PROJECT_TO_SCREEN( pos, tloc ); + TargetScreenX = tloc.X; + TargetScreenY = tloc.Y; + } + } +} + + +// enlist object in list of visible objects ----------------------------------- +// +INLINE +void EnlistVisibleObject( GenObject *list, GenObject *obj ) +{ + ASSERT( list != NULL ); + ASSERT( obj != NULL ); + + if ( !AUX_DISABLE_OBJECT_DEPTH_SORT ) { + + geomv_t insertZ = obj->CurrentXmatrx[ 2 ][ 3 ]; + + // search to right position + while ( ( list->NextVisObj != NULL ) && + ( list->NextVisObj->CurrentXmatrx[ 2 ][ 3 ] > insertZ ) ) + list = list->NextVisObj; + } + + // indicate that object visible in this frame + obj->VisibleFrame = CurVisibleFrame; + + // insert object into list + obj->NextVisObj = list->NextVisObj; + list->NextVisObj = obj; +} + + +// switch object detail level ------------------------------------------------- +// +void OBJ_SwitchObjectLod( GenObject *obj, dword lod ) +{ + ASSERT( obj != NULL ); + ASSERT( lod < obj->NumLodObjects ); + + // store active lod + obj->CurrentLod = lod; + + // retrieve source geometry + ASSERT( obj->LodObjects != NULL ); + GenLodObject *lodobj = obj->LodObjects[ lod ].LodObject; + + // switch geometry + ASSERT( lodobj != NULL ); + obj->NumVerts = lodobj->NumVerts; + obj->NumPolyVerts = lodobj->NumPolyVerts; + obj->NumNormals = lodobj->NumNormals; + obj->VertexList = lodobj->VertexList; + obj->X_VertexList = lodobj->X_VertexList; + obj->S_VertexList = lodobj->S_VertexList; + obj->NumPolys = lodobj->NumPolys; + obj->PolyList = lodobj->PolyList; + obj->NumFaces = lodobj->NumFaces; + obj->FaceList = lodobj->FaceList; + obj->VisPolyList = lodobj->VisPolyList; + obj->SortedPolyList = lodobj->SortedPolyList; + obj->AuxList = lodobj->AuxList; + obj->BSPTree = lodobj->BSPTree; + obj->AuxBSPTree = lodobj->AuxBSPTree; + obj->AuxObject = lodobj->AuxObject; + obj->NumWedges = lodobj->NumWedges; + obj->NumLayers = lodobj->NumLayers; + obj->WedgeFlags = lodobj->WedgeFlags; + obj->WedgeVertIndxs = lodobj->WedgeVertIndxs; + obj->WedgeNormals = lodobj->WedgeNormals; + obj->WedgeColors = lodobj->WedgeColors; + obj->WedgeTexCoords = lodobj->WedgeTexCoords; + obj->WedgeLighted = lodobj->WedgeLighted; + obj->WedgeSpecular = lodobj->WedgeSpecular; + obj->WedgeFogged = lodobj->WedgeFogged; + obj->ActiveFaceAnims = lodobj->ActiveFaceAnims; + obj->ActiveVtxAnims = lodobj->ActiveVtxAnims; +} + + +// check whether object lod needs to be changed ------------------------------- +// +void OBJ_AutoSelectObjectLod( GenObject *obj ) +{ + ASSERT( obj != NULL ); + + if ( obj->NumLodObjects == 0 ) { + return; + } + + ASSERT( obj->LodObjects != NULL ); + ASSERT( obj->CurrentLod < obj->NumLodObjects ); + + if ( AUXDATA_LOD_DISCRETE_GEOMETRY_BIAS < 0 ) + AUXDATA_LOD_DISCRETE_GEOMETRY_BIAS = 0; + int lodbias = AUXDATA_LOD_DISCRETE_GEOMETRY_BIAS; + + // determine boundary for threshold checking + int boundlod = obj->CurrentLod - lodbias; + if ( boundlod < 0 ) { + boundlod = 0; + } + + // check for magnification + int clod = 0; + for ( clod = 0; clod < boundlod; clod++ ) { + GenLodInfo *info = &obj->LodObjects[ clod ]; + if ( obj->CurrentXmatrx[ 2 ][ 3 ] < info->MagTreshold ) { + clod += lodbias; + if ( clod >= obj->NumLodObjects ) + clod = obj->NumLodObjects - 1; + OBJ_SwitchObjectLod( obj, clod ); + return; + } + } + + // check for minification + for ( clod = obj->NumLodObjects - 1; clod > boundlod; clod-- ) { + GenLodInfo *info = &obj->LodObjects[ clod - 1 ]; + if ( obj->CurrentXmatrx[ 2 ][ 3 ] > info->MinTreshold ) { + clod += lodbias; + if ( clod >= obj->NumLodObjects ) + clod = obj->NumLodObjects - 1; + OBJ_SwitchObjectLod( obj, clod ); + return; + } + } + + // check virtual minification boundary to the far left + if ( obj->CurrentLod < lodbias ) { + clod = lodbias; + if ( clod >= obj->NumLodObjects ) + clod = obj->NumLodObjects - 1; + OBJ_SwitchObjectLod( obj, clod ); + } +} + + +// scan a single objectlist; determine visibility, and build list ------------- +// +PRIVATE +void BuildVisList( GenObject *sourcelist, GenObject *destlist, const Camera camera ) +{ + //NOTE: + // sourcelist may be NULL if an empty + // list is being merged. + + ASSERT( destlist != NULL ); + + // walk list of objects + for ( GenObject *scanpo = sourcelist; scanpo; scanpo = scanpo->NextObj ) { + + //NOTE: + // there are two possibilities for GenObject::NumVerts to be zero at + // this stage (later on the clipper might clear it for object rejection) + // 1. the object consists of subobjects only (baseobject is empty) + // 2. the object is a virtual custom object (contains no geometry) + // in case 1 we have to render the object, whereas in case 2 we + // do not even insert it into the list of visible objects. + + if ( scanpo->NumVerts == 0 ) { + + ASSERT( scanpo->NumPolys == 0 ); + ASSERT( scanpo->NumFaces == 0 ); + + // skip virtual (type-only) custom objects + if ( OBJECT_TYPE_CUSTOM( scanpo ) ) { + continue; + } + } + + Vertex3 objpos; + + //NOTE: + // the radar needs the position in view-space + // even if we cull in world-space and wouldn't + // need the position for culling. + +//#ifndef CULL_IN_WORLD_SPACE + + CalcViewSpacePosition( camera, scanpo, objpos ); + + scanpo->CurrentXmatrx[ 0 ][ 3 ] = objpos.X; + scanpo->CurrentXmatrx[ 1 ][ 3 ] = objpos.Y; + scanpo->CurrentXmatrx[ 2 ][ 3 ] = objpos.Z; + +//#endif + + // cull objects, enlist visible ones + if ( ObjectInFrustum( camera, scanpo ) && ( scanpo->VisibleFrame != VISFRAME_NEVER ) ) { + + // check if object is current target + ObjectIsTarget( camera, scanpo, objpos ); + + // insert object in list of visible objects + EnlistVisibleObject( destlist, scanpo ); + + // check for lod change + OBJ_AutoSelectObjectLod( scanpo ); + } + } +} + + +// scan objectlists; determine visibility; create list of visibles ------------ +// +void ScanActiveObjects( const Camera camera ) +{ + ASSERT( VObjList != NULL ); + + VObjList->NextVisObj = NULL; + +#ifdef ALLOW_OBJLIST_DISABLING + + if ( !AUX_OBJCTRL_DISABLE_LASEROBJECTS ) + BuildVisList( FetchFirstLaser(), VObjList, camera ); + + if ( !AUX_OBJCTRL_DISABLE_EXTRAOBJECTS ) + BuildVisList( FetchFirstExtra(), VObjList, camera ); + + if ( !AUX_OBJCTRL_DISABLE_CUSTOMOBJECTS ) + BuildVisList( FetchFirstCustom(), VObjList, camera ); + + if ( !AUX_OBJCTRL_DISABLE_MISSLOBJECTS ) + BuildVisList( FetchFirstMissile(), VObjList, camera ); + + if ( !AUX_OBJCTRL_DISABLE_SHIPOBJECTS ) + BuildVisList( FetchShipListHead(), VObjList, camera ); +#else + + BuildVisList( FetchFirstLaser(), VObjList, camera ); + BuildVisList( FetchFirstExtra(), VObjList, camera ); + BuildVisList( FetchFirstCustom(), VObjList, camera ); + BuildVisList( FetchFirstMissile(), VObjList, camera ); + BuildVisList( FetchShipListHead(), VObjList, camera ); + +#endif + +} + + + -- cgit v1.2.3