aboutsummaryrefslogtreecommitdiff
path: root/src/parsec/g_telep.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/parsec/g_telep.cpp')
-rw-r--r--src/parsec/g_telep.cpp2006
1 files changed, 2006 insertions, 0 deletions
diff --git a/src/parsec/g_telep.cpp b/src/parsec/g_telep.cpp
new file mode 100644
index 0000000..b0b732e
--- /dev/null
+++ b/src/parsec/g_telep.cpp
@@ -0,0 +1,2006 @@
+/*
+ * PARSEC - Teleporter Model
+ *
+ * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:37 $
+ *
+ * Orginally written by:
+ * Copyright (c) Clemens Beer <cbx@parsec.org> 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 <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <limits.h>
+
+// 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();
+}
+