2001-01-19 03:30:16 +00:00
/*
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ( " PARALLAX " ) . PARALLAX , IN DISTRIBUTING THE CODE TO
END - USERS , AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN , GRANTS A
ROYALTY - FREE , PERPETUAL LICENSE TO SUCH END - USERS FOR USE BY SUCH END - USERS
IN USING , DISPLAYING , AND CREATING DERIVATIVE WORKS THEREOF , SO LONG AS
SUCH USE , DISPLAY OR CREATION IS FOR NON - COMMERCIAL , ROYALTY OR REVENUE
FREE PURPOSES . IN NO EVENT SHALL THE END - USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE - BEARING PURPOSES . THE END - USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE .
COPYRIGHT 1993 - 1999 PARALLAX SOFTWARE CORPORATION . ALL RIGHTS RESERVED .
*/
2001-01-31 15:18:05 +00:00
# ifdef HAVE_CONFIG_H
2001-01-19 03:30:16 +00:00
# include <conf.h>
2001-01-31 15:18:05 +00:00
# endif
2001-01-19 03:30:16 +00:00
# include <stdlib.h>
# include <stdio.h>
# include <string.h> // for memset()
# include "u_mem.h"
# include "inferno.h"
# include "game.h"
# include "error.h"
# include "mono.h"
# include "vecmat.h"
# include "gameseg.h"
# include "wall.h"
# include "fuelcen.h"
# include "bm.h"
# include "fvi.h"
# include "byteswap.h"
# ifdef RCS
2001-01-31 15:18:05 +00:00
static char rcsid [ ] = " $Id: gameseg.c,v 1.3 2001-01-31 15:17:53 bradleyb Exp $ " ;
2001-01-19 03:30:16 +00:00
# endif
// How far a point can be from a plane, and still be "in" the plane
# define PLANE_DIST_TOLERANCE 250
dl_index Dl_indices [ MAX_DL_INDICES ] ;
delta_light Delta_lights [ MAX_DELTA_LIGHTS ] ;
int Num_static_lights ;
// ------------------------------------------------------------------------------------------
// Compute the center point of a side of a segment.
// The center point is defined to be the average of the 4 points defining the side.
void compute_center_point_on_side ( vms_vector * vp , segment * sp , int side )
{
int v ;
vm_vec_zero ( vp ) ;
for ( v = 0 ; v < 4 ; v + + )
vm_vec_add2 ( vp , & Vertices [ sp - > verts [ Side_to_verts [ side ] [ v ] ] ] ) ;
vm_vec_scale ( vp , F1_0 / 4 ) ;
}
// ------------------------------------------------------------------------------------------
// Compute segment center.
// The center point is defined to be the average of the 8 points defining the segment.
void compute_segment_center ( vms_vector * vp , segment * sp )
{
int v ;
vm_vec_zero ( vp ) ;
for ( v = 0 ; v < 8 ; v + + )
vm_vec_add2 ( vp , & Vertices [ sp - > verts [ v ] ] ) ;
vm_vec_scale ( vp , F1_0 / 8 ) ;
}
// -----------------------------------------------------------------------------
// Given two segments, return the side index in the connecting segment which connects to the base segment
// Optimized by MK on 4/21/94 because it is a 2% load.
int find_connect_side ( segment * base_seg , segment * con_seg )
{
int s ;
short base_seg_num = base_seg - Segments ;
short * childs = con_seg - > children ;
for ( s = 0 ; s < MAX_SIDES_PER_SEGMENT ; s + + ) {
if ( * childs + + = = base_seg_num )
return s ;
}
// legal to return -1, used in object_move_one(), mk, 06/08/94: Assert(0); // Illegal -- there is no connecting side between these two segments
return - 1 ;
}
// -----------------------------------------------------------------------------------
// Given a side, return the number of faces
int get_num_faces ( side * sidep )
{
switch ( sidep - > type ) {
case SIDE_IS_QUAD :
return 1 ;
break ;
case SIDE_IS_TRI_02 :
case SIDE_IS_TRI_13 :
return 2 ;
break ;
default :
Error ( " Illegal type = %i \n " , sidep - > type ) ;
break ;
}
}
// Fill in array with four absolute point numbers for a given side
void get_side_verts ( short * vertlist , int segnum , int sidenum )
{
int i ;
byte * sv = Side_to_verts [ sidenum ] ;
short * vp = Segments [ segnum ] . verts ;
for ( i = 4 ; i - - ; )
vertlist [ i ] = vp [ sv [ i ] ] ;
}
# ifdef EDITOR
// -----------------------------------------------------------------------------------
// Create all vertex lists (1 or 2) for faces on a side.
// Sets:
// num_faces number of lists
// vertices vertices in all (1 or 2) faces
// If there is one face, it has 4 vertices.
// If there are two faces, they both have three vertices, so face #0 is stored in vertices 0,1,2,
// face #1 is stored in vertices 3,4,5.
// Note: these are not absolute vertex numbers, but are relative to the segment
// Note: for triagulated sides, the middle vertex of each trianle is the one NOT
// adjacent on the diagonal edge
void create_all_vertex_lists ( int * num_faces , int * vertices , int segnum , int sidenum )
{
side * sidep = & Segments [ segnum ] . sides [ sidenum ] ;
int * sv = Side_to_verts_int [ sidenum ] ;
Assert ( ( segnum < = Highest_segment_index ) & & ( segnum > = 0 ) ) ;
Assert ( ( sidenum > = 0 ) & & ( sidenum < 6 ) ) ;
switch ( sidep - > type ) {
case SIDE_IS_QUAD :
vertices [ 0 ] = sv [ 0 ] ;
vertices [ 1 ] = sv [ 1 ] ;
vertices [ 2 ] = sv [ 2 ] ;
vertices [ 3 ] = sv [ 3 ] ;
* num_faces = 1 ;
break ;
case SIDE_IS_TRI_02 :
* num_faces = 2 ;
vertices [ 0 ] = sv [ 0 ] ;
vertices [ 1 ] = sv [ 1 ] ;
vertices [ 2 ] = sv [ 2 ] ;
vertices [ 3 ] = sv [ 2 ] ;
vertices [ 4 ] = sv [ 3 ] ;
vertices [ 5 ] = sv [ 0 ] ;
//IMPORTANT: DON'T CHANGE THIS CODE WITHOUT CHANGING GET_SEG_MASKS()
//CREATE_ABS_VERTEX_LISTS(), CREATE_ALL_VERTEX_LISTS(), CREATE_ALL_VERTNUM_LISTS()
break ;
case SIDE_IS_TRI_13 :
* num_faces = 2 ;
vertices [ 0 ] = sv [ 3 ] ;
vertices [ 1 ] = sv [ 0 ] ;
vertices [ 2 ] = sv [ 1 ] ;
vertices [ 3 ] = sv [ 1 ] ;
vertices [ 4 ] = sv [ 2 ] ;
vertices [ 5 ] = sv [ 3 ] ;
//IMPORTANT: DON'T CHANGE THIS CODE WITHOUT CHANGING GET_SEG_MASKS()
//CREATE_ABS_VERTEX_LISTS(), CREATE_ALL_VERTEX_LISTS(), CREATE_ALL_VERTNUM_LISTS()
break ;
default :
Error ( " Illegal side type (1), type = %i, segment # = %i, side # = %i \n " , sidep - > type , segnum , sidenum ) ;
break ;
}
}
# endif
// -----------------------------------------------------------------------------------
// Like create all vertex lists, but returns the vertnums (relative to
// the side) for each of the faces that make up the side.
// If there is one face, it has 4 vertices.
// If there are two faces, they both have three vertices, so face #0 is stored in vertices 0,1,2,
// face #1 is stored in vertices 3,4,5.
void create_all_vertnum_lists ( int * num_faces , int * vertnums , int segnum , int sidenum )
{
side * sidep = & Segments [ segnum ] . sides [ sidenum ] ;
Assert ( ( segnum < = Highest_segment_index ) & & ( segnum > = 0 ) ) ;
switch ( sidep - > type ) {
case SIDE_IS_QUAD :
vertnums [ 0 ] = 0 ;
vertnums [ 1 ] = 1 ;
vertnums [ 2 ] = 2 ;
vertnums [ 3 ] = 3 ;
* num_faces = 1 ;
break ;
case SIDE_IS_TRI_02 :
* num_faces = 2 ;
vertnums [ 0 ] = 0 ;
vertnums [ 1 ] = 1 ;
vertnums [ 2 ] = 2 ;
vertnums [ 3 ] = 2 ;
vertnums [ 4 ] = 3 ;
vertnums [ 5 ] = 0 ;
//IMPORTANT: DON'T CHANGE THIS CODE WITHOUT CHANGING GET_SEG_MASKS()
//CREATE_ABS_VERTEX_LISTS(), CREATE_ALL_VERTEX_LISTS(), CREATE_ALL_VERTNUM_LISTS()
break ;
case SIDE_IS_TRI_13 :
* num_faces = 2 ;
vertnums [ 0 ] = 3 ;
vertnums [ 1 ] = 0 ;
vertnums [ 2 ] = 1 ;
vertnums [ 3 ] = 1 ;
vertnums [ 4 ] = 2 ;
vertnums [ 5 ] = 3 ;
//IMPORTANT: DON'T CHANGE THIS CODE WITHOUT CHANGING GET_SEG_MASKS()
//CREATE_ABS_VERTEX_LISTS(), CREATE_ALL_VERTEX_LISTS(), CREATE_ALL_VERTNUM_LISTS()
break ;
default :
Error ( " Illegal side type (2), type = %i, segment # = %i, side # = %i \n " , sidep - > type , segnum , sidenum ) ;
break ;
}
}
// -----
//like create_all_vertex_lists(), but generate absolute point numbers
void create_abs_vertex_lists ( int * num_faces , int * vertices , int segnum , int sidenum )
{
short * vp = Segments [ segnum ] . verts ;
side * sidep = & Segments [ segnum ] . sides [ sidenum ] ;
int * sv = Side_to_verts_int [ sidenum ] ;
Assert ( ( segnum < = Highest_segment_index ) & & ( segnum > = 0 ) ) ;
switch ( sidep - > type ) {
case SIDE_IS_QUAD :
vertices [ 0 ] = vp [ sv [ 0 ] ] ;
vertices [ 1 ] = vp [ sv [ 1 ] ] ;
vertices [ 2 ] = vp [ sv [ 2 ] ] ;
vertices [ 3 ] = vp [ sv [ 3 ] ] ;
* num_faces = 1 ;
break ;
case SIDE_IS_TRI_02 :
* num_faces = 2 ;
vertices [ 0 ] = vp [ sv [ 0 ] ] ;
vertices [ 1 ] = vp [ sv [ 1 ] ] ;
vertices [ 2 ] = vp [ sv [ 2 ] ] ;
vertices [ 3 ] = vp [ sv [ 2 ] ] ;
vertices [ 4 ] = vp [ sv [ 3 ] ] ;
vertices [ 5 ] = vp [ sv [ 0 ] ] ;
//IMPORTANT: DON'T CHANGE THIS CODE WITHOUT CHANGING GET_SEG_MASKS(),
//CREATE_ABS_VERTEX_LISTS(), CREATE_ALL_VERTEX_LISTS(), CREATE_ALL_VERTNUM_LISTS()
break ;
case SIDE_IS_TRI_13 :
* num_faces = 2 ;
vertices [ 0 ] = vp [ sv [ 3 ] ] ;
vertices [ 1 ] = vp [ sv [ 0 ] ] ;
vertices [ 2 ] = vp [ sv [ 1 ] ] ;
vertices [ 3 ] = vp [ sv [ 1 ] ] ;
vertices [ 4 ] = vp [ sv [ 2 ] ] ;
vertices [ 5 ] = vp [ sv [ 3 ] ] ;
//IMPORTANT: DON'T CHANGE THIS CODE WITHOUT CHANGING GET_SEG_MASKS()
//CREATE_ABS_VERTEX_LISTS(), CREATE_ALL_VERTEX_LISTS(), CREATE_ALL_VERTNUM_LISTS()
break ;
default :
Error ( " Illegal side type (3), type = %i, segment # = %i, side # = %i \n " , sidep - > type , segnum , sidenum ) ;
break ;
}
}
//returns 3 different bitmasks with info telling if this sphere is in
//this segment. See segmasks structure for info on fields
segmasks get_seg_masks ( vms_vector * checkp , int segnum , fix rad )
{
int sn , facebit , sidebit ;
segmasks masks ;
int num_faces ;
int vertex_list [ 6 ] ;
segment * seg ;
if ( segnum = = - 1 )
Error ( " segnum == -1 in get_seg_masks() " ) ;
Assert ( ( segnum < = Highest_segment_index ) & & ( segnum > = 0 ) ) ;
seg = & Segments [ segnum ] ;
//check point against each side of segment. return bitmask
masks . sidemask = masks . facemask = masks . centermask = 0 ;
for ( sn = 0 , facebit = sidebit = 1 ; sn < 6 ; sn + + , sidebit < < = 1 ) {
# ifndef COMPACT_SEGS
side * s = & seg - > sides [ sn ] ;
# endif
int side_pokes_out ;
int vertnum , fn ;
// Get number of faces on this side, and at vertex_list, store vertices.
// If one face, then vertex_list indicates a quadrilateral.
// If two faces, then 0,1,2 define one triangle, 3,4,5 define the second.
create_abs_vertex_lists ( & num_faces , vertex_list , segnum , sn ) ;
//ok...this is important. If a side has 2 faces, we need to know if
//those faces form a concave or convex side. If the side pokes out,
//then a point is on the back of the side if it is behind BOTH faces,
//but if the side pokes in, a point is on the back if behind EITHER face.
if ( num_faces = = 2 ) {
fix dist ;
int side_count , center_count ;
# ifdef COMPACT_SEGS
vms_vector normals [ 2 ] ;
# endif
vertnum = min ( vertex_list [ 0 ] , vertex_list [ 2 ] ) ;
# ifdef COMPACT_SEGS
get_side_normals ( seg , sn , & normals [ 0 ] , & normals [ 1 ] ) ;
# endif
if ( vertex_list [ 4 ] < vertex_list [ 1 ] )
# ifdef COMPACT_SEGS
dist = vm_dist_to_plane ( & Vertices [ vertex_list [ 4 ] ] , & normals [ 0 ] , & Vertices [ vertnum ] ) ;
# else
dist = vm_dist_to_plane ( & Vertices [ vertex_list [ 4 ] ] , & s - > normals [ 0 ] , & Vertices [ vertnum ] ) ;
# endif
else
# ifdef COMPACT_SEGS
dist = vm_dist_to_plane ( & Vertices [ vertex_list [ 1 ] ] , & normals [ 1 ] , & Vertices [ vertnum ] ) ;
# else
dist = vm_dist_to_plane ( & Vertices [ vertex_list [ 1 ] ] , & s - > normals [ 1 ] , & Vertices [ vertnum ] ) ;
# endif
side_pokes_out = ( dist > PLANE_DIST_TOLERANCE ) ;
side_count = center_count = 0 ;
for ( fn = 0 ; fn < 2 ; fn + + , facebit < < = 1 ) {
# ifdef COMPACT_SEGS
dist = vm_dist_to_plane ( checkp , & normals [ fn ] , & Vertices [ vertnum ] ) ;
# else
dist = vm_dist_to_plane ( checkp , & s - > normals [ fn ] , & Vertices [ vertnum ] ) ;
# endif
if ( dist < - PLANE_DIST_TOLERANCE ) //in front of face
center_count + + ;
if ( dist - rad < - PLANE_DIST_TOLERANCE ) {
masks . facemask | = facebit ;
side_count + + ;
}
}
if ( ! side_pokes_out ) { //must be behind both faces
if ( side_count = = 2 )
masks . sidemask | = sidebit ;
if ( center_count = = 2 )
masks . centermask | = sidebit ;
}
else { //must be behind at least one face
if ( side_count )
masks . sidemask | = sidebit ;
if ( center_count )
masks . centermask | = sidebit ;
}
}
else { //only one face on this side
fix dist ;
int i ;
# ifdef COMPACT_SEGS
vms_vector normal ;
# endif
//use lowest point number
vertnum = vertex_list [ 0 ] ;
for ( i = 1 ; i < 4 ; i + + )
if ( vertex_list [ i ] < vertnum )
vertnum = vertex_list [ i ] ;
# ifdef COMPACT_SEGS
get_side_normal ( seg , sn , 0 , & normal ) ;
dist = vm_dist_to_plane ( checkp , & normal , & Vertices [ vertnum ] ) ;
# else
dist = vm_dist_to_plane ( checkp , & s - > normals [ 0 ] , & Vertices [ vertnum ] ) ;
# endif
if ( dist < - PLANE_DIST_TOLERANCE )
masks . centermask | = sidebit ;
if ( dist - rad < - PLANE_DIST_TOLERANCE ) {
masks . facemask | = facebit ;
masks . sidemask | = sidebit ;
}
facebit < < = 2 ;
}
}
return masks ;
}
//this was converted from get_seg_masks()...it fills in an array of 6
//elements for the distace behind each side, or zero if not behind
//only gets centermask, and assumes zero rad
ubyte get_side_dists ( vms_vector * checkp , int segnum , fix * side_dists )
{
int sn , facebit , sidebit ;
ubyte mask ;
int num_faces ;
int vertex_list [ 6 ] ;
segment * seg ;
Assert ( ( segnum < = Highest_segment_index ) & & ( segnum > = 0 ) ) ;
if ( segnum = = - 1 )
Error ( " segnum == -1 in get_seg_dists() " ) ;
seg = & Segments [ segnum ] ;
//check point against each side of segment. return bitmask
mask = 0 ;
for ( sn = 0 , facebit = sidebit = 1 ; sn < 6 ; sn + + , sidebit < < = 1 ) {
# ifndef COMPACT_SEGS
side * s = & seg - > sides [ sn ] ;
# endif
int side_pokes_out ;
int fn ;
side_dists [ sn ] = 0 ;
// Get number of faces on this side, and at vertex_list, store vertices.
// If one face, then vertex_list indicates a quadrilateral.
// If two faces, then 0,1,2 define one triangle, 3,4,5 define the second.
create_abs_vertex_lists ( & num_faces , vertex_list , segnum , sn ) ;
//ok...this is important. If a side has 2 faces, we need to know if
//those faces form a concave or convex side. If the side pokes out,
//then a point is on the back of the side if it is behind BOTH faces,
//but if the side pokes in, a point is on the back if behind EITHER face.
if ( num_faces = = 2 ) {
fix dist ;
int center_count ;
int vertnum ;
# ifdef COMPACT_SEGS
vms_vector normals [ 2 ] ;
# endif
vertnum = min ( vertex_list [ 0 ] , vertex_list [ 2 ] ) ;
# ifdef COMPACT_SEGS
get_side_normals ( seg , sn , & normals [ 0 ] , & normals [ 1 ] ) ;
# endif
if ( vertex_list [ 4 ] < vertex_list [ 1 ] )
# ifdef COMPACT_SEGS
dist = vm_dist_to_plane ( & Vertices [ vertex_list [ 4 ] ] , & normals [ 0 ] , & Vertices [ vertnum ] ) ;
# else
dist = vm_dist_to_plane ( & Vertices [ vertex_list [ 4 ] ] , & s - > normals [ 0 ] , & Vertices [ vertnum ] ) ;
# endif
else
# ifdef COMPACT_SEGS
dist = vm_dist_to_plane ( & Vertices [ vertex_list [ 1 ] ] , & normals [ 1 ] , & Vertices [ vertnum ] ) ;
# else
dist = vm_dist_to_plane ( & Vertices [ vertex_list [ 1 ] ] , & s - > normals [ 1 ] , & Vertices [ vertnum ] ) ;
# endif
side_pokes_out = ( dist > PLANE_DIST_TOLERANCE ) ;
center_count = 0 ;
for ( fn = 0 ; fn < 2 ; fn + + , facebit < < = 1 ) {
# ifdef COMPACT_SEGS
dist = vm_dist_to_plane ( checkp , & normals [ fn ] , & Vertices [ vertnum ] ) ;
# else
dist = vm_dist_to_plane ( checkp , & s - > normals [ fn ] , & Vertices [ vertnum ] ) ;
# endif
if ( dist < - PLANE_DIST_TOLERANCE ) { //in front of face
center_count + + ;
side_dists [ sn ] + = dist ;
}
}
if ( ! side_pokes_out ) { //must be behind both faces
if ( center_count = = 2 ) {
mask | = sidebit ;
side_dists [ sn ] / = 2 ; //get average
}
}
else { //must be behind at least one face
if ( center_count ) {
mask | = sidebit ;
if ( center_count = = 2 )
side_dists [ sn ] / = 2 ; //get average
}
}
}
else { //only one face on this side
fix dist ;
int i , vertnum ;
# ifdef COMPACT_SEGS
vms_vector normal ;
# endif
//use lowest point number
vertnum = vertex_list [ 0 ] ;
for ( i = 1 ; i < 4 ; i + + )
if ( vertex_list [ i ] < vertnum )
vertnum = vertex_list [ i ] ;
# ifdef COMPACT_SEGS
get_side_normal ( seg , sn , 0 , & normal ) ;
dist = vm_dist_to_plane ( checkp , & normal , & Vertices [ vertnum ] ) ;
# else
dist = vm_dist_to_plane ( checkp , & s - > normals [ 0 ] , & Vertices [ vertnum ] ) ;
# endif
if ( dist < - PLANE_DIST_TOLERANCE ) {
mask | = sidebit ;
side_dists [ sn ] = dist ;
}
facebit < < = 2 ;
}
}
return mask ;
}
# ifndef NDEBUG
# ifndef COMPACT_SEGS
//returns true if errors detected
int check_norms ( int segnum , int sidenum , int facenum , int csegnum , int csidenum , int cfacenum )
{
vms_vector * n0 , * n1 ;
n0 = & Segments [ segnum ] . sides [ sidenum ] . normals [ facenum ] ;
n1 = & Segments [ csegnum ] . sides [ csidenum ] . normals [ cfacenum ] ;
if ( n0 - > x ! = - n1 - > x | | n0 - > y ! = - n1 - > y | | n0 - > z ! = - n1 - > z ) {
mprintf ( ( 0 , " Seg %x, side %d, norm %d doesn't match seg %x, side %d, norm %d: \n "
" %8x %8x %8x \n "
" %8x %8x %8x (negated) \n " ,
segnum , sidenum , facenum , csegnum , csidenum , cfacenum ,
n0 - > x , n0 - > y , n0 - > z , - n1 - > x , - n1 - > y , - n1 - > z ) ) ;
return 1 ;
}
else
return 0 ;
}
//heavy-duty error checking
int check_segment_connections ( void )
{
int segnum , sidenum ;
int errors = 0 ;
for ( segnum = 0 ; segnum < = Highest_segment_index ; segnum + + ) {
segment * seg ;
seg = & Segments [ segnum ] ;
for ( sidenum = 0 ; sidenum < 6 ; sidenum + + ) {
side * s ;
segment * cseg ;
side * cs ;
int num_faces , csegnum , csidenum , con_num_faces ;
int vertex_list [ 6 ] , con_vertex_list [ 6 ] ;
s = & seg - > sides [ sidenum ] ;
create_abs_vertex_lists ( & num_faces , vertex_list , segnum , sidenum ) ;
csegnum = seg - > children [ sidenum ] ;
if ( csegnum > = 0 ) {
cseg = & Segments [ csegnum ] ;
csidenum = find_connect_side ( seg , cseg ) ;
if ( csidenum = = - 1 ) {
mprintf ( ( 0 , " Could not find connected side for seg %x back to seg %x, side %d \n " , csegnum , segnum , sidenum ) ) ;
errors = 1 ;
continue ;
}
cs = & cseg - > sides [ csidenum ] ;
create_abs_vertex_lists ( & con_num_faces , con_vertex_list , csegnum , csidenum ) ;
if ( con_num_faces ! = num_faces ) {
mprintf ( ( 0 , " Seg %x, side %d: num_faces (%d) mismatch with seg %x, side %d (%d) \n " , segnum , sidenum , num_faces , csegnum , csidenum , con_num_faces ) ) ;
errors = 1 ;
}
else
if ( num_faces = = 1 ) {
int t ;
for ( t = 0 ; t < 4 & & con_vertex_list [ t ] ! = vertex_list [ 0 ] ; t + + ) ;
if ( t = = 4 | |
vertex_list [ 0 ] ! = con_vertex_list [ t ] | |
vertex_list [ 1 ] ! = con_vertex_list [ ( t + 3 ) % 4 ] | |
vertex_list [ 2 ] ! = con_vertex_list [ ( t + 2 ) % 4 ] | |
vertex_list [ 3 ] ! = con_vertex_list [ ( t + 1 ) % 4 ] ) {
mprintf ( ( 0 , " Seg %x, side %d: vertex list mismatch with seg %x, side %d \n "
" %x %x %x %x \n "
" %x %x %x %x \n " ,
segnum , sidenum , csegnum , csidenum ,
vertex_list [ 0 ] , vertex_list [ 1 ] , vertex_list [ 2 ] , vertex_list [ 3 ] ,
con_vertex_list [ 0 ] , con_vertex_list [ 1 ] , con_vertex_list [ 2 ] , con_vertex_list [ 3 ] ) ) ;
errors = 1 ;
}
else
errors | = check_norms ( segnum , sidenum , 0 , csegnum , csidenum , 0 ) ;
}
else {
if ( vertex_list [ 1 ] = = con_vertex_list [ 1 ] ) {
if ( vertex_list [ 4 ] ! = con_vertex_list [ 4 ] | |
vertex_list [ 0 ] ! = con_vertex_list [ 2 ] | |
vertex_list [ 2 ] ! = con_vertex_list [ 0 ] | |
vertex_list [ 3 ] ! = con_vertex_list [ 5 ] | |
vertex_list [ 5 ] ! = con_vertex_list [ 3 ] ) {
mprintf ( ( 0 , " Seg %x, side %d: vertex list mismatch with seg %x, side %d \n "
" %x %x %x %x %x %x \n "
" %x %x %x %x %x %x \n " ,
segnum , sidenum , csegnum , csidenum ,
vertex_list [ 0 ] , vertex_list [ 1 ] , vertex_list [ 2 ] , vertex_list [ 3 ] , vertex_list [ 4 ] , vertex_list [ 5 ] ,
con_vertex_list [ 0 ] , con_vertex_list [ 1 ] , con_vertex_list [ 2 ] , con_vertex_list [ 3 ] , con_vertex_list [ 4 ] , con_vertex_list [ 5 ] ) ) ;
mprintf ( ( 0 , " Changing seg:side %4i:%i from %i to %i \n " , csegnum , csidenum , Segments [ csegnum ] . sides [ csidenum ] . type , 5 - Segments [ csegnum ] . sides [ csidenum ] . type ) ) ;
Segments [ csegnum ] . sides [ csidenum ] . type = 5 - Segments [ csegnum ] . sides [ csidenum ] . type ;
} else {
errors | = check_norms ( segnum , sidenum , 0 , csegnum , csidenum , 0 ) ;
errors | = check_norms ( segnum , sidenum , 1 , csegnum , csidenum , 1 ) ;
}
} else {
if ( vertex_list [ 1 ] ! = con_vertex_list [ 4 ] | |
vertex_list [ 4 ] ! = con_vertex_list [ 1 ] | |
vertex_list [ 0 ] ! = con_vertex_list [ 5 ] | |
vertex_list [ 5 ] ! = con_vertex_list [ 0 ] | |
vertex_list [ 2 ] ! = con_vertex_list [ 3 ] | |
vertex_list [ 3 ] ! = con_vertex_list [ 2 ] ) {
mprintf ( ( 0 , " Seg %x, side %d: vertex list mismatch with seg %x, side %d \n "
" %x %x %x %x %x %x \n "
" %x %x %x %x %x %x \n " ,
segnum , sidenum , csegnum , csidenum ,
vertex_list [ 0 ] , vertex_list [ 1 ] , vertex_list [ 2 ] , vertex_list [ 3 ] , vertex_list [ 4 ] , vertex_list [ 5 ] ,
con_vertex_list [ 0 ] , con_vertex_list [ 1 ] , con_vertex_list [ 2 ] , con_vertex_list [ 3 ] , con_vertex_list [ 4 ] , vertex_list [ 5 ] ) ) ;
mprintf ( ( 0 , " Changing seg:side %4i:%i from %i to %i \n " , csegnum , csidenum , Segments [ csegnum ] . sides [ csidenum ] . type , 5 - Segments [ csegnum ] . sides [ csidenum ] . type ) ) ;
Segments [ csegnum ] . sides [ csidenum ] . type = 5 - Segments [ csegnum ] . sides [ csidenum ] . type ;
} else {
errors | = check_norms ( segnum , sidenum , 0 , csegnum , csidenum , 1 ) ;
errors | = check_norms ( segnum , sidenum , 1 , csegnum , csidenum , 0 ) ;
}
}
}
}
}
}
// mprintf((0,"\n DONE \n"));
return errors ;
}
# endif
# endif
// Used to become a constant based on editor, but I wanted to be able to set
// this for omega blob find_point_seg calls. Would be better to pass a paremeter
// to the routine...--MK, 01/17/96
int Doing_lighting_hack_flag = 0 ;
//figure out what seg the given point is in, tracing through segments
//returns segment number, or -1 if can't find segment
int trace_segs ( vms_vector * p0 , int oldsegnum )
{
int centermask ;
segment * seg ;
fix side_dists [ 6 ] ;
Assert ( ( oldsegnum < = Highest_segment_index ) & & ( oldsegnum > = 0 ) ) ;
centermask = get_side_dists ( p0 , oldsegnum , side_dists ) ; //check old segment
if ( centermask = = 0 ) //we're in the old segment
return oldsegnum ; //..say so
else { //not in old seg. trace through to find seg
int biggest_side ;
do {
int sidenum , bit ;
fix biggest_val ;
seg = & Segments [ oldsegnum ] ;
biggest_side = - 1 ; biggest_val = 0 ;
for ( sidenum = 0 , bit = 1 ; sidenum < 6 ; sidenum + + , bit < < = 1 )
if ( ( centermask & bit ) & & ( seg - > children [ sidenum ] > - 1 ) )
if ( side_dists [ sidenum ] < biggest_val ) {
biggest_val = side_dists [ sidenum ] ;
biggest_side = sidenum ;
}
if ( biggest_side ! = - 1 ) {
int check ;
side_dists [ biggest_side ] = 0 ;
check = trace_segs ( p0 , seg - > children [ biggest_side ] ) ; //trace into adjacent segment
if ( check ! = - 1 ) //we've found a segment
return check ;
}
} while ( biggest_side ! = - 1 ) ;
return - 1 ; //we haven't found a segment
}
}
int Exhaustive_count = 0 , Exhaustive_failed_count = 0 ;
//Tries to find a segment for a point, in the following way:
// 1. Check the given segment
// 2. Recursively trace through attached segments
// 3. Check all the segmentns
//Returns segnum if found, or -1
int find_point_seg ( vms_vector * p , int segnum )
{
int newseg ;
//allow segnum==-1, meaning we have no idea what segment point is in
Assert ( ( segnum < = Highest_segment_index ) & & ( segnum > = - 1 ) ) ;
if ( segnum ! = - 1 ) {
newseg = trace_segs ( p , segnum ) ;
if ( newseg ! = - 1 ) //we found a segment!
return newseg ;
}
//couldn't find via attached segs, so search all segs
// MK: 10/15/94
// This Doing_lighting_hack_flag thing added by mk because the hundreds of scrolling messages were
// slowing down lighting, and in about 98% of cases, it would just return -1 anyway.
// Matt: This really should be fixed, though. We're probably screwing up our lighting in a few places.
if ( ! Doing_lighting_hack_flag ) {
mprintf ( ( 1 , " Warning: doing exhaustive search to find point segment (%i times) \n " , + + Exhaustive_count ) ) ;
for ( newseg = 0 ; newseg < = Highest_segment_index ; newseg + + )
if ( get_seg_masks ( p , newseg , 0 ) . centermask = = 0 )
return newseg ;
mprintf ( ( 1 , " Warning: could not find point segment (%i times) \n " , + + Exhaustive_failed_count ) ) ;
return - 1 ; //no segment found
} else
return - 1 ;
}
//--repair-- // ------------------------------------------------------------------------------
//--repair-- void clsd_repair_center(int segnum)
//--repair-- {
//--repair-- int sidenum;
//--repair--
//--repair-- // --- Set repair center bit for all repair center segments.
//--repair-- if (Segments[segnum].special == SEGMENT_IS_REPAIRCEN) {
//--repair-- Lsegments[segnum].special_type |= SS_REPAIR_CENTER;
//--repair-- Lsegments[segnum].special_segment = segnum;
//--repair-- }
//--repair--
//--repair-- // --- Set repair center bit for all segments adjacent to a repair center.
//--repair-- for (sidenum=0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++) {
//--repair-- int s = Segments[segnum].children[sidenum];
//--repair--
//--repair-- if ( (s != -1) && (Segments[s].special==SEGMENT_IS_REPAIRCEN) ) {
//--repair-- Lsegments[segnum].special_type |= SS_REPAIR_CENTER;
//--repair-- Lsegments[segnum].special_segment = s;
//--repair-- }
//--repair-- }
//--repair-- }
//--repair-- // ------------------------------------------------------------------------------
//--repair-- // --- Set destination points for all Materialization centers.
//--repair-- void clsd_materialization_center(int segnum)
//--repair-- {
//--repair-- if (Segments[segnum].special == SEGMENT_IS_ROBOTMAKER) {
//--repair--
//--repair-- }
//--repair-- }
//--repair--
//--repair-- int Lsegment_highest_segment_index, Lsegment_highest_vertex_index;
//--repair--
//--repair-- // ------------------------------------------------------------------------------
//--repair-- // Create data specific to mine which doesn't get written to disk.
//--repair-- // Highest_segment_index and Highest_object_index must be valid.
//--repair-- // 07/21: set repair center bit
//--repair-- void create_local_segment_data(void)
//--repair-- {
//--repair-- int segnum;
//--repair--
//--repair-- // --- Initialize all Lsegments.
//--repair-- for (segnum=0; segnum <= Highest_segment_index; segnum++) {
//--repair-- Lsegments[segnum].special_type = 0;
//--repair-- Lsegments[segnum].special_segment = -1;
//--repair-- }
//--repair--
//--repair-- for (segnum=0; segnum <= Highest_segment_index; segnum++) {
//--repair--
//--repair-- clsd_repair_center(segnum);
//--repair-- clsd_materialization_center(segnum);
//--repair--
//--repair-- }
//--repair--
//--repair-- // Set check variables.
//--repair-- // In main game loop, make sure these are valid, else Lsegments is not valid.
//--repair-- Lsegment_highest_segment_index = Highest_segment_index;
//--repair-- Lsegment_highest_vertex_index = Highest_vertex_index;
//--repair-- }
//--repair--
//--repair-- // ------------------------------------------------------------------------------------------
//--repair-- // Sort of makes sure create_local_segment_data has been called for the currently executing mine.
//--repair-- // It is not failsafe, as you will see if you look at the code.
//--repair-- // Returns 1 if Lsegments appears valid, 0 if not.
//--repair-- int check_lsegments_validity(void)
//--repair-- {
//--repair-- return ((Lsegment_highest_segment_index == Highest_segment_index) && (Lsegment_highest_vertex_index == Highest_vertex_index));
//--repair-- }
# define MAX_LOC_POINT_SEGS 64
int Connected_segment_distance ;
# define MIN_CACHE_FCD_DIST (F1_0*80) // Must be this far apart for cache lookup to succeed. Recognizes small changes in distance matter at small distances.
# define MAX_FCD_CACHE 8
typedef struct {
int seg0 , seg1 , csd ;
fix dist ;
} fcd_data ;
int Fcd_index = 0 ;
fcd_data Fcd_cache [ MAX_FCD_CACHE ] ;
fix Last_fcd_flush_time ;
// ----------------------------------------------------------------------------------------------------------
void flush_fcd_cache ( void )
{
int i ;
Fcd_index = 0 ;
for ( i = 0 ; i < MAX_FCD_CACHE ; i + + )
Fcd_cache [ i ] . seg0 = - 1 ;
}
// ----------------------------------------------------------------------------------------------------------
void add_to_fcd_cache ( int seg0 , int seg1 , int depth , fix dist )
{
if ( dist > MIN_CACHE_FCD_DIST ) {
Fcd_cache [ Fcd_index ] . seg0 = seg0 ;
Fcd_cache [ Fcd_index ] . seg1 = seg1 ;
Fcd_cache [ Fcd_index ] . csd = depth ;
Fcd_cache [ Fcd_index ] . dist = dist ;
Fcd_index + + ;
if ( Fcd_index > = MAX_FCD_CACHE )
Fcd_index = 0 ;
// -- mprintf((0, "Adding seg0=%i, seg1=%i to cache.\n", seg0, seg1));
} else {
// If it's in the cache, remove it.
int i ;
for ( i = 0 ; i < MAX_FCD_CACHE ; i + + )
if ( Fcd_cache [ i ] . seg0 = = seg0 )
if ( Fcd_cache [ i ] . seg1 = = seg1 ) {
Fcd_cache [ Fcd_index ] . seg0 = - 1 ;
break ;
}
}
}
// ----------------------------------------------------------------------------------------------------------
// Determine whether seg0 and seg1 are reachable in a way that allows sound to pass.
// Search up to a maximum depth of max_depth.
// Return the distance.
fix find_connected_distance ( vms_vector * p0 , int seg0 , vms_vector * p1 , int seg1 , int max_depth , int wid_flag )
{
int cur_seg ;
int sidenum ;
int qtail = 0 , qhead = 0 ;
int i ;
byte visited [ MAX_SEGMENTS ] ;
seg_seg seg_queue [ MAX_SEGMENTS ] ;
short depth [ MAX_SEGMENTS ] ;
int cur_depth ;
int num_points ;
point_seg point_segs [ MAX_LOC_POINT_SEGS ] ;
fix dist ;
// If > this, will overrun point_segs buffer
# ifdef WINDOWS
if ( max_depth = = - 1 ) max_depth = 200 ;
# endif
if ( max_depth > MAX_LOC_POINT_SEGS - 2 ) {
mprintf ( ( 1 , " Warning: In find_connected_distance, max_depth = %i, limited to %i \n " , max_depth , MAX_LOC_POINT_SEGS - 2 ) ) ;
max_depth = MAX_LOC_POINT_SEGS - 2 ;
}
if ( seg0 = = seg1 ) {
Connected_segment_distance = 0 ;
return vm_vec_dist_quick ( p0 , p1 ) ;
} else {
int conn_side ;
if ( ( conn_side = find_connect_side ( & Segments [ seg0 ] , & Segments [ seg1 ] ) ) ! = - 1 ) {
if ( WALL_IS_DOORWAY ( & Segments [ seg1 ] , conn_side ) & wid_flag ) {
Connected_segment_distance = 1 ;
//mprintf((0, "\n"));
return vm_vec_dist_quick ( p0 , p1 ) ;
}
}
}
// Periodically flush cache.
if ( ( GameTime - Last_fcd_flush_time > F1_0 * 2 ) | | ( GameTime < Last_fcd_flush_time ) ) {
flush_fcd_cache ( ) ;
Last_fcd_flush_time = GameTime ;
}
// Can't quickly get distance, so see if in Fcd_cache.
for ( i = 0 ; i < MAX_FCD_CACHE ; i + + )
if ( ( Fcd_cache [ i ] . seg0 = = seg0 ) & & ( Fcd_cache [ i ] . seg1 = = seg1 ) ) {
Connected_segment_distance = Fcd_cache [ i ] . csd ;
// -- mprintf((0, "In cache, seg0=%i, seg1=%i. Returning.\n", seg0, seg1));
return Fcd_cache [ i ] . dist ;
}
num_points = 0 ;
memset ( visited , 0 , Highest_segment_index + 1 ) ;
memset ( depth , 0 , sizeof ( depth [ 0 ] ) * ( Highest_segment_index + 1 ) ) ;
cur_seg = seg0 ;
visited [ cur_seg ] = 1 ;
cur_depth = 0 ;
while ( cur_seg ! = seg1 ) {
segment * segp = & Segments [ cur_seg ] ;
for ( sidenum = 0 ; sidenum < MAX_SIDES_PER_SEGMENT ; sidenum + + ) {
int snum = sidenum ;
if ( WALL_IS_DOORWAY ( segp , snum ) & wid_flag ) {
int this_seg = segp - > children [ snum ] ;
if ( ! visited [ this_seg ] ) {
seg_queue [ qtail ] . start = cur_seg ;
seg_queue [ qtail ] . end = this_seg ;
visited [ this_seg ] = 1 ;
depth [ qtail + + ] = cur_depth + 1 ;
if ( max_depth ! = - 1 ) {
if ( depth [ qtail - 1 ] = = max_depth ) {
Connected_segment_distance = 1000 ;
add_to_fcd_cache ( seg0 , seg1 , Connected_segment_distance , F1_0 * 1000 ) ;
return - 1 ;
}
} else if ( this_seg = = seg1 ) {
goto fcd_done1 ;
}
}
}
} // for (sidenum...
if ( qhead > = qtail ) {
Connected_segment_distance = 1000 ;
add_to_fcd_cache ( seg0 , seg1 , Connected_segment_distance , F1_0 * 1000 ) ;
return - 1 ;
}
cur_seg = seg_queue [ qhead ] . end ;
cur_depth = depth [ qhead ] ;
qhead + + ;
fcd_done1 : ;
} // while (cur_seg ...
// Set qtail to the segment which ends at the goal.
while ( seg_queue [ - - qtail ] . end ! = seg1 )
if ( qtail < 0 ) {
Connected_segment_distance = 1000 ;
add_to_fcd_cache ( seg0 , seg1 , Connected_segment_distance , F1_0 * 1000 ) ;
return - 1 ;
}
while ( qtail > = 0 ) {
int parent_seg , this_seg ;
this_seg = seg_queue [ qtail ] . end ;
parent_seg = seg_queue [ qtail ] . start ;
point_segs [ num_points ] . segnum = this_seg ;
compute_segment_center ( & point_segs [ num_points ] . point , & Segments [ this_seg ] ) ;
num_points + + ;
if ( parent_seg = = seg0 )
break ;
while ( seg_queue [ - - qtail ] . end ! = parent_seg )
Assert ( qtail > = 0 ) ;
}
point_segs [ num_points ] . segnum = seg0 ;
compute_segment_center ( & point_segs [ num_points ] . point , & Segments [ seg0 ] ) ;
num_points + + ;
if ( num_points = = 1 ) {
Connected_segment_distance = num_points ;
return vm_vec_dist_quick ( p0 , p1 ) ;
} else {
dist = vm_vec_dist_quick ( p1 , & point_segs [ 1 ] . point ) ;
dist + = vm_vec_dist_quick ( p0 , & point_segs [ num_points - 2 ] . point ) ;
for ( i = 1 ; i < num_points - 2 ; i + + ) {
fix ndist ;
ndist = vm_vec_dist_quick ( & point_segs [ i ] . point , & point_segs [ i + 1 ] . point ) ;
dist + = ndist ;
}
}
Connected_segment_distance = num_points ;
add_to_fcd_cache ( seg0 , seg1 , num_points , dist ) ;
return dist ;
}
byte convert_to_byte ( fix f )
{
if ( f > = 0x00010000 )
return MATRIX_MAX ;
else if ( f < = - 0x00010000 )
return - MATRIX_MAX ;
else
return f > > MATRIX_PRECISION ;
}
# define VEL_PRECISION 12
// Create a shortpos struct from an object.
// Extract the matrix into byte values.
// Create a position relative to vertex 0 with 1/256 normal "fix" precision.
// Stuff segment in a short.
void create_shortpos ( shortpos * spp , object * objp , int swap_bytes )
{
// int segnum;
byte * sp ;
sp = spp - > bytemat ;
* sp + + = convert_to_byte ( objp - > orient . rvec . x ) ;
* sp + + = convert_to_byte ( objp - > orient . uvec . x ) ;
* sp + + = convert_to_byte ( objp - > orient . fvec . x ) ;
* sp + + = convert_to_byte ( objp - > orient . rvec . y ) ;
* sp + + = convert_to_byte ( objp - > orient . uvec . y ) ;
* sp + + = convert_to_byte ( objp - > orient . fvec . y ) ;
* sp + + = convert_to_byte ( objp - > orient . rvec . z ) ;
* sp + + = convert_to_byte ( objp - > orient . uvec . z ) ;
* sp + + = convert_to_byte ( objp - > orient . fvec . z ) ;
spp - > xo = ( objp - > pos . x - Vertices [ Segments [ objp - > segnum ] . verts [ 0 ] ] . x ) > > RELPOS_PRECISION ;
spp - > yo = ( objp - > pos . y - Vertices [ Segments [ objp - > segnum ] . verts [ 0 ] ] . y ) > > RELPOS_PRECISION ;
spp - > zo = ( objp - > pos . z - Vertices [ Segments [ objp - > segnum ] . verts [ 0 ] ] . z ) > > RELPOS_PRECISION ;
spp - > segment = objp - > segnum ;
spp - > velx = ( objp - > mtype . phys_info . velocity . x ) > > VEL_PRECISION ;
spp - > vely = ( objp - > mtype . phys_info . velocity . y ) > > VEL_PRECISION ;
spp - > velz = ( objp - > mtype . phys_info . velocity . z ) > > VEL_PRECISION ;
// swap the short values for the big-endian machines.
if ( swap_bytes ) {
spp - > xo = INTEL_SHORT ( spp - > xo ) ;
spp - > yo = INTEL_SHORT ( spp - > yo ) ;
spp - > zo = INTEL_SHORT ( spp - > zo ) ;
spp - > segment = INTEL_SHORT ( spp - > segment ) ;
spp - > velx = INTEL_SHORT ( spp - > velx ) ;
spp - > vely = INTEL_SHORT ( spp - > vely ) ;
spp - > velz = INTEL_SHORT ( spp - > velz ) ;
}
// mprintf((0, "Matrix: %08x %08x %08x %08x %08x %08x\n", objp->orient.m1,objp->orient.m2,objp->orient.m3,
// spp->bytemat[0] << MATRIX_PRECISION,spp->bytemat[1] << MATRIX_PRECISION,spp->bytemat[2] << MATRIX_PRECISION));
//
// mprintf((0, " %08x %08x %08x %08x %08x %08x\n", objp->orient.m4,objp->orient.m5,objp->orient.m6,
// spp->bytemat[3] << MATRIX_PRECISION,spp->bytemat[4] << MATRIX_PRECISION,spp->bytemat[5] << MATRIX_PRECISION));
//
// mprintf((0, " %08x %08x %08x %08x %08x %08x\n", objp->orient.m7,objp->orient.m8,objp->orient.m9,
// spp->bytemat[6] << MATRIX_PRECISION,spp->bytemat[7] << MATRIX_PRECISION,spp->bytemat[8] << MATRIX_PRECISION));
//
// mprintf((0, "Positn: %08x %08x %08x %08x %08x %08x\n", objp->pos.x, objp->pos.y, objp->pos.z,
// (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x,
// (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y,
// (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z));
// mprintf((0, "Segment: %3i %3i\n", objp->segnum, spp->segment));
}
void extract_shortpos ( object * objp , shortpos * spp , int swap_bytes )
{
int segnum ;
byte * sp ;
sp = spp - > bytemat ;
objp - > orient . rvec . x = * sp + + < < MATRIX_PRECISION ;
objp - > orient . uvec . x = * sp + + < < MATRIX_PRECISION ;
objp - > orient . fvec . x = * sp + + < < MATRIX_PRECISION ;
objp - > orient . rvec . y = * sp + + < < MATRIX_PRECISION ;
objp - > orient . uvec . y = * sp + + < < MATRIX_PRECISION ;
objp - > orient . fvec . y = * sp + + < < MATRIX_PRECISION ;
objp - > orient . rvec . z = * sp + + < < MATRIX_PRECISION ;
objp - > orient . uvec . z = * sp + + < < MATRIX_PRECISION ;
objp - > orient . fvec . z = * sp + + < < MATRIX_PRECISION ;
if ( swap_bytes ) {
spp - > xo = INTEL_SHORT ( spp - > xo ) ;
spp - > yo = INTEL_SHORT ( spp - > yo ) ;
spp - > zo = INTEL_SHORT ( spp - > zo ) ;
spp - > segment = INTEL_SHORT ( spp - > segment ) ;
spp - > velx = INTEL_SHORT ( spp - > velx ) ;
spp - > vely = INTEL_SHORT ( spp - > vely ) ;
spp - > velz = INTEL_SHORT ( spp - > velz ) ;
}
segnum = spp - > segment ;
Assert ( ( segnum > = 0 ) & & ( segnum < = Highest_segment_index ) ) ;
objp - > pos . x = ( spp - > xo < < RELPOS_PRECISION ) + Vertices [ Segments [ segnum ] . verts [ 0 ] ] . x ;
objp - > pos . y = ( spp - > yo < < RELPOS_PRECISION ) + Vertices [ Segments [ segnum ] . verts [ 0 ] ] . y ;
objp - > pos . z = ( spp - > zo < < RELPOS_PRECISION ) + Vertices [ Segments [ segnum ] . verts [ 0 ] ] . z ;
objp - > mtype . phys_info . velocity . x = ( spp - > velx < < VEL_PRECISION ) ;
objp - > mtype . phys_info . velocity . y = ( spp - > vely < < VEL_PRECISION ) ;
objp - > mtype . phys_info . velocity . z = ( spp - > velz < < VEL_PRECISION ) ;
obj_relink ( objp - Objects , segnum ) ;
// mprintf((0, "Matrix: %08x %08x %08x %08x %08x %08x\n", objp->orient.m1,objp->orient.m2,objp->orient.m3,
// spp->bytemat[0],spp->bytemat[1],spp->bytemat[2]));
//
// mprintf((0, " %08x %08x %08x %08x %08x %08x\n", objp->orient.m4,objp->orient.m5,objp->orient.m6,
// spp->bytemat[3],spp->bytemat[4],spp->bytemat[5]));
//
// mprintf((0, " %08x %08x %08x %08x %08x %08x\n", objp->orient.m7,objp->orient.m8,objp->orient.m9,
// spp->bytemat[6],spp->bytemat[7],spp->bytemat[8]));
//
// mprintf((0, "Positn: %08x %08x %08x %08x %08x %08x\n", objp->pos.x, objp->pos.y, objp->pos.z,
// (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x, (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y, (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z));
// mprintf((0, "Segment: %3i %3i\n", objp->segnum, spp->segment));
}
//--unused-- void test_shortpos(void)
//--unused-- {
//--unused-- shortpos spp;
//--unused--
//--unused-- create_shortpos(&spp, &Objects[0]);
//--unused-- extract_shortpos(&Objects[0], &spp);
//--unused--
//--unused-- }
// -----------------------------------------------------------------------------
// Segment validation functions.
// Moved from editor to game so we can compute surface normals at load time.
// -------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------
// Extract a vector from a segment. The vector goes from the start face to the end face.
// The point on each face is the average of the four points forming the face.
void extract_vector_from_segment ( segment * sp , vms_vector * vp , int start , int end )
{
int i ;
vms_vector vs , ve ;
vm_vec_zero ( & vs ) ;
vm_vec_zero ( & ve ) ;
for ( i = 0 ; i < 4 ; i + + ) {
vm_vec_add2 ( & vs , & Vertices [ sp - > verts [ Side_to_verts [ start ] [ i ] ] ] ) ;
vm_vec_add2 ( & ve , & Vertices [ sp - > verts [ Side_to_verts [ end ] [ i ] ] ] ) ;
}
vm_vec_sub ( vp , & ve , & vs ) ;
vm_vec_scale ( vp , F1_0 / 4 ) ;
}
//create a matrix that describes the orientation of the given segment
void extract_orient_from_segment ( vms_matrix * m , segment * seg )
{
vms_vector fvec , uvec ;
extract_vector_from_segment ( seg , & fvec , WFRONT , WBACK ) ;
extract_vector_from_segment ( seg , & uvec , WBOTTOM , WTOP ) ;
//vector to matrix does normalizations and orthogonalizations
vm_vector_2_matrix ( m , & fvec , & uvec , NULL ) ;
}
# ifdef EDITOR
// ------------------------------------------------------------------------------------------
// Extract the forward vector from segment *sp, return in *vp.
// The forward vector is defined to be the vector from the the center of the front face of the segment
// to the center of the back face of the segment.
void extract_forward_vector_from_segment ( segment * sp , vms_vector * vp )
{
extract_vector_from_segment ( sp , vp , WFRONT , WBACK ) ;
}
// ------------------------------------------------------------------------------------------
// Extract the right vector from segment *sp, return in *vp.
// The forward vector is defined to be the vector from the the center of the left face of the segment
// to the center of the right face of the segment.
void extract_right_vector_from_segment ( segment * sp , vms_vector * vp )
{
extract_vector_from_segment ( sp , vp , WLEFT , WRIGHT ) ;
}
// ------------------------------------------------------------------------------------------
// Extract the up vector from segment *sp, return in *vp.
// The forward vector is defined to be the vector from the the center of the bottom face of the segment
// to the center of the top face of the segment.
void extract_up_vector_from_segment ( segment * sp , vms_vector * vp )
{
extract_vector_from_segment ( sp , vp , WBOTTOM , WTOP ) ;
}
# endif
void add_side_as_quad ( segment * sp , int sidenum , vms_vector * normal )
{
side * sidep = & sp - > sides [ sidenum ] ;
sidep - > type = SIDE_IS_QUAD ;
# ifdef COMPACT_SEGS
normal = normal ; //avoid compiler warning
# else
sidep - > normals [ 0 ] = * normal ;
sidep - > normals [ 1 ] = * normal ;
# endif
// If there is a connection here, we only formed the faces for the purpose of determining segment boundaries,
// so don't generate polys, else they will get rendered.
// if (sp->children[sidenum] != -1)
// sidep->render_flag = 0;
// else
// sidep->render_flag = 1;
}
// -------------------------------------------------------------------------------
// Return v0, v1, v2 = 3 vertices with smallest numbers. If *negate_flag set, then negate normal after computation.
// Note, you cannot just compute the normal by treating the points in the opposite direction as this introduces
// small differences between normals which should merely be opposites of each other.
void get_verts_for_normal ( int va , int vb , int vc , int vd , int * v0 , int * v1 , int * v2 , int * v3 , int * negate_flag )
{
int i , j ;
int v [ 4 ] , w [ 4 ] ;
// w is a list that shows how things got scrambled so we know if our normal is pointing backwards
for ( i = 0 ; i < 4 ; i + + )
w [ i ] = i ;
v [ 0 ] = va ;
v [ 1 ] = vb ;
v [ 2 ] = vc ;
v [ 3 ] = vd ;
for ( i = 1 ; i < 4 ; i + + )
for ( j = 0 ; j < i ; j + + )
if ( v [ j ] > v [ i ] ) {
int t ;
t = v [ j ] ; v [ j ] = v [ i ] ; v [ i ] = t ;
t = w [ j ] ; w [ j ] = w [ i ] ; w [ i ] = t ;
}
Assert ( ( v [ 0 ] < v [ 1 ] ) & & ( v [ 1 ] < v [ 2 ] ) & & ( v [ 2 ] < v [ 3 ] ) ) ;
// Now, if for any w[i] & w[i+1]: w[i+1] = (w[i]+3)%4, then must swap
* v0 = v [ 0 ] ;
* v1 = v [ 1 ] ;
* v2 = v [ 2 ] ;
* v3 = v [ 3 ] ;
if ( ( ( ( w [ 0 ] + 3 ) % 4 ) = = w [ 1 ] ) | | ( ( ( w [ 1 ] + 3 ) % 4 ) = = w [ 2 ] ) )
* negate_flag = 1 ;
else
* negate_flag = 0 ;
}
// -------------------------------------------------------------------------------
void add_side_as_2_triangles ( segment * sp , int sidenum )
{
vms_vector norm ;
byte * vs = Side_to_verts [ sidenum ] ;
fix dot ;
vms_vector vec_13 ; // vector from vertex 1 to vertex 3
side * sidep = & sp - > sides [ sidenum ] ;
// Choose how to triangulate.
// If a wall, then
// Always triangulate so segment is convex.
// Use Matt's formula: Na . AD > 0, where ABCD are vertices on side, a is face formed by A,B,C, Na is normal from face a.
// If not a wall, then triangulate so whatever is on the other side is triangulated the same (ie, between the same absoluate vertices)
if ( ! IS_CHILD ( sp - > children [ sidenum ] ) ) {
vm_vec_normal ( & norm , & Vertices [ sp - > verts [ vs [ 0 ] ] ] , & Vertices [ sp - > verts [ vs [ 1 ] ] ] , & Vertices [ sp - > verts [ vs [ 2 ] ] ] ) ;
vm_vec_sub ( & vec_13 , & Vertices [ sp - > verts [ vs [ 3 ] ] ] , & Vertices [ sp - > verts [ vs [ 1 ] ] ] ) ;
dot = vm_vec_dot ( & norm , & vec_13 ) ;
// Now, signifiy whether to triangulate from 0:2 or 1:3
if ( dot > = 0 )
sidep - > type = SIDE_IS_TRI_02 ;
else
sidep - > type = SIDE_IS_TRI_13 ;
# ifndef COMPACT_SEGS
// Now, based on triangulation type, set the normals.
if ( sidep - > type = = SIDE_IS_TRI_02 ) {
vm_vec_normal ( & norm , & Vertices [ sp - > verts [ vs [ 0 ] ] ] , & Vertices [ sp - > verts [ vs [ 1 ] ] ] , & Vertices [ sp - > verts [ vs [ 2 ] ] ] ) ;
sidep - > normals [ 0 ] = norm ;
vm_vec_normal ( & norm , & Vertices [ sp - > verts [ vs [ 0 ] ] ] , & Vertices [ sp - > verts [ vs [ 2 ] ] ] , & Vertices [ sp - > verts [ vs [ 3 ] ] ] ) ;
sidep - > normals [ 1 ] = norm ;
} else {
vm_vec_normal ( & norm , & Vertices [ sp - > verts [ vs [ 0 ] ] ] , & Vertices [ sp - > verts [ vs [ 1 ] ] ] , & Vertices [ sp - > verts [ vs [ 3 ] ] ] ) ;
sidep - > normals [ 0 ] = norm ;
vm_vec_normal ( & norm , & Vertices [ sp - > verts [ vs [ 1 ] ] ] , & Vertices [ sp - > verts [ vs [ 2 ] ] ] , & Vertices [ sp - > verts [ vs [ 3 ] ] ] ) ;
sidep - > normals [ 1 ] = norm ;
}
# endif
} else {
int i , v [ 4 ] , vsorted [ 4 ] ;
int negate_flag ;
for ( i = 0 ; i < 4 ; i + + )
v [ i ] = sp - > verts [ vs [ i ] ] ;
get_verts_for_normal ( v [ 0 ] , v [ 1 ] , v [ 2 ] , v [ 3 ] , & vsorted [ 0 ] , & vsorted [ 1 ] , & vsorted [ 2 ] , & vsorted [ 3 ] , & negate_flag ) ;
if ( ( vsorted [ 0 ] = = v [ 0 ] ) | | ( vsorted [ 0 ] = = v [ 2 ] ) ) {
sidep - > type = SIDE_IS_TRI_02 ;
# ifndef COMPACT_SEGS
// Now, get vertices for normal for each triangle based on triangulation type.
get_verts_for_normal ( v [ 0 ] , v [ 1 ] , v [ 2 ] , 32767 , & vsorted [ 0 ] , & vsorted [ 1 ] , & vsorted [ 2 ] , & vsorted [ 3 ] , & negate_flag ) ;
vm_vec_normal ( & norm , & Vertices [ vsorted [ 0 ] ] , & Vertices [ vsorted [ 1 ] ] , & Vertices [ vsorted [ 2 ] ] ) ;
if ( negate_flag )
vm_vec_negate ( & norm ) ;
sidep - > normals [ 0 ] = norm ;
get_verts_for_normal ( v [ 0 ] , v [ 2 ] , v [ 3 ] , 32767 , & vsorted [ 0 ] , & vsorted [ 1 ] , & vsorted [ 2 ] , & vsorted [ 3 ] , & negate_flag ) ;
vm_vec_normal ( & norm , & Vertices [ vsorted [ 0 ] ] , & Vertices [ vsorted [ 1 ] ] , & Vertices [ vsorted [ 2 ] ] ) ;
if ( negate_flag )
vm_vec_negate ( & norm ) ;
sidep - > normals [ 1 ] = norm ;
# endif
} else {
sidep - > type = SIDE_IS_TRI_13 ;
# ifndef COMPACT_SEGS
// Now, get vertices for normal for each triangle based on triangulation type.
get_verts_for_normal ( v [ 0 ] , v [ 1 ] , v [ 3 ] , 32767 , & vsorted [ 0 ] , & vsorted [ 1 ] , & vsorted [ 2 ] , & vsorted [ 3 ] , & negate_flag ) ;
vm_vec_normal ( & norm , & Vertices [ vsorted [ 0 ] ] , & Vertices [ vsorted [ 1 ] ] , & Vertices [ vsorted [ 2 ] ] ) ;
if ( negate_flag )
vm_vec_negate ( & norm ) ;
sidep - > normals [ 0 ] = norm ;
get_verts_for_normal ( v [ 1 ] , v [ 2 ] , v [ 3 ] , 32767 , & vsorted [ 0 ] , & vsorted [ 1 ] , & vsorted [ 2 ] , & vsorted [ 3 ] , & negate_flag ) ;
vm_vec_normal ( & norm , & Vertices [ vsorted [ 0 ] ] , & Vertices [ vsorted [ 1 ] ] , & Vertices [ vsorted [ 2 ] ] ) ;
if ( negate_flag )
vm_vec_negate ( & norm ) ;
sidep - > normals [ 1 ] = norm ;
# endif
}
}
}
int sign ( fix v )
{
if ( v > PLANE_DIST_TOLERANCE )
return 1 ;
else if ( v < - ( PLANE_DIST_TOLERANCE + 1 ) ) //neg & pos round differently
return - 1 ;
else
return 0 ;
}
// -------------------------------------------------------------------------------
void create_walls_on_side ( segment * sp , int sidenum )
{
int vm0 , vm1 , vm2 , vm3 , negate_flag ;
int v0 , v1 , v2 , v3 ;
vms_vector vn ;
fix dist_to_plane ;
v0 = sp - > verts [ Side_to_verts [ sidenum ] [ 0 ] ] ;
v1 = sp - > verts [ Side_to_verts [ sidenum ] [ 1 ] ] ;
v2 = sp - > verts [ Side_to_verts [ sidenum ] [ 2 ] ] ;
v3 = sp - > verts [ Side_to_verts [ sidenum ] [ 3 ] ] ;
get_verts_for_normal ( v0 , v1 , v2 , v3 , & vm0 , & vm1 , & vm2 , & vm3 , & negate_flag ) ;
vm_vec_normal ( & vn , & Vertices [ vm0 ] , & Vertices [ vm1 ] , & Vertices [ vm2 ] ) ;
dist_to_plane = abs ( vm_dist_to_plane ( & Vertices [ vm3 ] , & vn , & Vertices [ vm0 ] ) ) ;
//if ((sp-Segments == 0x7b) && (sidenum == 3)) {
// mprintf((0, "Verts = %3i %3i %3i %3i, negate flag = %3i, dist = %8x\n", vm0, vm1, vm2, vm3, negate_flag, dist_to_plane));
// mprintf((0, " Normal = %8x %8x %8x\n", vn.x, vn.y, vn.z));
// mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm0, Vertices[vm0].x, Vertices[vm0].y, Vertices[vm0].z));
// mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm1, Vertices[vm1].x, Vertices[vm1].y, Vertices[vm1].z));
// mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm2, Vertices[vm2].x, Vertices[vm2].y, Vertices[vm2].z));
// mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm3, Vertices[vm3].x, Vertices[vm3].y, Vertices[vm3].z));
//}
//if ((sp-Segments == 0x86) && (sidenum == 5)) {
// mprintf((0, "Verts = %3i %3i %3i %3i, negate flag = %3i, dist = %8x\n", vm0, vm1, vm2, vm3, negate_flag, dist_to_plane));
// mprintf((0, " Normal = %8x %8x %8x\n", vn.x, vn.y, vn.z));
// mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm0, Vertices[vm0].x, Vertices[vm0].y, Vertices[vm0].z));
// mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm1, Vertices[vm1].x, Vertices[vm1].y, Vertices[vm1].z));
// mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm2, Vertices[vm2].x, Vertices[vm2].y, Vertices[vm2].z));
// mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm3, Vertices[vm3].x, Vertices[vm3].y, Vertices[vm3].z));
//}
if ( negate_flag )
vm_vec_negate ( & vn ) ;
if ( dist_to_plane < = PLANE_DIST_TOLERANCE )
add_side_as_quad ( sp , sidenum , & vn ) ;
else {
add_side_as_2_triangles ( sp , sidenum ) ;
//this code checks to see if we really should be triangulated, and
//de-triangulates if we shouldn't be.
{
int num_faces ;
int vertex_list [ 6 ] ;
fix dist0 , dist1 ;
int s0 , s1 ;
int vertnum ;
side * s ;
create_abs_vertex_lists ( & num_faces , vertex_list , sp - Segments , sidenum ) ;
Assert ( num_faces = = 2 ) ;
s = & sp - > sides [ sidenum ] ;
vertnum = min ( vertex_list [ 0 ] , vertex_list [ 2 ] ) ;
# ifdef COMPACT_SEGS
{
vms_vector normals [ 2 ] ;
get_side_normals ( sp , sidenum , & normals [ 0 ] , & normals [ 1 ] ) ;
dist0 = vm_dist_to_plane ( & Vertices [ vertex_list [ 1 ] ] , & normals [ 1 ] , & Vertices [ vertnum ] ) ;
dist1 = vm_dist_to_plane ( & Vertices [ vertex_list [ 4 ] ] , & normals [ 0 ] , & Vertices [ vertnum ] ) ;
}
# else
dist0 = vm_dist_to_plane ( & Vertices [ vertex_list [ 1 ] ] , & s - > normals [ 1 ] , & Vertices [ vertnum ] ) ;
dist1 = vm_dist_to_plane ( & Vertices [ vertex_list [ 4 ] ] , & s - > normals [ 0 ] , & Vertices [ vertnum ] ) ;
# endif
s0 = sign ( dist0 ) ;
s1 = sign ( dist1 ) ;
if ( s0 = = 0 | | s1 = = 0 | | s0 ! = s1 ) {
sp - > sides [ sidenum ] . type = SIDE_IS_QUAD ; //detriangulate!
# ifndef COMPACT_SEGS
sp - > sides [ sidenum ] . normals [ 0 ] = vn ;
sp - > sides [ sidenum ] . normals [ 1 ] = vn ;
# endif
}
}
}
}
# ifdef COMPACT_SEGS
//#define CACHE_DEBUG 1
# define MAX_CACHE_NORMALS 128
# define CACHE_MASK 127
typedef struct ncache_element {
short segnum ;
ubyte sidenum ;
vms_vector normals [ 2 ] ;
} ncache_element ;
int ncache_initialized = 0 ;
ncache_element ncache [ MAX_CACHE_NORMALS ] ;
# ifdef CACHE_DEBUG
int ncache_counter = 0 ;
int ncache_hits = 0 ;
int ncache_misses = 0 ;
# endif
void ncache_init ( )
{
ncache_flush ( ) ;
ncache_initialized = 1 ;
}
void ncache_flush ( )
{
int i ;
for ( i = 0 ; i < MAX_CACHE_NORMALS ; i + + ) {
ncache [ i ] . segnum = - 1 ;
}
}
// -------------------------------------------------------------------------------
int find_ncache_element ( int segnum , int sidenum , int face_flags )
{
uint i ;
if ( ! ncache_initialized ) ncache_init ( ) ;
# ifdef CACHE_DEBUG
if ( ( ( + + ncache_counter % 5000 ) = = 1 ) & & ( ncache_hits + ncache_misses > 0 ) )
mprintf ( ( 0 , " NCACHE %d%% missed, H:%d, M:%d \n " , ( ncache_misses * 100 ) / ( ncache_hits + ncache_misses ) , ncache_hits , ncache_misses ) ) ;
# endif
i = ( ( segnum < < 2 ) ^ sidenum ) & CACHE_MASK ;
if ( ( ncache [ i ] . segnum = = segnum ) & & ( ( ncache [ i ] . sidenum & 0xf ) = = sidenum ) ) {
uint f1 ;
# ifdef CACHE_DEBUG
ncache_hits + + ;
# endif
f1 = ncache [ i ] . sidenum > > 4 ;
if ( ( f1 & face_flags ) = = face_flags )
return i ;
if ( f1 & 1 )
uncached_get_side_normal ( & Segments [ segnum ] , sidenum , 1 , & ncache [ i ] . normals [ 1 ] ) ;
else
uncached_get_side_normal ( & Segments [ segnum ] , sidenum , 0 , & ncache [ i ] . normals [ 0 ] ) ;
ncache [ i ] . sidenum | = face_flags < < 4 ;
return i ;
}
# ifdef CACHE_DEBUG
ncache_misses + + ;
# endif
switch ( face_flags ) {
case 1 :
uncached_get_side_normal ( & Segments [ segnum ] , sidenum , 0 , & ncache [ i ] . normals [ 0 ] ) ;
break ;
case 2 :
uncached_get_side_normal ( & Segments [ segnum ] , sidenum , 1 , & ncache [ i ] . normals [ 1 ] ) ;
break ;
case 3 :
uncached_get_side_normals ( & Segments [ segnum ] , sidenum , & ncache [ i ] . normals [ 0 ] , & ncache [ i ] . normals [ 1 ] ) ;
break ;
}
ncache [ i ] . segnum = segnum ;
ncache [ i ] . sidenum = sidenum | ( face_flags < < 4 ) ;
return i ;
}
void get_side_normal ( segment * sp , int sidenum , int face_num , vms_vector * vm )
{
int i ;
i = find_ncache_element ( sp - Segments , sidenum , 1 < < face_num ) ;
* vm = ncache [ i ] . normals [ face_num ] ;
if ( 0 ) {
vms_vector tmp ;
uncached_get_side_normal ( sp , sidenum , face_num , & tmp ) ;
Assert ( tmp . x = = vm - > x ) ;
Assert ( tmp . y = = vm - > y ) ;
Assert ( tmp . z = = vm - > z ) ;
}
}
void get_side_normals ( segment * sp , int sidenum , vms_vector * vm1 , vms_vector * vm2 )
{
int i ;
i = find_ncache_element ( sp - Segments , sidenum , 3 ) ;
* vm1 = ncache [ i ] . normals [ 0 ] ;
* vm2 = ncache [ i ] . normals [ 1 ] ;
if ( 0 ) {
vms_vector tmp ;
uncached_get_side_normal ( sp , sidenum , 0 , & tmp ) ;
Assert ( tmp . x = = vm1 - > x ) ;
Assert ( tmp . y = = vm1 - > y ) ;
Assert ( tmp . z = = vm1 - > z ) ;
uncached_get_side_normal ( sp , sidenum , 1 , & tmp ) ;
Assert ( tmp . x = = vm2 - > x ) ;
Assert ( tmp . y = = vm2 - > y ) ;
Assert ( tmp . z = = vm2 - > z ) ;
}
}
void uncached_get_side_normal ( segment * sp , int sidenum , int face_num , vms_vector * vm )
{
int vm0 , vm1 , vm2 , vm3 , negate_flag ;
char * vs = Side_to_verts [ sidenum ] ;
switch ( sp - > sides [ sidenum ] . type ) {
case SIDE_IS_QUAD :
get_verts_for_normal ( sp - > verts [ vs [ 0 ] ] , sp - > verts [ vs [ 1 ] ] , sp - > verts [ vs [ 2 ] ] , sp - > verts [ vs [ 3 ] ] , & vm0 , & vm1 , & vm2 , & vm3 , & negate_flag ) ;
vm_vec_normal ( vm , & Vertices [ vm0 ] , & Vertices [ vm1 ] , & Vertices [ vm2 ] ) ;
if ( negate_flag )
vm_vec_negate ( vm ) ;
break ;
case SIDE_IS_TRI_02 :
if ( face_num = = 0 )
vm_vec_normal ( vm , & Vertices [ sp - > verts [ vs [ 0 ] ] ] , & Vertices [ sp - > verts [ vs [ 1 ] ] ] , & Vertices [ sp - > verts [ vs [ 2 ] ] ] ) ;
else
vm_vec_normal ( vm , & Vertices [ sp - > verts [ vs [ 0 ] ] ] , & Vertices [ sp - > verts [ vs [ 2 ] ] ] , & Vertices [ sp - > verts [ vs [ 3 ] ] ] ) ;
break ;
case SIDE_IS_TRI_13 :
if ( face_num = = 0 )
vm_vec_normal ( vm , & Vertices [ sp - > verts [ vs [ 0 ] ] ] , & Vertices [ sp - > verts [ vs [ 1 ] ] ] , & Vertices [ sp - > verts [ vs [ 3 ] ] ] ) ;
else
vm_vec_normal ( vm , & Vertices [ sp - > verts [ vs [ 1 ] ] ] , & Vertices [ sp - > verts [ vs [ 2 ] ] ] , & Vertices [ sp - > verts [ vs [ 3 ] ] ] ) ;
break ;
}
}
void uncached_get_side_normals ( segment * sp , int sidenum , vms_vector * vm1 , vms_vector * vm2 )
{
int vvm0 , vvm1 , vvm2 , vvm3 , negate_flag ;
char * vs = Side_to_verts [ sidenum ] ;
switch ( sp - > sides [ sidenum ] . type ) {
case SIDE_IS_QUAD :
get_verts_for_normal ( sp - > verts [ vs [ 0 ] ] , sp - > verts [ vs [ 1 ] ] , sp - > verts [ vs [ 2 ] ] , sp - > verts [ vs [ 3 ] ] , & vvm0 , & vvm1 , & vvm2 , & vvm3 , & negate_flag ) ;
vm_vec_normal ( vm1 , & Vertices [ vvm0 ] , & Vertices [ vvm1 ] , & Vertices [ vvm2 ] ) ;
if ( negate_flag )
vm_vec_negate ( vm1 ) ;
* vm2 = * vm1 ;
break ;
case SIDE_IS_TRI_02 :
vm_vec_normal ( vm1 , & Vertices [ sp - > verts [ vs [ 0 ] ] ] , & Vertices [ sp - > verts [ vs [ 1 ] ] ] , & Vertices [ sp - > verts [ vs [ 2 ] ] ] ) ;
vm_vec_normal ( vm2 , & Vertices [ sp - > verts [ vs [ 0 ] ] ] , & Vertices [ sp - > verts [ vs [ 2 ] ] ] , & Vertices [ sp - > verts [ vs [ 3 ] ] ] ) ;
break ;
case SIDE_IS_TRI_13 :
vm_vec_normal ( vm1 , & Vertices [ sp - > verts [ vs [ 0 ] ] ] , & Vertices [ sp - > verts [ vs [ 1 ] ] ] , & Vertices [ sp - > verts [ vs [ 3 ] ] ] ) ;
vm_vec_normal ( vm2 , & Vertices [ sp - > verts [ vs [ 1 ] ] ] , & Vertices [ sp - > verts [ vs [ 2 ] ] ] , & Vertices [ sp - > verts [ vs [ 3 ] ] ] ) ;
break ;
}
}
# endif
// -------------------------------------------------------------------------------
void validate_removable_wall ( segment * sp , int sidenum , int tmap_num )
{
create_walls_on_side ( sp , sidenum ) ;
sp - > sides [ sidenum ] . tmap_num = tmap_num ;
// assign_default_uvs_to_side(sp, sidenum);
// assign_light_to_side(sp, sidenum);
}
// -------------------------------------------------------------------------------
// Make a just-modified segment side valid.
void validate_segment_side ( segment * sp , int sidenum )
{
if ( sp - > sides [ sidenum ] . wall_num = = - 1 )
create_walls_on_side ( sp , sidenum ) ;
else
// create_removable_wall(sp, sidenum, sp->sides[sidenum].tmap_num);
validate_removable_wall ( sp , sidenum , sp - > sides [ sidenum ] . tmap_num ) ;
// Set render_flag.
// If side doesn't have a child, then render wall. If it does have a child, but there is a temporary
// wall there, then do render wall.
// if (sp->children[sidenum] == -1)
// sp->sides[sidenum].render_flag = 1;
// else if (sp->sides[sidenum].wall_num != -1)
// sp->sides[sidenum].render_flag = 1;
// else
// sp->sides[sidenum].render_flag = 0;
}
extern int check_for_degenerate_segment ( segment * sp ) ;
// -------------------------------------------------------------------------------
// Make a just-modified segment valid.
// check all sides to see how many faces they each should have (0,1,2)
// create new vector normals
void validate_segment ( segment * sp )
{
int side ;
# ifdef EDITOR
check_for_degenerate_segment ( sp ) ;
# endif
for ( side = 0 ; side < MAX_SIDES_PER_SEGMENT ; side + + )
validate_segment_side ( sp , side ) ;
// assign_default_uvs_to_segment(sp);
}
// -------------------------------------------------------------------------------
// Validate all segments.
// Highest_segment_index must be set.
// For all used segments (number <= Highest_segment_index), segnum field must be != -1.
void validate_segment_all ( void )
{
int s ;
for ( s = 0 ; s < = Highest_segment_index ; s + + )
# ifdef EDITOR
if ( Segments [ s ] . segnum ! = - 1 )
# endif
validate_segment ( & Segments [ s ] ) ;
# ifdef EDITOR
{
int said = 0 ;
for ( s = Highest_segment_index + 1 ; s < MAX_SEGMENTS ; s + + )
if ( Segments [ s ] . segnum ! = - 1 ) {
if ( ! said ) {
mprintf ( ( 0 , " Segment %i has invalid segnum. Bashing to -1. Silently bashing all others... " , s ) ) ;
}
said + + ;
Segments [ s ] . segnum = - 1 ;
}
if ( said )
mprintf ( ( 0 , " %i fixed. \n " , said ) ) ;
}
# endif
# ifndef NDEBUG
# ifndef COMPACT_SEGS
if ( check_segment_connections ( ) )
Int3 ( ) ; //Get Matt, si vous plait.
# endif
# endif
}
// ------------------------------------------------------------------------------------------------------
// Picks a random point in a segment like so:
// From center, go up to 50% of way towards any of the 8 vertices.
void pick_random_point_in_seg ( vms_vector * new_pos , int segnum )
{
int vnum ;
vms_vector vec2 ;
compute_segment_center ( new_pos , & Segments [ segnum ] ) ;
vnum = ( rand ( ) * MAX_VERTICES_PER_SEGMENT ) > > 15 ;
vm_vec_sub ( & vec2 , & Vertices [ Segments [ segnum ] . verts [ vnum ] ] , new_pos ) ;
vm_vec_scale ( & vec2 , rand ( ) ) ; // rand() always in 0..1/2
vm_vec_add2 ( new_pos , & vec2 ) ;
}
// ----------------------------------------------------------------------------------------------------------
// Set the segment depth of all segments from start_seg in *segbuf.
// Returns maximum depth value.
int set_segment_depths ( int start_seg , ubyte * segbuf )
{
int i , curseg ;
ubyte visited [ MAX_SEGMENTS ] ;
int queue [ MAX_SEGMENTS ] ;
int head , tail ;
int depth ;
2001-01-20 13:49:18 +00:00
int parent_depth = 0 ;
2001-01-19 03:30:16 +00:00
depth = 1 ;
head = 0 ;
tail = 0 ;
for ( i = 0 ; i < = Highest_segment_index ; i + + )
visited [ i ] = 0 ;
if ( segbuf [ start_seg ] = = 0 )
return 1 ;
queue [ tail + + ] = start_seg ;
visited [ start_seg ] = 1 ;
segbuf [ start_seg ] = depth + + ;
if ( depth = = 0 )
depth = 255 ;
while ( head < tail ) {
curseg = queue [ head + + ] ;
parent_depth = segbuf [ curseg ] ;
for ( i = 0 ; i < MAX_SIDES_PER_SEGMENT ; i + + ) {
int childnum ;
childnum = Segments [ curseg ] . children [ i ] ;
if ( childnum ! = - 1 )
if ( segbuf [ childnum ] )
if ( ! visited [ childnum ] ) {
visited [ childnum ] = 1 ;
segbuf [ childnum ] = parent_depth + 1 ;
queue [ tail + + ] = childnum ;
}
}
}
return parent_depth + 1 ;
}
//these constants should match the ones in seguvs
# define LIGHT_DISTANCE_THRESHOLD (F1_0*80)
# define Magical_light_constant (F1_0*16)
# define MAX_CHANGED_SEGS 30
short changed_segs [ MAX_CHANGED_SEGS ] ;
int n_changed_segs ;
// ------------------------------------------------------------------------------------------
//cast static light from a segment to nearby segments
void apply_light_to_segment ( segment * segp , vms_vector * segment_center , fix light_intensity , int recursion_depth )
{
vms_vector r_segment_center ;
fix dist_to_rseg ;
int i , segnum = segp - Segments , sidenum ;
for ( i = 0 ; i < n_changed_segs ; i + + )
if ( changed_segs [ i ] = = segnum )
break ;
if ( i = = n_changed_segs ) {
compute_segment_center ( & r_segment_center , segp ) ;
dist_to_rseg = vm_vec_dist_quick ( & r_segment_center , segment_center ) ;
if ( dist_to_rseg < = LIGHT_DISTANCE_THRESHOLD ) {
fix light_at_point ;
if ( dist_to_rseg > F1_0 )
light_at_point = fixdiv ( Magical_light_constant , dist_to_rseg ) ;
else
light_at_point = Magical_light_constant ;
if ( light_at_point > = 0 ) {
segment2 * seg2p = & Segment2s [ segnum ] ;
light_at_point = fixmul ( light_at_point , light_intensity ) ;
if ( light_at_point > = F1_0 )
light_at_point = F1_0 - 1 ;
if ( light_at_point < = - F1_0 )
light_at_point = - ( F1_0 - 1 ) ;
seg2p - > static_light + = light_at_point ;
if ( seg2p - > static_light < 0 ) // if it went negative, saturate
seg2p - > static_light = 0 ;
} // end if (light_at_point...
} // end if (dist_to_rseg...
changed_segs [ n_changed_segs + + ] = segnum ;
}
if ( recursion_depth < 2 )
for ( sidenum = 0 ; sidenum < 6 ; sidenum + + ) {
if ( WALL_IS_DOORWAY ( segp , sidenum ) & WID_RENDPAST_FLAG )
apply_light_to_segment ( & Segments [ segp - > children [ sidenum ] ] , segment_center , light_intensity , recursion_depth + 1 ) ;
}
}
extern object * old_viewer ;
//update the static_light field in a segment, which is used for object lighting
//this code is copied from the editor routine calim_process_all_lights()
void change_segment_light ( int segnum , int sidenum , int dir )
{
segment * segp = & Segments [ segnum ] ;
if ( WALL_IS_DOORWAY ( segp , sidenum ) & WID_RENDER_FLAG ) {
side * sidep = & segp - > sides [ sidenum ] ;
fix light_intensity ;
light_intensity = TmapInfo [ sidep - > tmap_num ] . lighting + TmapInfo [ sidep - > tmap_num2 & 0x3fff ] . lighting ;
light_intensity * = dir ;
n_changed_segs = 0 ;
if ( light_intensity ) {
vms_vector segment_center ;
compute_segment_center ( & segment_center , segp ) ;
apply_light_to_segment ( segp , & segment_center , light_intensity , 0 ) ;
}
}
//this is a horrible hack to get around the horrible hack used to
//smooth lighting values when an object moves between segments
old_viewer = NULL ;
}
// ------------------------------------------------------------------------------------------
// dir = +1 -> add light
// dir = -1 -> subtract light
// dir = 17 -> add 17x light
// dir = 0 -> you are dumb
void change_light ( int segnum , int sidenum , int dir )
{
int i , j , k ;
for ( i = 0 ; i < Num_static_lights ; i + + ) {
if ( ( Dl_indices [ i ] . segnum = = segnum ) & & ( Dl_indices [ i ] . sidenum = = sidenum ) ) {
delta_light * dlp ;
dlp = & Delta_lights [ Dl_indices [ i ] . index ] ;
for ( j = 0 ; j < Dl_indices [ i ] . count ; j + + ) {
for ( k = 0 ; k < 4 ; k + + ) {
fix dl , new_l ;
dl = dir * dlp - > vert_light [ k ] * DL_SCALE ;
Assert ( ( dlp - > segnum > = 0 ) & & ( dlp - > segnum < = Highest_segment_index ) ) ;
Assert ( ( dlp - > sidenum > = 0 ) & & ( dlp - > sidenum < MAX_SIDES_PER_SEGMENT ) ) ;
new_l = ( Segments [ dlp - > segnum ] . sides [ dlp - > sidenum ] . uvls [ k ] . l + = dl ) ;
if ( new_l < 0 )
Segments [ dlp - > segnum ] . sides [ dlp - > sidenum ] . uvls [ k ] . l = 0 ;
}
dlp + + ;
}
}
}
//recompute static light for segment
change_segment_light ( segnum , sidenum , dir ) ;
}
// Subtract light cast by a light source from all surfaces to which it applies light.
// This is precomputed data, stored at static light application time in the editor (the slow lighting function).
// returns 1 if lights actually subtracted, else 0
int subtract_light ( int segnum , int sidenum )
{
if ( Light_subtracted [ segnum ] & ( 1 < < sidenum ) ) {
//mprintf((0, "Warning: Trying to subtract light from a source twice!\n"));
return 0 ;
}
Light_subtracted [ segnum ] | = ( 1 < < sidenum ) ;
change_light ( segnum , sidenum , - 1 ) ;
return 1 ;
}
// Add light cast by a light source from all surfaces to which it applies light.
// This is precomputed data, stored at static light application time in the editor (the slow lighting function).
// You probably only want to call this after light has been subtracted.
// returns 1 if lights actually added, else 0
int add_light ( int segnum , int sidenum )
{
if ( ! ( Light_subtracted [ segnum ] & ( 1 < < sidenum ) ) ) {
//mprintf((0, "Warning: Trying to add light which has never been subtracted!\n"));
return 0 ;
}
Light_subtracted [ segnum ] & = ~ ( 1 < < sidenum ) ;
change_light ( segnum , sidenum , 1 ) ;
return 1 ;
}
// Light_subtracted[i] contains bit indicators for segment #i.
// If bit n (1 << n) is set, then side #n in segment #i has had light subtracted from original (editor-computed) value.
ubyte Light_subtracted [ MAX_SEGMENTS ] ;
// Parse the Light_subtracted array, turning on or off all lights.
void apply_all_changed_light ( void )
{
int i , j ;
for ( i = 0 ; i < = Highest_segment_index ; i + + ) {
for ( j = 0 ; j < MAX_SIDES_PER_SEGMENT ; j + + )
if ( Light_subtracted [ i ] & ( 1 < < j ) )
change_light ( i , j , - 1 ) ;
}
}
//@@// Scans Light_subtracted bit array.
//@@// For all light sources which have had their light subtracted, adds light back in.
//@@void restore_all_lights_in_mine(void)
//@@{
//@@ int i, j, k;
//@@
//@@ for (i=0; i<Num_static_lights; i++) {
//@@ int segnum, sidenum;
//@@ delta_light *dlp;
//@@
//@@ segnum = Dl_indices[i].segnum;
//@@ sidenum = Dl_indices[i].sidenum;
//@@ if (Light_subtracted[segnum] & (1 << sidenum)) {
//@@ dlp = &Delta_lights[Dl_indices[i].index];
//@@
//@@ Light_subtracted[segnum] &= ~(1 << sidenum);
//@@ for (j=0; j<Dl_indices[i].count; j++) {
//@@ for (k=0; k<4; k++) {
//@@ fix dl;
//@@ dl = dlp->vert_light[k] * DL_SCALE;
//@@ Assert((dlp->segnum >= 0) && (dlp->segnum <= Highest_segment_index));
//@@ Assert((dlp->sidenum >= 0) && (dlp->sidenum < MAX_SIDES_PER_SEGMENT));
//@@ Segments[dlp->segnum].sides[dlp->sidenum].uvls[k].l += dl;
//@@ }
//@@ dlp++;
//@@ }
//@@ }
//@@ }
//@@}
// Should call this whenever a new mine gets loaded.
// More specifically, should call this whenever something global happens
// to change the status of static light in the mine.
void clear_light_subtracted ( void )
{
int i ;
for ( i = 0 ; i < = Highest_segment_index ; i + + )
Light_subtracted [ i ] = 0 ;
}
// -----------------------------------------------------------------------------
fix find_connected_distance_segments ( int seg0 , int seg1 , int depth , int wid_flag )
{
vms_vector p0 , p1 ;
compute_segment_center ( & p0 , & Segments [ seg0 ] ) ;
compute_segment_center ( & p1 , & Segments [ seg1 ] ) ;
return find_connected_distance ( & p0 , seg0 , & p1 , seg1 , depth , wid_flag ) ;
}
# define AMBIENT_SEGMENT_DEPTH 5
// -----------------------------------------------------------------------------
// Do a bfs from segnum, marking slots in marked_segs if the segment is reachable.
void ambient_mark_bfs ( int segnum , byte * marked_segs , int depth )
{
int i ;
if ( depth < 0 )
return ;
marked_segs [ segnum ] = 1 ;
for ( i = 0 ; i < MAX_SIDES_PER_SEGMENT ; i + + ) {
int child = Segments [ segnum ] . children [ i ] ;
if ( IS_CHILD ( child ) & & ( WALL_IS_DOORWAY ( & Segments [ segnum ] , i ) & WID_RENDPAST_FLAG ) & & ! marked_segs [ child ] )
ambient_mark_bfs ( child , marked_segs , depth - 1 ) ;
}
}
// -----------------------------------------------------------------------------
// Indicate all segments which are within audible range of falling water or lava,
// and so should hear ambient gurgles.
void set_ambient_sound_flags_common ( int tmi_bit , int s2f_bit )
{
int i , j ;
byte marked_segs [ MAX_SEGMENTS ] ;
// Now, all segments containing ambient lava or water sound makers are flagged.
// Additionally flag all segments which are within range of them.
for ( i = 0 ; i < = Highest_segment_index ; i + + ) {
marked_segs [ i ] = 0 ;
Segment2s [ i ] . s2_flags & = ~ s2f_bit ;
}
// Mark all segments which are sources of the sound.
for ( i = 0 ; i < = Highest_segment_index ; i + + ) {
segment * segp = & Segments [ i ] ;
segment2 * seg2p = & Segment2s [ i ] ;
for ( j = 0 ; j < MAX_SIDES_PER_SEGMENT ; j + + ) {
side * sidep = & segp - > sides [ j ] ;
if ( ( TmapInfo [ sidep - > tmap_num ] . flags & tmi_bit ) | | ( TmapInfo [ sidep - > tmap_num2 & 0x3fff ] . flags & tmi_bit ) ) {
if ( ! IS_CHILD ( segp - > children [ j ] ) | | ( sidep - > wall_num ! = - 1 ) ) {
seg2p - > s2_flags | = s2f_bit ;
marked_segs [ i ] = 1 ; // Say it's itself that it is close enough to to hear something.
}
}
}
}
// Next mark all segments within N segments of a source.
for ( i = 0 ; i < = Highest_segment_index ; i + + ) {
segment2 * seg2p = & Segment2s [ i ] ;
if ( seg2p - > s2_flags & s2f_bit )
ambient_mark_bfs ( i , marked_segs , AMBIENT_SEGMENT_DEPTH ) ;
}
// Now, flip bits in all segments which can hear the ambient sound.
for ( i = 0 ; i < = Highest_segment_index ; i + + )
if ( marked_segs [ i ] )
Segment2s [ i ] . s2_flags | = s2f_bit ;
}
// -----------------------------------------------------------------------------
// Indicate all segments which are within audible range of falling water or lava,
// and so should hear ambient gurgles.
// Bashes values in Segment2s array.
void set_ambient_sound_flags ( void )
{
set_ambient_sound_flags_common ( TMI_VOLATILE , S2F_AMBIENT_LAVA ) ;
set_ambient_sound_flags_common ( TMI_WATER , S2F_AMBIENT_WATER ) ;
}