2024-04-20 16:23:08 +00:00
/*
* Descent 3
* Copyright ( C ) 2024 Parallax Software
*
* 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 3 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 , see < http : //www.gnu.org/licenses/>.
2024-05-06 15:12:44 +00:00
- - - HISTORICAL COMMENTS FOLLOW - - -
2024-04-19 20:58:24 +00:00
* $ Logfile : / DescentIII / Main / editor / HRoom . cpp $
* $ Revision : 1.1 .1 .1 $
* $ Date : 2003 - 08 - 26 03 : 57 : 38 $
* $ Author : kevinb $
*
* Code to implement room functions
*
* $ Log : not supported by cvs2svn $
*
* 106 9 / 15 / 99 1 : 56 p Matt
* Added the option to allow rooms or groups placed on the terrain to
* either align with the terrain or with gravity .
*
* 105 9 / 07 / 99 12 : 10 p Matt
* Added a function to propagate a texture to all adjacent coplanar faces
* in a room .
*
* 104 8 / 30 / 99 1 : 00 p Gwar
* use the " true " center of faces for determining attach points in room
* connecting functions
*
* 103 8 / 19 / 99 6 : 57 p Gwar
* call ned_FindSharedEdge in CombineFaces to see if we can ' t combine
* because of a flipped normal
*
* 102 8 / 17 / 99 7 : 11 p Gwar
*
* 101 8 / 17 / 99 12 : 08 p Gwar
* don ' t set World_changed in functions that can be called from palette
* rooms
*
* 100 8 / 12 / 99 12 : 08 a Gwar
* NEWEDITOR : texture , door and object memory management ; and call SetPrim
* when changing Curroomp , which now force unmarks everything in current
* room view
*
* 99 7 / 04 / 99 4 : 55 p Gwar
* changes for texture management in NEWEDITOR
*
* 98 5 / 08 / 99 6 : 39 p Matt
* Added a function to delete a face and all faces connected to it .
*
* 97 5 / 04 / 99 12 : 32 p Matt
* Fixed stupid bug and stupider bug .
*
* 96 4 / 30 / 99 6 : 52 p Matt
* Added a function to merge an object ' s geometry into a room .
*
* 95 4 / 28 / 99 12 : 51 a Matt
* Added some funtions to edit face geometry .
*
* 94 4 / 25 / 99 2 : 38 p Gwar
* brought in HRoom . cpp and RoomUVs . cpp , and several misc game functions
* were added to globals . cpp to make it possible
*
* 93 4 / 19 / 99 12 : 10 a Matt
* Added a menu item to delete a vertex from a face .
*
* 92 4 / 18 / 99 11 : 26 p Matt
* Restored point - to - plane check to FixCracks ( ) . This check is necessary
* because the point - to - edge check assumes that the points are all in the
* same plane , and will not work if they are not .
*
* 91 4 / 07 / 99 4 : 39 p Matt
* In link to new external room , copy the textures from the old faces to
* the new faces .
*
* 90 4 / 07 / 99 1 : 50 p Matt
* Took point - in - plane check out of fix level cracks , because it prevented
* tjoints from being fixed for some non - planar faces .
*
* 89 4 / 06 / 99 3 : 48 p Matt
* Allow combining of rooms with rendered portals .
*
* 88 4 / 06 / 99 10 : 23 a Matt
*
* 87 4 / 01 / 99 2 : 55 p Matt
* Fixed triangulate face code to deal with colinear points
*
* 86 4 / 01 / 99 1 : 25 p Matt
* Fixed bug in MatchPortalFaces when checking for match and a colinear
* point was found .
*
* 85 3 / 31 / 99 12 : 58 p Matt
* Added snap - point - to - face
*
* 84 3 / 30 / 99 8 : 03 p Matt
* Deleted commented - out code
*
* 83 3 / 30 / 99 8 : 01 p Matt
* Took out normals - are - same check when combining adjacent faces . It was
* the wrong way to check the faces ( the right way ( in the D3 editor , that
* is ) is to check for point in the plane ) and we were only using it as a
* first - pass check anyway .
*
* 82 3 / 30 / 99 6 : 25 p Matt
* Use new error system for combine faces function .
*
* 81 3 / 29 / 99 6 : 46 p Matt
* When combining faces , use FaceIsPlanar ( ) so the face will pass mine
* verification .
*
* 80 3 / 27 / 99 5 : 01 p Matt
* Fixed stupid bug in build bridge code , that assigned UVs to all faces
* instead of one face , causing an Int3 sometimes for faces that hadn ' t
* yet had normals computed .
*
* 79 3 / 27 / 99 4 : 45 p Matt
* Fixed a surprising fundamental flaw in the polygon clip code .
*
* 78 3 / 23 / 99 5 : 12 p Matt
* Added function to combine rooms .
*
* 77 2 / 27 / 99 3 : 47 p Mark
* Place room on terrain was improperly clearing out the placed door .
* ( MattT on Mark ' s machine )
*
* 76 1 / 29 / 99 12 : 48 p Matt
* Rewrote the doorway system
*
* 75 1 / 21 / 99 11 : 34 a Matt
* Got rid of portal triggers . Since we don ' t have multi - face portals , a
* face trigger works fine for a portal . Also fixed a few editor / trigger
* bugs .
*
* 74 12 / 23 / 98 6 : 32 p Matt
* Changed , slightly , the process of creating a new face
*
* 73 12 / 23 / 98 10 : 53 a Matt
* Added functions to create a face
*
* 72 10 / 07 / 98 12 : 48 p Matt
* Added some extra error checking to prevent doubly - formed portals .
*
* 71 10 / 03 / 98 8 : 31 p Matt
* Added Join Rooms Exact function .
*
* 70 9 / 25 / 98 5 : 25 p Matt
* Don ' t try to fix cracks in unused rooms .
*
* 69 9 / 24 / 98 5 : 00 p Matt
* Improved error checking for running out of rooms .
*
* 68 9 / 16 / 98 10 : 02 a Matt
* Fixed serval bugs , in JoinAllOverlappingFaces ( ) and MatchPortalFaces ( ) .
*
* 67 9 / 11 / 98 4 : 46 p Matt
* Don ' t allow combining of portal faces , pending code to do the combine
* correctly .
*
* 66 9 / 10 / 98 6 : 38 p Matt
* Deal with error joining faces after smooth bridge .
*
* 65 9 / 09 / 98 4 : 07 p Matt
* Added Smooth Bridge function
*
* 64 9 / 08 / 98 3 : 25 p Matt
* Changed an error message .
*
* 63 9 / 08 / 98 12 : 05 p Jason
* moved doorway . h out of room . h
*
* 62 9 / 07 / 98 10 : 58 p Matt
* Added snap point to point .
*
* 61 9 / 07 / 98 8 : 14 p Matt
* Don ' t delete colinear points ( & thus form t - joints ) when combining
* faces , and deal properly with combining faces that share multiple
* edges .
*
* 60 9 / 04 / 98 3 : 34 p Matt
* Added groovy vertex snap code
*
* 59 9 / 04 / 98 12 : 29 p Matt
* Added marked edge & vertex in the editor
*
* 58 9 / 04 / 98 11 : 34 a Matt
* Fixed small bug in portal matching code , that caused one point on the
* portals not to be set exactly the same .
*
* 57 9 / 03 / 98 5 : 29 p Matt
* Added code to fix cracks in levels .
*
* 56 9 / 02 / 98 5 : 57 p Matt
* Added code to make faces match exactly when forming a portal . This
* should get rid of all remaining cracking problems .
*
* 55 9 / 02 / 98 4 : 07 p Matt
* Fixed one kind of cracking : after clip , make sure old points that
* more - or - less line up are exactly the same .
*
* 54 9 / 02 / 98 2 : 29 p Matt
* Added code to prevent t - joints from being formed when joining rooms
*
* 53 9 / 01 / 98 12 : 04 p Matt
* Ripped out multi - face portal code
*
* 52 8 / 28 / 98 6 : 21 p Matt
* Added a function to flip a face .
*
* 51 8 / 24 / 98 3 : 02 p Matt
* Gave the user the option , when joining rooms , of extruding straight out
* or toward the center of the base face .
*
* 50 7 / 01 / 98 7 : 53 p Matt
* Added code to allow forcing of join even when the two rooms stick
* through each other .
*
* 49 6 / 25 / 98 7 : 15 p Matt
* Added a function to delete a pair of portals .
*
* 48 6 / 23 / 98 2 : 45 p Matt
* Added option when propagating to all faces in a room to only do so to
* ones with the same texture .
*
* 47 6 / 08 / 98 12 : 28 p Matt
* Added a function to triangulate a face
*
* 46 5 / 22 / 98 4 : 47 p Matt
* Added menu item to propagate a texture to all the faces in a room .
*
* 45 4 / 27 / 98 6 : 41 p Matt
* Added code to join all adjacent faces between two rooms
*
* 44 4 / 17 / 98 6 : 25 p Matt
* In CheckFaceToPlane ( ) , store the distance in a variable so it can be
* looked at in the debugger .
*
* 43 4 / 17 / 98 6 : 23 p Matt
* Added code to make sure that the two faces of a portal have the same
* number of vertices , but commented it out for now . See the note in the
* code .
*
* 42 4 / 16 / 98 11 : 50 a Matt
* Made snap code work for placed groups .
*
* 41 4 / 07 / 98 4 : 19 p Matt
* Fixed ( I hope ) a problem joining faces that matched perfectly .
*
* 40 4 / 02 / 98 12 : 23 p Jason
* trimmed some fat from our structures
*
* 39 4 / 01 / 98 5 : 55 p Matt
* Partially re - wrote JoinRooms ( ) to allow creation of adjacent portals .
*
* 38 2 / 18 / 98 2 : 26 a Matt
* Fixed typo
*
* 37 2 / 16 / 98 2 : 53 p Matt
* Deal with small floating - point precision problem .
*
* 36 2 / 16 / 98 1 : 27 p Matt
* Added function to snap the placed room to a vertex / edge on the base
* room / face
*
* 35 2 / 11 / 98 12 : 38 p Matt
* Added epsilon to point - to - plane check
*
* 34 2 / 08 / 98 6 : 02 p Matt
* Use groovy new vm_MatrixMulTMatrix ( ) function
*
* 33 2 / 04 / 98 6 : 23 p Matt
* Changed object room number to indicate a terrain cell via a flag . Got
* rid of the object flag which used to indicate terrain .
*
* 32 1 / 30 / 98 1 : 27 p Matt
* Orthogonalize after computing rotation matrix for placed rooms ( fixed
* probem of bad matrix making rooms bigger ) .
*
* 31 1 / 21 / 98 12 : 40 p Matt
* When attaching a room to the ground , delete the room ' s attach face
*
* 30 1 / 21 / 98 12 : 32 p Matt
* Revamped viewer system
*
* 29 1 / 20 / 98 4 : 43 p Matt
* Fixed up the bridge and join functions
*
* 28 1 / 15 / 98 7 : 34 p Matt
* Revamped error checking when computing face normals
*
* 27 11 / 17 / 97 7 : 39 p Sean
* Changed join epsilon , and took out a return from a bogus error message
* ( Matt on Sean ' s machine )
*
* 26 11 / 06 / 97 6 : 47 p Sean
* FROM JASON : Changed < Highest_room_index to < =
*
* 25 11 / 05 / 97 7 : 13 p Matt
* Added join rooms function
*
* 24 10 / 03 / 97 3 : 38 p Matt
* Added place and attach code for rooms on the terrain
*
* 23 10 / 01 / 97 7 : 51 p Matt
* Added code for external rooms
*
* 22 9 / 29 / 97 12 : 09 p Jason
* added functionality to doorway system
*
* 21 9 / 26 / 97 6 : 01 p Matt
* Fixed delete room code so that Curroomp is never NULL
*
* 20 9 / 24 / 97 3 : 22 p Matt
* Added Drop Room function
*
* 19 9 / 15 / 97 5 : 56 p Matt
* Changed message ( & fixed handling ) when user tries to delete a room
* attached to the terrain .
*
* 18 9 / 12 / 97 9 : 01 p Craig
* Fixed orientation of door object ( Matt on Craig ' s machine )
*
* 17 9 / 12 / 97 5 : 38 p Jason
* got doors working
*
* 16 9 / 11 / 97 5 : 38 p Jason
* initial door coding for room engine
*
* 15 9 / 06 / 97 3 : 18 p Matt
* Use LinkRoomsSimple ( ) when adding , attaching , & bridging
* Reset Curportal when switch rooms
*
* 14 9 / 04 / 97 4 : 39 p Matt
* Added includes needed as a result of removing includes from d3edit . h
*
* 13 9 / 02 / 97 2 : 26 p Matt
* Made ComputePlacedRoomMatrix ( ) deal w / placed rooms or groups
* Made polygon clipper deal with points on the the edge of the clip
* polygon
*
* 12 8 / 29 / 97 5 : 43 p Matt
* Moved some funcs to erooms . cpp , and fixed some stuff for selected rooms
*
* 11 8 / 27 / 97 10 : 30 a Matt
* Fixed bogosity assigning UVs to faces in add room
*
* 10 8 / 22 / 97 9 : 30 a Matt
* When adding a room , make the new room the current room , and make the
* face opposite the connection the current one . That way you can add
* several times in a row to create a long tunnel .
*
* 9 8 / 21 / 97 6 : 01 p Matt
* Added combine faces & delete room functions
*
* 8 8 / 19 / 97 1 : 01 p Matt
* Added error check for facing not overlapping when attaching rooms
*
* 7 8 / 18 / 97 6 : 59 p Matt
* Implemented Place Room / Attach room system
*
* 6 8 / 05 / 97 10 : 47 a Matt
* Added some error checking , and fixed a bug
*
* 5 8 / 04 / 97 7 : 38 p Matt
* Don ' t add a room if there ' s already a room at the face
*
* 4 8 / 04 / 97 12 : 46 p Matt
* Added BuildBridge ( ) and SetMarkedRoom ( )
*
*
* 3 8 / 01 / 97 6 : 15 p Matt
* Added code to attach rooms
*
* 2 7 / 22 / 97 10 : 33 a Matt
* Added AddRoom ( )
*
* 1 7 / 21 / 97 4 : 48 p Matt
*
* $ NoKeywords : $
*/
# include <stdlib.h>
# include "HRoom.h"
# ifndef NEWEDITOR
# include "d3edit.h"
# else
# include "..\neweditor\globals.h"
# include "..\neweditor\ned_geometry.h"
# endif
# include "erooms.h"
# include "RoomUVs.h"
# include "HView.h"
# include "SelectedRoom.h"
# include "group.h"
# include "door.h"
# include "doorway.h"
# include "terrain.h"
# include "HTexture.h"
# include "trigger.h"
//Make the Marked room/face the current room/face
void SetMarkedRoom ( )
{
Markedroomp = Curroomp ;
Markedface = Curface ;
Markededge = Curedge ;
Markedvert = Curvert ;
State_changed = 1 ;
EditorStatus ( " Marked room:face set to %d:%d " , ROOMNUM ( Markedroomp ) , Markedface ) ;
}
// Select next face on current room
void SelectNextFace ( )
{
if ( + + Curface > = Curroomp - > num_faces )
Curface = 0 ;
State_changed = 1 ;
}
// Select previous face on current room
void SelectPrevFace ( )
{
if ( - - Curface < 0 )
Curface = Curroomp - > num_faces - 1 ;
State_changed = 1 ;
}
# define DEFAULT_ROOM_LENGTH 20.0
// Adds a room at the current room/face. The room is created by extuding out from the current face
void AddRoom ( )
{
room * rp ;
face * cfp ;
int cnv , nfaces ;
vector room_delta ;
int i ;
//Get values from current room & face
cfp = & Curroomp - > faces [ Curface ] ; //pointer to current face
cnv = cfp - > num_verts ; //number of verts in current face
//Check for portal already here
if ( cfp - > portal_num ! = - 1 ) {
OutrageMessageBox ( " Can't add room: There is already a connection at the current room:face. " ) ;
return ;
}
//Get values for new room
nfaces = cnv + 2 ;
//Get a pointer to our room
rp = CreateNewRoom ( cnv * 2 , nfaces , 0 ) ;
if ( rp = = NULL ) {
OutrageMessageBox ( " Cannot add room: No free rooms. " ) ;
return ;
}
//Compute delta vector
room_delta = cfp - > normal * - DEFAULT_ROOM_LENGTH ;
//Set the vertices for the room
for ( i = 0 ; i < cnv ; i + + ) {
rp - > verts [ i ] = Curroomp - > verts [ cfp - > face_verts [ cnv - 1 - i ] ] ;
rp - > verts [ cnv + i ] = rp - > verts [ i ] + room_delta ;
}
//Set the faces for the room
InitRoomFace ( & rp - > faces [ 0 ] , cnv ) ;
for ( i = 0 ; i < cnv ; i + + )
rp - > faces [ 0 ] . face_verts [ i ] = i ;
InitRoomFace ( & rp - > faces [ 1 ] , cnv ) ;
for ( i = 0 ; i < cnv ; i + + )
rp - > faces [ 1 ] . face_verts [ i ] = cnv * 2 - 1 - i ;
for ( i = 0 ; i < nfaces - 2 ; i + + ) {
InitRoomFace ( & rp - > faces [ i + 2 ] , 4 ) ;
rp - > faces [ i + 2 ] . face_verts [ 0 ] = i ;
rp - > faces [ i + 2 ] . face_verts [ 1 ] = i + cnv ;
rp - > faces [ i + 2 ] . face_verts [ 2 ] = ( ( i + 1 ) % cnv ) + cnv ;
rp - > faces [ i + 2 ] . face_verts [ 3 ] = ( i + 1 ) % cnv ;
}
# ifndef NEWEDITOR
//Set normals & textures for each face
for ( i = 0 ; i < nfaces ; i + + ) {
if ( ! ComputeFaceNormal ( rp , i ) )
Int3 ( ) ; //Bad! Get Matt!
//Assign arbitrary texture map
rp - > faces [ i ] . tmap = i + 1 ;
}
# else
int texnum = Editor_state . GetCurrentTexture ( ) ;
//Set normals & textures for each face
for ( i = 0 ; i < nfaces ; i + + ) {
if ( ! ComputeFaceNormal ( rp , i ) )
Int3 ( ) ; //Bad! Get Matt!
// Apply texture
HTextureApplyToRoomFace ( rp , i , texnum ) ;
}
# endif
//Set UVs for all the faces
AssignDefaultUVsToRoom ( rp ) ;
//Form the portals between the rooms
LinkRoomsSimple ( Rooms , ROOMNUM ( Curroomp ) , Curface , ROOMNUM ( rp ) , 0 ) ;
//Make the new room the current room
# ifndef NEWEDITOR
Curroomp = rp ;
Curface = 1 ;
Curedge = Curvert = 0 ;
Curportal = - 1 ;
# else
theApp . m_pLevelWnd - > SetPrim ( rp , 1 , - 1 , 0 , 0 ) ;
# endif
//Set the flag
World_changed = 1 ;
}
//Computes the orientation matrix for the placed room
void ComputePlacedRoomMatrix ( )
{
room * placedroomp ;
int placedface ;
matrix srcmat ;
vector t ;
if ( Placed_room ! = - 1 ) {
placedroomp = & Rooms [ Placed_room ] ;
placedface = Placed_room_face ;
}
else {
ASSERT ( Placed_group ! = NULL ) ;
placedroomp = & Placed_group - > rooms [ Placed_group - > attachroom ] ;
placedface = Placed_group - > attachface ;
}
//Compute source and destination matrices
t = - placedroomp - > faces [ placedface ] . normal ;
vm_VectorToMatrix ( & srcmat , & t , NULL , NULL ) ;
vm_VectorAngleToMatrix ( & Placed_room_orient , & Placed_room_orient . fvec , Placed_room_angle ) ;
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " srcmat: %f %f %f \n " , vm_GetMagnitude ( & srcmat . fvec ) , vm_GetMagnitude ( & srcmat . rvec ) , vm_GetMagnitude ( & srcmat . uvec ) ) ;
mprintf ( 0 , " orient: %f %f %f \n " , vm_GetMagnitude ( & Placed_room_orient . fvec ) , vm_GetMagnitude ( & Placed_room_orient . rvec ) , vm_GetMagnitude ( & Placed_room_orient . uvec ) ) ;
2024-04-19 20:58:24 +00:00
//Make sure the matrices are ok
vm_Orthogonalize ( & srcmat ) ;
vm_Orthogonalize ( & Placed_room_orient ) ;
//Compute matrix to rotate src -> dest
vm_MatrixMulTMatrix ( & Placed_room_rotmat , & srcmat , & Placed_room_orient ) ;
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " rotmat: %f %f %f \n " , vm_GetMagnitude ( & Placed_room_rotmat . fvec ) , vm_GetMagnitude ( & Placed_room_rotmat . rvec ) , vm_GetMagnitude ( & Placed_room_rotmat . uvec ) ) ;
2024-04-19 20:58:24 +00:00
//Make sure the matrix is ok
vm_Orthogonalize ( & Placed_room_rotmat ) ;
}
//Place a room for orientation before attachment
//Parameters: baseroomp - pointer to the room in the mine to which the new room will be attached
// baseface - the face on baseroomp to attach to
// placed_room - the number of the room to be attached
// placed_room_face the face on placed_room that's attached
void PlaceRoom ( room * baseroomp , int baseface , int placed_room , int placed_room_face , int placed_room_door )
{
//Clear the placed group if one exists
Placed_group = NULL ;
ASSERT ( baseroomp - > faces [ baseface ] . portal_num = = - 1 ) ;
room * placedroomp = & Rooms [ placed_room ] ;
//Set globals
Placed_room = placed_room ;
Placed_room_face = placed_room_face ;
Placed_room_orient . fvec = baseroomp - > faces [ baseface ] . normal ;
Placed_room_angle = 0 ;
Placed_baseroomp = baseroomp ;
Placed_baseface = baseface ;
Placed_door = placed_room_door ;
//Compute attach points on each face
# ifndef NEWEDITOR
ComputeCenterPointOnFace ( & Placed_room_attachpoint , baseroomp , baseface ) ;
ComputeCenterPointOnFace ( & Placed_room_origin , placedroomp , placed_room_face ) ;
# else
ComputeFaceBoundingCircle ( & Placed_room_attachpoint , baseroomp , baseface ) ;
ComputeFaceBoundingCircle ( & Placed_room_origin , placedroomp , placed_room_face ) ;
# endif
//Compute initial orientation matrix
ComputePlacedRoomMatrix ( ) ;
//Set the flag
State_changed = 1 ;
}
//Lined up a placed room. Moves the placed room so the closest vert to basevert lines up exactly,
//and the edge from basevert to basevert+1 lines up with the corresponding edge on the placed room.
void SnapRoom ( int basevert )
{
vector basecenter , attcenter ;
room * baseroomp , * attroomp ;
face * bfp , * afp ;
vector * bvp ;
int baseface , attface ;
vector baseedge , attedge ;
//Get values for the attach room
if ( Placed_room ! = - 1 ) {
attroomp = & Rooms [ Placed_room ] ;
attface = Placed_room_face ;
}
else {
ASSERT ( Placed_group ! = NULL ) ;
attroomp = & Placed_group - > rooms [ Placed_group - > attachroom ] ;
attface = Placed_group - > attachface ;
}
//Set some vars
baseroomp = Placed_baseroomp ;
baseface = Placed_baseface ;
attcenter = Placed_room_origin ;
basecenter = Placed_room_attachpoint ;
bfp = & baseroomp - > faces [ baseface ] ;
afp = & attroomp - > faces [ attface ] ;
bvp = & baseroomp - > verts [ bfp - > face_verts [ basevert ] ] ;
//Find matching vert in attach face
float closest_dist = FLT_MAX ;
int closest_v ;
for ( int v = 0 ; v < afp - > num_verts ; v + + ) {
vector rotvert ;
float dist ;
rotvert = ( ( attroomp - > verts [ afp - > face_verts [ v ] ] - attcenter ) * Placed_room_rotmat ) + basecenter ;
dist = vm_VectorDistance ( bvp , & rotvert ) ;
if ( dist < closest_dist ) {
closest_dist = dist ;
closest_v = v ;
}
}
//Compute edge vector in base room
vm_GetNormalizedDir ( & baseedge , & baseroomp - > verts [ bfp - > face_verts [ ( basevert + 1 ) % bfp - > num_verts ] ] , bvp ) ;
//Compute edge vector in attach room
vector v0 = ( ( attroomp - > verts [ afp - > face_verts [ closest_v ] ] - attcenter ) * Placed_room_rotmat ) + basecenter ;
vector v1 = ( ( attroomp - > verts [ afp - > face_verts [ ( closest_v + afp - > num_verts - 1 ) % afp - > num_verts ] ] - attcenter ) * Placed_room_rotmat ) + basecenter ;
vm_GetNormalizedDir ( & attedge , & v1 , & v0 ) ;
//Get angle between two edge vectors
double dot = baseedge * attedge ;
if ( dot > 1.0 )
dot = 1.0 ;
double ac = acos ( dot ) ;
float delta_ang = 32768.0 * ac / PI ;
//Get sign of angle
vector checkv ;
vm_CrossProduct ( & checkv , & baseedge , & attedge ) ;
if ( ( bfp - > normal * checkv ) > 0 )
delta_ang = - delta_ang ;
//Update placed room angle
Placed_room_angle + = delta_ang ;
while ( Placed_room_angle < 0 )
Placed_room_angle + = 65536 ;
//Regenerate matrix
ComputePlacedRoomMatrix ( ) ;
//Move the room to line up the points
v0 = ( ( attroomp - > verts [ afp - > face_verts [ closest_v ] ] - attcenter ) * Placed_room_rotmat ) + basecenter ;
Placed_room_attachpoint + = * bvp - v0 ;
//Update view
State_changed = 1 ;
}
//Attach an already-placed room
void AttachRoom ( )
{
vector basecenter , attcenter ;
room * baseroomp , * attroomp , * newroomp ;
int baseface , attface ;
ASSERT ( Placed_room ! = - 1 ) ;
//Set some vars
baseroomp = Placed_baseroomp ;
baseface = Placed_baseface ;
attroomp = & Rooms [ Placed_room ] ;
attface = Placed_room_face ;
attcenter = Placed_room_origin ;
basecenter = Placed_room_attachpoint ;
//Get the new room
newroomp = CreateNewRoom ( attroomp - > num_verts , attroomp - > num_faces , 0 ) ;
if ( newroomp = = NULL ) {
OutrageMessageBox ( " Cannot attach room: No free rooms. " ) ;
return ;
}
//Rotate verts, copying into new room
for ( int i = 0 ; i < attroomp - > num_verts ; i + + )
newroomp - > verts [ i ] = ( ( attroomp - > verts [ i ] - attcenter ) * Placed_room_rotmat ) + basecenter ;
//Copy faces to new room
for ( i = 0 ; i < attroomp - > num_faces ; i + + )
{
CopyFace ( & newroomp - > faces [ i ] , & attroomp - > faces [ i ] ) ;
# ifdef NEWEDITOR
LevelTexIncrementTexture ( attroomp - > faces [ i ] . tmap ) ;
# endif
}
//Recompute normals for the faces
if ( ! ResetRoomFaceNormals ( newroomp ) )
Int3 ( ) ; //Get Matt
//Copy other values for this room
newroomp - > flags = attroomp - > flags ;
newroomp - > num_portals = 0 ;
//Check for terrain or mine room
if ( baseroomp = = NULL ) { //a terrain room
//Flag this as an external room
newroomp - > flags | = RF_EXTERNAL ;
//Delete the attach face, which should now be facing the ground
DeleteRoomFace ( newroomp , attface ) ;
}
else { //a mine room
//Clip the connecting faces against each other
if ( ! ClipFacePair ( newroomp , attface , baseroomp , baseface ) ) {
OutrageMessageBox ( " Error making portal -- faces probably don't overlap. " ) ;
FreeRoom ( newroomp ) ;
return ;
}
//Make the two faces match exactly
MatchPortalFaces ( baseroomp , baseface , newroomp , attface ) ;
//Create the portals between the rooms
LinkRoomsSimple ( Rooms , ROOMNUM ( baseroomp ) , baseface , ROOMNUM ( newroomp ) , attface ) ;
// If there is a door, place it!
if ( Placed_door ! = - 1 )
{
vector room_center ;
matrix orient = ~ Placed_room_rotmat ;
vector doorcenter = { 0 , 0 , 0 } ;
FreeRoom ( & Rooms [ Placed_room ] ) ;
room_center = ( ( doorcenter - attcenter ) * Placed_room_rotmat ) + basecenter ;
ObjCreate ( OBJ_DOOR , Placed_door , newroomp - Rooms , & room_center , & orient ) ;
doorway * dp = DoorwayAdd ( newroomp , Placed_door ) ;
# ifdef NEWEDITOR
LevelDoorIncrementDoorway ( Placed_door ) ;
# endif
Placed_door = - 1 ;
}
}
//Un-place the now-attached room
Placed_room = - 1 ;
//Flag the world as changed
World_changed = 1 ;
}
//structure for keeping track of vertices inserted in edges (from clipping)
2024-05-30 07:35:17 +00:00
struct edge_insert {
2024-04-19 20:58:24 +00:00
int v0 , v1 ; //the edge that got the new vert
int new_v ; //the new vertex
2024-05-30 07:35:17 +00:00
} ;
2024-04-19 20:58:24 +00:00
//List of vertices inserted in edges. Used only during clipping.
edge_insert Edge_inserts [ MAX_VERTS_PER_FACE ] ;
int Num_edge_inserts ;
//Add an insert to the edge list
void AddEdgeInsert ( int v0 , int v1 , int new_v )
{
Edge_inserts [ Num_edge_inserts ] . v0 = v0 ;
Edge_inserts [ Num_edge_inserts ] . v1 = v1 ;
Edge_inserts [ Num_edge_inserts ] . new_v = new_v ;
Num_edge_inserts + + ;
}
//Clip a polygon against one edge of another polygon
//Fills inbuf and maybe outbuf with new polygons, and writes any new verts to the vertices array
//Parameters: nv - the number of verts in the polygon to be clipped
// vertnums - pointer to list of vertex numbers in the polygon
// vertices - list of vertices referred to in vertnums
// v0,v1 - the edge we're clipping against
// normal - the surface normal of the polygon
// inbuf - the clipped polygon is written to this buffer
// inv - the number of verts in inbuf is written here
// outbuf - the new polygon created by the part of the input polygon that was clipped away
// onv - the number of verys in outbuf
// num_vertices - pointer to the number of verts in the vertices array
2024-05-24 03:27:12 +00:00
void ClipAgainstEdge ( int nv , int16_t * vertnums , vertex * vertices , int * num_vertices , vector * v0 , vector * v1 , vector * normal , int16_t * inbuf , int * inv , int16_t * outbuf , int * onv )
2024-04-19 20:58:24 +00:00
{
int i , prev , next , check ;
2024-05-24 03:27:12 +00:00
int16_t * ip = inbuf , * op = outbuf ;
2024-04-19 20:58:24 +00:00
vertex * curv , * prevv , * nextv ;
int inside_points = 0 , outside_points = 0 ; //real inside/outside points, distinct from edge points
for ( i = 0 , prev = nv - 1 , next = 1 ; i < nv ; i + + ) {
curv = & vertices [ vertnums [ i ] ] ;
//Find out where point lies
check = CheckPointAgainstEdge ( & curv - > vec , v0 , v1 , normal ) ;
if ( check = = 0 ) { //Current vertex is on edge
//Add to both inside & outside lists
* op + + = vertnums [ i ] ;
* ip + + = vertnums [ i ] ;
}
else if ( check = = - 1 ) { //Current vertex is outside
int check2 ;
prevv = & vertices [ vertnums [ prev ] ] ;
nextv = & vertices [ vertnums [ next ] ] ;
//Clip edge w/ previous vertex
check2 = CheckPointAgainstEdge ( & prevv - > vec , v0 , v1 , normal ) ;
if ( check2 = = 1 ) { //prev inside, so clip
ClipEdge ( normal , prevv , curv , v0 , v1 , & vertices [ * num_vertices ] ) ;
AddEdgeInsert ( vertnums [ prev ] , vertnums [ i ] , * num_vertices ) ;
* op + + = * ip + + = ( * num_vertices ) + + ;
}
//Add current vertex to outside polygon
* op + + = vertnums [ i ] ;
outside_points + + ;
//Clip edge w/ next vertex
check2 = CheckPointAgainstEdge ( & nextv - > vec , v0 , v1 , normal ) ;
if ( check2 = = 1 ) { //next inside, so clip
ClipEdge ( normal , curv , nextv , v0 , v1 , & vertices [ * num_vertices ] ) ;
AddEdgeInsert ( vertnums [ i ] , vertnums [ next ] , * num_vertices ) ;
* op + + = * ip + + = ( * num_vertices ) + + ;
}
}
else { //Current vertex is inside
ASSERT ( check = = 1 ) ;
//Add current vertex to inside polygon
* ip + + = vertnums [ i ] ;
inside_points + + ;
}
prev = i ;
if ( + + next = = nv )
next = 0 ;
}
//Set number of verts for return. If no real inside or outside points, then don't count edge points
* inv = inside_points ? ( ip - inbuf ) : 0 ;
* onv = outside_points ? ( op - outbuf ) : 0 ;
}
//Adds a new vertex to all instances of a given edge
void AddVertToAllEdges ( room * rp , int v0 , int v1 , int new_v )
{
face * fp ;
int f , v ;
for ( f = 0 , fp = rp - > faces ; f < rp - > num_faces ; f + + , fp + + ) {
for ( v = 0 ; v < fp - > num_verts ; v + + ) {
if ( ( ( fp - > face_verts [ v ] = = v0 ) & & ( fp - > face_verts [ ( v + 1 ) % fp - > num_verts ] = = v1 ) ) | |
( ( fp - > face_verts [ v ] = = v1 ) & & ( fp - > face_verts [ ( v + 1 ) % fp - > num_verts ] = = v0 ) ) )
AddVertToFace ( rp , f , new_v , v ) ;
}
}
}
//Adds a new point to all instances of a given edge
//First adds the point to the room (or finds it there), then calls AddVertToAllEdges()
void AddPointToAllEdges ( room * rp , int v0 , int v1 , vector * new_v )
{
int newvertnum = RoomAddVertices ( rp , 1 ) ;
rp - > verts [ newvertnum ] = * new_v ;
AddVertToAllEdges ( rp , v0 , v1 , newvertnum ) ;
}
//Clips a face against another. Produces one polygon that is the intersection of the two input
//faces, and zero or more extra polygons, which are parts of the input face outside the clipping face.
//The input face is replaced by the clipped face, and new faces (formed by the parts of the input
//face outside the clip-against face) are added to the end of the room's facelist.
//This routine assumes that part or all of the being-clipped face is inside the clip-against face
//Parameters: arp - the room with the face that is being changed
// afacenum - the face being changed
// brp - the room with the face we're clipping against
// bfacenum - the face we're clipping against
//Returns: true if the clip was ok, false if there was an error
bool ClipFace ( room * arp , int afacenum , room * brp , int bfacenum )
{
face * afp = & arp - > faces [ afacenum ] ;
face * bfp = & brp - > faces [ bfacenum ] ;
int edgenum ;
2024-05-24 03:27:12 +00:00
int16_t vbuf0 [ MAX_VERTS_PER_FACE ] , vbuf1 [ MAX_VERTS_PER_FACE ] ;
int16_t newface_verts [ MAX_VERTS_PER_FACE ] [ MAX_VERTS_PER_FACE ] ;
2024-04-19 20:58:24 +00:00
int newface_nvs [ MAX_VERTS_PER_FACE ] ;
vertex newverts [ MAX_VERTS_PER_FACE ] ;
int newvertnums [ MAX_VERTS_PER_FACE ] ;
int num_newverts ;
int num_newfaces = 0 ;
2024-05-24 03:27:12 +00:00
int16_t * src , * dest ;
2024-04-19 20:58:24 +00:00
int nv ;
int i ;
//Init some stuff
nv = afp - > num_verts ;
src = vbuf0 ;
dest = vbuf1 ;
Num_edge_inserts = 0 ;
//copy our vertices into one buffer
for ( i = 0 ; i < nv ; i + + ) {
newverts [ i ] . vec = arp - > verts [ afp - > face_verts [ i ] ] ;
newverts [ i ] . uvl = afp - > face_uvls [ i ] ;
newvertnums [ i ] = afp - > face_verts [ i ] ;
src [ i ] = i ;
}
num_newverts = nv ;
//Clip our polygon against each edge
for ( edgenum = 0 ; edgenum < bfp - > num_verts ; edgenum + + ) {
vector * v0 , * v1 ;
2024-05-24 03:27:12 +00:00
int16_t * outbuf = newface_verts [ num_newfaces ] ;
2024-04-19 20:58:24 +00:00
int * onv = & newface_nvs [ num_newfaces ] ;
v0 = & brp - > verts [ bfp - > face_verts [ ( bfp - > num_verts - edgenum ) % bfp - > num_verts ] ] ;
v1 = & brp - > verts [ bfp - > face_verts [ bfp - > num_verts - edgenum - 1 ] ] ;
ClipAgainstEdge ( nv , src , newverts , & num_newverts , v0 , v1 , & afp - > normal , dest , & nv , outbuf , onv ) ;
if ( nv < = 2 ) //no new face -- faces must not overlap
return 0 ;
src = dest ;
dest = ( src = = vbuf0 ) ? vbuf1 : vbuf0 ;
if ( newface_nvs [ num_newfaces ] ) //is there a new face?
num_newfaces + + ; //..yes, increment counter
}
//Now we have the clipped face and the other new faces
//Replace the old face, and add the new faces
int first_new_vert , first_new_face ;
face * fp ;
//Allocate space for the new verts
first_new_vert = RoomAddVertices ( arp , num_newverts - afp - > num_verts ) ;
//Copy new vertices into room & get real vert numbers
for ( i = 0 ; i < num_newverts - afp - > num_verts ; i + + ) {
arp - > verts [ first_new_vert + i ] = newverts [ afp - > num_verts + i ] . vec ;
newvertnums [ afp - > num_verts + i ] = first_new_vert + i ;
}
//Replace the input face with the clipped face
ReInitRoomFace ( afp , nv ) ;
for ( i = 0 ; i < nv ; i + + ) {
afp - > face_verts [ i ] = newvertnums [ src [ i ] ] ;
afp - > face_uvls [ i ] = newverts [ src [ i ] ] . uvl ;
}
if ( ! ComputeFaceNormal ( arp , afacenum ) )
Int3 ( ) ; //Get Matt
//Allocate space for the new faces
first_new_face = RoomAddFaces ( arp , num_newfaces ) ;
//Copy data for new faces (the outside faces)
for ( i = 0 ; i < num_newfaces ; i + + ) {
fp = & arp - > faces [ first_new_face + i ] ;
InitRoomFace ( fp , newface_nvs [ i ] ) ;
for ( int j = 0 ; j < newface_nvs [ i ] ; j + + ) {
fp - > face_verts [ j ] = newvertnums [ newface_verts [ i ] [ j ] ] ;
fp - > face_uvls [ j ] = newverts [ newface_verts [ i ] [ j ] ] . uvl ;
}
if ( ! ComputeFaceNormal ( arp , first_new_face + i ) )
Int3 ( ) ; //Get Matt
# ifndef NEWEDITOR
fp - > tmap = arp - > faces [ afacenum ] . tmap ;
# else
// Apply texture
HTextureApplyToRoomFace ( arp , first_new_face + i , arp - > faces [ afacenum ] . tmap ) ;
# endif
CopyFaceFlags ( fp , & arp - > faces [ afacenum ] ) ;
}
//Add new verts to edges
for ( i = 0 ; i < Num_edge_inserts ; i + + ) {
int v0 , v1 , new_v ;
v0 = newvertnums [ Edge_inserts [ i ] . v0 ] ;
v1 = newvertnums [ Edge_inserts [ i ] . v1 ] ;
new_v = newvertnums [ Edge_inserts [ i ] . new_v ] ;
AddVertToAllEdges ( arp , v0 , v1 , new_v ) ;
}
//Done!
return 1 ;
}
//Adds a point to a face
//Parameters: rp,facenum - the face we're adding the point to
// vertindex - the position in the face's vertex list at which to add the point
// vp - the point to add
void AddPointToFace ( room * rp , int facenum , int vertindex , vector * vp )
{
face * fp = & rp - > faces [ facenum ] ;
int old_verts [ MAX_VERTS_PER_FACE ] ;
roomUVL old_uvls [ MAX_VERTS_PER_FACE ] ;
int t ;
//Add the new point to the room
int newvertnum = RoomAddVertices ( rp , 1 ) ;
rp - > verts [ newvertnum ] = * vp ;
//Make copy of old verts
for ( t = 0 ; t < fp - > num_verts ; t + + ) {
old_verts [ t ] = fp - > face_verts [ t ] ;
old_uvls [ t ] = fp - > face_uvls [ t ] ;
}
//Allocate for new verts
ReInitRoomFace ( fp , fp - > num_verts + 1 ) ;
//Copy old verts
for ( t = 0 ; t < vertindex ; t + + ) {
fp - > face_verts [ t ] = old_verts [ t ] ;
fp - > face_uvls [ t ] = old_uvls [ t ] ;
}
fp - > face_verts [ t + + ] = newvertnum ;
//fp0->face_uvls[t] = ??
for ( ; t < fp - > num_verts ; t + + ) {
fp - > face_verts [ t ] = old_verts [ t - 1 ] ;
fp - > face_uvls [ t ] = old_uvls [ t - 1 ] ;
}
}
//Deletes a point from a face
//Parameters: rp,facenum - the face we're deleting the point from
// vertindex - the vertex to delete
void DeletePointFromFace ( room * rp , int facenum , int vertindex )
{
face * fp = & rp - > faces [ facenum ] ;
int old_verts [ MAX_VERTS_PER_FACE ] ;
roomUVL old_uvls [ MAX_VERTS_PER_FACE ] ;
int t ;
//Make copy of old verts
for ( t = 0 ; t < fp - > num_verts ; t + + ) {
old_verts [ t ] = fp - > face_verts [ t ] ;
old_uvls [ t ] = fp - > face_uvls [ t ] ;
}
//Allocate for new verts
ReInitRoomFace ( fp , fp - > num_verts - 1 ) ;
//Copy old verts
for ( t = 0 ; t < vertindex ; t + + ) {
fp - > face_verts [ t ] = old_verts [ t ] ;
fp - > face_uvls [ t ] = old_uvls [ t ] ;
}
for ( ; t < fp - > num_verts ; t + + ) {
fp - > face_verts [ t ] = old_verts [ t + 1 ] ;
fp - > face_uvls [ t ] = old_uvls [ t + 1 ] ;
}
# ifndef NEWEDITOR
World_changed = 1 ;
# endif
}
//Takes two faces which are going to be made into a portal and makes them match exactly
//Alternatively, checks if the two faces can be matched
//After this function the two faces will have exactly the same vertices
//Parameters: rp0,facenum0 - one of the faces
// rp1,facenum1 - the other face
// check_only - if set, doesn't change anything; just checks the faces
//Returns the number of points added to the faces
//If just_checking set, returns true if faces match, else false
int MatchPortalFaces ( room * rp0 , int facenum0 , room * rp1 , int facenum1 , bool check_only )
{
face * fp0 = & rp0 - > faces [ facenum0 ] ;
face * fp1 = & rp1 - > faces [ facenum1 ] ;
vector * v0 , * v1 , * prev_v0 , * prev_v1 ;
int n0 , n1 , i , j , prev_vn0 , prev_vn1 , max_nv ;
int points_added = 0 ;
check_faces : ;
//First, find one point in common
for ( i = 0 ; i < fp0 - > num_verts ; i + + ) {
for ( j = 0 ; j < fp1 - > num_verts ; j + + )
if ( PointsAreSame ( & rp0 - > verts [ fp0 - > face_verts [ i ] ] , & rp1 - > verts [ fp1 - > face_verts [ j ] ] ) )
break ;
if ( j < fp1 - > num_verts )
break ;
}
if ( i > = fp0 - > num_verts ) {
if ( ! check_only )
Int3 ( ) ; //Counldn't find common point! This is very bad. Get Matt.
return 0 ; //no match
}
prev_vn0 = fp0 - > face_verts [ i ] ;
prev_vn1 = fp1 - > face_verts [ j ] ;
prev_v0 = & rp0 - > verts [ prev_vn0 ] ;
prev_v1 = & rp1 - > verts [ prev_vn1 ] ;
//Make starting points identical
if ( ! check_only )
* prev_v0 = * prev_v1 ;
//Use the larger number of vert
max_nv = __max ( fp0 - > num_verts , fp1 - > num_verts ) ;
//Trace through faces, adding points where needed
for ( n0 = n1 = 1 ; n0 < max_nv & & n1 < max_nv ; n0 + + , n1 + + ) {
int vn0 , vn1 ;
recheck : ;
vn0 = fp0 - > face_verts [ ( i + n0 ) % fp0 - > num_verts ] ;
vn1 = fp1 - > face_verts [ ( j - n1 + fp1 - > num_verts ) % fp1 - > num_verts ] ;
v0 = & rp0 - > verts [ vn0 ] ;
v1 = & rp1 - > verts [ vn1 ] ;
if ( PointsAreSame ( v0 , v1 ) ) { //Points are at least very close.
if ( ! check_only )
* v0 = * v1 ; //Make the points identical
}
else { //The points are not the same, so we check for an extra (colinear) point
float d0 , d1 ;
//One of these points should lie along the edge of the other. Find which is which
d0 = vm_VectorDistance ( v0 , prev_v0 ) ;
d1 = vm_VectorDistance ( v1 , prev_v1 ) ;
if ( d0 > d1 ) { //Point 1 is presumably on the edge prev_v0 -> v0
//Make sure the point is actually on the edge
if ( CheckPointAgainstEdge ( v1 , prev_v0 , v0 , & fp0 - > normal ) ) {
if ( check_only )
return 0 ;
Int3 ( ) ; //point isn't on edge! Bad! Get Matt!
}
else {
if ( ! check_only ) {
//Add the point
AddPointToAllEdges ( rp0 , prev_vn0 , vn0 , v1 ) ;
points_added + + ;
}
else {
n1 + + ; //skip edge point & resume checking
goto recheck ;
}
}
}
else { //Point 0 is presumably on the edge prev_v1 -> v1
//Make sure the point is actually on the edge
if ( CheckPointAgainstEdge ( v0 , prev_v1 , v1 , & fp1 - > normal ) ) {
if ( check_only )
return 0 ;
Int3 ( ) ; //point isn't on edge! Bad! Get Matt!
}
else {
if ( ! check_only ) {
//Add the point
AddPointToAllEdges ( rp1 , prev_vn1 , vn1 , v0 ) ;
points_added + + ;
}
else {
n0 + + ; //skip edge point & resume checking
goto recheck ;
}
}
}
//Start check again
if ( ! check_only )
goto check_faces ;
}
prev_vn0 = vn0 ;
prev_vn1 = vn1 ;
prev_v0 = v0 ;
prev_v1 = v1 ;
}
if ( check_only )
return 1 ; //no errors found, so faces ok
ASSERT ( fp0 - > num_verts = = fp1 - > num_verts ) ; //Get Matt!
return points_added ;
}
//Clips a pair of faces against each other.
//Produces one polygon in each face that is the intersection of the two input faces,
//and zero or more extra polygons, which are parts of the input faces outside the clipping faces.
//The input faces are replaced by the clipped faces, and new faces (formed by the parts of the input
//face outside the clip-against face) are added to the end of the room's facelist.
//This routine assumes that part or all of the being-clipped face is inside the clip-against face
//Parameters: rp0,face0 - the first room:face
// rp1,face1 - the second room:face
//Returns: true if the clip was ok, false if there was an error
bool ClipFacePair ( room * rp0 , int face0 , room * rp1 , int face1 )
{
//Clip each face aginst the other
return ( ClipFace ( rp0 , face0 , rp1 , face1 ) & & ClipFace ( rp1 , face1 , rp0 , face0 ) ) ;
}
//Creates a new bridge room connecting two rooms
//Parameters: attroomp,attface - one end of the new room
// baseroom,baseface - the other end of the new room
//The new room is created by extruding from attroom/attface
void BuildBridge ( room * attroomp , int attface , room * baseroomp , int baseface )
{
room * newroomp ;
face * afp , * bfp ;
int nv , nfaces ;
vector delta_vec , bc , ac ;
vector * basevert ; //a vertex on the base face
int i ;
float cos , dist ;
//Check that the two faces don't already have portals
if ( attroomp - > faces [ attface ] . portal_num ! = - 1 ) {
OutrageMessageBox ( " Cannot build bridge: There is already a portal at %d:%d " , ROOMNUM ( attroomp ) , attface ) ;
return ;
}
if ( baseroomp - > faces [ baseface ] . portal_num ! = - 1 ) {
OutrageMessageBox ( " Cannot build bridge: There is already a portal at %d:%d " , ROOMNUM ( baseroomp ) , baseface ) ;
return ;
}
//Get values from current room & face
afp = & attroomp - > faces [ attface ] ; //pointer to attach face
bfp = & baseroomp - > faces [ baseface ] ; //pointer to base face
nv = afp - > num_verts ; //number of verts in attach face
nfaces = nv + 2 ; //number of faces in new room
//Make sure all points on each face are on the front of the other face
vector tnorm = - afp - > normal ;
int t = CheckFaceToPlane ( baseroomp , baseface , & attroomp - > verts [ afp - > face_verts [ 0 ] ] , & tnorm ) ;
if ( t ! = - 1 ) {
OutrageMessageBox ( " Can't build bridge: Vertex %d on %d:%d is behind %d:%d " , t , ROOMNUM ( baseroomp ) , baseface , ROOMNUM ( attroomp ) , attface ) ;
return ;
}
tnorm = - bfp - > normal ;
t = CheckFaceToPlane ( attroomp , attface , & baseroomp - > verts [ bfp - > face_verts [ 0 ] ] , & tnorm ) ;
if ( t ! = - 1 ) {
OutrageMessageBox ( " Can't build bridge: Vertex %d on %d:%d is behind %d:%d " , t , ROOMNUM ( attroomp ) , attface , ROOMNUM ( baseroomp ) , baseface ) ;
return ;
}
//Compute normalized vector from attach face to base face
# ifndef NEWEDITOR
ComputeCenterPointOnFace ( & bc , baseroomp , baseface ) ;
ComputeCenterPointOnFace ( & ac , attroomp , attface ) ;
# else
ComputeFaceBoundingCircle ( & bc , baseroomp , baseface ) ;
ComputeFaceBoundingCircle ( & ac , attroomp , attface ) ;
# endif
dist = vm_GetNormalizedDir ( & delta_vec , & bc , & ac ) ;
//Compute the cosine of the angle between our vector and the normal of the base face
cos = delta_vec * bfp - > normal ;
//Get a pointer to a vertex on the base face
basevert = & baseroomp - > verts [ bfp - > face_verts [ 0 ] ] ;
//Get a pointer to our room
newroomp = CreateNewRoom ( nv * 2 , nfaces , 0 ) ;
if ( newroomp = = NULL ) {
OutrageMessageBox ( " Cannot build bridge: No free rooms. " ) ;
return ;
}
//Create new vertices in the plane of the base face
for ( i = 0 ; i < nv ; i + + ) {
float dist ;
//Copy from att face to new room
newroomp - > verts [ i ] = attroomp - > verts [ afp - > face_verts [ nv - 1 - i ] ] ;
//Get distance from base face to this vertex
dist = ( ( * basevert - newroomp - > verts [ i ] ) * bfp - > normal ) / cos ;
//Get the new vert
newroomp - > verts [ nv + i ] = newroomp - > verts [ i ] + ( delta_vec * dist ) ;
}
//Set the faces for the room
InitRoomFace ( & newroomp - > faces [ 0 ] , nv ) ;
for ( i = 0 ; i < nv ; i + + )
newroomp - > faces [ 0 ] . face_verts [ i ] = i ;
InitRoomFace ( & newroomp - > faces [ 1 ] , nv ) ;
for ( i = 0 ; i < nv ; i + + )
newroomp - > faces [ 1 ] . face_verts [ i ] = nv * 2 - 1 - i ;
for ( i = 0 ; i < nfaces - 2 ; i + + ) {
InitRoomFace ( & newroomp - > faces [ i + 2 ] , 4 ) ;
newroomp - > faces [ i + 2 ] . face_verts [ 0 ] = i ;
newroomp - > faces [ i + 2 ] . face_verts [ 1 ] = i + nv ;
newroomp - > faces [ i + 2 ] . face_verts [ 2 ] = ( ( i + 1 ) % nv ) + nv ;
newroomp - > faces [ i + 2 ] . face_verts [ 3 ] = ( i + 1 ) % nv ;
}
//Set normals
if ( ! ResetRoomFaceNormals ( newroomp ) )
Int3 ( ) ; //Get Matt
//Set textures
# ifndef NEWEDITOR
for ( i = 0 ; i < nfaces ; i + + )
newroomp - > faces [ i ] . tmap = i + 1 ;
# else
int texnum = Editor_state . GetCurrentTexture ( ) ;
// Apply texture
for ( i = 0 ; i < nfaces ; i + + )
HTextureApplyToRoomFace ( newroomp , i , texnum ) ;
# endif
//Assign UVs
AssignDefaultUVsToRoom ( newroomp ) ;
//Connect the new room to the attach room
LinkRoomsSimple ( Rooms , ROOMNUM ( attroomp ) , attface , ROOMNUM ( newroomp ) , 0 ) ;
//Clip the connecting faces against each other
t = ClipFacePair ( newroomp , 1 , baseroomp , baseface ) ; ASSERT ( t ! = 0 ) ;
//Make the two faces match exactly
MatchPortalFaces ( baseroomp , baseface , newroomp , 1 ) ;
//Connect the new room to the base room
LinkRoomsSimple ( Rooms , ROOMNUM ( baseroomp ) , baseface , ROOMNUM ( newroomp ) , 1 ) ;
//Set Changed flag
World_changed = 1 ;
}
# define JOIN_EPSILON 0.01
//Connects two rooms by changing the shape of one room to attach to the other
//Pretty similar to BuildBridge(), but doesn't create a new room
//Parameters: attroomp,attface - one end of the new room
// baseroom,baseface - the other end of the new room
void JoinRooms ( room * attroomp , int attface , room * baseroomp , int baseface )
{
face * afp , * bfp ;
int nv , nfaces , t ;
vector delta_vec ;
vector * basevert ; //a vertex on the base face
int i ;
float cos ;
vector save_verts [ MAX_VERTS_PER_FACE ] ; //save verts for face in case we bail
//Check that the two faces don't already have portals
if ( attroomp - > faces [ attface ] . portal_num ! = - 1 ) {
OutrageMessageBox ( " Cannot join: There is already a portal at %d:%d " , ROOMNUM ( attroomp ) , attface ) ;
return ;
}
if ( baseroomp - > faces [ baseface ] . portal_num ! = - 1 ) {
OutrageMessageBox ( " Cannot join: There is already a portal at %d:%d " , ROOMNUM ( baseroomp ) , baseface ) ;
return ;
}
//Get values from current room & face
afp = & attroomp - > faces [ attface ] ; //pointer to attach face
bfp = & baseroomp - > faces [ baseface ] ; //pointer to base face
nv = afp - > num_verts ; //number of verts in attach face
nfaces = nv + 2 ; //number of faces in new room
//Get a pointer to a vertex on the base face
basevert = & baseroomp - > verts [ bfp - > face_verts [ 0 ] ] ;
//Make sure all points on the attach face are on the front of the base face
//We could probably take this check out and catch the problem when we're moving the points
vector tnorm = - bfp - > normal ;
t = CheckFaceToPlane ( attroomp , attface , basevert , & tnorm ) ;
if ( t ! = - 1 ) {
if ( OutrageMessageBox ( MBOX_YESNO , " One or more points on the attach face are behind the base face. \n Do you still want to join? " ) ! = IDYES ) {
EditorStatus ( " Join aborted. " ) ;
return ;
}
}
//Make a list of all verts in the attach room that are in a portal
int portal_list [ MAX_VERTS_PER_ROOM ] ;
int num_portal_verts ;
num_portal_verts = BuildListOfPortalVerts ( attroomp , portal_list ) ;
//Save old verts
for ( i = 0 ; i < nv ; i + + )
save_verts [ i ] = attroomp - > verts [ afp - > face_verts [ i ] ] ;
int answer = OutrageMessageBox ( MBOX_YESNO , " Do you want to connect to the center of the base face? \n \n "
" Answer NO to extrude straight from the current face. " ) ;
//Compute normalized vector from attach face to base face.
//This is the direction in which we will move the attach face
if ( answer = = IDYES ) { //extrude toward the center of the base face
vector bc , ac ;
# ifndef NEWEDITOR
ComputeCenterPointOnFace ( & bc , baseroomp , baseface ) ;
ComputeCenterPointOnFace ( & ac , attroomp , attface ) ;
# else
ComputeFaceBoundingCircle ( & bc , baseroomp , baseface ) ;
ComputeFaceBoundingCircle ( & ac , attroomp , attface ) ;
# endif
vm_GetNormalizedDir ( & delta_vec , & bc , & ac ) ;
}
else { //extrude straight from attach face
delta_vec = afp - > normal ;
}
//Compute the cosine of the angle between our vector and the normal of the base face
cos = delta_vec * bfp - > normal ;
//If cos is zero, two center points are the same, so no move
if ( fabs ( cos ) < JOIN_EPSILON )
goto no_move_points ;
//Move the verts on the attach face to lie in the plane as the base face
for ( i = 0 ; i < nv ; i + + ) {
vector * vp = & attroomp - > verts [ afp - > face_verts [ i ] ] ;
float dist ;
//Get distance from base face to this vertex
dist = ( ( * basevert - * vp ) * bfp - > normal ) / cos ;
//If we're moving the point, make sure it's not part of a portal
if ( fabs ( dist ) > POINT_TO_PLANE_EPSILON ) {
//Look through list of portal verts for this vert
for ( t = 0 ; t < num_portal_verts ; t + + )
if ( portal_list [ t ] = = afp - > face_verts [ i ] ) { //Vert is in a portal. Abort!
OutrageMessageBox ( " Can't form joint because vertex %d on %d:%d is shared with a portal. \n \n Try doing the join the other way, or using the Bridge function. " , i , ROOMNUM ( attroomp ) , attface ) ;
goto abort_join ;
}
//Move the vert
* vp + = ( delta_vec * dist ) ;
}
}
//Recompute the normals in the room
if ( ! ResetRoomFaceNormals ( attroomp ) )
Int3 ( ) ; //Get Matt
//What about the uvs for the faces that changed?
no_move_points : ;
//Clip the connecting faces against each other
if ( ! ClipFacePair ( attroomp , attface , baseroomp , baseface ) ) {
OutrageMessageBox ( " Error making portal -- faces probably don't overlap. " ) ;
abort_join : ;
//Restore old verts
for ( i = 0 ; i < nv ; i + + )
attroomp - > verts [ afp - > face_verts [ i ] ] = save_verts [ i ] ;
//Recompute the normals in the room
if ( ! ResetRoomFaceNormals ( attroomp ) )
Int3 ( ) ; //Get Matt
return ;
}
//Make the two faces match exactly
MatchPortalFaces ( baseroomp , baseface , attroomp , attface ) ;
//Create the portals between the rooms
LinkRoomsSimple ( Rooms , ROOMNUM ( baseroomp ) , baseface , ROOMNUM ( attroomp ) , attface ) ;
//Set Changed flag
World_changed = 1 ;
}
//Connects two rooms if they already match up exactly
//Parameters: attroomp,attface - one end of the new room
// baseroom,baseface - the other end of the new room
void JoinRoomsExact ( room * attroomp , int attface , room * baseroomp , int baseface )
{
//Check that the two faces don't already have portals
if ( attroomp - > faces [ attface ] . portal_num ! = - 1 ) {
OutrageMessageBox ( " Cannot join: There is already a portal at %d:%d " , ROOMNUM ( attroomp ) , attface ) ;
return ;
}
if ( baseroomp - > faces [ baseface ] . portal_num ! = - 1 ) {
OutrageMessageBox ( " Cannot join: There is already a portal at %d:%d " , ROOMNUM ( baseroomp ) , baseface ) ;
return ;
}
//Check if we can do an exact match
if ( ! MatchPortalFaces ( baseroomp , baseface , attroomp , attface , 1 ) ) {
OutrageMessageBox ( " Cannot do exact join: These faces do not match " ) ;
return ;
}
//Make the two faces match exactly
MatchPortalFaces ( baseroomp , baseface , attroomp , attface ) ;
//Create the portals between the rooms
LinkRoomsSimple ( Rooms , ROOMNUM ( baseroomp ) , baseface , ROOMNUM ( attroomp ) , attface ) ;
//Set Changed flag
World_changed = 1 ;
}
2024-05-24 03:27:12 +00:00
bool FaceIsPlanar ( int nv , int16_t * face_verts , vector * normal , vector * verts ) ;
2024-04-19 20:58:24 +00:00
//Combine two faces, if they can be combined
//Parameters: rp - the room the faces are in
// face0,face1 - the two faces
//Returns: true if the faces were combined, else false
//Note: The UV coordinates of the new face are derrived from face0
bool CombineFaces ( room * rp , int face0 , int face1 )
{
face * fp0 = & rp - > faces [ face0 ] , * fp1 = & rp - > faces [ face1 ] ;
int nv0 = fp0 - > num_verts , nv1 = fp1 - > num_verts ;
int v0 , v1 ;
2024-05-24 03:27:12 +00:00
int16_t vertlist [ MAX_VERTS_PER_FACE ] ;
2024-04-19 20:58:24 +00:00
roomUVL uvllist [ MAX_VERTS_PER_FACE ] ;
int i , nv ;
int first0 , first1 , n0 , n1 ;
ASSERT ( face0 ! = face1 ) ;
//Check for portals
if ( ( fp0 - > portal_num ! = - 1 ) | | ( fp1 - > portal_num ! = - 1 ) ) {
# ifndef NEWEDITOR
SetErrorMessage ( " You cannot combine portal faces. \n \n MattT will add this functionality if you need it. Bug him about it. " ) ;
# else
SetErrorMessage ( " You cannot combine portal faces. " ) ;
# endif
return 0 ;
}
//Make sure faces share an edge
if ( ! FindSharedEdge ( fp0 , fp1 , & v0 , & v1 ) ) {
# ifdef NEWEDITOR
if ( ned_FindSharedEdge ( fp0 , fp1 , & v0 , & v1 ) )
SetErrorMessage ( " The face normals point in opposite directions. " ) ;
else
# endif
SetErrorMessage ( " The faces do not share an edge. " ) ;
return 0 ;
}
//Get indices for first & last points in each face. One shared point is copied with each face.
first0 = v0 + 1 ;
n0 = nv0 - 1 ;
first1 = v1 + 1 ;
n1 = nv1 - 1 ;
//See if faces share additional edges
while ( 1 ) { //check forward on face0, backward on face1
int check0 = ( first0 + 1 ) % nv0 ,
check1 = ( first1 + n1 - 1 ) % nv1 ;
if ( fp0 - > face_verts [ check0 ] ! = fp1 - > face_verts [ check1 ] )
break ;
first0 + + ; n0 - - ;
n1 - - ;
}
while ( 1 ) { //check forward on face1, backward on face0
int check1 = ( first1 + 1 ) % nv1 ,
check0 = ( first0 + n0 - 1 ) % nv0 ;
if ( fp0 - > face_verts [ check0 ] ! = fp1 - > face_verts [ check1 ] )
break ;
first1 + + ; n1 - - ;
n0 - - ;
}
//Build list of verts for new face
nv = 0 ;
for ( i = 0 ; i < n0 ; i + + ) { //Add points from first face
vertlist [ nv ] = fp0 - > face_verts [ ( first0 + i ) % nv0 ] ;
uvllist [ nv ] = fp0 - > face_uvls [ ( first0 + i ) % nv0 ] ;
nv + + ;
}
for ( i = 0 ; i < n1 ; i + + ) { //Add points from second face
vertlist [ nv ] = fp1 - > face_verts [ ( first1 + i ) % nv1 ] ;
uvllist [ nv ] = fp1 - > face_uvls [ ( first1 + i ) % nv1 ] ;
nv + + ;
}
//Calculate new normal
vector new_normal ;
ComputeNormal ( & new_normal , nv , vertlist , rp - > verts ) ;
//Check if new face is planar
if ( ! FaceIsPlanar ( nv , vertlist , & new_normal , rp - > verts ) ) {
SetErrorMessage ( " The new face would not be planar. " ) ;
return 0 ;
}
//Check if new face is convex
int t ;
if ( ( t = CheckFaceConcavity ( nv , vertlist , & new_normal , rp - > verts ) ) ! = - 1 ) {
SetErrorMessage ( " The new face would be concave (at vertex %d). " , vertlist [ t ] ) ;
return 0 ;
}
//Reset face0 for new verts
ReInitRoomFace ( fp0 , nv ) ;
//Copy into new face
for ( i = 0 ; i < nv ; i + + ) {
fp0 - > face_verts [ i ] = vertlist [ i ] ;
fp0 - > face_uvls [ i ] = uvllist [ i ] ;
}
//Set new normal
fp0 - > normal = new_normal ;
//Reassign uvls in face
AssignUVsToFace ( rp , face0 , & fp0 - > face_uvls [ 0 ] , & fp0 - > face_uvls [ 1 ] , 0 , 1 ) ;
//Delete the old face
DeleteRoomFace ( rp , face1 ) ;
//Set flag
World_changed = 1 ;
//Everything's ok
return 1 ;
}
//Deletes the given room from the mine
void DeleteRoomFromMine ( room * rp )
{
int i , p ;
//See if this room is linked to the terrain
for ( p = 0 ; p < rp - > num_portals ; p + + ) {
portal * pp = & rp - > portals [ p ] ;
if ( pp - > croom = = - 1 ) {
OutrageMessageBox ( " You can't delete a room that attaches to the terrain. Detach the room before deleting. " ) ;
return ;
}
}
//Check if player or viewer object in this room
for ( i = rp - > objects ; i ! = - 1 ; i = Objects [ i ] . next ) {
//Is this the player object?
if ( i = = OBJNUM ( Player_object ) ) {
OutrageMessageBox ( " You can't delete the room with the player in it. " ) ;
return ;
}
# ifndef NEWEDITOR
//Is this the viewer object?
if ( i = = OBJNUM ( Viewer_object ) ) { //Try to find another viewer
int objnum = FindNextViewerObject ( Viewer_object - > id + 1 , - 1 ) ;
if ( objnum = = OBJNUM ( Viewer_object ) ) {
OutrageMessageBox ( " You can't delete the room with the only viewer in it. " ) ;
return ;
}
else
SelectNextViewer ( ) ;
}
# endif
}
// count the number of rooms used. if only one, don't delete it.
for ( i = 0 ; i < = Highest_room_index ; i + + )
if ( Rooms [ i ] . used & & ( i ! = ROOMNUM ( rp ) ) )
break ;
if ( i > Highest_room_index ) {
OutrageMessageBox ( " You can't delete the only room in the mine. " ) ;
return ;
}
//If this is current room, set new current room
if ( rp = = Curroomp ) {
room * newroomp = NULL ;
//Look for connected room
for ( p = 0 ; p < rp - > num_portals ; p + + )
if ( rp - > portals [ p ] . croom ! = - 1 ) {
newroomp = & Rooms [ rp - > portals [ 0 ] . croom ] ;
break ;
}
//If didn't find connected room, look for any room
if ( newroomp = = NULL )
{
for ( i = 0 ; i < = Highest_room_index ; i + + )
{
if ( Rooms [ i ] . used & & ( i ! = ROOMNUM ( rp ) ) )
{
newroomp = & Rooms [ i ] ;
break ;
}
}
}
ASSERT ( newroomp ! = NULL ) ;
# ifndef NEWEDITOR
Curroomp = newroomp ;
Curface = Curedge = Curvert = 0 ;
Curportal = - 1 ;
# else
theApp . m_pLevelWnd - > SetPrim ( newroomp , 0 , - 1 , 0 , 0 ) ;
# endif
EditorStatus ( " Current room set to %d. " , ROOMNUM ( Curroomp ) ) ;
}
//Delete connections
while ( rp - > num_portals ) {
portal * pp = & rp - > portals [ 0 ] ;
ASSERT ( pp - > croom ! = - 1 ) ;
DeleteRoomPortal ( & Rooms [ pp - > croom ] , pp - > cportal ) ;
DeleteRoomPortal ( rp , 0 ) ;
}
//If this is marked room, clear marked room
if ( Markedroomp = = rp )
Markedroomp = NULL ;
//Free the objects in this room
for ( i = rp - > objects ; i ! = - 1 ; i = Objects [ i ] . next )
{
# ifdef NEWEDITOR
if ( IS_GENERIC ( Objects [ i ] . type ) )
LevelObjDecrementObject ( Objects [ i ] . id ) ;
else if ( Objects [ i ] . type = = OBJ_DOOR )
{
ASSERT ( rp - > flags & RF_DOOR ) ;
LevelDoorDecrementDoorway ( Objects [ i ] . id ) ;
}
# endif
ObjDelete ( i ) ;
}
# ifdef NEWEDITOR
// Unmark the textures in this room
for ( i = 0 ; i < rp - > num_faces ; i + + )
LevelTexDecrementTexture ( rp - > faces [ i ] . tmap ) ;
# endif
//Delete any triggers in this room
face * fp ;
for ( i = 0 , fp = rp - > faces ; i < rp - > num_faces ; fp + + , i + + )
if ( fp - > flags & FF_HAS_TRIGGER )
DeleteTrigger ( ROOMNUM ( rp ) , i ) ;
//Remove this room from the selected list (if there)
RemoveRoomFromSelectedList ( ROOMNUM ( rp ) ) ;
//Free up the room
FreeRoom ( rp ) ;
}
# define DROP_ROOM_OFFSET 20.0
//Places a room a short distance from the specified room & face
//The new room is not attached to anything
//Parameters: baseroomp,baseface - the new room is dropped off of this face
// droproom_num - the room to be dropped
void DropRoom ( room * baseroomp , int baseface , int droproom_num )
{
int roomnum ;
room * newroomp ;
vector oldcenter , facecenter , newcenter , offset ;
float rad ;
//Create a new room
roomnum = GetFreeRoom ( 0 ) ;
if ( roomnum = = - 1 ) {
OutrageMessageBox ( " Cannot drop room: No free rooms. " ) ;
return ;
}
newroomp = & Rooms [ roomnum ] ;
//Copy data to new room
CopyRoom ( newroomp , & Rooms [ droproom_num ] ) ;
//Compute offset
rad = ComputeRoomBoundingSphere ( & oldcenter , newroomp ) ;
# ifndef NEWEDITOR
ComputeCenterPointOnFace ( & facecenter , baseroomp , baseface ) ;
# else
ComputeFaceBoundingCircle ( & facecenter , baseroomp , baseface ) ;
# endif
newcenter = facecenter - ( baseroomp - > faces [ baseface ] . normal * ( rad + DROP_ROOM_OFFSET ) ) ;
offset = newcenter - oldcenter ;
//Add in offset to all the points
for ( int i = 0 ; i < newroomp - > num_verts ; i + + )
newroomp - > verts [ i ] + = offset ;
//Set current room to new room
# ifndef NEWEDITOR
Curroomp = newroomp ;
Curface = Curedge = Curvert = 0 ;
Curportal = - 1 ;
# else
theApp . m_pLevelWnd - > SetPrim ( newroomp , 0 , - 1 , 0 , 0 ) ;
# endif
//We're done
World_changed = 1 ;
}
//Place a room on the terrain for orientation before attachment
//Parameters: cellnum - the cell where the room is being placed
// placed_room - the number of the room to be attached
// placed_room_face the face on placed_room that's attached
void PlaceExternalRoom ( int cellnum , int placed_room , int placed_room_face , bool align_to_terrain )
{
room * placedroomp = & Rooms [ placed_room ] ;
//Clear the placed group if one exists
Placed_group = NULL ;
//Clear the placed door if one exists
if ( Placed_door ! = - 1 ) {
ASSERT ( Placed_room ! = - 1 ) ;
FreeRoom ( & Rooms [ Placed_room ] ) ;
Placed_door = Placed_door = - 1 ;
}
//Set globals
Placed_room = placed_room ;
Placed_room_face = placed_room_face ;
Placed_room_angle = 0 ;
Placed_baseroomp = NULL ;
if ( align_to_terrain )
Placed_room_orient . fvec = TerrainNormals [ MAX_TERRAIN_LOD - 1 ] [ cellnum ] . normal1 ;
else
Placed_room_orient . fvec = Identity_matrix . uvec ;
//Compute attach point on terrain
ComputeTerrainSegmentCenter ( & Placed_room_attachpoint , cellnum ) ;
//Compute attach points on each face
# ifndef NEWEDITOR
ComputeCenterPointOnFace ( & Placed_room_origin , placedroomp , placed_room_face ) ;
# else
ComputeFaceBoundingCircle ( & Placed_room_origin , placedroomp , placed_room_face ) ;
# endif
//Compute initial orientation matrix
ComputePlacedRoomMatrix ( ) ;
//Set the flag
State_changed = 1 ;
}
//Find all the matching faces between two rooms and join them
//Returns the number of new portals created
int JoinAllMatchingFaces ( room * rp0 , room * rp1 )
{
face * fp0 , * fp1 ;
int f0 , f1 ;
int join_count = 0 ;
//Check each face in room 0 against each in room 1
for ( f0 = 0 , fp0 = rp0 - > faces ; f0 < rp0 - > num_faces ; f0 + + , fp0 + + ) {
//Check if face 0 is already part of portal
if ( fp0 - > portal_num ! = - 1 )
continue ;
//Check current face against all faces in room 1
for ( f1 = 0 , fp1 = rp1 - > faces ; f1 < rp1 - > num_faces ; f1 + + , fp1 + + ) {
//Check if face 1 is already part of portal
if ( fp1 - > portal_num ! = - 1 )
continue ;
//First check if faces have same number of verts
//This used to check if the normals were the same, but I decided that was bad. -MT, 3/30/99
if ( ( fp0 - > num_verts = = fp1 - > num_verts ) ) {
int i , j , n ;
//Find one point in common
for ( i = 0 ; i < fp0 - > num_verts ; i + + ) {
for ( j = 0 ; j < fp1 - > num_verts ; j + + )
if ( PointsAreSame ( & rp0 - > verts [ fp0 - > face_verts [ i ] ] , & rp1 - > verts [ fp1 - > face_verts [ j ] ] ) )
break ;
if ( j < fp1 - > num_verts )
break ;
}
if ( i > = fp0 - > num_verts ) //Couldn't find a match
continue ; //..so go on to next face
//Trace through verts in faces, making sure they match
for ( n = 1 ; n < fp0 - > num_verts ; n + + ) {
vector * v0 , * v1 ;
v0 = & rp0 - > verts [ fp0 - > face_verts [ ( i + n ) % fp0 - > num_verts ] ] ;
v1 = & rp1 - > verts [ fp1 - > face_verts [ ( j - n + fp1 - > num_verts ) % fp1 - > num_verts ] ] ;
if ( ! PointsAreSame ( v0 , v1 ) ) //Found mismatch
break ;
* v0 = * v1 ; //make points *exactly* the same
}
if ( n < fp0 - > num_verts ) //Found a mismatch
continue ; //..so go on to next face
//Found extact match, so join
LinkRoomsSimple ( Rooms , ROOMNUM ( rp0 ) , f0 , ROOMNUM ( rp1 ) , f1 ) ;
//Increment count
join_count + + ;
}
}
}
return join_count ;
}
//Find all the overlapping faces between two rooms and join them
//Returns the number of new portals created
int JoinAllOverlappingFaces ( room * rp0 , room * rp1 )
{
face * fp0 , * fp1 ;
int f0 , f1 ;
int join_count = 0 ;
//Check each face in room 0 against each in room 1
for ( f0 = 0 , fp0 = rp0 - > faces ; f0 < rp0 - > num_faces ; f0 + + , fp0 + + ) {
//Check if face 0 is already part of portal
if ( fp0 - > portal_num ! = - 1 )
continue ;
//Check current face against all faces in room 1
for ( f1 = 0 , fp1 = rp1 - > faces ; f1 < rp1 - > num_faces ; f1 + + , fp1 + + ) {
//Check if face 1 is already part of portal
if ( fp1 - > portal_num ! = - 1 )
continue ;
//This used to check if normals the same, but I decided that was bad. -MT, 3/30/99
{
int i ;
vector * planepoint , * norm ;
//Normals are same, so check if points are all in the same plane
planepoint = & rp1 - > verts [ fp1 - > face_verts [ 0 ] ] ;
norm = & fp1 - > normal ;
for ( i = 0 ; i < fp0 - > num_verts ; i + + )
if ( CheckPointToPlane ( & rp0 - > verts [ fp0 - > face_verts [ i ] ] , planepoint , norm ) )
break ;
if ( i < fp0 - > num_verts )
continue ;
planepoint = & rp0 - > verts [ fp0 - > face_verts [ 0 ] ] ;
norm = & fp0 - > normal ;
for ( i = 0 ; i < fp1 - > num_verts ; i + + )
if ( CheckPointToPlane ( & rp1 - > verts [ fp1 - > face_verts [ i ] ] , planepoint , norm ) )
break ;
if ( i < fp1 - > num_verts )
continue ;
//Clip the connecting faces against each other
if ( ClipFacePair ( rp0 , f0 , rp1 , f1 ) ) {
//Make the two faces match exactly
MatchPortalFaces ( rp0 , f0 , rp1 , f1 ) ;
//Create the portals between the rooms
LinkRoomsSimple ( Rooms , ROOMNUM ( rp0 ) , f0 , ROOMNUM ( rp1 ) , f1 ) ;
//Reset face pointer, which may have been changed by the clip
fp0 = & rp0 - > faces [ f0 ] ;
//Increment count
join_count + + ;
//Stop checking this face, since we've formed a portal for it
break ;
}
else { //it's kind of bad if the clip fails, but we have to live with it
//Reset face pointers, which may have been changed by the clip
fp0 = & rp0 - > faces [ f0 ] ;
fp1 = & rp1 - > faces [ f1 ] ;
}
}
}
}
return join_count ;
}
//Find all the adjacent faces between two rooms and join them
void JoinAllAdjacentFaces ( room * rp0 , room * rp1 )
{
int join_count ;
int answer ;
answer = OutrageMessageBox ( MBOX_YESNOCANCEL , " Do you want to include non-matching faces? \n \n "
" Selecting YES means faces will be joined if they overlap. \n "
" Selecting NO means faces much match exactly. " ) ;
if ( answer = = IDCANCEL )
return ;
if ( answer = = IDYES )
join_count = JoinAllOverlappingFaces ( rp0 , rp1 ) ;
else
join_count = JoinAllMatchingFaces ( rp0 , rp1 ) ;
World_changed = 1 ;
OutrageMessageBox ( " %d new portals created. \n " , join_count ) ;
}
int FindNeighbor ( room * rp , int facenum , int edgenum )
{
face * fp = & rp - > faces [ facenum ] ;
int v0 , v1 , f , v ;
v0 = fp - > face_verts [ edgenum ] ;
v1 = fp - > face_verts [ ( edgenum + 1 ) % fp - > num_verts ] ;
for ( f = 0 , fp = rp - > faces ; f < rp - > num_faces ; f + + , fp + + )
for ( v = 0 ; v < fp - > num_verts ; v + + )
if ( ( fp - > face_verts [ v ] = = v1 ) & & ( fp - > face_verts [ ( v + 1 ) % fp - > num_verts ] = = v0 ) )
return f ;
return - 1 ;
}
2024-05-24 03:07:26 +00:00
void PropagateFromFace ( room * rp , int facenum , uint8_t * face_flags , bool matching_faces_only )
2024-04-19 20:58:24 +00:00
{
face * fp = & rp - > faces [ facenum ] ;
face_flags [ facenum ] = 1 ;
for ( int v = 0 ; v < fp - > num_verts ; v + + ) {
int t ;
t = FindNeighbor ( rp , facenum , v ) ;
if ( ( t ! = - 1 ) & & ! face_flags [ t ] )
if ( ! matching_faces_only | | ( rp - > faces [ t ] . tmap = = fp - > tmap ) ) {
HTexturePropagateToFace ( rp , t , rp , facenum ) ;
PropagateFromFace ( rp , t , face_flags , matching_faces_only ) ;
}
}
}
//Propagate a texture from one face to all the faces in the room
//If matching_faces_only is set, only propagate if the face has the same texture
void PropagateToAllFaces ( room * rp , int facenum , bool matching_faces_only )
{
2024-05-24 03:07:26 +00:00
uint8_t face_flags [ MAX_FACES_PER_ROOM ] ;
2024-04-19 20:58:24 +00:00
//Clear flags
for ( int i = 0 ; i < rp - > num_faces ; i + + )
face_flags [ i ] = 0 ;
//Start recursive function
PropagateFromFace ( rp , facenum , face_flags , matching_faces_only ) ;
//Set flag
World_changed = 1 ;
}
//Splits a face unto triangles, by fanning
//Parameters: rp,facenum - the face to triangulate
// vertnum - the vert that is the base of the fan
void TriangulateFace ( room * rp , int facenum , int vertnum )
{
face * fp = & rp - > faces [ facenum ] , * nfp ;
int num_new_faces , first_new_face , i ;
if ( fp - > num_verts = = 3 ) {
OutrageMessageBox ( " Face %d has three verts and cannot be split. " , facenum ) ;
return ;
}
if ( fp - > portal_num ! = - 1 ) {
OutrageMessageBox ( " You cannot split a face that's part of a portal. " ) ;
return ;
}
if ( fp - > flags & ( FF_HAS_TRIGGER + FF_FLOATING_TRIG ) ) {
OutrageMessageBox ( " You cannot split a face that has a trigger. " ) ;
return ;
}
//Add new faces
num_new_faces = fp - > num_verts - 2 ;
first_new_face = RoomAddFaces ( rp , num_new_faces ) ;
//Reset pointer to our old face
fp = & rp - > faces [ facenum ] ;
//Set up each face
for ( i = 0 , nfp = & rp - > faces [ first_new_face ] ; i < num_new_faces ; i + + , nfp + + ) {
InitRoomFace ( nfp , 3 ) ;
//Set verts & uvls for this face
nfp - > face_verts [ 0 ] = fp - > face_verts [ vertnum ] ;
nfp - > face_uvls [ 0 ] = fp - > face_uvls [ vertnum ] ;
for ( int v = 1 ; v < 3 ; v + + ) {
nfp - > face_verts [ v ] = fp - > face_verts [ ( vertnum + i + v ) % fp - > num_verts ] ;
nfp - > face_uvls [ v ] = fp - > face_uvls [ ( vertnum + i + v ) % fp - > num_verts ] ;
}
//Copy other data for this face
CopyFaceFlags ( nfp , fp ) ;
# ifndef NEWEDITOR
nfp - > tmap = fp - > tmap ;
# else
// Apply texture
HTextureApplyToRoomFace ( rp , first_new_face + i , nfp - > tmap ) ;
# endif
nfp - > light_multiple = fp - > light_multiple ;
//Compute the normal for this face
ComputeFaceNormal ( rp , first_new_face + i ) ;
}
//Delete original face
DeleteRoomFace ( rp , facenum ) ;
//Check for degenerate faces (caused by colinear points) and merge with adjacent faces
first_new_face - - ; //original face deleted, causing shift
bool ok ;
do { //Check first face
ok = ComputeFaceNormal ( rp , first_new_face ) ;
if ( ! ok ) {
face * fp = & rp - > faces [ first_new_face ] ;
ASSERT ( first_new_face < rp - > num_faces - 1 ) ;
AddVertToFace ( rp , first_new_face , rp - > faces [ first_new_face + 1 ] . face_verts [ 2 ] , fp - > num_verts - 1 ) ;
rp - > faces [ first_new_face ] . face_uvls [ rp - > faces [ first_new_face ] . num_verts - 1 ] = rp - > faces [ first_new_face + 1 ] . face_uvls [ 2 ] ;
DeleteRoomFace ( rp , first_new_face + 1 ) ;
num_new_faces - - ;
}
} while ( ! ok ) ;
do { //Check last face
ok = ComputeFaceNormal ( rp , rp - > num_faces - 1 ) ;
if ( ! ok ) {
face * fp = & rp - > faces [ rp - > num_faces - 2 ] ;
ASSERT ( first_new_face < rp - > num_faces - 1 ) ;
AddVertToFace ( rp , rp - > num_faces - 1 , rp - > faces [ rp - > num_faces - 2 ] . face_verts [ 1 ] , 0 ) ;
rp - > faces [ rp - > num_faces - 1 ] . face_uvls [ 1 ] = rp - > faces [ rp - > num_faces - 2 ] . face_uvls [ 1 ] ;
DeleteRoomFace ( rp , rp - > num_faces - 2 ) ;
num_new_faces - - ;
}
} while ( ! ok ) ;
//We're done
EditorStatus ( " Face %d replace by %d new faces " , facenum , num_new_faces ) ;
# ifndef NEWEDITOR
World_changed = 1 ;
# endif
}
//Deletes the connection between two rooms. Deletes both portals.
void DeletePortalPair ( room * rp , int portalnum )
{
int roomnum = ROOMNUM ( rp ) ;
portal * pp = & rp - > portals [ portalnum ] ;
int croom = pp - > croom , cportal = pp - > cportal ;
//Make sure the connecting portal points back to this portal
if ( ( Rooms [ croom ] . portals [ cportal ] . croom ! = roomnum ) | | ( Rooms [ croom ] . portals [ cportal ] . cportal ! = portalnum ) ) {
OutrageMessageBox ( " Cannot delete this portal: the connecting portal does not point back at it. \n " ) ;
return ;
}
//Check all the faces in this portal to make sure they point back to the portal
if ( rp - > faces [ pp - > portal_face ] . portal_num ! = portalnum ) {
OutrageMessageBox ( " Cannot delete this portal: its face does not point back at it. " ) ;
return ;
}
DeleteRoomPortal ( rp , portalnum ) ;
DeleteRoomPortal ( & Rooms [ croom ] , cportal ) ;
EditorStatus ( " Deleted room %d portal %d and room %d portal %d " , roomnum , portalnum , croom , cportal ) ;
World_changed = 1 ;
}
//Flips a face
void FlipFace ( room * rp , int facenum )
{
face * fp = & rp - > faces [ facenum ] ;
ASSERT ( fp - > portal_num = = - 1 ) ;
for ( int i = 0 ; i < fp - > num_verts / 2 ; i + + ) {
int v = fp - > face_verts [ i ] ;
fp - > face_verts [ i ] = fp - > face_verts [ fp - > num_verts - 1 - i ] ;
fp - > face_verts [ fp - > num_verts - 1 - i ] = v ;
roomUVL uvl = fp - > face_uvls [ i ] ;
fp - > face_uvls [ i ] = fp - > face_uvls [ fp - > num_verts - 1 - i ] ;
fp - > face_uvls [ fp - > num_verts - 1 - i ] = uvl ;
}
//Get new normal
if ( ! ComputeFaceNormal ( rp , facenum ) )
Int3 ( ) ; //Bad! Get Matt!
# ifndef NEWEDITOR
World_changed = 1 ;
EditorStatus ( " Room %d face %d flipped. " , ROOMNUM ( rp ) , facenum ) ;
# else
if ( ROOMNUM ( rp ) < MAX_ROOMS )
EditorStatus ( " Room %d face %d flipped. " , ROOMNUM ( rp ) , facenum ) ;
else
EditorStatus ( " Face %d flipped. " , facenum ) ;
# endif
}
//Attempt to fix the cracks in a level
void FixCracks ( )
{
int r , p , f , f2 , v , v2 ;
room * rp ;
face * fp , * fp2 ;
int possible_tjoints = 0 , tjoints_fixed = 0 ;
int points_added = 0 ;
int points_are_same_count = 0 ;
if ( OutrageMessageBox ( MBOX_YESNO , " This function is somewhat dangerous. \n \n "
" You should be sure to save your level beforehand, and you should check the results very carefully afterwards. \n \n "
" Continue? " ) ! = IDYES )
return ;
//First fix gaps at portals
for ( r = 0 , rp = Rooms ; r < = Highest_room_index ; r + + , rp + + ) {
if ( rp - > used ) {
for ( p = 0 ; p < rp - > num_portals ; p + + ) {
portal * pp = & rp - > portals [ p ] ;
points_added + = MatchPortalFaces ( rp , pp - > portal_face , & Rooms [ pp - > croom ] , Rooms [ pp - > croom ] . portals [ pp - > cportal ] . portal_face ) ;
}
}
}
//Now search for and fix t-joints
for ( r = 0 , rp = Rooms ; r < = Highest_room_index ; r + + , rp + + ) {
if ( rp - > used )
for ( f = 0 , fp = rp - > faces ; f < rp - > num_faces ; f + + , fp + + ) {
//if (fp->portal_num == -1)
{ //don't check portal faces
recheck_face : ;
for ( v = 0 ; v < fp - > num_verts ; v + + ) {
int vv0 = fp - > face_verts [ v ] , vv1 = fp - > face_verts [ ( v + 1 ) % fp - > num_verts ] ;
for ( f2 = 0 , fp2 = rp - > faces ; f2 < rp - > num_faces ; f2 + + , fp2 + + ) {
if ( f2 ! = f ) {
for ( v2 = 0 ; v2 < fp2 - > num_verts ; v2 + + ) {
int tt0 = fp2 - > face_verts [ v2 ] , tt1 = fp2 - > face_verts [ ( v2 + 1 ) % fp2 - > num_verts ] ;
if ( ( vv0 = = tt1 ) & & ( vv1 = = tt0 ) )
break ; //found one, so stop
}
if ( v2 < fp2 - > num_verts )
break ;
}
}
if ( f2 = = rp - > num_faces ) { //didn't find a match
2024-05-24 15:46:07 +00:00
//mprintf(0,"Couldn't find shared edge for %d:%d edge %d\n",r,f,v);
2024-04-19 20:58:24 +00:00
possible_tjoints + + ;
//Look for vert on this edge
for ( f2 = 0 , fp2 = rp - > faces ; f2 < rp - > num_faces ; f2 + + , fp2 + + ) {
if ( f2 ! = f ) {
for ( v2 = 0 ; v2 < fp2 - > num_verts ; v2 + + ) {
int tt0 = fp2 - > face_verts [ v2 ] , tt1 = fp2 - > face_verts [ ( v2 + 1 ) % fp2 - > num_verts ] ;
if ( vv0 = = tt1 ) { //one point maches; check if next is on the edge
//if (PointsAreColinear(&rp->verts[vv0],&rp->verts[tt0],&rp->verts[vv1])) {
if ( PointsAreSame ( & rp - > verts [ tt0 ] , & rp - > verts [ vv0 ] ) )
points_are_same_count + + ;
if ( PointsAreSame ( & rp - > verts [ tt0 ] , & rp - > verts [ vv1 ] ) )
points_are_same_count + + ;
if ( CheckPointToPlane ( & rp - > verts [ tt0 ] , & rp - > verts [ vv0 ] , & fp - > normal ) = = 0 )
{
if ( CheckPointAgainstEdge ( & rp - > verts [ tt0 ] , & rp - > verts [ vv0 ] , & rp - > verts [ vv1 ] , & fp - > normal ) = = 0 ) {
//make sure the new point is actually between the two edge points
float edge_len = vm_VectorDistance ( & rp - > verts [ vv1 ] , & rp - > verts [ vv0 ] ) ;
float d0 = vm_VectorDistance ( & rp - > verts [ tt0 ] , & rp - > verts [ vv0 ] ) ;
float d1 = vm_VectorDistance ( & rp - > verts [ tt0 ] , & rp - > verts [ vv1 ] ) ;
if ( ( d0 < edge_len ) & & ( d1 < edge_len ) ) {
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " Adding %d to edge %d of %d:%d (from face %d) \n " , tt0 , v , r , f , f2 ) ;
2024-04-19 20:58:24 +00:00
AddVertToFace ( rp , f , tt0 , v ) ;
tjoints_fixed + + ;
goto recheck_face ;
}
}
}
}
}
}
}
}
}
}
}
}
//Fix portals again because points may have been added when fixing t-joints
for ( r = 0 , rp = Rooms ; r < = Highest_room_index ; r + + , rp + + ) {
if ( rp - > used ) {
for ( p = 0 ; p < rp - > num_portals ; p + + ) {
portal * pp = & rp - > portals [ p ] ;
points_added + = MatchPortalFaces ( rp , pp - > portal_face , & Rooms [ pp - > croom ] , Rooms [ pp - > croom ] . portals [ pp - > cportal ] . portal_face ) ;
}
}
}
2024-05-24 15:46:07 +00:00
mprintf ( 0 , " %d possible t-joints \n " , possible_tjoints ) ;
mprintf ( 0 , " points_are_same_count = %d \n " , points_are_same_count ) ;
2024-04-19 20:58:24 +00:00
OutrageMessageBox ( " Level fix results: \n \n "
" %d points added to portals \n "
" %d T-joints fixed " ,
points_added , tjoints_fixed ) ;
World_changed = 1 ;
}
//Variables for undoing a snap
int Snap_roomnum = - 1 ;
int Snap_vertnum ;
vector Snap_old_point , Snap_new_point ;
//Moves a vertex to lie on a specified edge
void SnapPointToEdge ( room * rp , int vertnum , vector * v0 , vector * v1 )
{
vector * vp = & rp - > verts [ vertnum ] ;
vector edge_vec , edge_normal ;
float d ;
//Save info for undo
Snap_roomnum = ROOMNUM ( rp ) ;
Snap_vertnum = vertnum ;
Snap_old_point = * vp ;
//Calculate new point
edge_normal = edge_vec = * v1 - * v0 ;
vm_NormalizeVector ( & edge_normal ) ;
d = vm_DistToPlane ( vp , & edge_normal , v0 ) ;
* vp = * v0 + edge_normal * d ;
//Set the point
Snap_new_point = * vp ;
//Done
EditorStatus ( " Room %d point %d snapped. " , Snap_roomnum , Snap_vertnum ) ;
World_changed = 1 ;
}
//Moves a vertex to be conincident with another vertex
void SnapPointToPoint ( room * rp , int vertnum , room * snapto_rp , int snapto_vertnum )
{
vector * vp = & rp - > verts [ vertnum ] ;
//Save info for undo
Snap_roomnum = ROOMNUM ( rp ) ;
Snap_vertnum = vertnum ;
Snap_old_point = * vp ;
* vp = snapto_rp - > verts [ snapto_vertnum ] ;
//Set the point
Snap_new_point = * vp ;
//Done
EditorStatus ( " Room %d point %d snapped. " , Snap_roomnum , Snap_vertnum ) ;
World_changed = 1 ;
}
//Moves a vertex to lie on a specified plane
void SnapPointToFace ( room * rp , int vertnum , vector * v0 , vector * normal )
{
vector * vp = & rp - > verts [ vertnum ] ;
float d ;
//Save info for undo
Snap_roomnum = ROOMNUM ( rp ) ;
Snap_vertnum = vertnum ;
Snap_old_point = * vp ;
//Calculate new point
d = vm_DistToPlane ( vp , normal , v0 ) ;
* vp - = * normal * d ;
//Set the point
Snap_new_point = * vp ;
//Done
EditorStatus ( " Room %d point %d snapped. " , Snap_roomnum , Snap_vertnum ) ;
World_changed = 1 ;
}
//Undoes the most recently-performed snap
void UndoSnap ( )
{
if ( Snap_roomnum = = - 1 )
return ;
if ( ( Snap_roomnum < = Highest_room_index ) & & Rooms [ Snap_roomnum ] . used ) {
vector * vp = & Rooms [ Snap_roomnum ] . verts [ Snap_vertnum ] ;
if ( ( vp - > x = = Snap_new_point . x ) & & ( vp - > y = = Snap_new_point . y ) & & ( vp - > z = = Snap_new_point . z ) ) {
* vp = Snap_old_point ;
World_changed = 1 ;
return ;
}
}
OutrageMessageBox ( " Cannot Undo: The snapped point has changed since the snap. " ) ;
Snap_roomnum = - 1 ;
}
//Gets the angle between two vectors in the range 0..1
//Parameters: base - the start of both vectors
// v0,v1 - the end points of the two vectors
// normal - the surface normal of the face these verts lie in
//Returns the cosine of the angle between <base,v0> and <base,v1>
float GetAngle ( vector * base , vector * v0 , vector * v1 , vector * normal )
{
vector edge0 , edge1 , cross ;
float dot ;
vm_GetNormalizedDir ( & edge0 , v0 , base ) ;
vm_GetNormalizedDir ( & edge1 , v1 , base ) ;
dot = edge0 * edge1 ;
cross = edge0 ^ edge1 ;
if ( cross * * normal > 0 )
return 0.25 - dot / 4.0 ;
else
return 0.75 + dot / 4.0 ;
}
//Connects two rooms in a pleasing way
void BuildSmoothBridge ( room * rp0 , int facenum0 , room * rp1 , int facenum1 )
{
room * rp ;
int nv0 , nv1 , nverts , nfaces ;
int i ;
vector tverts [ MAX_VERTS_PER_FACE ] ;
//Get values from current faces
face * fp0 = & rp0 - > faces [ facenum0 ] ;
face * fp1 = & rp1 - > faces [ facenum1 ] ;
nv0 = fp0 - > num_verts ;
nv1 = fp1 - > num_verts ;
//Check for portal already here
if ( fp0 - > portal_num ! = - 1 ) {
OutrageMessageBox ( " Cannot build bridge: There is already a connection at %d:%d. " , ROOMNUM ( rp0 ) , facenum0 ) ;
return ;
}
if ( fp1 - > portal_num ! = - 1 ) {
OutrageMessageBox ( " Cannot build bridge: There is already a connection at %d:%d. " , ROOMNUM ( rp1 ) , facenum1 ) ;
return ;
}
//Get values for new room
nverts = nv0 + nv1 ;
nfaces = nverts + 2 ;
//Get a pointer to our room
rp = CreateNewRoom ( nverts , nfaces ) ;
if ( rp = = NULL ) {
OutrageMessageBox ( " Cannot build bridge: No free rooms. " ) ;
return ;
}
//Set the vertices for the room
for ( i = 0 ; i < nv0 ; i + + )
rp - > verts [ i ] = rp0 - > verts [ fp0 - > face_verts [ i ] ] ;
for ( i = 0 ; i < nv1 ; i + + )
rp - > verts [ nv0 + i ] = rp1 - > verts [ fp1 - > face_verts [ nv1 - 1 - i ] ] ;
//Create the connecting faces
//Compute normalized vector from attach face to base face
vector c0 , c1 , delta_vec ;
# ifndef NEWEDITOR
ComputeCenterPointOnFace ( & c0 , rp0 , facenum0 ) ;
ComputeCenterPointOnFace ( & c1 , rp1 , facenum1 ) ;
# else
ComputeFaceBoundingCircle ( & c0 , rp0 , facenum0 ) ;
ComputeFaceBoundingCircle ( & c1 , rp1 , facenum1 ) ;
# endif
vm_GetNormalizedDir ( & delta_vec , & c1 , & c0 ) ;
//Compute the cosine of the angle between our vector and the normal of the base face
float cos = delta_vec * fp1 - > normal ;
//Get a pointer to a vertex on face1
vector * vp = & rp - > verts [ nv0 ] ;
//Project the verts from end face0 into end face1
for ( i = 0 ; i < nv0 ; i + + ) {
//Get distance from base face to this vertex
float dist = ( ( * vp - rp - > verts [ i ] ) * fp1 - > normal ) / cos ;
//Get the new vert
tverts [ i ] = rp - > verts [ i ] + ( delta_vec * dist ) ;
}
//Find closest face0 point to point 0 of face1
int smallest_i = - 1 ;
float smallest_angle = 1.0 ;
vector tnorm = - fp1 - > normal ;
for ( i = 0 ; i < nv0 ; i + + ) {
float angle = GetAngle ( & c1 , & rp - > verts [ nv0 ] , & tverts [ i ] , & tnorm ) ;
if ( angle > 0.5 )
angle = 1.0 - angle ; //we want closest in either direction
if ( angle < smallest_angle ) {
smallest_i = i ;
smallest_angle = angle ;
}
}
ASSERT ( smallest_i ! = - 1 ) ;
//Generate new faces
int prev1 = 0 , //set up the previous edge
prev0 = smallest_i ;
for ( i = 0 ; i < nverts ; i + + ) {
float angle0 , angle1 ;
face * fp = & rp - > faces [ i ] ;
InitRoomFace ( fp , 3 ) ;
fp - > face_verts [ 0 ] = nv0 + prev1 ;
fp - > face_verts [ 1 ] = prev0 ;
if ( i = = nverts - 1 ) { //special case for last edge
if ( ( rp - > faces [ 0 ] . face_verts [ 0 ] ! = fp - > face_verts [ 0 ] ) & & ( rp - > faces [ 0 ] . face_verts [ 0 ] ! = fp - > face_verts [ 1 ] ) )
fp - > face_verts [ 2 ] = rp - > faces [ 0 ] . face_verts [ 0 ] ;
else {
ASSERT ( ( rp - > faces [ 0 ] . face_verts [ 1 ] ! = fp - > face_verts [ 0 ] ) & & ( rp - > faces [ 0 ] . face_verts [ 0 ] ! = fp - > face_verts [ 1 ] ) ) ;
fp - > face_verts [ 2 ] = rp - > faces [ 0 ] . face_verts [ 1 ] ;
}
break ;
}
//Pick point for third vert
angle1 = GetAngle ( & c1 , & rp - > verts [ nv0 + prev1 ] , & rp - > verts [ nv0 + ( prev1 + 1 ) % nv1 ] , & tnorm ) ;
angle0 = GetAngle ( & c1 , & rp - > verts [ nv0 + prev1 ] , & tverts [ ( prev0 + 1 ) % nv0 ] , & tnorm ) ;
if ( angle1 < angle0 ) {
prev1 = ( prev1 + 1 ) % nv1 ;
fp - > face_verts [ 2 ] = nv0 + prev1 ;
}
else
prev0 = fp - > face_verts [ 2 ] = ( prev0 + 1 ) % nv0 ;
}
//Set the two end faces for the room
InitRoomFace ( & rp - > faces [ nfaces - 2 ] , fp0 - > num_verts ) ;
for ( i = 0 ; i < fp0 - > num_verts ; i + + )
rp - > faces [ nfaces - 2 ] . face_verts [ i ] = nv0 - 1 - i ;
InitRoomFace ( & rp - > faces [ nfaces - 1 ] , fp1 - > num_verts ) ;
for ( i = 0 ; i < fp1 - > num_verts ; i + + )
rp - > faces [ nfaces - 1 ] . face_verts [ i ] = fp0 - > num_verts + i ;
//Set normals & textures for each face
for ( i = 0 ; i < nfaces ; i + + ) {
if ( ! ComputeFaceNormal ( rp , i ) )
Int3 ( ) ; //Bad! Get Matt!
}
//Attempt to combine faces
for ( i = 0 ; i < nfaces - 2 ; i + + ) {
int t = ( i + 1 ) % ( nfaces - 2 ) ;
//Try compbining the faces
if ( CombineFaces ( rp , i , t ) ) {
i - - ; //check this face again
nfaces - - ;
}
}
//Set textures for each face
# ifndef NEWEDITOR
for ( i = 0 ; i < nfaces ; i + + )
rp - > faces [ i ] . tmap = i + 1 ;
# else
int texnum = Editor_state . GetCurrentTexture ( ) ;
for ( i = 0 ; i < nfaces ; i + + )
{
// Apply texture
HTextureApplyToRoomFace ( rp , i , texnum ) ;
}
# endif
//Set UVs for all the faces
AssignDefaultUVsToRoom ( rp ) ;
//Form the portals between the rooms
LinkRoomsSimple ( Rooms , ROOMNUM ( rp0 ) , facenum0 , ROOMNUM ( rp ) , nfaces - 2 ) ;
LinkRoomsSimple ( Rooms , ROOMNUM ( rp1 ) , facenum1 , ROOMNUM ( rp ) , nfaces - 1 ) ;
//Make the new room the current room
# ifndef NEWEDITOR
Curroomp = rp ;
Curface = nfaces - 2 ;
Curedge = Curvert = 0 ;
Curportal = - 1 ;
# else
theApp . m_pLevelWnd - > SetPrim ( rp , nfaces - 2 , - 1 , 0 , 0 ) ;
# endif
//Set the flag
World_changed = 1 ;
}
2024-05-24 03:27:12 +00:00
int16_t New_face_verts [ MAX_VERTS_PER_FACE ] ;
2024-04-19 20:58:24 +00:00
int New_face_num_verts = - 1 ;
room * New_face_roomp ;
void AddNewFaceVert ( )
{
if ( Curroomp ! = New_face_roomp ) {
OutrageMessageBox ( " All points in the new face must be in the same room " ) ;
return ;
}
if ( New_face_num_verts = = - 1 ) {
OutrageMessageBox ( " You must start a new face before adding to it " ) ;
return ;
}
New_face_verts [ New_face_num_verts + + ] = Curroomp - > faces [ Curface ] . face_verts [ Curvert ] ;
EditorStatus ( " Added %d as vert #%d in new face " , Curroomp - > faces [ Curface ] . face_verts [ Curvert ] , New_face_num_verts ) ;
}
void StartNewFace ( )
{
New_face_roomp = Curroomp ;
New_face_num_verts = 0 ;
AddNewFaceVert ( ) ;
EditorStatus ( " Starting new face with vertex %d " , Curroomp - > faces [ Curface ] . face_verts [ Curvert ] ) ;
}
void EndNewFace ( )
{
AddNewFaceVert ( ) ;
if ( New_face_num_verts = = - 1 ) {
OutrageMessageBox ( " You must start a new face before ending it " ) ;
return ;
}
if ( New_face_num_verts < 3 ) {
OutrageMessageBox ( " A face must have at least three vertices " ) ;
return ;
}
int facenum ;
facenum = RoomAddFaces ( Curroomp , 1 ) ;
face * fp = & Curroomp - > faces [ facenum ] ;
InitRoomFace ( fp , New_face_num_verts ) ;
for ( int i = 0 ; i < New_face_num_verts ; i + + )
fp - > face_verts [ i ] = New_face_verts [ i ] ;
# ifndef NEWEDITOR
fp - > tmap = 0 ;
# else
int texnum = Editor_state . GetCurrentTexture ( ) ;
// Apply texture
HTextureApplyToRoomFace ( Curroomp , facenum , texnum ) ;
# endif
ComputeFaceNormal ( Curroomp , facenum ) ;
AssignDefaultUVsToRoomFace ( Curroomp , facenum ) ;
EditorStatus ( " New face %d created " , facenum ) ;
New_face_num_verts = - 1 ;
World_changed = 1 ;
}
void ObjRelink ( int objnum , int newroomnum ) ;
int RemoveDuplicatePoints ( room * rp ) ;
trigger * FindTrigger ( int roomnum , int facenum ) ;
//Combine the two rooms. base_rp stays, att_rp goes away
//Returns true if combine sucessful, false if can't join
bool CombineRooms ( room * base_rp , room * att_rp )
{
int i ;
face * fp ;
bool connected = 0 , rendered_portals = 0 ;
//Make sure these rooms share a portal
for ( i = 0 ; i < base_rp - > num_portals ; i + + ) {
if ( base_rp - > portals [ i ] . croom = = ROOMNUM ( att_rp ) ) {
connected = 1 ;
if ( ( base_rp - > faces [ base_rp - > portals [ i ] . portal_face ] . flags & FF_HAS_TRIGGER ) | |
( att_rp - > faces [ att_rp - > portals [ base_rp - > portals [ i ] . cportal ] . portal_face ] . flags & FF_HAS_TRIGGER ) ) {
OutrageMessageBox ( " Cannot combine rooms: a portal connecting them has a trigger. " ) ;
return 0 ;
}
if ( ( base_rp - > portals [ i ] . flags & PF_RENDER_FACES ) | |
( att_rp - > portals [ base_rp - > portals [ i ] . cportal ] . flags & PF_RENDER_FACES ) ) {
rendered_portals = 1 ;
}
}
}
if ( ! connected ) {
OutrageMessageBox ( " Cannot combine rooms: the rooms are not connected. " ) ;
return 0 ;
}
if ( rendered_portals ) {
if ( OutrageMessageBox ( MBOX_YESNO , " One or more of the portals connecting these rooms has the Render Faces flag set. "
" The rendered face will remain in the room after combine. \n \n Do you still want to combine? " ) ! = IDYES )
return 0 ;
}
//Check for doors
if ( base_rp - > doorway_data | | att_rp - > doorway_data ) {
OutrageMessageBox ( " Cannot combine rooms: one room is a door. " ) ;
return 0 ;
}
//Allocate new verts & faces
int first_new_vert = RoomAddVertices ( base_rp , att_rp - > num_verts ) ;
int first_new_face = RoomAddFaces ( base_rp , att_rp - > num_faces ) ;
//Copy verts to base room
for ( i = 0 ; i < att_rp - > num_verts ; i + + )
base_rp - > verts [ first_new_vert + i ] = att_rp - > verts [ i ] ;
//Copy faces to base room
for ( i = 0 , fp = & base_rp - > faces [ first_new_face ] ; i < att_rp - > num_faces ; fp + + , i + + ) {
//Copy the face
CopyFace ( fp , & att_rp - > faces [ i ] ) ;
fp - > flags = att_rp - > faces [ i ] . flags ;
//Update the vertices
for ( int v = 0 ; v < fp - > num_verts ; v + + )
fp - > face_verts [ v ] + = first_new_vert ;
//Move triggers from attach to base room
if ( att_rp - > faces [ i ] . flags & FF_HAS_TRIGGER ) {
trigger * tp = FindTrigger ( ROOMNUM ( att_rp ) , i ) ;
tp - > roomnum = ROOMNUM ( base_rp ) ;
tp - > facenum + = first_new_face ;
}
}
//Deal with attach room's portals
for ( i = 0 ; i < att_rp - > num_portals ; i + + ) {
int facenum , croom , cface , flags , cflags ;
facenum = att_rp - > portals [ i ] . portal_face + first_new_face ;
flags = att_rp - > portals [ i ] . flags ;
croom = att_rp - > portals [ i ] . croom ;
cface = Rooms [ croom ] . portals [ att_rp - > portals [ i ] . cportal ] . portal_face ;
cflags = Rooms [ croom ] . portals [ att_rp - > portals [ i ] . cportal ] . flags ;
//Delete old portal from attach room
DeletePortalPair ( att_rp , i ) ;
//If this portal attaches to the base room, mark the portal faces for deletion
if ( croom = = ROOMNUM ( base_rp ) ) {
if ( ! ( flags & PF_RENDER_FACES ) )
base_rp - > faces [ facenum ] . tmap = - 1 ; //-1 means to delete the face
if ( ! ( cflags & PF_RENDER_FACES ) )
base_rp - > faces [ cface ] . tmap = - 1 ; //-1 means to delete the face
}
else { //Attaches to third room, so make attachment to base room
LinkRooms ( Rooms , ROOMNUM ( base_rp ) , facenum , croom , cface ) ;
portal * pp = & base_rp - > portals [ base_rp - > num_portals - 1 ] ;
pp - > flags | = ( flags & ( PF_RENDER_FACES + PF_RENDERED_FLYTHROUGH ) ) ;
Rooms [ pp - > croom ] . portals [ pp - > cportal ] . flags | = ( cflags * ( PF_RENDER_FACES + PF_RENDERED_FLYTHROUGH ) ) ;
}
i - - ; //adjust for new portal numbering
}
//Delete obsolete faces
for ( i = 0 ; i < base_rp - > num_faces ; i + + )
if ( base_rp - > faces [ i ] . tmap = = - 1 ) {
DeleteRoomFace ( base_rp , i ) ;
i - - ;
}
//Link objects to new room
while ( att_rp - > objects ! = - 1 )
ObjRelink ( att_rp - > objects , ROOMNUM ( base_rp ) ) ;
//Delete the old room
DeleteRoomFromMine ( att_rp ) ;
//Get rid of duplicate verts
RemoveDuplicatePoints ( base_rp ) ;
//Done!
World_changed = 1 ;
return 1 ;
}
//Creates a an external room and links the specified faces to it
//Parameters: rp - the room to connect to the new room
// nfaces - how many faces connect to the new room (this becomes the number of portals)
// facenums - the list of faces to connect
void LinkToExternalRoom ( room * rp , int nfaces , int * facenums )
{
int vert_xlate [ MAX_VERTS_PER_ROOM ] ;
int v , i , nverts = 0 ;
for ( i = 0 ; i < rp - > num_verts ; i + + )
vert_xlate [ i ] = - 1 ;
for ( i = 0 ; i < nfaces ; i + + ) {
face * fp = & rp - > faces [ facenums [ i ] ] ;
for ( v = 0 ; v < fp - > num_verts ; v + + ) {
int vertnum = fp - > face_verts [ v ] ;
if ( vert_xlate [ vertnum ] = = - 1 )
vert_xlate [ vertnum ] = nverts + + ;
}
}
room * newrp = CreateNewRoom ( nverts , nfaces ) ;
newrp - > flags | = RF_EXTERNAL ;
for ( i = 0 ; i < rp - > num_verts ; i + + ) {
if ( vert_xlate [ i ] ! = - 1 )
newrp - > verts [ vert_xlate [ i ] ] = rp - > verts [ i ] ;
}
for ( i = 0 ; i < nfaces ; i + + ) {
face * newfp = & newrp - > faces [ i ] , * fp = & rp - > faces [ facenums [ i ] ] ;
InitRoomFace ( newfp , fp - > num_verts ) ;
for ( v = 0 ; v < fp - > num_verts ; v + + )
newfp - > face_verts [ v ] = vert_xlate [ fp - > face_verts [ fp - > num_verts - v - 1 ] ] ;
# ifndef NEWEDITOR
newfp - > tmap = fp - > tmap ;
# else
// Apply texture
HTextureApplyToRoomFace ( newrp , i , newfp - > tmap ) ;
# endif
if ( ! ComputeFaceNormal ( newrp , i ) )
Int3 ( ) ;
LinkRooms ( Rooms , ROOMNUM ( rp ) , facenums [ i ] , ROOMNUM ( newrp ) , i ) ;
}
AssignDefaultUVsToRoom ( newrp ) ;
World_changed = 1 ;
}
extern bool Disable_editor_rendering ;
//Splits a face into two pieces
//Parameters: rp,facenum - the face to split
// v0,v1 - the two verts that will form the new edge
void SplitFace ( room * rp , int facenum , int v0 , int v1 )
{
face * fp = & rp - > faces [ facenum ] , * nfp ;
int first_new_face , i ;
if ( v0 = = v1 ) {
OutrageMessageBox ( " You cannot split a face using the same point twice. " ) ;
return ;
}
if ( v1 < v0 )
{ int t = v1 ; v1 = v0 ; v0 = t ; }
if ( v1 = = ( v0 + 1 ) % fp - > num_verts ) {
OutrageMessageBox ( " You cannot split a face using two adjacent points. " ) ;
return ;
}
if ( fp - > num_verts = = 3 ) {
OutrageMessageBox ( " Face %d has three verts and cannot be split. " , facenum ) ;
return ;
}
if ( fp - > portal_num ! = - 1 ) {
OutrageMessageBox ( " You cannot split a face that's part of a portal. " ) ;
return ;
}
if ( fp - > flags & ( FF_HAS_TRIGGER + FF_FLOATING_TRIG ) ) {
OutrageMessageBox ( " You cannot split a face that has a trigger. " ) ;
return ;
}
Disable_editor_rendering = 1 ;
//Add new faces
first_new_face = RoomAddFaces ( rp , 2 ) ;
//Reset pointer to our old face
fp = & rp - > faces [ facenum ] ;
//Set up each face
bool ok [ 2 ] ;
for ( i = 0 , nfp = & rp - > faces [ first_new_face ] ; i < 2 ; i + + , nfp + + ) {
int nv , startv ;
if ( i = = 0 ) {
nv = v1 - v0 + 1 ;
startv = v0 ;
} else {
nv = fp - > num_verts - ( v1 - v0 ) + 1 ;
startv = v1 ;
}
InitRoomFace ( nfp , nv ) ;
//Set verts & uvls for this face
for ( int v = 0 ; v < nv ; v + + ) {
nfp - > face_verts [ v ] = fp - > face_verts [ ( startv + v ) % fp - > num_verts ] ;
nfp - > face_uvls [ v ] = fp - > face_uvls [ ( startv + v ) % fp - > num_verts ] ;
}
//Copy other data for this face
CopyFaceFlags ( nfp , fp ) ;
# ifndef NEWEDITOR
nfp - > tmap = fp - > tmap ;
# else
// Apply texture
HTextureApplyToRoomFace ( rp , first_new_face + i , fp - > tmap ) ;
# endif
nfp - > light_multiple = fp - > light_multiple ;
//Compute the normal for this face
ok [ i ] = ComputeFaceNormal ( rp , first_new_face + i ) ;
}
//Bail if either face is degenerate
if ( ! ok [ 0 ] | | ! ok [ 1 ] ) {
OutrageMessageBox ( " Cannot form face here: one new face is degenerate. " ) ;
DeleteRoomFace ( rp , first_new_face ) ;
DeleteRoomFace ( rp , first_new_face + 1 ) ;
Disable_editor_rendering = 0 ;
return ;
}
//Delete original face
DeleteRoomFace ( rp , facenum ) ;
//We're done
EditorStatus ( " Face %d replaced by new faces %d & %d " , facenum , first_new_face - 1 , first_new_face ) ;
Disable_editor_rendering = 0 ;
# ifndef NEWEDITOR
World_changed = 1 ;
# endif
}
//Adds a point to an edge
void SplitEdge ( room * rp , int facenum , int edgenum , float position )
{
face * fp = & rp - > faces [ facenum ] ;
int vert1 = ( edgenum + 1 ) % fp - > num_verts ;
vector * v0 , * v1 , newv ;
v0 = & rp - > verts [ fp - > face_verts [ edgenum ] ] ;
v1 = & rp - > verts [ fp - > face_verts [ vert1 ] ] ;
newv = * v0 + ( * v1 - * v0 ) * position ;
AddPointToAllEdges ( rp , fp - > face_verts [ edgenum ] , fp - > face_verts [ vert1 ] , & newv ) ;
World_changed = 1 ;
}
# define MIN_NORMAL_MAG 0.035
int FindConnectedFace ( room * rp , int facenum , int edgenum , int startface ) ;
//Deletes a vertex from an face
//Only deletes if the two adjacent edges are colinear, and there isn't a T-joint at this point
void DeleteVert ( room * rp , int facenum , int vertnum )
{
face * fp = & rp - > faces [ facenum ] ;
vector normal ;
float mag ;
int f0 , f1 ;
int prev_vertnum = ( vertnum + fp - > num_verts - 1 ) % fp - > num_verts ;
mag = vm_GetNormal ( & normal ,
& rp - > verts [ fp - > face_verts [ prev_vertnum ] ] ,
& rp - > verts [ fp - > face_verts [ vertnum ] ] ,
& rp - > verts [ fp - > face_verts [ ( vertnum + 1 ) % fp - > num_verts ] ] ) ;
if ( mag > = MIN_NORMAL_MAG ) {
OutrageMessageBox ( " Can't remove point: edges aren't colinear. " ) ;
return ;
}
//Make sure there isn't a t-joint at this face
f0 = FindConnectedFace ( rp , facenum , prev_vertnum , 0 ) ;
f1 = FindConnectedFace ( rp , facenum , vertnum , 0 ) ;
if ( ( f0 ! = - 1 ) & & ( f1 ! = - 1 ) & & ( f0 ! = f1 ) ) {
OutrageMessageBox ( " Can't delete vertex: apparent T-joint with faces %d & %d \n " , f0 , f1 ) ;
return ;
}
//Delete the point on our passed face
DeletePointFromFace ( rp , facenum , vertnum ) ;
//Delete the point on the connected face
face * fp2 = & rp - > faces [ f0 ] ;
for ( int v2 = 0 ; v2 < fp2 - > num_verts ; v2 + + )
if ( fp2 - > face_verts [ v2 ] = = fp - > face_verts [ vertnum ] )
break ;
ASSERT ( v2 < fp2 - > num_verts ) ;
DeletePointFromFace ( rp , f0 , v2 ) ;
# ifndef NEWEDITOR
World_changed = 1 ;
# endif
}
//Moves a vertex along its two adjacent edges
//Only moves if the two adjacent edges are colinear
void MoveVert ( room * rp , int facenum , int vertnum , float new_position )
{
face * fp = & rp - > faces [ facenum ] ;
vector normal ;
float mag ;
int prev_vertnum = ( vertnum + fp - > num_verts - 1 ) % fp - > num_verts ;
int next_vertnum = ( vertnum + 1 ) % fp - > num_verts ;
vector * v0 , * v1 , newv ;
mag = vm_GetNormal ( & normal ,
& rp - > verts [ fp - > face_verts [ prev_vertnum ] ] ,
& rp - > verts [ fp - > face_verts [ vertnum ] ] ,
& rp - > verts [ fp - > face_verts [ next_vertnum ] ] ) ;
if ( mag > = MIN_NORMAL_MAG ) {
OutrageMessageBox ( " Can't move point: edges aren't colinear. " ) ;
return ;
}
v0 = & rp - > verts [ fp - > face_verts [ prev_vertnum ] ] ;
v1 = & rp - > verts [ fp - > face_verts [ next_vertnum ] ] ;
newv = * v0 + ( * v1 - * v0 ) * new_position ;
rp - > verts [ fp - > face_verts [ vertnum ] ] = newv ;
World_changed = 1 ;
}
# include "polymodel.h"
//Incorporates the geometry from the specified object into the specified room. Deletes the object.
//Returns true if worked, false if some error
bool MergeObjectIntoRoom ( room * rp , int objnum )
{
object * objp = & Objects [ objnum ] ;
ASSERT ( objp - > render_type = = RT_POLYOBJ ) ;
poly_model * pm = GetPolymodelPointer ( objp - > rtype . pobj_info . model_num ) ;
if ( pm - > n_models > 1 ) {
# ifndef NEWEDITOR
OutrageMessageBox ( " Cannot merge: Only supported for objects with one submodel. \n \n If you really need this functions for objects with multiple submodles, talk to MattT. " ) ;
# else
OutrageMessageBox ( " Cannot merge: Only supported for objects with one submodel. " ) ;
# endif
return 0 ;
}
if ( ! pm - > n_ground ) {
OutrageMessageBox ( " Cannot merge: The object must have a ground plane to merge. " ) ;
return 0 ;
}
if ( OBJECT_OUTSIDE ( objp ) ) {
OutrageMessageBox ( " Cannot merge: The object is outside. " ) ;
return 0 ;
}
if ( objp - > roomnum ! = ROOMNUM ( rp ) ) {
OutrageMessageBox ( " Cannot merge: The object is not in the specified room. " ) ;
return 0 ;
}
//Get points to model data
bsp_info * sm = & pm - > submodel [ 0 ] ;
//Add verts to room
int first_new_vert = RoomAddVertices ( rp , sm - > nverts ) ;
//Rotate the points and copy them to the room
matrix rotmat = ~ objp - > orient ;
for ( int i = 0 ; i < sm - > nverts ; i + + ) {
rp - > verts [ first_new_vert + i ] = objp - > pos + sm - > verts [ i ] * rotmat ;
}
//Add faces to the room
int first_new_face = RoomAddFaces ( rp , sm - > num_faces ) ;
//Copy the faces
for ( i = 0 ; i < sm - > num_faces ; i + + ) {
polyface * pfp = & sm - > faces [ i ] ;
face * fp = & rp - > faces [ first_new_face + i ] ;
InitRoomFace ( fp , pfp - > nverts ) ;
for ( int v = 0 ; v < fp - > num_verts ; v + + ) {
fp - > face_verts [ v ] = first_new_vert + pfp - > vertnums [ v ] ;
fp - > face_uvls [ v ] . u = pfp - > u [ v ] ;
fp - > face_uvls [ v ] . v = pfp - > v [ v ] ;
}
# ifndef NEWEDITOR
fp - > tmap = pm - > textures [ pfp - > texnum ] ;
# else
// Apply texture
HTextureApplyToRoomFace ( rp , first_new_face + i , pm - > textures [ pfp - > texnum ] ) ;
# endif
if ( ! ComputeFaceNormal ( rp , first_new_face + i ) )
Int3 ( ) ; //Bad! Get Matt!
}
//Kill the object
ObjDelete ( objnum ) ;
//Done!
World_changed = 1 ;
return 1 ;
}
void MarkFaceForDeletion ( room * rp , int facenum )
{
face * fp = & rp - > faces [ facenum ] ;
//Mark this face
fp - > tmap = - 1 ;
//Find connect faces
for ( int e = 0 ; e < fp - > num_verts ; e + + ) {
int t = - 1 ;
while ( ( t = FindConnectedFace ( rp , facenum , e , t + 1 ) ) ! = - 1 ) {
if ( rp - > faces [ t ] . tmap ! = - 1 )
MarkFaceForDeletion ( rp , t ) ;
}
}
}
//Delete all the faces connected to the specified face
void DeleteAllConnectedFaces ( room * rp , int facenum )
{
//Make sure no faces using tmap of -1
for ( int f = 0 ; f < rp - > num_faces ; f + + )
ASSERT ( rp - > faces [ f ] . tmap ! = - 1 ) ;
//Do recursive marking
MarkFaceForDeletion ( rp , facenum ) ;
//Now delete the faces
for ( f = 0 ; f < rp - > num_faces ; ) {
if ( rp - > faces [ f ] . tmap = = - 1 )
DeleteRoomFace ( rp , f ) ;
else
f + + ;
}
//Done
World_changed = 1 ;
}
//returns the number of faces changed
int PropagateToConnectedFacesSub ( room * rp , int facenum , bool * face_checked )
{
int n_changed = 0 ;
face_checked [ facenum ] = 1 ;
face * fp = & rp - > faces [ facenum ] ;
for ( int e = 0 ; e < fp - > num_verts ; e + + ) {
int t = - 1 ;
do {
t = FindConnectedFace ( rp , facenum , e , t + 1 ) ;
if ( ( t ! = - 1 ) & & ! face_checked [ t ] ) {
if ( NormalsAreSame ( & fp - > normal , & rp - > faces [ t ] . normal ) ) {
HTexturePropagateToFace ( rp , t , rp , facenum ) ;
n_changed + + ;
n_changed + = PropagateToConnectedFacesSub ( rp , t , face_checked ) ;
}
}
} while ( t ! = - 1 ) ;
}
return n_changed ;
}
void PropagateToConnectedFaces ( room * rp , int facenum )
{
bool face_checked [ MAX_FACES_PER_ROOM ] ;
int f , n_changed ;
for ( f = 0 ; f < rp - > num_faces ; f + + )
face_checked [ f ] = 0 ;
face_checked [ facenum ] = 1 ;
n_changed = PropagateToConnectedFacesSub ( rp , facenum , face_checked ) ;
EditorStatus ( " %d faces were retextured. " , n_changed ) ;
if ( n_changed )
World_changed = 1 ;
}