/* * PARSEC - Teleporter Model * * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:37 $ * * Orginally written by: * Copyright (c) Clemens Beer 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 #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 "aud_defs.h" // subsystem linkage info #include "linkinfo.h" // drawing subsystem #include "d_iter.h" #include "d_misc.h" // mathematics header #include "utl_math.h" // model header #include "utl_model.h" // local module header #include "g_telep.h" // proprietary module headers #include "aud_game.h" #include "con_arg.h" #include "con_aux.h" #include "con_com.h" #include "con_ext.h" #include "con_info.h" #include "con_main.h" #include "e_callbk.h" #include "e_level.h" #include "e_supp.h" #include "net_defs.h" #include "obj_clas.h" #include "obj_ctrl.h" #include "obj_cust.h" #include "sys_date.h" #include "sys_path.h" // string constants ----------------------------------------------------------- // static char telep_level_inval_level_spec[] = "invalid level specified"; static char telep_level_inval_filename_spec[] = "invalid filename specified"; static char telep_exit_class_not_found[] = "object class for teleporter exit not found."; static char telep_texture_not_found[] = "texture '%s' was not found."; // flags ---------------------------------------------------------------------- // //#define ANIMATE_TUNNEL_FRAME PARSEC_DEBUG //#define DONT_MOVE_REMOTE_SHIPS // teleporter limits and constants -------------------------------------------- // #define TELEPORTER_DEFAULT_TEX_NAME_INTERIOR "tp_int.3df" #define TELEPORTER_DEFAULT_TEX_NAME_TUNNEL "tp_tun.3df" #define TELEPORTER_EXIT_MODEL_NAME "telep_exit" #define TELEP_BOUNDARY_HORIZ 38 // half of horizontal extent of the teleporter start interior rect #define TELEP_BOUNDARY_VERT 30.5 // half of vertical extent of the teleporter start interior rect // helper macros -------------------------------------------------------------- // //#define SETRGBA( x, r,g,b,a ) { (x)->R = r; (x)->G = g; (x)->B = b; (x)->A = a; } // module local functions ----------------------------------------------------- // PRIVATE void Teleporter_Animate_Tunnel ( Teleporter* teleporter ); PRIVATE void Teleporter_Calc_Tunnel_Verts ( Teleporter* teleporter ); PRIVATE int Teleporter_Check_Tunnel_Visibility ( Teleporter* teleporter ); PRIVATE void Teleporter_Draw_Tunnel ( Teleporter* teleporter ); PRIVATE void Teleporter_Draw_Entry_Interior ( Teleporter* teleporter ); PRIVATE void Teleporter_Debug_Show_Tunnel_Spline ( Teleporter* teleporter ); PRIVATE int TeleporterModify_ExitPropsChanged ( GenObject* base ); PRIVATE int TeleporterModify_StartPropsChanged ( GenObject* base ); PRIVATE int TeleporterModify_TexPropsChanged ( GenObject* base ); PRIVATE int TeleporterModify_SplinePropsChanged ( GenObject* base ); PRIVATE void Teleporter_Rotation_Transform ( float phi, float theta, Xmatrx trafo ); // offset definitions into the Teleporter ------------------------------------- // #define OFS_EXITDELTAX offsetof( Teleporter, exit_delta_x ) #define OFS_EXITDELTAY offsetof( Teleporter, exit_delta_y ) #define OFS_EXITDELTAZ offsetof( Teleporter, exit_delta_z ) #define OFS_EXITROTPHI offsetof( Teleporter, exit_rot_phi ) #define OFS_EXITROTTHETA offsetof( Teleporter, exit_rot_theta ) #define OFS_ACTOFFSET offsetof( Teleporter, actoffset ) #define OFS_ACTIVE offsetof( Teleporter, active ) #define OFS_TEXNAME_INT offsetof( Teleporter, tex_name_interior ) #define OFS_TEXNAME_TUNNEL offsetof( Teleporter, tex_name_tunnel ) #define OFS_U_VAR1 offsetof( Teleporter, u_variation[ 0 ] ) #define OFS_V_VAR1 offsetof( Teleporter, v_variation[ 0 ] ) #define OFS_U_VAR2 offsetof( Teleporter, u_variation[ 1 ] ) #define OFS_V_VAR2 offsetof( Teleporter, v_variation[ 1 ] ) #define OFS_START_SEC_LY offsetof( Teleporter, start_tex_sec_layer ) #define OFS_START_TEX_ALPHA offsetof( Teleporter, start_tex_alpha ) #define OFS_ACT_CONE_ANGLE offsetof( Teleporter, act_cone_angle ) #define OFS_TUNNEL_SLERP offsetof( Teleporter, tunnel_slerp ) #define OFS_TUNNEL_LERP offsetof( Teleporter, tunnel_lerp) #define OFS_TUNNEL_TAN_MUL1 offsetof( Teleporter, tunnel_tangent_scale1 ) #define OFS_TUNNEL_TAN_MUL2 offsetof( Teleporter, tunnel_tangent_scale2 ) #define OFS_TUNNEL_ANIM_SPD offsetof( Teleporter, tunnel_anim_speed ) #define OFS_TUNNEL_SPLINE offsetof( Teleporter, tunnel_spline ) #define OFS_TUNNEL_SPL_CORDS offsetof( Teleporter, tunnel_spline_cords ) #define OFS_TUNNEL_TEX_ALPHA offsetof( Teleporter, tunnel_tex_alpha ) #define OFS_TUNNEL_LEN offsetof( Teleporter, tunnel_len ) #define OFS_START_X offsetof( Teleporter, start.X ) #define OFS_START_Y offsetof( Teleporter, start.Y ) #define OFS_START_Z offsetof( Teleporter, start.Z ) #define OFS_STARTROTPHI offsetof( Teleporter, start_rot_phi ) #define OFS_STARTROTTHETA offsetof( Teleporter, start_rot_theta ) // list of console-accessible properties -------------------------------------- // PRIVATE proplist_s Teleporter_PropList[] = { { "exit_delta_x", OFS_EXITDELTAX, -0x40000000,0x40000000, PROPTYPE_FLOAT, TeleporterModify_ExitPropsChanged }, { "exit_delta_y", OFS_EXITDELTAY, -0x40000000,0x40000000, PROPTYPE_FLOAT, TeleporterModify_ExitPropsChanged }, { "exit_delta_z", OFS_EXITDELTAZ, -0x40000000,0x40000000, PROPTYPE_FLOAT, TeleporterModify_ExitPropsChanged }, { "exit_rot_phi", OFS_EXITROTPHI, 0x00000, 0x1680000, PROPTYPE_FLOAT, TeleporterModify_ExitPropsChanged }, { "exit_rot_theta", OFS_EXITROTTHETA, 0x00000, 0x1680000, PROPTYPE_FLOAT, TeleporterModify_ExitPropsChanged }, { "actoffset", OFS_ACTOFFSET, 0x10000, 0x4000000, PROPTYPE_FLOAT, NULL }, { "active", OFS_ACTIVE, 0x0, 0x1, PROPTYPE_INT, NULL }, { "tex_name_interior", OFS_TEXNAME_INT, 0, TELEPORTER_MAX_TEX_NAME, PROPTYPE_STRING, TeleporterModify_TexPropsChanged }, { "tex_name_tunnel", OFS_TEXNAME_TUNNEL, 0, TELEPORTER_MAX_TEX_NAME, PROPTYPE_STRING, TeleporterModify_TexPropsChanged }, { "tex_u_var1", OFS_U_VAR1, 0x00000, 0x0010000, PROPTYPE_FLOAT, TeleporterModify_TexPropsChanged }, { "tex_v_var1", OFS_V_VAR1, 0x00000, 0x0010000, PROPTYPE_FLOAT, TeleporterModify_TexPropsChanged }, { "tex_u_var2", OFS_U_VAR2, 0x00000, 0x0010000, PROPTYPE_FLOAT, TeleporterModify_TexPropsChanged }, { "tex_v_var2", OFS_V_VAR2, 0x00000, 0x0010000, PROPTYPE_FLOAT, TeleporterModify_TexPropsChanged }, { "secly", OFS_START_SEC_LY, 0x0, 0x1, PROPTYPE_INT, NULL }, { "start_tex_alpha", OFS_START_TEX_ALPHA,0x0, 0xff, PROPTYPE_INT, NULL }, { "act_cone_angle", OFS_ACT_CONE_ANGLE, 0x00000, 0x0590000, PROPTYPE_FLOAT, NULL }, { "tslerp", OFS_TUNNEL_SLERP, 0x0, 0x1, PROPTYPE_INT, NULL }, { "tlerp", OFS_TUNNEL_LERP, 0x0, 0x2, PROPTYPE_INT, NULL }, { "tunnel_ts1", OFS_TUNNEL_TAN_MUL1,0x1, 0x40000000, PROPTYPE_FLOAT, TeleporterModify_SplinePropsChanged }, { "tunnel_ts2", OFS_TUNNEL_TAN_MUL2,0x1, 0x40000000, PROPTYPE_FLOAT, TeleporterModify_SplinePropsChanged }, { "tunnel_spline_cords", OFS_TUNNEL_SPL_CORDS,0x1, 0x800000, PROPTYPE_INT, TeleporterModify_SplinePropsChanged }, { "tunnel_anim_speed", OFS_TUNNEL_ANIM_SPD,0x0, 0x2580000, PROPTYPE_FLOAT, NULL }, { "tunnel_spline", OFS_TUNNEL_SPLINE, 0x0, 0x3f, PROPTYPE_INT, NULL }, { "tunnel_tex_alpha", OFS_TUNNEL_TEX_ALPHA,0x0, 0xff, PROPTYPE_INT, NULL }, { "tunnel_len", OFS_TUNNEL_LEN, 0x0, 0x4000000, PROPTYPE_INT, TeleporterModify_SplinePropsChanged }, { "start.x", OFS_START_X, -0x7fffffff,0x7fffffff, PROPTYPE_GEOMV, TeleporterModify_StartPropsChanged }, { "start.y", OFS_START_Y, -0x7fffffff,0x7fffffff, PROPTYPE_GEOMV, TeleporterModify_StartPropsChanged }, { "start.z", OFS_START_Z, -0x7fffffff,0x7fffffff, PROPTYPE_GEOMV, TeleporterModify_StartPropsChanged }, { "start_rot_phi", OFS_STARTROTPHI, -0x2d00000, 0x2d00000, PROPTYPE_FLOAT, TeleporterModify_StartPropsChanged }, { "start_rot_theta", OFS_STARTROTTHETA, -0x2d00000, 0x2d00000, PROPTYPE_FLOAT, TeleporterModify_StartPropsChanged }, { NULL, 0, 0, 0, 0, NULL }, }; // calculate the vertices making up the tunnel geometry ----------------------- // PRIVATE void Teleporter_Calc_Tunnel_Verts( Teleporter* teleporter ) { ASSERT( teleporter != NULL ); // free any previous geometry if ( teleporter->tunnel_verts != NULL ) FREEMEM( teleporter->tunnel_verts ); int num_steps = ( teleporter->tunnel_spline_cords + 1 ); int num_vtxs = 4 * num_steps; Vertex3* vtxs; // allocate the space for the new vertices size_t allocsize = (size_t)num_vtxs * sizeof( Vertex3 ); teleporter->tunnel_verts = vtxs = (Vertex3*)ALLOCMEM( allocsize ); if ( vtxs == NULL ) OUTOFMEM( 0 ); memset( vtxs, 0, allocsize ); Vector3 start_pos, end_pos; Vector3 start_tan, end_tan; // get the tangents FetchZVector( teleporter->ObjPosition, &start_tan ); FetchZVector( teleporter->child_object->ObjPosition, &end_tan ); // scale the tangents VECMULS( &start_tan, &start_tan, teleporter->tunnel_tangent_scale1 ); VECMULS( &end_tan, &end_tan, teleporter->tunnel_tangent_scale2 ); // get the start/end pos //FetchTVector( teleporter->ObjPosition, &start_pos ); //FetchTVector( teleporter->child_object->ObjPosition, &end_pos ); // HACK to cover the different Z curve between OpenGL and our projection Xmatrx start_frame_ospc, end_frame_ospc; Xmatrx start_frame_wspc, end_frame_wspc; CalcOrthoInverse( teleporter->ObjPosition, start_frame_ospc ); CalcOrthoInverse( teleporter->child_object->ObjPosition, end_frame_ospc ); start_frame_ospc[ 2 ][ 3 ] -= 6; end_frame_ospc [ 2 ][ 3 ] += 6; CalcOrthoInverse( start_frame_ospc, start_frame_wspc ); CalcOrthoInverse( end_frame_ospc, end_frame_wspc ); FetchTVector( start_frame_wspc, &start_pos ); FetchTVector( end_frame_wspc, &end_pos ); // get the start and end frame Xmatrx start_frame, end_frame; MakeNonTranslationMatrx( teleporter->ObjPosition, start_frame ); MakeNonTranslationMatrx( teleporter->child_object->ObjPosition, end_frame ); // norm the start/end frame NormMtx( start_frame ); NormMtx( end_frame ); /* // get the axis vects from start/end frame Vector3 start_axis[ 3 ], end_axis[ 3 ]; FetchXVector( start_frame, &start_axis[ 0 ] ); FetchYVector( start_frame, &start_axis[ 1 ] ); FetchZVector( start_frame, &start_axis[ 2 ] ); FetchXVector( end_frame, &end_axis[ 0 ] ); FetchYVector( end_frame, &end_axis[ 1 ] ); FetchZVector( end_frame, &end_axis[ 2 ] ); // rotate start/end frame to be aligned to the positive z axis Vector3 zAxis; zAxis.X = GEOMV_0; zAxis.Y = GEOMV_0; zAxis.Z = GEOMV_1; // get the rotation axis Vector3 rot_axis_start, rot_axis_end; CrossProduct( &zAxis, &start_axis[ 2 ], &rot_axis_start ); CrossProduct( &zAxis, &end_axis[ 2 ], &rot_axis_end ); int ignore_start = ( VctLenX( &rot_axis_start ) == GEOMV_0 ); int ignore_end = ( VctLenX( &rot_axis_end ) == GEOMV_0 ); // norm the rotation axis if ( !ignore_start ) NormVctX( &rot_axis_start ); if ( !ignore_end ) NormVctX( &rot_axis_end ); // get the rotation angle float alpha_start_rad = ignore_start ? 0.0f : acos( (double)DotProduct( &start_axis[ 2 ], &zAxis ) ); float alpha_end_rad = ignore_end ? 0.0f : acos( (double)DotProduct( &end_axis[ 2 ], &zAxis ) ); // setup the transformations to rotate start/end frame Xmatrx start_rot_into_z, end_rot_into_z; if ( !ignore_start ) MatrxFromAngularDisplacement( start_rot_into_z, RAD_TO_BAMS( alpha_start_rad ), &rot_axis_start ); if ( !ignore_end ) MatrxFromAngularDisplacement( end_rot_into_z, RAD_TO_BAMS( alpha_end_rad ), &rot_axis_end ); // rotate the axis vects into Z Vector3 start_axis_rotated[ 3 ], end_axis_rotated[ 3 ]; for( int axis = 0; axis < 3; axis++ ) { if ( !ignore_start ) MtxVctMULt( start_rot_into_z, &start_axis[ axis ], &start_axis_rotated[ axis ] ); if ( !ignore_end ) MtxVctMULt( end_rot_into_z, &end_axis[ axis ], &end_axis_rotated[ axis ] ); } // store the rotated axis back into the start/end frame if ( !ignore_start ) { StoreXVector( start_frame, &start_axis_rotated[ 0 ] ); StoreYVector( start_frame, &start_axis_rotated[ 1 ] ); StoreZVector( start_frame, &start_axis_rotated[ 2 ] ); } if ( !ignore_end ) { StoreXVector( end_frame, &end_axis_rotated[ 0 ] ); StoreYVector( end_frame, &end_axis_rotated[ 1 ] ); StoreZVector( end_frame, &end_axis_rotated[ 2 ] ); } */ // setup the boundary in object space Vector3 boundary[ 4 ]; for( int i = 0; i < 4; i++ ) { boundary[ i ].X = teleporter->start_vtxlist[ i ].X; boundary[ i ].Y = teleporter->start_vtxlist[ i ].Y; boundary[ i ].Z = GEOMV_0; } // init the hermite arclen data Hermite_ArcLen* hermite_data; hermite_data = Hermite_ArcLen_InitData( num_steps * 10, &start_pos, &end_pos, &start_tan, &end_tan ); Vector3 interp_last; Vector3 tangent_last; Xmatrx frame_last; // hack to store the start/end frame for display memcpy( teleporter->start_frame, start_frame, sizeof( Xmatrx ) ); memcpy( teleporter->end_frame, end_frame, sizeof( Xmatrx ) ); if ( teleporter->spline_frames != NULL ) { FREEMEM( teleporter->spline_frames ); } teleporter->spline_frames = (Xmatrx*)ALLOCMEM( (size_t)num_steps * sizeof( Xmatrx ) ); Xmatrx xy_plane_transf; Xmatrx hermite_transf; Xmatrx cur_frame; MakeIdMatrx( xy_plane_transf ); // we make 2 passes in interpolating the frame along the spline. // the first pass is to get the the correction of the rotation in the xy plane // the second pass is to correct the frames for the rotation in the xy plane for( int pass = 0; pass < 2; pass++ ) { // get the correction for the rotation in the xy plane float xy_correction_alpha_rad = 0; Xmatrx xy_frame_2_correct; if( pass == 1 ) { // store the last cur_frame as correction frame in the xy plane MakeNonTranslationMatrx( frame_last, xy_frame_2_correct ); // get the angle between the last interpolated frame in the first run and the exit_frame as specified by the user Vector3 x_set, x_interp; FetchXVector( end_frame, &x_set ); FetchXVector( frame_last, &x_interp ); xy_correction_alpha_rad = acos( (double)DotProduct( &x_set, &x_interp ) ); xy_correction_alpha_rad /= num_steps ; } // interpolate pos on spline and orientation with quaternion for calculating the // boundary points of the tunnel ( all coordinates are in world space ) for( int step = 0; step < num_steps; step++ ) { // init the sub transforms to identity MakeIdMatrx( cur_frame ); MakeIdMatrx( hermite_transf ); // normalize s float s = (float)step / (float)( num_steps - 1); #ifdef ALLOW_Z_ROT // get slerp frame //QuaternionSlerpFrames( xy_plane_transf, start_frame, end_frame, s ); //ReOrthoNormMtx( xy_plane_transf ); #endif // ALLOW_Z_ROT // calculate the xy transform to be the correction between the set exit_frame and the last interpolated frame from the first pass if ( ( pass == 1 ) && ( step != 0 ) ) { MakeIdMatrx( xy_plane_transf ); //QuaternionSlerpFrames( xy_plane_transf, xy_frame_2_correct, end_frame, s ); ObjRotZ( xy_plane_transf, RAD_TO_BAMS( xy_correction_alpha_rad ) ); ReOrthoNormMtx( xy_plane_transf ); } // calculate the interpolated pos at the center of the tunnel Vector3 interp; Hermite_ArcLen_Interpolate( hermite_data, s, &interp ); StoreTVector( hermite_transf, &interp ); // get the next interpolation point Vector3 interp_next; if ( step < ( num_steps - 1 ) ) { float s_next = (float)( step + 1 ) / (float)( num_steps - 1 ); Hermite_ArcLen_Interpolate( hermite_data, s_next, &interp_next ); } // when not at step 0, we get the tangent at the current step and rotate the frame_last to be z aligned with the tangent Vector3 tangent; if ( step == 0 ) { FetchZVector( start_frame, &tangent ); Vector3 dummy; FetchXVector( start_frame, &dummy ); StoreXVector( hermite_transf, &dummy ); FetchYVector( start_frame, &dummy ); StoreYVector( hermite_transf, &dummy ); FetchZVector( start_frame, &dummy ); StoreZVector( hermite_transf, &dummy ); } else { // calculate the tangent ( tangent is paralell to the connection from samplepoint i-1 to i+1 ) if ( step < ( num_steps - 1 ) ) { Vector3 D1, D2; VECSUB( &D1, &interp, &interp_last ); VECSUB( &D2, &interp_next, &interp ); VECADD( &tangent, &D1, &D2 ); VECMULS( &tangent, &tangent, FLOAT_TO_GEOMV( 0.5f ) ); } else { FetchZVector( end_frame, &tangent ); } NormVctX( &tangent ); // get the rotation axis Vector3 rot_axis; CrossProduct( &tangent, &tangent_last, &rot_axis ); Vector3 frame_axis_last[ 3 ]; FetchXVector( frame_last, &frame_axis_last[ 0 ] ); FetchYVector( frame_last, &frame_axis_last[ 1 ] ); FetchZVector( frame_last, &frame_axis_last[ 2 ] ); // check if we have a decent rotation axis if( VctLenX( &rot_axis ) > 1e-5 ) { NormVctX( &rot_axis ); // get the angle for rotation float alpha_rad = acos( (double)DotProduct( &tangent, &tangent_last ) ); // get the rotation matrix Xmatrx rot_into_tangent; MatrxFromAngularDisplacement( rot_into_tangent, RAD_TO_BAMS( alpha_rad ), &rot_axis ); // rotate the (frame_last) axis vects into Z Vector3 frame_axis_last_rotated[ 3 ]; for( int axis = 0; axis < 3; axis++ ) { MtxVctMULt( rot_into_tangent, &frame_axis_last[ axis ], &frame_axis_last_rotated[ axis ] ); } // store the rotated frame into the hermite transformation StoreXVector( hermite_transf, &frame_axis_last_rotated[ 0 ] ); StoreYVector( hermite_transf, &frame_axis_last_rotated[ 1 ] ); StoreZVector( hermite_transf, &frame_axis_last_rotated[ 2 ] ); } else { // there is no rotation necessary // store the last frame into the hermite transformation StoreXVector( hermite_transf, &frame_axis_last[ 0 ] ); StoreYVector( hermite_transf, &frame_axis_last[ 1 ] ); StoreZVector( hermite_transf, &frame_axis_last[ 2 ] ); } } // create current frame from transformation in plane ( rot around z ) and translation/rotation from hermite interpolation MtxMtxMUL( hermite_transf, xy_plane_transf, cur_frame ); //memcpy( cur_frame, hermite_transf, sizeof( Xmatrx ) ); memcpy( &teleporter->spline_frames[ step ], cur_frame, sizeof( Xmatrx ) ); // transform boundary from object to world space for( int nCorner = 0; nCorner < 4; nCorner++ ) { MtxVctMUL( cur_frame, &boundary[ nCorner ], &vtxs[ step * 4 + nCorner ] ); } // store pos, tangent and frame for next step memcpy( &interp_last, &interp, sizeof( Vector3 ) ); memcpy( &tangent_last, &tangent, sizeof( Vector3 ) ); memcpy( frame_last, cur_frame, sizeof( Xmatrx ) ); } } // store the total arclength of the spline teleporter->tunnel_spline_arclen = hermite_data->total_arc_len; // kill the hermite arclen data Hermite_ArcLen_KillData( hermite_data ); // calculate the cullbox in objectspace of the teleporter entry CullBox3* cullbox = &teleporter->tunnel_cullbox; cullbox->minmax[ 0 ] = INT_TO_GEOMV( INT_MAX ); cullbox->minmax[ 1 ] = INT_TO_GEOMV( INT_MAX ); cullbox->minmax[ 2 ] = INT_TO_GEOMV( INT_MAX ); cullbox->minmax[ 3 ] = -INT_TO_GEOMV( INT_MAX ); cullbox->minmax[ 4 ] = -INT_TO_GEOMV( INT_MAX ); cullbox->minmax[ 5 ] = -INT_TO_GEOMV( INT_MAX ); // get the transformation from world to teleporter entry objectspace Xmatrx world2object; CalcOrthoInverse( teleporter->ObjPosition, world2object ); Vertex3 vtx_in_ospc; for( int nVertex = 0; nVertex < num_vtxs; nVertex++ ) { MtxVctMUL( world2object, &vtxs[ nVertex ], &vtx_in_ospc ); cullbox->minmax[ 0 ] = min( cullbox->minmax[ 0 ], vtx_in_ospc.X ); cullbox->minmax[ 1 ] = min( cullbox->minmax[ 1 ], vtx_in_ospc.Y ); cullbox->minmax[ 2 ] = min( cullbox->minmax[ 2 ], vtx_in_ospc.Z ); cullbox->minmax[ 3 ] = max( cullbox->minmax[ 3 ], vtx_in_ospc.X ); cullbox->minmax[ 4 ] = max( cullbox->minmax[ 4 ], vtx_in_ospc.Y ); cullbox->minmax[ 5 ] = max( cullbox->minmax[ 5 ], vtx_in_ospc.Z ); } } // notification callback when spline props changed ---------------------------- // PRIVATE int TeleporterModify_SplinePropsChanged( GenObject* base ) { ASSERT( base != NULL ); Teleporter *teleporter = (Teleporter *) base; // calculate the vertices for the tunnel Teleporter_Calc_Tunnel_Verts( teleporter ); // clamp to the total # of segs if ( teleporter->tunnel_len > teleporter->tunnel_spline_cords ) teleporter->tunnel_len = teleporter->tunnel_spline_cords; // reset the tunnel animation teleporter->tunnel_cur_anim_step = GEOMV_0; return TRUE; } // strip the quotations around a string --------------------------------------- // PRIVATE void StripQuotations( char* str2modify ) { ASSERT( str2modify != NULL ); char szBuffer[ 128 ]; size_t len = strlen( str2modify ); ASSERT( len < 128 ); // remove trainling if ( str2modify[ len - 1 ] == '"' ) { str2modify[ len - 1 ] = 0; } // remove leading if ( str2modify[ 0 ] == '"' ) { char* src = str2modify + 1; char* dst = str2modify; for(; *src != 0; src++, dst++ ) { *dst = *src; } *dst = 0; } } // notification callback when texture props changed --------------------------- // PRIVATE int TeleporterModify_TexPropsChanged( GenObject* base ) { ASSERT( base != NULL ); Teleporter *teleporter = (Teleporter *) base; // eventually remove leadin/trainling quotations from the texture names //StripQuotations( teleporter->tex_name_interior ); //StripQuotations( teleporter->tex_name_tunnel ); // get pointer to texture map teleporter->start_texmap = FetchTextureMap( teleporter->tex_name_interior ); if ( teleporter->start_texmap == NULL ) { MSGOUT( telep_texture_not_found, teleporter->tex_name_interior ); } else { // init the u/v deltas with some random value geomv_t uwidth = INT_TO_GEOMV( 1L << teleporter->start_texmap->Width ); geomv_t vheight = INT_TO_GEOMV( 1L << teleporter->start_texmap->Height ); teleporter->u_maxdelta[ 0 ] = uwidth * teleporter->u_variation[ 0 ]; teleporter->v_maxdelta[ 0 ] = vheight * teleporter->v_variation[ 0 ]; teleporter->u_maxdelta[ 1 ] = uwidth * teleporter->u_variation[ 1 ]; teleporter->v_maxdelta[ 1 ] = vheight * teleporter->v_variation[ 1 ]; } // get pointer to texture map teleporter->tunnel_texmap = FetchTextureMap( teleporter->tex_name_tunnel ); if ( teleporter->tunnel_texmap == NULL ) { MSGOUT( telep_texture_not_found, teleporter->tex_name_tunnel ); } return TRUE; } // init a quaternion from spherical coordinates ------------------------------- // void QuaternionFromSpherical_f( Quaternion_f *quat, float deg_angle, float deg_latitude, float deg_longitude ) { ASSERT( quat != NULL ); float sin_a = sin( DEG_TO_RAD( deg_angle ) / 2 ); float cos_a = cos( DEG_TO_RAD( deg_angle ) / 2 ); float sin_lat = sin( DEG_TO_RAD( deg_latitude ) ); float cos_lat = cos( DEG_TO_RAD( deg_latitude ) ); float sin_long = sin( DEG_TO_RAD( deg_longitude ) ); float cos_long = cos( DEG_TO_RAD( deg_longitude ) ); quat->X = sin_a * cos_lat * sin_long; quat->Y = sin_a * sin_lat; quat->Z = sin_a * sin_lat * cos_long; quat->W = cos_a; } // init a quaternion from spherical coordinates ------------------------------- // void QuaternionFromSpherical( Quaternion_f *quat, float deg_angle, float deg_latitude, float deg_longitude ) { ASSERT( quat != NULL ); geomv_t sin_a = FLOAT_TO_GEOMV( sin( DEG_TO_RAD( deg_angle ) / 2 ) ); geomv_t cos_a = FLOAT_TO_GEOMV( cos( DEG_TO_RAD( deg_angle ) / 2 ) ); geomv_t sin_lat = FLOAT_TO_GEOMV( sin( DEG_TO_RAD( deg_latitude ) ) ); geomv_t cos_lat = FLOAT_TO_GEOMV( cos( DEG_TO_RAD( deg_latitude ) ) ); geomv_t sin_long = FLOAT_TO_GEOMV( sin( DEG_TO_RAD( deg_longitude ) ) ); geomv_t cos_long = FLOAT_TO_GEOMV( cos( DEG_TO_RAD( deg_longitude ) ) ); quat->X = sin_a * cos_lat * sin_long; quat->Y = sin_a * sin_lat; quat->Z = sin_a * sin_lat * cos_long; quat->W = cos_a; } // calculate the transfomation for the exit rotation -------------------------- // PRIVATE void Teleporter_Rotation_Transform( float phi, float theta, Xmatrx trafo ) { ASSERT( ( phi >= 0.0f ) && ( phi <= 360.0f ) ); ASSERT( ( theta >= 0.0f ) && ( theta <= 360.0f ) ); ASSERT( trafo != NULL ); // NOTE: formula to get the position on the 1-sphere from angular coordinates: // // x = sin( phi ) * cos( theta ) // y = sin( phi ) * sin( theta ) // z = cos( phi ) // for the z_axis in the frame we must add 90 degs to phi float sin_phi = sin( DEG_TO_RAD( phi ) ); float cos_phi = cos( DEG_TO_RAD( phi ) ); float sin_theta = sin( DEG_TO_RAD( 90 ) + DEG_TO_RAD( theta ) ); float cos_theta = cos( DEG_TO_RAD( 90 ) + DEG_TO_RAD( theta ) ); float sin_phi2 = sin( DEG_TO_RAD( 90 ) + DEG_TO_RAD( phi ) ); float cos_phi2 = cos( DEG_TO_RAD( 90 ) + DEG_TO_RAD( phi ) ); Vector3 x_axis, y_axis, z_axis; z_axis.X = sin_phi * cos_theta; z_axis.Y = sin_phi * sin_theta; z_axis.Z = cos_phi; y_axis.X = sin_phi2 * cos_theta; y_axis.Y = sin_phi2 * sin_theta; y_axis.Z = cos_phi2; NormVctX( &z_axis ); NormVctX( &y_axis ); CrossProduct( &y_axis, &z_axis, &x_axis ); StoreXVector( trafo, &x_axis ); StoreYVector( trafo, &y_axis ); StoreZVector( trafo, &z_axis ); ReOrthoNormMtx( trafo ); } // notification callback when exit props changed ------------------------------ // PRIVATE int TeleporterModify_StartPropsChanged( GenObject* base ) { ASSERT( base != NULL ); Teleporter *teleporter = (Teleporter *) base; if ( teleporter->start_rot_phi > 360.0f ) { teleporter->start_rot_phi -= 360.0f; } if ( teleporter->start_rot_phi < 0.0f ) { teleporter->start_rot_phi += 360.0f; } if ( teleporter->start_rot_theta > 360.0f ) { teleporter->start_rot_theta -= 360.0f; } if ( teleporter->start_rot_theta < 0.0f ) { teleporter->start_rot_theta += 360.0f; } // get the trafo for the start rotation Xmatrx NewTrans; MakeIdMatrx( NewTrans ); Teleporter_Rotation_Transform( teleporter->start_rot_phi, teleporter->start_rot_theta, NewTrans ); MakeNonTranslationMatrx( NewTrans, teleporter->ObjPosition ); // set the translational part StoreTVector( teleporter->ObjPosition, &teleporter->start ); // recalc all start dependend data in the teleporter exit TeleporterModify_ExitPropsChanged( teleporter ); return TRUE; } // notification callback when exit props changed ------------------------------ // PRIVATE int TeleporterModify_ExitPropsChanged( GenObject* base ) { ASSERT( base != NULL ); Teleporter *teleporter = (Teleporter *) base; // modify the position of the teleporter exit Xmatrx ChildTrans; MakeIdMatrx( ChildTrans ); // get the trafo for the exit rotation Teleporter_Rotation_Transform( teleporter->exit_rot_phi, teleporter->exit_rot_theta, ChildTrans ); ChildTrans[ 0 ][ 3 ] = teleporter->exit_delta_x; ChildTrans[ 1 ][ 3 ] = teleporter->exit_delta_y; ChildTrans[ 2 ][ 3 ] = teleporter->exit_delta_z; MtxMtxMUL( teleporter->ObjPosition, ChildTrans, teleporter->child_object->ObjPosition ); TeleporterModify_SplinePropsChanged( base ); return TRUE; } // draw the interior of the teleporter entry gate ----------------------------- // void Teleporter_Draw_Entry_Interior( Teleporter* teleporter ) { ASSERT( teleporter != NULL ); // ensure we have the texture for the start interior if ( teleporter->start_texmap == NULL ) { return; } // only draw interior if front facing { Vector3 telepnormal; FetchZVector( teleporter->ObjPosition, &telepnormal ); Vertex3 teleppos; FetchTVector( teleporter->ObjPosition, &teleppos ); // get the world->view transform Xmatrx CameraInWorldSpace; CalcOrthoInverse( ViewCamera, CameraInWorldSpace ); //FIXME: we should have a GLOBAL storage for the camera in world space Vertex3 campos; FetchTVector( CameraInWorldSpace, &campos ); geomv_t camdot = -DOT_PRODUCT( &telepnormal, &campos ); geomv_t telepdot = -DOT_PRODUCT( &telepnormal, &teleppos ); if ( camdot < telepdot ) { return; } } // set vertex color to white with premultiplie alpha float alpha = ( (float)teleporter->start_tex_alpha ) / 255.0f; byte _red = (int)(255.0 * alpha); byte _green = (int)(255.0 * alpha); byte _blue = (int)(255.0 * alpha); byte _alpha = teleporter->start_tex_alpha; // get the width/height in pixels geomv_t uWidth = INT_TO_GEOMV( 1L << teleporter->start_texmap->Width ); geomv_t vHeight = INT_TO_GEOMV( 1L << teleporter->start_texmap->Height ); // setup transformation matrix; transform is world->view D_LoadIterMatrix( NULL ); IterRectangle3 itrect; itrect.flags = ITERFLAG_NONDESTRUCTIVE | ITERFLAG_Z_DIV_XYZ | ITERFLAG_Z_DIV_UVW | ITERFLAG_Z_TO_DEPTH /* | ITERFLAG_ONESIDED */; itrect.itertype = iter_texrgba | iter_specularadd; itrect.raststate = rast_zcompare | rast_texwrap | rast_chromakeyoff; itrect.rastmask = rast_nomask; itrect.texmap = teleporter->start_texmap; // calculate transformation matrix MtxMtxMUL( ViewCamera, teleporter->ObjPosition, DestXmatrx ); int nVtx = 0; for ( nVtx = 0; nVtx < 4; nVtx++ ) { MtxVctMUL( DestXmatrx, &teleporter->start_vtxlist[ nVtx ], (Vertex3*)&itrect.Vtxs[ nVtx ] ); itrect.Vtxs[ nVtx ].W = GEOMV_1; itrect.Vtxs[ nVtx ].U = teleporter->u_delta[ 0 ]; itrect.Vtxs[ nVtx ].V = teleporter->v_delta[ 0 ]; itrect.Vtxs[ nVtx ].R = _red; itrect.Vtxs[ nVtx ].G = _green; itrect.Vtxs[ nVtx ].B = _blue; itrect.Vtxs[ nVtx ].A = _alpha; } itrect.Vtxs[ 0 ].U += 0; itrect.Vtxs[ 0 ].V += 0; itrect.Vtxs[ 1 ].U += uWidth; itrect.Vtxs[ 1 ].V += 0; itrect.Vtxs[ 2 ].U += uWidth; itrect.Vtxs[ 2 ].V += vHeight; itrect.Vtxs[ 3 ].U += 0; itrect.Vtxs[ 3 ].V += vHeight; // draw quad D_DrawIterRectangle3( &itrect, 0x3f ); // draw the second layer for ( nVtx = 0; nVtx < 4; nVtx++ ) { MtxVctMUL( DestXmatrx, &teleporter->start_vtxlist[ nVtx ], (Vertex3*)&itrect.Vtxs[ nVtx ] ); itrect.Vtxs[ nVtx ].W = GEOMV_1; itrect.Vtxs[ nVtx ].U = teleporter->u_delta[ 1 ]; itrect.Vtxs[ nVtx ].V = teleporter->v_delta[ 1 ]; } itrect.Vtxs[ 0 ].U += 0; itrect.Vtxs[ 0 ].V += 0; itrect.Vtxs[ 1 ].U += uWidth; itrect.Vtxs[ 1 ].V += 0; itrect.Vtxs[ 2 ].U += uWidth; itrect.Vtxs[ 2 ].V += vHeight; itrect.Vtxs[ 3 ].U += 0; itrect.Vtxs[ 3 ].V += vHeight; //itrect.flags = ITERFLAG_NONDESTRUCTIVE | ITERFLAG_Z_DIV_XYZ | ITERFLAG_Z_DIV_UVW | ITERFLAG_Z_TO_DEPTH; D_DrawIterRectangle3( &itrect, 0x3f ); // restore identity transformation D_LoadIterMatrix( NULL ); } // check whether tunnel is visible -------------------------------------------- // PRIVATE int Teleporter_Check_Tunnel_Visibility( Teleporter* teleporter ) { // NOTE: the teleporter cullbox is given as an axis aligned bounding box // relative to the teleporter entry CullBox3 cullbox_world; MtxVctMUL( teleporter->ObjPosition, (Vector3*)&teleporter->tunnel_cullbox.minmax[ 0 ], (Vector3*)&cullbox_world.minmax[ 0 ] ); MtxVctMUL( teleporter->ObjPosition, (Vector3*)&teleporter->tunnel_cullbox.minmax[ 3 ], (Vector3*)&cullbox_world.minmax[ 3 ] ); dword cullmask = 0x3f; int cull_result = CULL_BoxAgainstVolume( &cullbox_world, World_ViewVolume, &cullmask ); return !cull_result; } // draw the teleporters ------------------------------------------------------- // PRIVATE int Teleporter_Draw( void *param ) { ASSERT( param != NULL ); Teleporter *teleporter = (Teleporter *) param; // determine visibility int entry_visible = ( teleporter->VisibleFrame == CurVisibleFrame ); int exit_visible = ( teleporter->child_object->VisibleFrame == CurVisibleFrame ); int tunnel_visible = entry_visible | exit_visible; if ( !tunnel_visible ) { // cull tunnel against frustum tunnel_visible = Teleporter_Check_Tunnel_Visibility( teleporter ); } // check whether to draw the entry interior if ( entry_visible ) { Teleporter_Draw_Entry_Interior( teleporter ); } // check whether the tunnel is visible if ( tunnel_visible ) { // draw the tunnel Teleporter_Draw_Tunnel( teleporter ); // draw the spline connecting start and exit ( for debugging only ) if ( teleporter->tunnel_spline != 0 ) { Teleporter_Debug_Show_Tunnel_Spline( teleporter ); } } return TRUE; } // macro to set the properties of a itervertex -------------------------------- // #define SET_ITER_VTX( iter_vtx, in_vect3, u, v, r, g, b, a ) \ MtxVctMUL( DestXmatrx, (in_vect3), (Vector3*)(iter_vtx) ); \ (iter_vtx)->W = GEOMV_1; \ (iter_vtx)->U = u; \ (iter_vtx)->V = v; \ (iter_vtx)->R = r; \ (iter_vtx)->G = g; \ (iter_vtx)->B = b; \ (iter_vtx)->A = a; // do a lerp between 2 vectors ------------------------------------------------ // void VctLerp( Vector3* dst, Vector3* src1, Vector3* src2, float t ) { Vector3 diff; VECSUB( &diff, src2, src1 ); dst->X = src1->X + ( t * diff.X ); dst->Y = src1->Y + ( t * diff.Y ); dst->Z = src1->Z + ( t * diff.Z ); } // draw a part of the tunnel connecting teleporter entry with exit ------------ // PRIVATE void Teleporter_Draw_Tunnel( Teleporter* teleporter ) { ASSERT( teleporter != NULL ); if ( teleporter->tunnel_texmap == NULL ) return; //NOTE: start_seg and end_seg specify points on the spline ( seperator planes of the tunnel ) int num_total_spline_segs = ( teleporter->tunnel_spline_cords + 1 ); int start_seg = (int)(teleporter->tunnel_cur_anim_step - teleporter->tunnel_len); int end_seg = start_seg + teleporter->tunnel_len; if ( start_seg < 0 ) start_seg = 0; if ( end_seg >= num_total_spline_segs ) { end_seg = num_total_spline_segs - 1; } // no tunnel to draw if ( start_seg == end_seg ) return; // tunnel is vanishing at the teleporter exit if( start_seg == ( teleporter->tunnel_spline_cords + 1 ) ) { return; } int num_tunnel_cords = ( end_seg - start_seg + 1 ); // we add a segment at the end of the tunnel, for smooth translation of the tunnel int num_tunnel_segs = num_tunnel_cords + 1; int num_verts = 4 * num_tunnel_segs; int start_offset = start_seg * 4; int end_offset = end_seg * 4; // calculate transformation matrix memcpy( DestXmatrx, ViewCamera, sizeof( Xmatrx ) ); // get the width/height in pixels geomv_t uWidth = INT_TO_GEOMV( 1L << teleporter->tunnel_texmap->Width ); geomv_t vHeight = INT_TO_GEOMV( 1L << teleporter->tunnel_texmap->Height ); // calculate the u_delta ( u advance for one cord ) geomv_t tunnel_height = teleporter->start_vtxlist[ 2 ].Y - teleporter->start_vtxlist[ 0 ].Y; geomv_t u_delta = uWidth * ( ( teleporter->tunnel_spline_arclen / teleporter->tunnel_spline_cords ) / tunnel_height ); // we calculate the first/last segment vertices by interpolation between the precalculated segments Vector3 first_seg_vtxs[ 4 ]; Vector3 last_seg_vtxs [ 4 ]; // calculate the interstep geomv_t cur_interseg = ( num_total_spline_segs - 1 ) * teleporter->tunnel_t; // clamp to the max if ( cur_interseg > ( num_total_spline_segs - 1 ) ) cur_interseg = ( num_total_spline_segs - 1 ); // calculate the interstep fraction ( that is the fraction between 2 spline points ) //geomv_t cur_interseg_frac = cur_interseg - (geomv_t)start_seg; geomv_t cur_interseg_frac = teleporter->tunnel_cur_anim_step - (geomv_t)floor( teleporter->tunnel_cur_anim_step ); geomv_t u_delta_first_cord = 0; geomv_t u_delta_last_cord = 0; int last_cord_removed = FALSE; // determine whether start segment is fixed to teleporter start int move_start_seg = teleporter->tunnel_cur_anim_step >= teleporter->tunnel_len; // snap to a already calculated point if ( cur_interseg_frac < GEOMV_VANISHING ) { for( int i = 0; i < 4; i++ ) { memcpy( &first_seg_vtxs[ i ], &teleporter->tunnel_verts[ start_offset + i ], sizeof( Vector3 ) ); memcpy( &last_seg_vtxs[ i ], &teleporter->tunnel_verts[ end_offset + i ], sizeof( Vector3 ) ); } u_delta_first_cord = u_delta; u_delta_last_cord = u_delta; // we do not draw the last cord as this has len 0 num_tunnel_cords--; num_tunnel_segs--; last_cord_removed = TRUE; } else { // determine whether to do lerping of start/end int lerp_start = move_start_seg; int lerp_end = ( end_seg < ( num_total_spline_segs - 1 ) ); // get the lerped position for the first/last segment for( int i = 0; i < 4; i++ ) { if ( lerp_start ) { VctLerp( &first_seg_vtxs[ i ], &teleporter->tunnel_verts[ start_offset + i ], &teleporter->tunnel_verts[ start_offset + 4 + i ], GEOMV_TO_FLOAT( cur_interseg_frac ) ); } else { memcpy( &first_seg_vtxs[ i ], &teleporter->tunnel_verts[ start_offset + i ], sizeof( Vector3 ) ); } if( lerp_end ) { VctLerp( &last_seg_vtxs [ i ], &teleporter->tunnel_verts[ end_offset + i ], &teleporter->tunnel_verts[ end_offset + 4 + i ], GEOMV_TO_FLOAT( cur_interseg_frac ) ); } else { memcpy( &last_seg_vtxs[ i ], &teleporter->tunnel_verts[ end_offset + i ], sizeof( Vector3 ) ); } } // get the u_delta for the first cord Vector3 diff; if ( lerp_start ) { VECSUB( &diff, &first_seg_vtxs[ 0 ], &teleporter->tunnel_verts[ start_offset + 4 ] ); geomv_t len = VctLenX( &diff ); u_delta_first_cord = uWidth * ( len / tunnel_height ); } else { u_delta_first_cord = 0; } // get the u_delta for the last cord if( lerp_end ) { VECSUB( &diff, &last_seg_vtxs[ 0 ], &teleporter->tunnel_verts[ end_offset + 4 ] ); geomv_t len = VctLenX( &diff ); u_delta_last_cord = uWidth * ( len / tunnel_height ); } else { u_delta_last_cord = u_delta; } } // create vertex array IterArray3 *itarray = (IterArray3 *) ALLOCMEM( (size_t)&((IterArray3*)0)->Vtxs[ num_verts ] ); if ( itarray == NULL ) OUTOFMEM( 0 ); itarray->NumVerts = num_verts; itarray->arrayinfo = ITERARRAY_USE_COLOR | ITERARRAY_USE_TEXTURE | ITERARRAY_GLOBAL_TEXTURE; itarray->flags = ITERFLAG_Z_DIV_XYZ | ITERFLAG_Z_DIV_UVW | ITERFLAG_Z_TO_DEPTH; itarray->itertype = iter_texrgba | iter_specularadd; //iter_rgba | iter_alphablend; itarray->raststate = rast_zcompare | rast_texclamp | rast_chromakeyoff; itarray->rastmask = rast_nomask; itarray->texmap = teleporter->tunnel_texmap; geomv_t u = 0; float alpha = ( (float)teleporter->tunnel_tex_alpha ) / 255.0f; float _fRed = 0.0f; float _fGreen = 0.0f; float _fBlue = 0.0f; float divisor = ( end_seg - start_seg ) / 3.0f; float _color_delta = ( 255.0 * alpha ) / divisor; float ramp_segs_start = ( divisor ); float ramp_segs_end = ( divisor * 2.0); for( int nSegment = 0; nSegment < num_tunnel_segs; nSegment++ ) { int nVtx = nSegment * 4; float segment_frac = 0.0f; if ( nSegment == ( num_tunnel_segs - 1 ) ) { segment_frac = num_tunnel_segs - 2; } else if ( nSegment == 0 ) { segment_frac = 0; } else { segment_frac = ( nSegment - cur_interseg_frac ); } // modify the color to do a fade in/out at the beginning and end of the tunnel if ( nSegment < ramp_segs_start ) { if ( move_start_seg ) { _fRed = segment_frac * _color_delta; _fGreen = segment_frac * _color_delta; _fBlue = segment_frac * _color_delta; } else { _fRed = nSegment * _color_delta; _fGreen = nSegment * _color_delta; _fBlue = nSegment * _color_delta; } } else if ( segment_frac > ramp_segs_end ) { _fRed = ( ( num_tunnel_segs - 2 ) - segment_frac ) * _color_delta; _fGreen = ( ( num_tunnel_segs - 2 ) - segment_frac ) * _color_delta; _fBlue = ( ( num_tunnel_segs - 2 ) - segment_frac ) * _color_delta; } else { _fRed = ( 255.0f * alpha ); _fGreen = ( 255.0f * alpha ); _fBlue = ( 255.0f * alpha ); } byte _red = _fRed > 255.0 ? 255 : (byte)_fRed; byte _green = _fGreen > 255.0 ? 255 : (byte)_fGreen; byte _blue = _fBlue > 255.0 ? 255 : (byte)_fBlue; byte _alpha = teleporter->tunnel_tex_alpha; if ( nSegment == 0 ) { SET_ITER_VTX( &itarray->Vtxs[ nVtx + 0 ], &first_seg_vtxs[ 0 ], u, vHeight, _red, _green, _blue, _alpha ); SET_ITER_VTX( &itarray->Vtxs[ nVtx + 1 ], &first_seg_vtxs[ 1 ], u, GEOMV_0, _red, _green, _blue, _alpha ); SET_ITER_VTX( &itarray->Vtxs[ nVtx + 2 ], &first_seg_vtxs[ 2 ], u, vHeight, _red, _green, _blue, _alpha ); SET_ITER_VTX( &itarray->Vtxs[ nVtx + 3 ], &first_seg_vtxs[ 3 ], u, GEOMV_0, _red, _green, _blue, _alpha ); u += u_delta_first_cord; } else { if ( nSegment == ( num_tunnel_segs - 1 ) ) { SET_ITER_VTX( &itarray->Vtxs[ nVtx + 0 ], &last_seg_vtxs[ 0 ], u, vHeight, _red, _green, _blue, _alpha ); SET_ITER_VTX( &itarray->Vtxs[ nVtx + 1 ], &last_seg_vtxs[ 1 ], u, GEOMV_0, _red, _green, _blue, _alpha ); SET_ITER_VTX( &itarray->Vtxs[ nVtx + 2 ], &last_seg_vtxs[ 2 ], u, vHeight, _red, _green, _blue, _alpha ); SET_ITER_VTX( &itarray->Vtxs[ nVtx + 3 ], &last_seg_vtxs[ 3 ], u, GEOMV_0, _red, _green, _blue, _alpha ); //u += u_delta; } else { SET_ITER_VTX( &itarray->Vtxs[ nVtx + 0 ], &teleporter->tunnel_verts[ start_offset + nVtx + 0 ], u, vHeight, _red, _green, _blue, _alpha ); SET_ITER_VTX( &itarray->Vtxs[ nVtx + 1 ], &teleporter->tunnel_verts[ start_offset + nVtx + 1 ], u, GEOMV_0, _red, _green, _blue, _alpha ); SET_ITER_VTX( &itarray->Vtxs[ nVtx + 2 ], &teleporter->tunnel_verts[ start_offset + nVtx + 2 ], u, vHeight, _red, _green, _blue, _alpha ); SET_ITER_VTX( &itarray->Vtxs[ nVtx + 3 ], &teleporter->tunnel_verts[ start_offset + nVtx + 3 ], u, GEOMV_0, _red, _green, _blue, _alpha ); if ( !last_cord_removed && ( nSegment == ( num_tunnel_segs - 1 - 1 ) ) ) { u += ( u_delta - u_delta_last_cord ); } else { u += u_delta; } } } } size_t num_triindxs = num_tunnel_cords * 8 * 3; dword *vindxs = (dword *) ALLOCMEM( num_triindxs * sizeof( dword ) ); if ( vindxs == NULL ) OUTOFMEM( 0 ); for( int nCord = 0; nCord < num_tunnel_cords; nCord++ ) { for( int nSide = 0; nSide < 4; nSide++ ) { vindxs[ ( nCord * 24 ) + ( nSide * 6 ) + 0 ] = ( nCord * 4 ) + ( nSide + 0 ); vindxs[ ( nCord * 24 ) + ( nSide * 6 ) + 1 ] = ( nCord * 4 ) + ( nSide + 4 ); vindxs[ ( nCord * 24 ) + ( nSide * 6 ) + 2 ] = ( nCord * 4 ) + 4 + ( ( nSide + 1 ) % 4 ); vindxs[ ( nCord * 24 ) + ( nSide * 6 ) + 3 ] = ( nCord * 4 ) + ( nSide + 0 ); vindxs[ ( nCord * 24 ) + ( nSide * 6 ) + 4 ] = ( nCord * 4 ) + 4 + ( ( nSide + 1 ) % 4 ); vindxs[ ( nCord * 24 ) + ( nSide * 6 ) + 5 ] = ( nCord * 4 ) + ( nSide + 1 ) % 4; } } // setup transformation matrix D_LoadIterMatrix( NULL ); // lock array D_LockIterArray3( itarray, 0, itarray->NumVerts ); // draw indexed triangles in a single call (no far-plane clipping!) D_DrawIterArrayIndexed( ITERARRAY_MODE_TRIANGLES, num_triindxs , vindxs, 0x3d ); // unlock array D_UnlockIterArray(); // restore identity transformation D_LoadIterMatrix( NULL ); // free vertex index array FREEMEM( vindxs ); // free vertex array FREEMEM( itarray ); } // draw a spline line between start & end point ------------------------------- // void D_DrawSplineLine( int num_steps, const Xmatrx transform, const Vector3* start, const Vector3* end, const Vector3* start_tan, const Vector3* end_tan, dword mode ) { ASSERT( start != NULL ); ASSERT( end != NULL ); ASSERT( start_tan != NULL ); ASSERT( end_tan != NULL ); ASSERT( ( mode >= D_DRAWLINE_STYLE_DEFAULT ) && ( mode <= D_DRAWLINE_STYLE_STIPPLED ) ); IterLine3 itline; itline.NumVerts = 2; itline.flags = ITERFLAG_Z_DIV_XYZ | ITERFLAG_Z_TO_DEPTH | ITERFLAG_NONDESTRUCTIVE; itline.flags |= ( ( mode & 0x7 ) << 12 ); // map line drawing styles to iter line drawin styles itline.itertype = iter_rgba; itline.raststate = rast_zbuffer; itline.rastmask = rast_nomask; itline.Vtxs[ 0 ].W = GEOMV_1; itline.Vtxs[ 0 ].R = 255; itline.Vtxs[ 0 ].G = 255; itline.Vtxs[ 0 ].B = 255; itline.Vtxs[ 0 ].A = 255; itline.Vtxs[ 0 ].flags = ITERVTXFLAG_NONE; itline.Vtxs[ 1 ].W = GEOMV_1; itline.Vtxs[ 1 ].R = 255; itline.Vtxs[ 1 ].G = 255; itline.Vtxs[ 1 ].B = 255; itline.Vtxs[ 1 ].A = itline.Vtxs[ 0 ].A; itline.Vtxs[ 1 ].flags = ITERVTXFLAG_NONE; // setup transformation matrix D_LoadIterMatrix( NULL ); // init the hermite arclen data Hermite_ArcLen* hermite_data; hermite_data = Hermite_ArcLen_InitData( num_steps * 10, start, end, start_tan, end_tan ); for( int step = 0; step < num_steps; step++ ) { float s = (float)step / (float)( num_steps - 1 ); // calculate the interpolated pos Vector3 interp; Hermite_ArcLen_Interpolate( hermite_data, s, &interp ); if( step == 0 ) { MtxVctMUL( transform, start, (Vertex3*)&itline.Vtxs[ 0 ] ); MtxVctMUL( transform, &interp, (Vertex3*)&itline.Vtxs[ 1 ] ); } else { memcpy( &itline.Vtxs[ 0 ], &itline.Vtxs[ 1 ], sizeof ( IterVertex3 ) ); MtxVctMUL( transform, &interp, (Vertex3*)&itline.Vtxs[ 1 ] ); } if ( ( step % 2 ) == 0 ) { SETRGBA( &itline.Vtxs[ 0 ], 255,0,0,255 ); SETRGBA( &itline.Vtxs[ 1 ], 255,0,0,255 ); } else { SETRGBA( &itline.Vtxs[ 0 ], 255,255,255,255 ); SETRGBA( &itline.Vtxs[ 1 ], 255,255,255,255 ); } D_DrawIterLine3( &itline, 0x00 ); } // kill the hermite arclen data Hermite_ArcLen_KillData( hermite_data ); // restore identity transformation D_LoadIterMatrix( NULL ); } // make a cube out of a cullbox ----------------------------------------------- // PRIVATE void CubeFromCullBox3( CullBox3* cullbox, Vertex3* vtxs ) { ASSERT( cullbox != NULL ); ASSERT( vtxs != NULL ); for( int nVertex = 0; nVertex < 8; nVertex++ ) { vtxs[ nVertex ].X = cullbox->minmax[ 0 + ( ( nVertex % 4 ) / 2 ) * 3 ]; vtxs[ nVertex ].Y = cullbox->minmax[ 1 + ( ( ( ( nVertex % 4 ) % 3 ) != 0 ) ? 3 : 0 ) ]; vtxs[ nVertex ].Z = cullbox->minmax[ 2 + ( nVertex / 4 ) * 3 ]; } } // show the spline connecting the start with the exit of the teleporter ------- // PRIVATE void Teleporter_Debug_Show_Tunnel_Spline( Teleporter* teleporter ) { ASSERT( teleporter != NULL ); // get the tangents Vector3 start_tan, end_tan; FetchZVector( teleporter->ObjPosition, &start_tan ); FetchZVector( teleporter->child_object->ObjPosition, &end_tan ); // scale the tangents VECMULS( &start_tan, &start_tan, teleporter->tunnel_tangent_scale1 ); VECMULS( &end_tan, &end_tan, teleporter->tunnel_tangent_scale2 ); // draw center of tunnel if ( teleporter->tunnel_spline & 1 ) { // get the start/end pos Vector3 start_pos, end_pos; FetchTVector( teleporter->ObjPosition, &start_pos ); FetchTVector( teleporter->child_object->ObjPosition, &end_pos ); // draw the spline showing the center of the tunnel D_DrawSplineLine( teleporter->tunnel_spline_cords + 1, ViewCamera, &start_pos, &end_pos, &start_tan, &end_tan, D_DRAWLINE_STYLE_ANTIALIASED ); } // draw tunnel boundary if ( teleporter->tunnel_spline & 2 ) { // calculate the boundaries of the tunnel Vector3 boundary_start, boundary_end; for( int nCorner = 0; nCorner < 4; nCorner++ ) { // transform the start/end boundary points from object to world space MtxVctMUL( teleporter->ObjPosition, &teleporter->start_vtxlist[ nCorner ], &boundary_start ); MtxVctMUL( teleporter->child_object->ObjPosition, &teleporter->start_vtxlist[ nCorner ], &boundary_end ); // draw the boundary spline D_DrawSplineLine( teleporter->tunnel_spline_cords + 1, ViewCamera, &boundary_start, &boundary_end, &start_tan, &end_tan, D_DRAWLINE_STYLE_ANTIALIASED ); } } // draw tunnel boundary rects if ( teleporter->tunnel_spline & 4 ) { ASSERT( teleporter->tunnel_verts != NULL ); int num_steps = ( teleporter->tunnel_spline_cords + 1 ); Vertex3* vtxs = teleporter->tunnel_verts; // setup transformation matrix D_LoadIterMatrix( NULL ); for( int step = 0; step < num_steps; step++ ) { IterLine3 itline; itline.NumVerts = 2; itline.flags = ITERFLAG_LS_ANTIALIASED | ITERFLAG_Z_DIV_XYZ | ITERFLAG_Z_TO_DEPTH | ITERFLAG_NONDESTRUCTIVE; itline.itertype = iter_rgba; itline.raststate = rast_zbuffer; itline.rastmask = rast_nomask; itline.Vtxs[ 0 ].W = GEOMV_1; itline.Vtxs[ 0 ].R = 0; itline.Vtxs[ 0 ].G = 255; itline.Vtxs[ 0 ].B = 0; itline.Vtxs[ 0 ].A = 255; itline.Vtxs[ 0 ].flags = ITERVTXFLAG_NONE; itline.Vtxs[ 1 ].W = GEOMV_1; itline.Vtxs[ 1 ].R = 0; itline.Vtxs[ 1 ].G = 255; itline.Vtxs[ 1 ].B = 0; itline.Vtxs[ 1 ].A = itline.Vtxs[ 0 ].A; itline.Vtxs[ 1 ].flags = ITERVTXFLAG_NONE; for( int nCorner = 0; nCorner < 4; nCorner++ ) { if( nCorner != 3 ) { MtxVctMUL( ViewCamera, &vtxs[ nCorner ], (Vertex3*)&itline.Vtxs[ 0 ] ); MtxVctMUL( ViewCamera, &vtxs[ nCorner + 1 ], (Vertex3*)&itline.Vtxs[ 1 ] ); } else { memcpy( &itline.Vtxs[ 0 ], &itline.Vtxs[ 1 ], sizeof ( IterVertex3 ) ); MtxVctMUL( ViewCamera, &vtxs[ 0 ], (Vertex3*)&itline.Vtxs[ 1 ] ); } D_DrawIterLine3( &itline, 0x00 ); } // advance to next rectangle vtxs += 4; } // restore identity transformation D_LoadIterMatrix( NULL ); } // draw the frames for start/exit if ( teleporter->tunnel_spline & 8 ) { Vector3 pos; FetchTVector( teleporter->ObjPosition, &pos ); D_FrameOfReference( teleporter->start_frame, &pos ); FetchTVector( teleporter->child_object->ObjPosition, &pos ); D_FrameOfReference( teleporter->end_frame, &pos ); //Draw_FrameOfReference( teleporter->ObjPosition, NULL ); } // draw the frames making up the spline if ( teleporter->tunnel_spline & 16 ) { int num_steps = ( teleporter->tunnel_spline_cords + 1 ); for( int step = 0; step < num_steps; step++ ) { D_FrameOfReference( teleporter->spline_frames[ step ], NULL ); } } // draw the bounding box of the tunnel if ( teleporter->tunnel_spline & 32 ) { CullBox3* cullbox = &teleporter->tunnel_cullbox; Vector3 vtxs_ospc[ 8 ]; CubeFromCullBox3( cullbox, vtxs_ospc ); // transform from object to worldspace Vector3 vtxs[ 8 ]; int nVertex = 0; for( nVertex = 0; nVertex < 8; nVertex++ ) { MtxVctMUL( teleporter->ObjPosition, &vtxs_ospc[ nVertex ], &vtxs[ nVertex ] ); } colrgba_s color; SETRGBA( &color, 0, 0, 255, 255 ); int nLine = 0; for( nLine = 0; nLine < 8; nLine++ ) { D_LineWorld( &vtxs[ 4 * ( nLine / 4 ) + nLine % 4 ], &vtxs[ 4 * ( nLine / 4 ) + ( nLine + 1 ) % 4 ], &color ); } for( nLine = 0; nLine < 4; nLine++ ) { D_LineWorld( &vtxs[ nLine ], &vtxs[ 4 + nLine ], &color ); } } return; } // type fields init function for teleporter ------------------------------------- // PRIVATE void TeleporterInitType( CustomObject *base ) { ASSERT( base != NULL ); Teleporter *teleporter = (Teleporter *) base; teleporter->active = TRUE; teleporter->exit_delta_x = 0; teleporter->exit_delta_y = 0; teleporter->exit_delta_z = 100; teleporter->exit_rot_phi = 0; teleporter->exit_rot_theta = 0; teleporter->actoffset = 5; teleporter->act_cone_angle = 30; teleporter->u_variation[ 0 ] = 0.05; // percentage of texture space teleporter->v_variation[ 0 ] = 0.05; teleporter->u_variation[ 1 ] = 0.03; // percentage of texture space teleporter->v_variation[ 1 ] = 0.03; teleporter->start_tex_sec_layer = 1; teleporter->start_tex_alpha = 128; teleporter->tunnel_slerp = 1; // do slerp teleporter->tunnel_lerp = 2; // do hermite lerp teleporter->tunnel_tangent_scale1 = 300; teleporter->tunnel_tangent_scale2 = 300; teleporter->tunnel_anim_speed = 2; teleporter->tunnel_spline = FALSE; teleporter->tunnel_spline_cords = 30; strcpy( teleporter->tex_name_interior, TELEPORTER_DEFAULT_TEX_NAME_INTERIOR ); strcpy( teleporter->tex_name_tunnel, TELEPORTER_DEFAULT_TEX_NAME_TUNNEL ); teleporter->start_texmap = NULL; teleporter->tunnel_texmap = NULL; teleporter->start_vtxlist = NULL; teleporter->start_texmap = NULL; teleporter->tunnel_verts = NULL; teleporter->tunnel_spline_arclen = GEOMV_0; teleporter->tunnel_tex_alpha = 128; } // teleporter constructor (class instantiation) --------------------------------- // PRIVATE void TeleporterInstantiate( CustomObject *base ) { ASSERT( base != NULL ); Teleporter *teleporter = (Teleporter *) base; teleporter->start_vtxlist = (Vertex3*)ALLOCMEM( 4 * sizeof( Vertex3 ) ); memset( teleporter->start_vtxlist, 0, 4 * sizeof( Vertex3 ) ); // init the vertices making up the filled interior of the teleporter start teleporter->start_vtxlist[ 0 ].X = (geomv_t)-TELEP_BOUNDARY_HORIZ; teleporter->start_vtxlist[ 0 ].Y = (geomv_t)-TELEP_BOUNDARY_VERT; teleporter->start_vtxlist[ 0 ].Z = GEOMV_0; teleporter->start_vtxlist[ 1 ].X = (geomv_t)TELEP_BOUNDARY_HORIZ; teleporter->start_vtxlist[ 1 ].Y = (geomv_t)-TELEP_BOUNDARY_VERT; teleporter->start_vtxlist[ 1 ].Z = GEOMV_0; teleporter->start_vtxlist[ 2 ].X = (geomv_t)TELEP_BOUNDARY_HORIZ; teleporter->start_vtxlist[ 2 ].Y = (geomv_t)TELEP_BOUNDARY_VERT; teleporter->start_vtxlist[ 2 ].Z = GEOMV_0; teleporter->start_vtxlist[ 3 ].X = (geomv_t)-TELEP_BOUNDARY_HORIZ; teleporter->start_vtxlist[ 3 ].Y = (geomv_t)TELEP_BOUNDARY_VERT; teleporter->start_vtxlist[ 3 ].Z = GEOMV_0; // summon the child teleporter ( exit ) as plain geometry type dword objclass = OBJ_FetchObjectClassId( TELEPORTER_EXIT_MODEL_NAME ); ASSERT( objclass != CLASS_ID_INVALID ); if ( objclass >= (dword)NumObjClasses ) { CON_AddLine( telep_exit_class_not_found ); return; } if ( objclass != CLASS_ID_INVALID ) { Xmatrx ChildTrans; MakeIdMatrx( ChildTrans ); // translate the teleporter exit ChildTrans[ 0 ][ 3 ] = teleporter->exit_delta_x; ChildTrans[ 1 ][ 3 ] = teleporter->exit_delta_y; ChildTrans[ 2 ][ 3 ] = teleporter->exit_delta_z; // get the trafo for the exit rotation Teleporter_Rotation_Transform( teleporter->exit_rot_phi, teleporter->exit_rot_theta, ChildTrans ); Xmatrx ChildPos; MtxMtxMUL( teleporter->ObjPosition, ChildTrans, ChildPos ); // summon a object from a class ( do not show stargate ) teleporter->child_object = SummonObject( objclass, ChildPos ); } // get the texture maps and calc the max u/v deltas TeleporterModify_TexPropsChanged( teleporter ); // init the angles teleporter->u_phi[ 0 ] = DEG_TO_BAMS( RAND() % 360 ); teleporter->v_phi[ 0 ] = DEG_TO_BAMS( RAND() % 360 ); teleporter->u_phi[ 1 ] = DEG_TO_BAMS( RAND() % 360 ); teleporter->v_phi[ 1 ] = DEG_TO_BAMS( RAND() % 360 ); // init the delta angles teleporter->u_phi_delta[ 0 ] = DEG_TO_BAMS( 0.1 ); teleporter->v_phi_delta[ 0 ] = DEG_TO_BAMS( 0.1 ); teleporter->u_phi_delta[ 1 ] = DEG_TO_BAMS( 0.1 ); teleporter->v_phi_delta[ 1 ] = DEG_TO_BAMS( 0.1 ); // init the tunnel frame to the frame of the teleporter start memcpy( &teleporter->tunnel_frame, &teleporter->ObjPosition, sizeof( Xmatrx ) ); // init the interpolation t //teleporter->tunnel_t = 0.0f; teleporter->tunnel_t = (float)RAND() / (float)0x7fff/*RAND_MAX*/; teleporter->spline_frames = NULL; // calculate the verts for the tunnel Teleporter_Calc_Tunnel_Verts( teleporter ); // set the properties for the tunnel animation teleporter->tunnel_len = 10; teleporter->tunnel_cur_anim_step = GEOMV_0; // get the initial start position FetchTVector( teleporter->ObjPosition, &teleporter->start ); //FIXME: we need to get the angles out of the matrix teleporter->start_rot_phi = 0; teleporter->start_rot_theta = 0; } // callback type and flags ---------------------------------------------------- // static int callback_type = CBTYPE_DRAW_CUSTOM_ITER | CBFLAG_REMOVE; // teleporter destructor (instance destruction) --------------------------------- // PRIVATE void TeleporterDestroy( CustomObject *base ) { ASSERT( base != NULL ); Teleporter *teleporter = (Teleporter *) base; // stop any playing teleporter sound AUD_TeleporterOff( teleporter ); // destroy attached vertex info ASSERT( teleporter->start_vtxlist != NULL ); ASSERT( teleporter->tunnel_verts != NULL ); FREEMEM( teleporter->start_vtxlist ); FREEMEM( teleporter->tunnel_verts ); teleporter->start_vtxlist = NULL; teleporter->tunnel_verts = NULL; if ( teleporter->spline_frames != NULL ) { FREEMEM( teleporter->spline_frames ); } // ensure pending callbacks are destroyed to avoid // calling them with invalid pointers int numremoved = CALLBACK_DestroyCallback( callback_type, (void *) base ); //ASSERT( numremoved <= 1 ); } // teleporter animation callback ---------------------------------------------- // PRIVATE int TeleporterAnimate( CustomObject *base ) { ASSERT( base != NULL ); Teleporter *teleporter = (Teleporter *) base; // do nothing if teleporter inactive if ( !teleporter->active ) return TRUE; teleporter->u_phi[ 0 ] += teleporter->u_phi_delta[ 0 ] * CurScreenRefFrames; teleporter->v_phi[ 0 ] += teleporter->v_phi_delta[ 0 ] * CurScreenRefFrames; teleporter->u_phi[ 1 ] += teleporter->u_phi_delta[ 1 ] * CurScreenRefFrames; teleporter->v_phi[ 1 ] += teleporter->v_phi_delta[ 1 ] * CurScreenRefFrames; sincosval_s sincosv; GetSinCos( teleporter->u_phi[ 0 ], &sincosv ); teleporter->u_delta[ 0 ] = GEOMV_MUL( teleporter->u_maxdelta[ 0 ], sincosv.cosval ); GetSinCos( teleporter->v_phi[ 0 ], &sincosv ); teleporter->v_delta[ 0 ] = GEOMV_MUL( teleporter->v_maxdelta[ 0 ], sincosv.sinval ); GetSinCos( teleporter->u_phi[ 1 ], &sincosv ); teleporter->u_delta[ 1 ] = GEOMV_MUL( teleporter->u_maxdelta[ 1 ], sincosv.cosval ); GetSinCos( teleporter->v_phi[ 1 ], &sincosv ); teleporter->v_delta[ 1 ] = GEOMV_MUL( teleporter->v_maxdelta[ 1 ], sincosv.sinval ); // animate the tunnel frame Teleporter_Animate_Tunnel( teleporter ); // register the drawing callback for drawing the interior of the teleporter CALLBACK_RegisterCallback( callback_type, Teleporter_Draw, (void *) base ); return TRUE; } // animate the tunnel frame --------------------------------------------------- // PRIVATE void Teleporter_Animate_Tunnel( Teleporter* teleporter ) { ASSERT( teleporter != NULL ); // do nothing if teleporter inactive if ( !teleporter->active ) return; // reset the interpolation if it was at the end of the animation if ( teleporter->tunnel_t == 1.0f ) { teleporter->tunnel_t = 0.0f; } else { // advance the t paramater teleporter->tunnel_t += (float)CurScreenRefFrames / ( (float)FRAME_MEASURE_TIMEBASE * (float)teleporter->tunnel_anim_speed ); if ( teleporter->tunnel_t > 1.0f ) { teleporter->tunnel_t = 1.0f; } } int num_steps = ( teleporter->tunnel_spline_cords + 1 ); teleporter->tunnel_cur_anim_step = ( num_steps - 1 + teleporter->tunnel_len ) * teleporter->tunnel_t; if ( teleporter->tunnel_cur_anim_step >= num_steps + teleporter->tunnel_len ) { teleporter->tunnel_cur_anim_step = num_steps - 1 + teleporter->tunnel_len; } #ifdef ANIMATE_TUNNEL_FRAME // reset the interpolation if it was at the end of the animation if ( teleporter->tunnel_t == 1.0f ) { teleporter->tunnel_t = 0.0f; memcpy( teleporter->tunnel_frame, teleporter->ObjPosition, sizeof( Xmatrx ) ); } // advance the t paramater teleporter->tunnel_t += (float)CurScreenRefFrames / ( (float)FRAME_MEASURE_TIMEBASE * (float)teleporter->tunnel_anim_speed ); if ( teleporter->tunnel_t > 1.0f ) { teleporter->tunnel_t = 1.0f; } // get the position of the tunnel frame ( source ) Vector3 tunnel_pos; FetchTVector( teleporter->tunnel_frame, &tunnel_pos ); // get the position of the teleporter exit ( destination ) Vector3 exit_pos; FetchTVector( teleporter->child_object->ObjPosition, &exit_pos ); // check whether to do the SLERP if ( teleporter->tunnel_slerp == 1 ) { // we only want todo the quaterionien slerp on the orientations not on the poses Xmatrx start_frame, end_frame; MakeNonTranslationMatrx( teleporter->ObjPosition, start_frame ); MakeNonTranslationMatrx( teleporter->child_object->ObjPosition, end_frame ); // get slerp frame QuaternionSlerpFrames( teleporter->tunnel_frame, start_frame, end_frame, teleporter->tunnel_t ); /* // get source orientation quaternion Quaternion srcquat; QuaternionFromMatrx( &srcquat, start_frame ); QuaternionMakeUnit( &srcquat ); // get destination orientation quaternion Quaternion dstquat; QuaternionFromMatrx( &dstquat, end_frame ); QuaternionMakeUnit( &dstquat ); // do slerp from src to dst (filter output this frame) Quaternion slerpquat; QuaternionSlerp( &slerpquat, &srcquat, &dstquat, teleporter->tunnel_t ); QuaternionMakeUnit( &slerpquat ); // fill R part of view camera matrix (filtered orientation this frame) MatrxFromQuaternion( teleporter->tunnel_frame, &slerpquat ); */ } // check whether to do simple LERP if ( teleporter->tunnel_lerp == 1 ) { // get the position of the tunnel frame FetchTVector( teleporter->tunnel_frame, &tunnel_pos ); // interpolation vector from last filtered to new filtered t float lerpx = GEOMV_TO_FLOAT( exit_pos.X - tunnel_pos.X ) * teleporter->tunnel_t; float lerpy = GEOMV_TO_FLOAT( exit_pos.Y - tunnel_pos.Y ) * teleporter->tunnel_t; float lerpz = GEOMV_TO_FLOAT( exit_pos.Z - tunnel_pos.Z ) * teleporter->tunnel_t; Vector3 deltatvec; deltatvec.X = FLOAT_TO_GEOMV( lerpx ); deltatvec.Y = FLOAT_TO_GEOMV( lerpy ); deltatvec.Z = FLOAT_TO_GEOMV( lerpz ); VECADD( &tunnel_pos, &deltatvec, &tunnel_pos ); } // check whether to do Hermite LERP for the position if ( teleporter->tunnel_lerp == 2 ) { Vector3 T1; Vector3 T2; FetchZVector( teleporter->ObjPosition, &T1 ); FetchZVector( teleporter->child_object->ObjPosition, &T2 ); Vector3 teleporter_pos; FetchTVector( teleporter->ObjPosition, &teleporter_pos ); VECMULS( &T1, &T1, teleporter->tunnel_tangent_scale1 ); VECMULS( &T2, &T2, teleporter->tunnel_tangent_scale2 ); Hermite_Interpolate( &tunnel_pos, teleporter->tunnel_t, &teleporter_pos, &exit_pos, &T1, &T2 ); } // store the tunnel pos back to the tunnel frame StoreTVector( teleporter->tunnel_frame, &tunnel_pos ); #endif // ANIMATE_TUNNEL_FRAME } // check whether a ship is in range of a teleporter entry --------------------- // PRIVATE int Teleporter_ShipInRange( Teleporter *teleporter, ShipObject *ship ) { ASSERT( teleporter != NULL ); ASSERT( ship != NULL ); //NOTE: // the ship is treated as a sphere for activation // range detection. Vector3 telepnormal; FetchZVector( teleporter->ObjPosition, &telepnormal ); Vertex3 teleppos; FetchTVector( teleporter->ObjPosition, &teleppos ); Vertex3 shippos; FetchTVector( ship->ObjPosition, &shippos ); geomv_t shipdot = -DOT_PRODUCT( &telepnormal, &shippos ); geomv_t telepdot = -DOT_PRODUCT( &telepnormal, &teleppos ); geomv_t distance = shipdot - telepdot; // not in range if ship in wrong halfspace ( already behind teleporter ) if ( GEOMV_NEGATIVE( distance ) ) { return FALSE; } // check whether inside of boundingsphere around teleporter Vector3 telepship; VECSUB( &telepship, &shippos, &teleppos ); geomv_t telepship_len = VctLenX( &telepship ); if ( telepship_len > teleporter->BoundingSphere ) { return FALSE; } // inside the activation distance ? if ( distance < teleporter->actoffset ) { // check whether inside of cone Vector3 shipnormal; FetchZVector( ship->ObjPosition, &shipnormal ); shipdot = DOT_PRODUCT( &telepnormal, &shipnormal ); sincosval_s sincosv; GetSinCos( DEG_TO_BAMS( teleporter->act_cone_angle ), &sincosv ); return ( shipdot >= sincosv.cosval ); } else { return FALSE; } } // teleporter collision callback ------------------------------------------------ // PRIVATE int TeleporterCollide( CustomObject *base ) { ASSERT( base != NULL ); Teleporter *teleporter = (Teleporter *) base; // do nothing if teleporter inactive if ( !teleporter->active ) return TRUE; // determine whether MyShip is in audio range of teleporter Vector3 diff; Vertex3 teleppos; Vertex3 shippos; FetchTVector( teleporter->ObjPosition, &teleppos ); FetchTVector( MyShip->ObjPosition, &shippos ); VECSUB( &diff, &teleppos, &shippos ); geomv_t distance = VctLenX( &diff ); if ( distance < MAX_VOLUME_DISTANCE_TELEPORTER ) { AUD_Teleporter( teleporter ); } else { AUD_TeleporterOff( teleporter ); } // get the world->object transform Xmatrx World2Telep; CalcOrthoInverse( teleporter->ObjPosition, World2Telep ); // first check local ship if ( Teleporter_ShipInRange( teleporter, MyShip ) ) { // get the world->view transform Xmatrx CameraInWorldSpace; CalcOrthoInverse( ShipViewCamera, CameraInWorldSpace ); // transform the camera to object space Xmatrx CameraInTelepSpace; MtxMtxMUL( World2Telep, CameraInWorldSpace, CameraInTelepSpace ); // transform camera to world space ( using the teleporter exit frame ) MtxMtxMUL( teleporter->child_object->ObjPosition, CameraInTelepSpace, CameraInWorldSpace ); // transform camera to view space CalcOrthoInverse( CameraInWorldSpace, ShipViewCamera ); CameraMoved = TRUE; } #ifndef DONT_MOVE_REMOTE_SHIPS // walk all ships and check for teleportings ShipObject *shippo = FetchFirstShip(); for ( ; shippo; shippo = (ShipObject *) shippo->NextObj ) { if ( shippo != MyShip ) { if ( Teleporter_ShipInRange( teleporter, shippo ) ) { // transform the ship to (teleporter) object space Xmatrx ShipInTelepSpace; MtxMtxMUL( World2Telep, shippo->ObjPosition, ShipInTelepSpace ); // transform ship to world space ( using the teleporter exit frame ) MtxMtxMUL( teleporter->child_object->ObjPosition, ShipInTelepSpace, shippo->ObjPosition ); } } } #endif // !DONT_MOVE_REMOTE_SHIPS return TRUE; } // register object type for Teleporter ------------------------------------------ // PRIVATE void TeleporterRegisterCustomType() { custom_type_info_s info; memset( &info, 0, sizeof( info ) ); info.type_name = "teleporter"; info.type_id = 0x00000000; info.type_size = sizeof( Teleporter ); info.type_template = NULL; info.type_flags = CUSTOM_TYPE_DEFAULT; info.callback_init = TeleporterInitType; info.callback_instant = TeleporterInstantiate; info.callback_destroy = TeleporterDestroy; info.callback_animate = TeleporterAnimate; info.callback_collide = TeleporterCollide; info.callback_notify = NULL; info.callback_persist = NULL;//TeleporterPersistFromStream; OBJ_RegisterCustomType( &info ); CON_RegisterCustomType( info.type_id, Teleporter_PropList ); memset( &info, 0, sizeof( info ) ); info.type_name = "telep_exit"; info.type_id = 0x00000000; info.type_size = sizeof( Teleporter ); info.type_template = NULL; info.type_flags = CUSTOM_TYPE_DEFAULT | CUSTOM_TYPE_NOT_PERSISTANT; info.callback_init = NULL; info.callback_instant = NULL; info.callback_destroy = NULL; info.callback_animate = NULL; info.callback_collide = NULL; info.callback_notify = NULL; info.callback_persist = NULL; OBJ_RegisterCustomType( &info ); } // module registration function ----------------------------------------------- // REGISTER_MODULE( G_TELEP ) { // register type TeleporterRegisterCustomType(); }