2024-04-20 16:23:08 +00:00
|
|
|
/*
|
2024-06-15 18:12:48 +00:00
|
|
|
* Descent 3
|
2024-04-20 16:23:08 +00:00
|
|
|
* 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/Erooms.cpp $
|
|
|
|
* $Revision: 1.1.1.1 $
|
|
|
|
* $Date: 2003-08-26 03:57:38 $
|
|
|
|
* $Author: kevinb $
|
|
|
|
*
|
|
|
|
* Functions to create, delete, etc. rooms
|
|
|
|
*
|
|
|
|
* $Log: not supported by cvs2svn $
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 167 10/14/99 4:21p Matt
|
|
|
|
* Added code to remove duplicate faces from a room.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 166 9/30/99 5:02p Matt
|
|
|
|
* Automatically normalize face UV values when a level is verified.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 165 9/07/99 4:36p Gwar
|
|
|
|
* disabled deletion of only the face verts of a face in DeleteRoomFace
|
|
|
|
* (it now deletes all unused verts, like before)
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 164 8/30/99 1:50p Gwar
|
|
|
|
* stupid me forgot a semicolon
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 163 8/30/99 1:23p Gwar
|
|
|
|
* In DeleteRoomFace, added a way to delete only verts that were in the
|
|
|
|
* deleted face
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 162 8/17/99 6:11p Gwar
|
|
|
|
* handle NEWEDITOR texture management in CopyRoom
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 161 8/15/99 3:10a Gwar
|
|
|
|
* decrement Curface in DeleteRoomFace if Curface is the last face in the
|
|
|
|
* room
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 160 8/13/99 4:52p Gwar
|
|
|
|
* fixed a stupid bug from my last checkin
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 159 8/13/99 4:43p Gwar
|
|
|
|
* decrement Markedface in DeleteRoomFace if Markedface is the last face
|
|
|
|
* in the room
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 158 7/17/99 1:36a Gwar
|
|
|
|
* fixed a string formatting bug in a call to OutrageMessageBox
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 157 7/14/99 9:07a Gwar
|
|
|
|
* added a parameter to AllocLoadRoom
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 156 7/06/99 10:22a Gwar
|
|
|
|
* removed and #ifdef NEWEDITOR for this build...
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 155 7/04/99 4:55p Gwar
|
|
|
|
* changes for texture management in NEWEDITOR
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 154 7/03/99 5:50p Gwar
|
|
|
|
* minor changes for NEWEDITOR
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 153 7/02/99 2:17p Gwar
|
|
|
|
* added a parameter to AllocLoadRoom to disable centering on origin
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 152 5/05/99 5:03a Gwar
|
|
|
|
* renamed ned_GameTextures array to GameTextures in new editor to make
|
|
|
|
* game code happy; 3D texture view still does not display textures
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 151 5/05/99 3:02a Gwar
|
|
|
|
* added a parameter to DeleteRoomFace (by permission)
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 150 4/30/99 2:21p Jason
|
|
|
|
* changes to verify level completeness
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 149 4/27/99 3:36p Jason
|
|
|
|
* fixed terrain occlusion bug
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 148 4/26/99 11:11a Chris
|
|
|
|
* Updated Bnode system
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 147 4/25/99 10:16p Matt
|
|
|
|
* Added soundsource check to verify mine.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 146 4/25/99 9:02p Chris
|
|
|
|
* Improving the Bnode system
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 145 4/21/99 10:20p Matt
|
|
|
|
* Don't compute shells for external rooms in mine verify.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 144 4/21/99 5:15a Gwar
|
|
|
|
* added support for paletted rooms (ORFs) into new editor
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 143 4/20/99 8:19p Matt
|
|
|
|
* Added an assert.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 142 4/15/99 3:10p Matt
|
|
|
|
* Took out Int3() in FindSharedEdge(), and took out some mrpintf()s in
|
|
|
|
* the shell calculation.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 141 4/12/99 10:43a Jason
|
|
|
|
* checked in so code would compile
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 140 4/10/99 5:38p Matt
|
|
|
|
* Commented out some code that wouldn't compile.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 139 4/08/99 5:46p Jason
|
|
|
|
* added texture finding option
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 138 4/08/99 3:15p Matt
|
|
|
|
* Don't compute a shell for external rooms.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 137 4/08/99 12:45p Jason
|
|
|
|
* don't count lights or non renderable portal faces in the level
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 136 4/07/99 2:22p Jason
|
|
|
|
* warned designers if they have over 60 128s in their level
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 135 4/07/99 12:43p Jason
|
|
|
|
* added texture counting to verify level
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 134 4/07/99 11:24a Jason
|
|
|
|
* verify level now checks terrain occlusion and boa
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 133 4/06/99 5:10p Matt
|
|
|
|
* Base room shell on all portals in the room, not just the first one.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 132 4/05/99 11:17a Matt
|
|
|
|
* Added code to list all the objects in the levels.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 131 4/01/99 9:42a Matt
|
|
|
|
* Added code to compute room shells, and made verify level do it.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 130 3/31/99 11:34a Matt
|
|
|
|
* Got rid of unused forcefield checkbox on room tab
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 129 3/31/99 11:33a Matt
|
|
|
|
* Make note of non-planar faces that are portals.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 128 3/30/99 7:57p Matt
|
|
|
|
* Added prototype and moved a function to be inline in the header.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 127 3/30/99 7:20p Matt
|
|
|
|
* Deleted PointsAreColinear(), since it was never used and worked the
|
|
|
|
* wrong way.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 126 3/29/99 6:45p Matt
|
|
|
|
* Changed concavity tolerance from 0.005 to 0.05.
|
|
|
|
* Added verify code to check for concave faces and degenerate faces.
|
|
|
|
* Added code to fix degenerate faces.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 125 3/24/99 5:53p Jason
|
|
|
|
* added per room ambience settings
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 124 3/23/99 1:41p Jason
|
|
|
|
* fixed concave normal bug
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 123 3/11/99 5:12p Matt
|
|
|
|
* Added some error messages for the new verify level checks
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 122 3/11/99 4:58p Matt
|
|
|
|
* Fixed extra-stupid malloc bug
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 121 3/11/99 3:20p Matt
|
|
|
|
* Added checks for T-joints and mismatched portals to verify level.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 120 2/19/99 1:24p Matt
|
|
|
|
* Moved ComputerCenterRoomOnFace() from editor to main
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 119 2/18/99 12:32p Jason
|
|
|
|
* added room multiplier
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 118 2/11/99 1:44p Matt
|
|
|
|
* Fix up triggers when deleting a face
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 117 2/03/99 1:10p Matt
|
2024-09-03 11:25:21 +00:00
|
|
|
* Changed the paletted room current faces to be stored in a separate
|
2024-04-19 20:58:24 +00:00
|
|
|
* array, instead of in the room structure.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 116 1/29/99 12:48p Matt
|
|
|
|
* Rewrote the doorway system
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 115 1/25/99 7:39p Luke
|
|
|
|
* Added comment to tell me when ROOMFILE_VERSION changes (so I can change
|
|
|
|
* RoomView)
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 114 1/21/99 11:34a 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.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 113 12/22/98 2:03p Matt
|
|
|
|
* Added room names, and made rooms not compress so that room numbers are
|
|
|
|
* suitable for persistant uses.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 112 12/09/98 3:12p Jason
|
|
|
|
* save/load light multiples with faces
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 111 12/07/98 12:00p Sean
|
|
|
|
* Adjusted co-planar epsilon
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 110 11/17/98 5:58p Sean
|
|
|
|
* Changed concavity tolerance from 0.0001 to 0.0005 (MattT on Sean's
|
|
|
|
* machine)
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 109 11/03/98 6:20p Matt
|
|
|
|
* Changed the concavity check to not be dependent on the length of the
|
|
|
|
* edges being checked.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 108 11/02/98 6:15p Chris
|
|
|
|
* Room AABBs get saved with the level and the sort_face and dec_sort_face
|
|
|
|
* list s have been removed
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 107 11/01/98 1:58a Jeff
|
|
|
|
* converted the vsprintf calls to use the Pvsprintf, which is a safe
|
|
|
|
* vsprintf, no buffer overflows allowed
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 106 10/08/98 8:16p Matt
|
|
|
|
* Made AssignDefaultUVsToRoom() call AssignDefaultUVsToRoomFace()
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 105 10/08/98 4:23p Kevin
|
|
|
|
* Changed code to comply with memory library usage. Always use mem_malloc
|
|
|
|
* , mem_free and mem_strdup
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 104 10/07/98 12:48p Matt
|
|
|
|
* Added some extra error checking to prevent doubly-formed portals.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 103 10/03/98 8:05p Matt
|
|
|
|
* Changed the way CheckIfFaceIsPlanar() works (& renamed it to
|
|
|
|
* FaceIsPlanar()) and added planar check to VerifyMine() & VerifyRoom()
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 102 9/29/98 4:00p Matt
|
|
|
|
* Fix selected room list when compressing the mine.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 101 9/28/98 10:55a Jason
|
|
|
|
* fixed some bugs that VC6 caught
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 100 9/24/98 5:00p Matt
|
|
|
|
* Improved error checking for running out of rooms.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 99 9/14/98 12:18p Matt
|
|
|
|
* Took out mprintf() that was slowing down room importing.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 98 9/10/98 6:37p Matt
|
|
|
|
* Increased concavity check tolerance (from 0.002 to 0.003) to fix
|
|
|
|
* problem forming smooth bridge on Dan's level 9.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 97 9/09/98 9:10p Matt
|
|
|
|
* In verify & fix functions, don't process non-used rooms.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 96 9/08/98 6:12p Matt
|
|
|
|
* Added code to check for and fix duplicate and unused points.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 95 9/08/98 12:54p Matt
|
|
|
|
* Changed CONCAVITY_TOLERANCE from 0.0000000001 to 0.002 to fix a problem
|
|
|
|
* Sean was having joining faces.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 94 9/08/98 12:53p Matt
|
|
|
|
* Added duplicate point check to level verify
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 93 9/08/98 12:05p Jason
|
|
|
|
* moved doorway.h out of room.h
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 92 9/03/98 1:23p Matt
|
|
|
|
* Made AddVertToFace() compute alpha, u2, & v2 values for the new point.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 91 9/02/98 5:56p Matt
|
|
|
|
* Changed comment
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 90 9/02/98 4:20p Matt
|
|
|
|
* Cleaned up some linking code now that we don't have multi-face portals.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 89 9/02/98 2:28p Matt
|
|
|
|
* New function to add a vertex to a face.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 88 9/01/98 12:04p Matt
|
|
|
|
* Ripped out multi-face portal code
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 87 8/31/98 4:37p Matt
|
|
|
|
* Added improved room & mine error checking.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 86 8/14/98 4:46p Matt
|
|
|
|
* Fix doorways when compressing the mine
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 85 8/11/98 12:46p Matt
|
|
|
|
* Temporary fix for forming portals between faces with different numbers
|
|
|
|
* of vertices.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 84 7/21/98 4:25p Chris
|
|
|
|
* Fixed a bug with the new AABB stuff
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 83 7/21/98 2:14p Chris
|
|
|
|
* Some FVI speedups - not done
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 82 7/17/98 9:56a Chris
|
|
|
|
* Intermediate checkin
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 81 7/16/98 8:29p Chris
|
|
|
|
* Partial implementation of the new collide code
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 80 6/26/98 12:30p Matt
|
|
|
|
* In PointsAreSame(), check for the middle point being the same as either
|
|
|
|
* of the end points.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 79 6/25/98 7:15p Matt
|
|
|
|
* Added code to check for errors in portal linkage
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 78 6/19/98 12:22p Jason
|
|
|
|
* fixed special faces bug
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 77 6/08/98 12:27p Matt
|
|
|
|
* Added function to copy from one face to another the flags that should
|
|
|
|
* be inherited.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 76 6/02/98 6:03p Jason
|
|
|
|
* added specular lightmaps
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 75 5/25/98 3:46p Jason
|
|
|
|
* added better light glows
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 74 5/22/98 3:28p Jason
|
|
|
|
* added specular lighting
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 73 5/04/98 12:27p Matt
|
|
|
|
* Fixed misspelling
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 72 4/27/98 6:41p Matt
|
|
|
|
* Cleaned up epsilons some more, and moved some code here from hroom.cpp.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 71 4/21/98 4:05p Jason
|
|
|
|
* made copyface take into accound light multiples
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 70 4/21/98 2:44p Matt
|
|
|
|
* Added code to check a level, but haven't finished or made it available
|
|
|
|
* yet.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 69 4/16/98 3:33p Matt
|
|
|
|
* Cleaned up epsilon values, again.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 68 4/16/98 12:59p Matt
|
|
|
|
* Changed point-to-edge epsilon, and took out Int3s's
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 67 4/16/98 10:41a Matt
|
|
|
|
* Added FindSharedEdgeAcrossRooms()
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 66 4/15/98 4:57p Matt
|
|
|
|
* Fixed massive stupidity in the way I computed if a point was outside an
|
|
|
|
* edge for polygon:polygon clipping.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 65 4/02/98 12:23p Jason
|
|
|
|
* trimmed some fat from our structures
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 64 3/31/98 3:49p Jason
|
|
|
|
* added memory lib
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 63 3/18/98 4:31p Chris
|
|
|
|
* Speed up fvi and fixed some bugs
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 62 3/16/98 5:50p Chris
|
|
|
|
* Added sorted face lists for fvi
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 61 2/24/98 1:54p Jason
|
|
|
|
* added much stricter concavity testing
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 60 2/23/98 4:25p Jason
|
|
|
|
* got rid of stupid compiler warning
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 59 2/22/98 3:02p Jason
|
|
|
|
* changed concavity tolerance in CheckIfFaceIsPlanar
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 58 2/19/98 2:20p Jason
|
|
|
|
* fixed lightmap combining to eliminate tjoint problems
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 57 2/16/98 1:07p Matt
|
|
|
|
* Changed assert to int3
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 56 2/12/98 9:10p Matt
|
|
|
|
* Adjusted epsilon when clipping rooms.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 55 2/11/98 4:03p Matt
|
|
|
|
* When loading a room, center it at the origin
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 54 2/11/98 12:38p Matt
|
|
|
|
* Fixed small bug in DeleteRoomVert()
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 53 2/10/98 3:50p Jason
|
|
|
|
* added pulsing walls
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 52 2/08/98 6:03p Matt
|
|
|
|
* When copying a face, don't copy the has_trigger flag
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 51 1/20/98 4:05p Matt
|
|
|
|
* Move BuildListOfPortalVerts() from roomkeypaddialog.cpp to erooms.cpp
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 50 1/16/98 11:53a Matt
|
|
|
|
* Relaxed low-precision normal check, and added mprintfs with info on
|
|
|
|
* problem normals
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 49 1/15/98 7:34p Matt
|
|
|
|
* Revamped error checking when computing face normals
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 48 1/12/98 5:05p Luke
|
|
|
|
* Code was using wrong constant when searching palette rooms (MattT on
|
|
|
|
* Luke's machine)
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 47 12/30/97 3:33p Jason
|
|
|
|
* fixed stupid variable bug
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 46 11/24/97 1:30a Jason
|
|
|
|
* first attempt at adding shadows
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 45 11/17/97 7:37p Sean
|
|
|
|
* Increased point edge epsilon (Matt on Sean's machine)
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 44 11/11/97 6:45p Matt
|
|
|
|
* Adjusted epsilon value for edge clip
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 43 11/04/97 7:30p Matt
|
|
|
|
* Dealt with yet more precision problems in room clip
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 42 10/30/97 6:31p Jason
|
|
|
|
* fixed potential lightmap bugs
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 41 10/29/97 12:36p Matt
|
|
|
|
* Dealt with some precision problems in clipper
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 40 10/13/97 5:08p Matt
|
|
|
|
* Moved ComputeRoomBoundingSphere() & CreateRoomObjects() from editor to
|
|
|
|
* main
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 39 10/01/97 7:51p Matt
|
|
|
|
* Added code for external rooms
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 38 9/24/97 3:20p Matt
|
|
|
|
* Added ComputeRoomMinMax()
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 37 9/19/97 2:52p Jason
|
|
|
|
* changes to fix lightmap seam problem
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 36 9/17/97 11:52a Samir
|
|
|
|
* BIG SEGMENT RIPOUT
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 35 9/16/97 4:27p Matt
|
|
|
|
* Got rid of static_light & changed fields in the room struct
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 34 9/15/97 2:21p Matt
|
|
|
|
* Remap triggers when compress mine. (Also deleted some segment engine
|
|
|
|
* code.)
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 33 9/15/97 11:03a Jason
|
|
|
|
* fixed bug with alpha
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 32 9/12/97 2:27p Matt
|
|
|
|
* Added code to delete any unused vertices when deleting a face
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 31 9/10/97 3:02p Matt
|
|
|
|
* Moved GetIJ() from erooms.cpp to room.cpp
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 30 9/09/97 10:25a Matt
|
|
|
|
* Clip alpha values when clipping edge
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 29 9/03/97 2:00p Jason
|
|
|
|
* made LIGHTMAP_SPACING #define for controlling spacing of lightmap
|
|
|
|
* elements
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 28 9/02/97 5:17p Jason
|
|
|
|
* changes for dynamic lighting
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 27 9/02/97 2:28p Matt
|
|
|
|
* Changed PointOutsideEdge() to the more generally-useful
|
|
|
|
* CheckPointAgainstEdge()
|
2024-06-15 18:12:48 +00:00
|
|
|
* Made CopyFace() clear the portal number
|
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 26 9/02/97 11:47a Jason
|
|
|
|
* Got alpha per vertex working
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 25 9/02/97 11:01a Matt
|
|
|
|
* Moved assert()
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 24 8/29/97 5:44p Matt
|
|
|
|
* Moved a couple functions from hroom.cpp, and added a couple more useful
|
|
|
|
* functions.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 23 8/28/97 12:31p Jason
|
|
|
|
* added hi-res lightmaps for radiosity
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 22 8/22/97 1:02p Matt
|
|
|
|
* Got CompressMine() working for rooms
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 21 8/21/97 5:56p Matt
|
|
|
|
* Added a bunch of useful functions, and make CheckFaceConcavity() more
|
|
|
|
* generally useful.
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 20 8/18/97 11:47a Sean
|
|
|
|
* FROM JASON: Recompute normals on LoadRoom
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 19 8/04/97 6:46p Jason
|
|
|
|
* added code for a lightmap system
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 18 8/01/97 6:11p Matt
|
|
|
|
* Added several functions that I needed for attach room code
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 17 8/01/97 4:41p Jason
|
|
|
|
* made FixConcaveFaces reset all room normals as an extra precaution
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 16 8/01/97 12:50p Jason
|
|
|
|
* added code to support scaling of rooms/faces/edges
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 15 7/31/97 3:31p Jason
|
|
|
|
* added functions to rotate portal rooms
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 14 7/29/97 1:54p Matt
|
|
|
|
* Added some generally useful room/face functions
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 13 7/21/97 12:14p Matt
|
|
|
|
* Fixed stupid bug in concavity check
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 12 7/18/97 5:36p Jason
|
|
|
|
* save changed paletted rooms on exit
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
* 11 7/18/97 4:37p Matt
|
|
|
|
* Added function CheckFaceConcavity()
|
2024-06-15 18:12:48 +00:00
|
|
|
*
|
|
|
|
*
|
2024-04-19 20:58:24 +00:00
|
|
|
*
|
|
|
|
* $NoKeywords: $
|
|
|
|
*/
|
|
|
|
|
2024-04-27 19:30:57 +00:00
|
|
|
#include <cstdarg>
|
|
|
|
#include <cstdio>
|
2024-04-19 20:58:24 +00:00
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#ifndef NEWEDITOR
|
|
|
|
#include "d3edit.h"
|
|
|
|
#else
|
|
|
|
#include "..\neweditor\stdafx.h"
|
|
|
|
#include "..\neweditor\neweditor.h"
|
|
|
|
#include "..\neweditor\globals.h"
|
|
|
|
#include "..\neweditor\ned_geometry.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "erooms.h"
|
|
|
|
#include "room.h"
|
|
|
|
#include "gametexture.h"
|
|
|
|
#include "terrain.h"
|
|
|
|
#include "special_face.h"
|
|
|
|
#include "lighting.h"
|
|
|
|
#include "trigger.h"
|
|
|
|
#include "mem.h"
|
2024-08-24 21:57:17 +00:00
|
|
|
#include "mono.h"
|
2024-04-19 20:58:24 +00:00
|
|
|
#include "doorway.h"
|
|
|
|
|
|
|
|
#ifndef NEWEDITOR
|
|
|
|
#include "editor_lighting.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "boa.h"
|
|
|
|
#include "bnode.h"
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// List of current faces for the palette rooms
|
2024-04-19 20:58:24 +00:00
|
|
|
int Current_faces[MAX_PALETTE_ROOMS];
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Returns a free room number, & marks it no longer free. Returns -1 if none free.
|
|
|
|
// If palette_room is true, allocate out of the part of the array for the room palette
|
|
|
|
int GetFreeRoom(bool palette_room) {
|
|
|
|
int roomnum;
|
|
|
|
int start, end;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
int old_hri = Highest_room_index;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
if (palette_room) {
|
|
|
|
start = FIRST_PALETTE_ROOM;
|
|
|
|
end = start + MAX_PALETTE_ROOMS;
|
|
|
|
} else {
|
|
|
|
start = 0;
|
|
|
|
end = MAX_ROOMS;
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (roomnum = start; roomnum < end; roomnum++)
|
|
|
|
if (!Rooms[roomnum].used)
|
|
|
|
break;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
if (roomnum == end) // couldn't find a free room
|
|
|
|
return -1;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
if (!palette_room) {
|
|
|
|
if (roomnum > Highest_room_index)
|
|
|
|
Highest_room_index = roomnum;
|
|
|
|
} else
|
|
|
|
Current_faces[roomnum - FIRST_PALETTE_ROOM] = 0;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
BNode_RemapTerrainRooms(old_hri, Highest_room_index);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
return roomnum;
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Allocates a room & initializes it.
|
|
|
|
// Memory is allocated for faces & verts arrays, but the elements are *not* initialized
|
|
|
|
// The number of portals is set to zero.
|
|
|
|
// If palette_room is true, allocate out of the part of the array for the room palette
|
|
|
|
// Returns: pointer to new room, or NULL if no free rooms
|
|
|
|
room *CreateNewRoom(int nverts, int nfaces, bool palette_room) {
|
|
|
|
int roomnum;
|
|
|
|
room *rp;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Get a free room
|
|
|
|
roomnum = GetFreeRoom(palette_room);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
if (roomnum == -1)
|
|
|
|
return NULL;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
rp = &Rooms[roomnum];
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Initalize room, allocating memory
|
|
|
|
InitRoom(rp, nverts, nfaces, 0);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
return rp;
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Find the uv associated with a face vertex
|
|
|
|
// This routine works by projecting the face on to its own normals plane
|
|
|
|
// and just treating the face like a 2d surface
|
|
|
|
|
|
|
|
// Important - vertnum is the index into the face_verts[] array in the face structure,
|
|
|
|
// not an index into the verts[] array of the room structure
|
2024-06-15 18:12:48 +00:00
|
|
|
void GetUVLForRoomPoint(int roomnum, int facenum, int vertnum, roomUVL *uvl) {
|
|
|
|
matrix face_matrix, trans_matrix;
|
|
|
|
vector fvec;
|
|
|
|
vector avg_vert;
|
|
|
|
vector verts[MAX_VERTS_PER_FACE];
|
|
|
|
vector rot_vert;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(Rooms[roomnum].used);
|
|
|
|
ASSERT(Rooms[roomnum].faces[facenum].num_verts >= 3);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// find the center point of this face
|
|
|
|
vm_MakeZero(&avg_vert);
|
2024-06-29 09:28:14 +00:00
|
|
|
int i;
|
|
|
|
for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++)
|
2024-06-15 18:12:48 +00:00
|
|
|
avg_vert += Rooms[roomnum].verts[Rooms[roomnum].faces[facenum].face_verts[i]];
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
avg_vert /= i;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Make the orientation matrix
|
|
|
|
// Reverse the normal because we're looking "at" the face, not from it
|
|
|
|
fvec = -Rooms[roomnum].faces[facenum].normal;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
vm_VectorToMatrix(&face_matrix, &fvec, NULL, NULL);
|
|
|
|
// Make the transformation matrix
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
angvec avec;
|
|
|
|
vm_ExtractAnglesFromMatrix(&avec, &face_matrix);
|
|
|
|
vm_AnglesToMatrix(&trans_matrix, avec.p, avec.h, avec.b);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Rotate all the points
|
|
|
|
for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) {
|
|
|
|
vector vert = Rooms[roomnum].verts[Rooms[roomnum].faces[facenum].face_verts[i]];
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
vert -= avg_vert;
|
|
|
|
vm_MatrixMulVector(&rot_vert, &vert, &trans_matrix);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
verts[i] = rot_vert;
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Find left most point
|
|
|
|
int leftmost_point = -1;
|
|
|
|
float leftmost_x = 900000.00f; // a big number
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) {
|
|
|
|
if (verts[i].x < leftmost_x) {
|
|
|
|
leftmost_point = i;
|
|
|
|
leftmost_x = verts[i].x;
|
|
|
|
}
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(leftmost_point != -1);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Find top most point
|
|
|
|
int topmost_point = -1;
|
|
|
|
float topmost_y = -900000.0f; // a big number
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) {
|
|
|
|
if (verts[i].y > topmost_y) {
|
|
|
|
topmost_point = i;
|
|
|
|
topmost_y = verts[i].y;
|
|
|
|
}
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(topmost_point != -1);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Find right most point
|
|
|
|
int rightmost_point = -1;
|
|
|
|
float rightmost_x = -900000.00f; // a big number
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) {
|
|
|
|
if (verts[i].x > rightmost_x) {
|
|
|
|
rightmost_point = i;
|
|
|
|
rightmost_x = verts[i].x;
|
|
|
|
}
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(rightmost_point != -1);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Find bottom most point
|
|
|
|
int bottommost_point = -1;
|
|
|
|
float bottommost_y = 900000.0f; // a big number
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) {
|
|
|
|
if (verts[i].y < bottommost_y) {
|
|
|
|
bottommost_point = i;
|
|
|
|
bottommost_y = verts[i].y;
|
|
|
|
}
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(bottommost_point != -1);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// now set the base vertex, which is where we base uv 0,0 on
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
vector base_vector;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
base_vector.x = verts[leftmost_point].x;
|
|
|
|
base_vector.y = verts[topmost_point].y;
|
|
|
|
base_vector.z = 0;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// now actually find the uv of our specified point
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
uvl->u = (verts[vertnum].x - base_vector.x) / 20.0;
|
|
|
|
uvl->v = (base_vector.y - verts[vertnum].y) / 20.0;
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
#define DEFAULT_ALPHA 255
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Goes through each face of the passed room and sets the default uvs
|
|
|
|
void AssignDefaultUVsToRoom(room *rp) {
|
|
|
|
ASSERT(rp->used >= 1);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (int i = 0; i < rp->num_faces; i++)
|
|
|
|
AssignDefaultUVsToRoomFace(rp, i);
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Sets the default UVS for a room face
|
|
|
|
void AssignDefaultUVsToRoomFace(room *rp, int facenum) {
|
|
|
|
ASSERT(rp->used >= 1);
|
|
|
|
ASSERT(facenum < rp->num_faces);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
int t;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (t = 0; t < rp->faces[facenum].num_verts; t++) {
|
|
|
|
GetUVLForRoomPoint(ROOMNUM(rp), facenum, t, &rp->faces[facenum].face_uvls[t]);
|
|
|
|
rp->faces[facenum].face_uvls[t].alpha = DEFAULT_ALPHA;
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Searches thru all rooms for a specific name, returns -1 if not found
|
|
|
|
// or index of room with name
|
|
|
|
int FindRoomName(char *name) {
|
|
|
|
ASSERT(name != NULL);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int i = FIRST_PALETTE_ROOM; i < FIRST_PALETTE_ROOM + MAX_PALETTE_ROOMS; i++)
|
2024-06-15 18:12:48 +00:00
|
|
|
if (Rooms[i].used && Rooms[i].name && !stricmp(name, Rooms[i].name))
|
|
|
|
return i;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
return -1;
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
#define ROOM_HEADER_CHUNK 0
|
|
|
|
#define ROOM_VERTEX_CHUNK 1
|
|
|
|
#define ROOM_FACES_CHUNK 2
|
|
|
|
#define ROOM_END_CHUNK 3
|
|
|
|
#define ROOM_TEXTURE_CHUNK 4
|
|
|
|
#define ROOM_NEW_HEADER_CHUNK 5
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
#define ROOMFILE_VERSION 4 // Please tell Luke if you change this (and why) -- THANKS!!
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// saves a room in our ORF (Outrage room file) format
|
|
|
|
void SaveRoom(int n, char *filename) {
|
|
|
|
CFILE *outfile;
|
|
|
|
int headsize, savepos, vertsize, facesize, texsize;
|
|
|
|
int highest_index = 0;
|
|
|
|
int16_t Room_to_texture[MAX_TEXTURES];
|
|
|
|
int t, found_it = 0;
|
|
|
|
|
2024-09-03 10:34:37 +00:00
|
|
|
// Make sure it is in use!
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(Rooms[n].used);
|
|
|
|
|
|
|
|
outfile = (CFILE *)cfopen(filename, "wb");
|
|
|
|
if (!outfile) {
|
|
|
|
mprintf(0, "Couldn't save room %s!\n", filename);
|
|
|
|
Int3();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// write out header info
|
|
|
|
cf_WriteInt(outfile, ROOM_NEW_HEADER_CHUNK);
|
|
|
|
headsize = cftell(outfile);
|
|
|
|
cf_WriteInt(outfile, -1);
|
|
|
|
|
|
|
|
cf_WriteInt(outfile, ROOMFILE_VERSION);
|
|
|
|
cf_WriteInt(outfile, Rooms[n].num_verts);
|
|
|
|
cf_WriteInt(outfile, Rooms[n].num_faces);
|
|
|
|
|
|
|
|
savepos = cftell(outfile);
|
|
|
|
cfseek(outfile, headsize, SEEK_SET);
|
|
|
|
cf_WriteInt(outfile, (savepos - headsize) - 4);
|
|
|
|
cfseek(outfile, savepos, SEEK_SET);
|
|
|
|
|
|
|
|
// write out vertex info
|
|
|
|
cf_WriteInt(outfile, ROOM_VERTEX_CHUNK);
|
|
|
|
vertsize = cftell(outfile);
|
|
|
|
cf_WriteInt(outfile, -1);
|
|
|
|
|
|
|
|
for (int i = 0; i < Rooms[n].num_verts; i++) {
|
|
|
|
cf_WriteFloat(outfile, Rooms[n].verts[i].x);
|
|
|
|
cf_WriteFloat(outfile, Rooms[n].verts[i].y);
|
|
|
|
cf_WriteFloat(outfile, Rooms[n].verts[i].z);
|
|
|
|
}
|
|
|
|
|
|
|
|
savepos = cftell(outfile);
|
|
|
|
cfseek(outfile, vertsize, SEEK_SET);
|
|
|
|
cf_WriteInt(outfile, (savepos - vertsize) - 4);
|
|
|
|
cfseek(outfile, savepos, SEEK_SET);
|
|
|
|
|
|
|
|
// figure out correct texture ordering
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int i = 0; i < Rooms[n].num_faces; i++) {
|
2024-06-15 18:12:48 +00:00
|
|
|
int16_t index = Rooms[n].faces[i].tmap;
|
|
|
|
|
|
|
|
for (found_it = 0, t = 0; t < highest_index; t++) {
|
|
|
|
if (Room_to_texture[t] == index) {
|
|
|
|
// This texture is already there
|
|
|
|
found_it = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found_it == 0) {
|
|
|
|
// Add this index to our list of textures
|
|
|
|
Room_to_texture[highest_index] = index;
|
|
|
|
highest_index++;
|
|
|
|
ASSERT(highest_index < MAX_TEXTURES);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// write out texture info
|
|
|
|
cf_WriteInt(outfile, ROOM_TEXTURE_CHUNK);
|
|
|
|
texsize = cftell(outfile);
|
|
|
|
cf_WriteInt(outfile, -1);
|
|
|
|
|
|
|
|
// Write out how many different textures there are and then write their names
|
|
|
|
cf_WriteInt(outfile, highest_index);
|
|
|
|
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int i = 0; i < highest_index; i++) {
|
2024-06-15 18:12:48 +00:00
|
|
|
int index = Room_to_texture[i];
|
|
|
|
cf_WriteString(outfile, GameTextures[index].used ? GameTextures[index].name : "");
|
|
|
|
}
|
|
|
|
|
|
|
|
savepos = cftell(outfile);
|
|
|
|
cfseek(outfile, texsize, SEEK_SET);
|
|
|
|
cf_WriteInt(outfile, (savepos - texsize) - 4);
|
|
|
|
cfseek(outfile, savepos, SEEK_SET);
|
|
|
|
|
|
|
|
// write out face info
|
|
|
|
cf_WriteInt(outfile, ROOM_FACES_CHUNK);
|
|
|
|
facesize = cftell(outfile);
|
|
|
|
cf_WriteInt(outfile, -1);
|
|
|
|
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int i = 0; i < Rooms[n].num_faces; i++) {
|
2024-06-15 18:12:48 +00:00
|
|
|
cf_WriteByte(outfile, Rooms[n].faces[i].light_multiple);
|
|
|
|
cf_WriteInt(outfile, Rooms[n].faces[i].num_verts);
|
|
|
|
|
|
|
|
cf_WriteFloat(outfile, Rooms[n].faces[i].normal.x);
|
|
|
|
cf_WriteFloat(outfile, Rooms[n].faces[i].normal.y);
|
|
|
|
cf_WriteFloat(outfile, Rooms[n].faces[i].normal.z);
|
|
|
|
|
|
|
|
// Search through our texture list and write out that index
|
|
|
|
for (t = 0; t < highest_index; t++) {
|
|
|
|
if (Rooms[n].faces[i].tmap == Room_to_texture[t]) {
|
|
|
|
cf_WriteShort(outfile, t);
|
|
|
|
t = highest_index; // stupid way to break out
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (t = 0; t < Rooms[n].faces[i].num_verts; t++) {
|
|
|
|
cf_WriteShort(outfile, Rooms[n].faces[i].face_verts[t]);
|
|
|
|
cf_WriteFloat(outfile, Rooms[n].faces[i].face_uvls[t].u);
|
|
|
|
cf_WriteFloat(outfile, Rooms[n].faces[i].face_uvls[t].v);
|
|
|
|
cf_WriteFloat(outfile, 0);
|
|
|
|
cf_WriteFloat(outfile, 0);
|
|
|
|
cf_WriteFloat(outfile, 0);
|
|
|
|
cf_WriteFloat(outfile, 0);
|
|
|
|
cf_WriteFloat(outfile, Ubyte_to_float[Rooms[n].faces[i].face_uvls[t].alpha]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
savepos = cftell(outfile);
|
|
|
|
cfseek(outfile, facesize, SEEK_SET);
|
|
|
|
cf_WriteInt(outfile, (savepos - facesize) - 4);
|
|
|
|
cfseek(outfile, savepos, SEEK_SET);
|
|
|
|
|
|
|
|
cf_WriteInt(outfile, ROOM_END_CHUNK);
|
|
|
|
cf_WriteInt(outfile, 4);
|
|
|
|
|
|
|
|
cfclose(outfile);
|
|
|
|
|
|
|
|
mprintf(0, "Room file %s saved.\n", filename);
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Allocates a room and then tries to load it
|
|
|
|
// Returns index into Rooms[] array on success
|
|
|
|
// -1 on fail
|
|
|
|
int AllocLoadRoom(char *filename, bool bCenter, bool palette_room) {
|
|
|
|
CFILE *infile;
|
|
|
|
int done = 0, initted = 0;
|
|
|
|
int command, len, room_num = -1, i;
|
|
|
|
room *rp;
|
|
|
|
char texture_names[MAX_TEXTURES][PAGENAME_LEN];
|
|
|
|
int highest_index;
|
|
|
|
int16_t tex_index;
|
|
|
|
int room_version = 0;
|
|
|
|
|
|
|
|
infile = (CFILE *)cfopen(filename, "rb");
|
|
|
|
if (!infile) {
|
|
|
|
Int3(); // hey, couldn't load this room!
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!done) {
|
|
|
|
command = cf_ReadInt(infile);
|
|
|
|
len = cf_ReadInt(infile);
|
|
|
|
|
|
|
|
switch (command) {
|
|
|
|
// Read room header stuff
|
|
|
|
case ROOM_HEADER_CHUNK:
|
|
|
|
case ROOM_NEW_HEADER_CHUNK:
|
|
|
|
int num_verts, num_faces;
|
|
|
|
|
|
|
|
if (command == ROOM_NEW_HEADER_CHUNK)
|
|
|
|
room_version = cf_ReadInt(infile);
|
|
|
|
num_verts = cf_ReadInt(infile);
|
|
|
|
num_faces = cf_ReadInt(infile);
|
|
|
|
|
|
|
|
// room_num=AllocRoom (num_verts,num_faces);
|
|
|
|
rp = CreateNewRoom(num_verts, num_faces, palette_room);
|
|
|
|
|
|
|
|
if (rp == NULL) {
|
|
|
|
done = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
initted = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ROOM_VERTEX_CHUNK:
|
|
|
|
if (!initted) {
|
|
|
|
Int3(); // bad format info, get jason
|
|
|
|
done = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (i = 0; i < rp->num_verts; i++) {
|
|
|
|
rp->verts[i].x = cf_ReadFloat(infile);
|
|
|
|
rp->verts[i].y = cf_ReadFloat(infile);
|
|
|
|
rp->verts[i].z = cf_ReadFloat(infile);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ROOM_TEXTURE_CHUNK:
|
|
|
|
if (!initted) {
|
|
|
|
Int3(); // bad format info, get jason
|
|
|
|
done = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
highest_index = cf_ReadInt(infile);
|
|
|
|
for (i = 0; i < highest_index; i++)
|
|
|
|
cf_ReadString(texture_names[i], PAGENAME_LEN, infile); // get old name
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ROOM_FACES_CHUNK:
|
|
|
|
if (!initted) {
|
|
|
|
Int3(); // bad format info, get jason
|
|
|
|
done = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < rp->num_faces; i++) {
|
|
|
|
uint8_t light_multiple = 4;
|
|
|
|
if (room_version >= 4)
|
|
|
|
light_multiple = cf_ReadByte(infile);
|
|
|
|
|
|
|
|
int nverts = cf_ReadInt(infile);
|
|
|
|
|
|
|
|
// rp->faces[i].num_verts=cf_ReadInt (infile);
|
|
|
|
// rp->faces[i].face_verts=(int16_t *)mem_malloc (sizeof(int16_t)*rp->faces[i].num_verts);
|
|
|
|
// rp->faces[i].face_uvls=(g3UVL *)mem_malloc (sizeof(g3UVL)*rp->faces[i].num_verts);
|
|
|
|
|
|
|
|
InitRoomFace(&rp->faces[i], nverts);
|
|
|
|
|
|
|
|
ASSERT(rp->faces[i].face_verts);
|
|
|
|
ASSERT(rp->faces[i].face_uvls);
|
|
|
|
|
|
|
|
rp->faces[i].normal.x = cf_ReadFloat(infile);
|
|
|
|
rp->faces[i].normal.y = cf_ReadFloat(infile);
|
|
|
|
rp->faces[i].normal.z = cf_ReadFloat(infile);
|
|
|
|
rp->faces[i].light_multiple = light_multiple;
|
|
|
|
|
|
|
|
tex_index = cf_ReadShort(infile);
|
|
|
|
tex_index = FindTextureName(texture_names[tex_index]);
|
|
|
|
|
|
|
|
if (tex_index == -1) // If this texture doesn't exist, bash to error texture
|
|
|
|
rp->faces[i].tmap = 0;
|
|
|
|
else
|
|
|
|
rp->faces[i].tmap = tex_index;
|
|
|
|
|
|
|
|
for (int t = 0; t < rp->faces[i].num_verts; t++) {
|
|
|
|
rp->faces[i].face_verts[t] = cf_ReadShort(infile);
|
|
|
|
rp->faces[i].face_uvls[t].u = cf_ReadFloat(infile);
|
|
|
|
rp->faces[i].face_uvls[t].v = cf_ReadFloat(infile);
|
|
|
|
cf_ReadFloat(infile);
|
|
|
|
cf_ReadFloat(infile);
|
|
|
|
cf_ReadFloat(infile);
|
|
|
|
cf_ReadFloat(infile);
|
|
|
|
if (room_version >= 1)
|
|
|
|
rp->faces[i].face_uvls[t].alpha = Float_to_ubyte(cf_ReadFloat(infile));
|
|
|
|
|
|
|
|
if (room_version <= 1)
|
|
|
|
rp->faces[i].face_uvls[t].alpha = 255;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ROOM_END_CHUNK:
|
|
|
|
done = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// skip the ones we don't know
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
cf_ReadByte(infile);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cfclose(infile);
|
|
|
|
|
|
|
|
if (!ResetRoomFaceNormals(rp))
|
|
|
|
OutrageMessageBox("Warning: This room has faces with bad normals.\n\n"
|
|
|
|
"It is recommended that you fix this room before using it.");
|
|
|
|
|
|
|
|
// Center the room
|
|
|
|
if (bCenter) {
|
|
|
|
vector center;
|
|
|
|
#ifndef NEWEDITOR
|
|
|
|
ComputeRoomCenter(¢er, rp);
|
|
|
|
#else
|
|
|
|
ComputeRoomBoundingSphere(¢er, rp);
|
|
|
|
#endif
|
|
|
|
for (int v = 0; v < rp->num_verts; v++)
|
|
|
|
rp->verts[v] -= center;
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// We're done
|
|
|
|
return ROOMNUM(rp);
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Gets next palette room (from n) that has actually been alloced
|
|
|
|
int GetNextRoom(int n) {
|
|
|
|
ASSERT((n == -1) || ((n >= FIRST_PALETTE_ROOM) && n < FIRST_PALETTE_ROOM + MAX_PALETTE_ROOMS));
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
if (n == -1) // start at beginning
|
|
|
|
n = FIRST_PALETTE_ROOM - 1;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int i = n + 1; i < FIRST_PALETTE_ROOM + MAX_PALETTE_ROOMS; i++)
|
2024-06-15 18:12:48 +00:00
|
|
|
if (Rooms[i].used)
|
|
|
|
return i;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int i = FIRST_PALETTE_ROOM; i <= n; i++)
|
2024-06-15 18:12:48 +00:00
|
|
|
if (Rooms[i].used)
|
|
|
|
return i;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// no room found
|
|
|
|
|
|
|
|
return -1;
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
void GetIJ(const vector *normal, int *ii, int *jj);
|
|
|
|
|
|
|
|
// How much slack to give to give the concavity test
|
|
|
|
#define CONCAVITY_TOLERANCE 0.05
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Deterimines whether a face is concave or convex
|
|
|
|
// Parameters: num_verts - the number of vertices in the face to be tested
|
|
|
|
// face_verts - list of vertex numbers in this face
|
|
|
|
// normal - the surface normal of this face
|
|
|
|
// verts - array of vertices into which face_verts elements index
|
|
|
|
// Returns: If the face is concave, returns the number of the vertex that makes the concavity.
|
|
|
|
// If the face is convex, returns -1
|
|
|
|
// NOTE: A face could have multiple concavities, and this will only find the one with the
|
|
|
|
// lowest-numbered vertex
|
|
|
|
int CheckFaceConcavity(int num_verts, int16_t *face_verts, vector *normal, vector *verts) {
|
|
|
|
int ii, jj;
|
|
|
|
float i0, j0, i1, j1;
|
|
|
|
float *v0, *v1;
|
|
|
|
int vn;
|
|
|
|
|
|
|
|
// Get the vertices for projection
|
|
|
|
GetIJ(normal, &ii, &jj);
|
|
|
|
|
|
|
|
// Get vector from last vertex to first vertex
|
|
|
|
v0 = (float *)&verts[face_verts[num_verts - 1]];
|
|
|
|
v1 = (float *)&verts[face_verts[0]];
|
|
|
|
i1 = v1[ii] - v0[ii];
|
|
|
|
j1 = v1[jj] - v0[jj];
|
|
|
|
|
|
|
|
// Go through each vert and check for concavity
|
|
|
|
for (vn = 0; vn < num_verts; vn++) {
|
|
|
|
float dot;
|
|
|
|
|
|
|
|
// Copy previous vertex & edge values
|
|
|
|
v0 = v1;
|
|
|
|
i0 = i1;
|
|
|
|
j0 = j1;
|
|
|
|
|
|
|
|
// Compute new values
|
|
|
|
v1 = (float *)&verts[face_verts[(vn + 1) % num_verts]];
|
|
|
|
i1 = v1[ii] - v0[ii];
|
|
|
|
j1 = v1[jj] - v0[jj];
|
|
|
|
|
|
|
|
// Now we have two vectors, <i0,j0> and <i1,j1>. Let's see if we have a concavity
|
|
|
|
dot = (-j0 * i1) + (i0 * j1);
|
|
|
|
dot /= sqrt(i0 * i0 + j0 * j0) * sqrt(i1 * i1 + j1 * j1);
|
|
|
|
|
|
|
|
if (dot > CONCAVITY_TOLERANCE) { // we have a concavity!
|
|
|
|
// mprintf(0,"Concavity check dot = %f\n",dot);
|
|
|
|
return vn; //..so return this vertex number
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No concavity found
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Recompute the surface normals for all the faces in a room
|
|
|
|
// Parameters: rp - pointer to the room
|
|
|
|
// Returns: true if normals computed ok, false if some normals were bad
|
|
|
|
bool ResetRoomFaceNormals(room *rp) {
|
|
|
|
int bad_normals = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < rp->num_faces; i++)
|
|
|
|
if (!ComputeFaceNormal(rp, i))
|
|
|
|
bad_normals++;
|
|
|
|
|
|
|
|
if (bad_normals > 0) {
|
|
|
|
mprintf(1, "Warning: Room %d has %d bad or low-precision normals\n", ROOMNUM(rp), bad_normals);
|
|
|
|
return 0;
|
|
|
|
} else
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copies the contents of one face to another. Sets the portal num in the new face to -1.
|
|
|
|
// Parameters: dfp - pointer to the destination face. This face should be uninitialized.
|
|
|
|
// sfp - pointer to the source face
|
|
|
|
void CopyFace(face *dfp, face *sfp) {
|
|
|
|
InitRoomFace(dfp, sfp->num_verts);
|
|
|
|
|
|
|
|
dfp->flags = sfp->flags;
|
|
|
|
dfp->portal_num = -1;
|
|
|
|
dfp->normal = sfp->normal;
|
|
|
|
dfp->tmap = sfp->tmap;
|
|
|
|
dfp->light_multiple = sfp->light_multiple;
|
|
|
|
|
|
|
|
// Clear the flags that we don't want transferred over
|
|
|
|
dfp->flags &= ~FF_LIGHTMAP;
|
|
|
|
dfp->flags &= ~FF_HAS_TRIGGER;
|
|
|
|
|
|
|
|
// Copy vertices and uvls
|
|
|
|
for (int i = 0; i < sfp->num_verts; i++) {
|
|
|
|
dfp->face_verts[i] = sfp->face_verts[i];
|
|
|
|
dfp->face_uvls[i] = sfp->face_uvls[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks to see if a face is planar.
|
|
|
|
// See if all the points are within a certain distance of an average point
|
|
|
|
// Returns 1 if face is planar, 0 if not
|
|
|
|
bool FaceIsPlanar(int nv, int16_t *face_verts, vector *normal, vector *verts) {
|
|
|
|
// Triangles are always planar
|
|
|
|
if (nv == 3)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
// Get average distance from origin for points on this face
|
|
|
|
float average_d = 0;
|
|
|
|
for (int v = 0; v < nv; v++)
|
|
|
|
average_d += verts[face_verts[v]] * *normal;
|
|
|
|
average_d /= nv;
|
|
|
|
|
|
|
|
// Look for points too far from the average
|
|
|
|
float d;
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int v = 0; v < nv; v++) {
|
2024-06-15 18:12:48 +00:00
|
|
|
d = verts[face_verts[v]] * *normal;
|
|
|
|
if (fabs(d - average_d) > POINT_TO_PLANE_EPSILON)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Didn't find anything wrong, so face is planar
|
|
|
|
return 1;
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Fixes all the concave/nonplanar faces of facelist of room rp
|
|
|
|
void FixConcaveFaces(room *rp, int *facelist, int facecount) {
|
|
|
|
int i, t, k;
|
|
|
|
face *newfaces;
|
|
|
|
|
|
|
|
for (i = 0; i < facecount; i++) {
|
|
|
|
// If this face is concave, build a triangle strip out of the concave face
|
|
|
|
if (!FaceIsPlanar(rp, facelist[i])) {
|
|
|
|
int concave_verts[MAX_VERTS_PER_FACE];
|
|
|
|
|
|
|
|
int old_num_faces = rp->num_faces;
|
|
|
|
int concave_count = rp->faces[facelist[i]].num_verts;
|
|
|
|
int old_tmap = rp->faces[facelist[i]].tmap;
|
|
|
|
int num_new_faces = concave_count - 3;
|
|
|
|
ASSERT(num_new_faces > 0);
|
|
|
|
|
|
|
|
mprintf(0, "Creating %d new faces from face %d!\n", num_new_faces, facelist[i]);
|
|
|
|
|
|
|
|
// copy the concave vert indices for later use
|
|
|
|
for (t = 0; t < concave_count; t++)
|
|
|
|
concave_verts[t] = rp->faces[facelist[i]].face_verts[t];
|
|
|
|
|
|
|
|
// Allocate memory for our new faces
|
|
|
|
int nfaces = rp->num_faces + num_new_faces;
|
|
|
|
|
2024-08-30 18:52:36 +00:00
|
|
|
newfaces = mem_rmalloc<face>(nfaces);
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(newfaces != NULL);
|
|
|
|
|
|
|
|
// Copy all the faces into our new array
|
|
|
|
for (t = 0; t < rp->num_faces; t++) {
|
|
|
|
|
|
|
|
if (t != facelist[i]) {
|
|
|
|
int nverts = rp->faces[t].num_verts;
|
|
|
|
|
2024-08-30 18:52:36 +00:00
|
|
|
newfaces[t].face_verts = mem_rmalloc<int16_t>(nverts);
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(newfaces[t].face_verts != NULL);
|
2024-08-30 18:52:36 +00:00
|
|
|
newfaces[t].face_uvls = mem_rmalloc<roomUVL>(nverts);
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(newfaces[t].face_uvls != NULL);
|
|
|
|
|
|
|
|
newfaces[t].normal = rp->faces[t].normal;
|
|
|
|
newfaces[t].tmap = rp->faces[t].tmap;
|
|
|
|
newfaces[t].flags = rp->faces[t].flags;
|
|
|
|
newfaces[t].portal_num = rp->faces[t].portal_num;
|
|
|
|
newfaces[t].num_verts = rp->faces[t].num_verts;
|
|
|
|
newfaces[t].special_handle = BAD_SPECIAL_FACE_INDEX;
|
|
|
|
|
|
|
|
for (k = 0; k < nverts; k++) {
|
|
|
|
newfaces[t].face_verts[k] = rp->faces[t].face_verts[k];
|
|
|
|
newfaces[t].face_uvls[k] = rp->faces[t].face_uvls[k];
|
|
|
|
}
|
|
|
|
} else // special case the concave face into a triangle
|
|
|
|
{
|
|
|
|
int nverts = 3;
|
|
|
|
|
2024-08-30 18:52:36 +00:00
|
|
|
newfaces[t].face_verts = mem_rmalloc<int16_t>(nverts);
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(newfaces[t].face_verts != NULL);
|
2024-08-30 18:52:36 +00:00
|
|
|
newfaces[t].face_uvls = mem_rmalloc<roomUVL>(nverts);
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(newfaces[t].face_uvls != NULL);
|
|
|
|
|
|
|
|
newfaces[t].tmap = rp->faces[t].tmap;
|
|
|
|
newfaces[t].flags = rp->faces[t].flags;
|
|
|
|
newfaces[t].portal_num = rp->faces[t].portal_num;
|
|
|
|
newfaces[t].num_verts = 3;
|
|
|
|
newfaces[t].special_handle = BAD_SPECIAL_FACE_INDEX;
|
|
|
|
|
|
|
|
for (k = 0; k < nverts; k++) {
|
|
|
|
newfaces[t].face_verts[k] = rp->faces[t].face_verts[k];
|
|
|
|
newfaces[t].face_uvls[k] = rp->faces[t].face_uvls[k];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get new normal
|
|
|
|
if (!ComputeFaceNormal(rp, t))
|
|
|
|
Int3(); // Bad normal: get Matt or Jason, or ignore.
|
|
|
|
|
|
|
|
newfaces[t].normal = rp->faces[t].normal;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free up the old list of faces
|
|
|
|
mem_free(rp->faces);
|
|
|
|
rp->faces = newfaces;
|
|
|
|
rp->num_faces = nfaces;
|
|
|
|
|
|
|
|
if (rp->num_bbf_regions) {
|
|
|
|
for (i = 0; i < rp->num_bbf_regions; i++) {
|
|
|
|
mem_free(rp->bbf_list[i]);
|
|
|
|
}
|
|
|
|
mem_free(rp->bbf_list);
|
|
|
|
mem_free(rp->num_bbf);
|
|
|
|
mem_free(rp->bbf_list_min_xyz);
|
|
|
|
mem_free(rp->bbf_list_max_xyz);
|
|
|
|
mem_free(rp->bbf_list_sector);
|
|
|
|
rp->num_bbf_regions = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now build the remaining triangles of our concave face
|
|
|
|
for (t = 0; t < num_new_faces; t++)
|
|
|
|
InitRoomFace(&rp->faces[old_num_faces + t], 3);
|
|
|
|
|
|
|
|
for (t = 0; t < concave_count - 3; t++) {
|
|
|
|
rp->faces[old_num_faces + t].face_verts[0] = concave_verts[0];
|
|
|
|
rp->faces[old_num_faces + t].face_verts[1] = concave_verts[2 + t];
|
|
|
|
rp->faces[old_num_faces + t].face_verts[2] = concave_verts[3 + t];
|
|
|
|
rp->faces[old_num_faces + t].tmap = old_tmap;
|
|
|
|
|
|
|
|
if (!ComputeFaceNormal(rp, old_num_faces + t))
|
|
|
|
Int3(); // Bad normal: get Matt or Jason, or ignore.
|
|
|
|
|
|
|
|
AssignDefaultUVsToRoomFace(rp, old_num_faces + t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ResetRoomFaceNormals(rp))
|
|
|
|
Int3(); // Get Matt or Jason
|
|
|
|
}
|
|
|
|
|
|
|
|
// Changes the number of verts in a face. Frees and reallocates the face_verts & face_uvls arrays.
|
|
|
|
// Leaves all other fields the same
|
|
|
|
void ReInitRoomFace(face *fp, int nverts) {
|
|
|
|
ASSERT(nverts != 0);
|
|
|
|
|
|
|
|
fp->num_verts = nverts;
|
|
|
|
|
|
|
|
mem_free(fp->face_verts);
|
|
|
|
mem_free(fp->face_uvls);
|
|
|
|
|
|
|
|
fp->face_verts = (int16_t *)mem_malloc(nverts * sizeof(*fp->face_verts));
|
|
|
|
ASSERT(fp->face_verts != NULL);
|
2024-09-08 11:29:41 +00:00
|
|
|
fp->face_uvls = mem_rmalloc<roomUVL>(nverts);
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(fp->face_uvls != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determines if two points are close enough together to be considered the same
|
|
|
|
// Parameters: v0,v1 - the two points
|
|
|
|
// Returns: true if the points are the same or very close; else false
|
|
|
|
bool PointsAreSame(vector *v0, vector *v1) {
|
|
|
|
float d = vm_VectorDistance(v0, v1);
|
|
|
|
|
|
|
|
return (d < POINT_TO_POINT_EPSILON);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check to see if a point is in, in front of, or behind a plane
|
|
|
|
// Parameters: checkpoint - the point to check
|
2024-04-19 20:58:24 +00:00
|
|
|
// planepoint,normal - the plane we're checking against
|
2024-06-15 18:12:48 +00:00
|
|
|
// Returns: 0 if on the plane, -1 if behind, 1 if in front
|
|
|
|
int CheckPointToPlane(vector *checkpoint, vector *planepoint, vector *normal) {
|
|
|
|
float d = (*checkpoint - *planepoint) * *normal;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
if (d < -POINT_TO_PLANE_EPSILON)
|
|
|
|
return -1;
|
|
|
|
else if (d > POINT_TO_PLANE_EPSILON)
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return 0;
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Check to see if all the points on a face are in front of a plane
|
|
|
|
// Parameters: rp,facenum - the face to check
|
2024-04-19 20:58:24 +00:00
|
|
|
// planepoint,normal - define the plane we're checking against
|
2024-06-15 18:12:48 +00:00
|
|
|
// Returns: the number of the first point found on the back of the plane, or -1 of all on front
|
|
|
|
int CheckFaceToPlane(room *rp, int facenum, vector *planepoint, vector *normal) {
|
|
|
|
face *fp = &rp->faces[facenum];
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (int i = 0; i < fp->num_verts; i++)
|
|
|
|
if (CheckPointToPlane(&rp->verts[fp->face_verts[i]], planepoint, normal) < 0)
|
|
|
|
return i;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
return -1; // no points found behind the plane
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Create space for additional vertices in a room.
|
|
|
|
// Allocates a new array of vertices, copies from the old list, and frees the old list
|
|
|
|
// The new vertices are at the end of the list, so none of the old vertices change number
|
|
|
|
// Parameters: rp - the room
|
2024-04-19 20:58:24 +00:00
|
|
|
// n_new_verts - how many vertices are being added to the room
|
2024-06-15 18:12:48 +00:00
|
|
|
// Returns: the number of the first new vertex
|
|
|
|
int RoomAddVertices(room *rp, int num_new_verts) {
|
|
|
|
if (num_new_verts == 0)
|
|
|
|
return 0;
|
|
|
|
|
2024-09-08 11:29:41 +00:00
|
|
|
auto newverts = mem_rmalloc<vector>(rp->num_verts + num_new_verts);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(newverts != NULL);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (int i = 0; i < rp->num_verts; i++) {
|
|
|
|
newverts[i] = rp->verts[i];
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
mem_free(rp->verts);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
rp->verts = newverts;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
rp->num_verts += num_new_verts;
|
|
|
|
|
|
|
|
return (rp->num_verts - num_new_verts);
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Create space for additional faces in a room.
|
|
|
|
// Allocates a new faces array, copies from the old list, and frees the old list
|
|
|
|
// The new faces are at the end of the list, so none of the old faces change number
|
|
|
|
// Parameters: rp - the room
|
2024-04-19 20:58:24 +00:00
|
|
|
// num_new_faces - how many faces are being added to the room
|
2024-06-15 18:12:48 +00:00
|
|
|
// Returns: the number of the first new face
|
|
|
|
int RoomAddFaces(room *rp, int num_new_faces) {
|
|
|
|
if (num_new_faces == 0)
|
|
|
|
return 0;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-09-08 11:29:41 +00:00
|
|
|
auto newfaces = mem_rmalloc<face>(rp->num_faces + num_new_faces);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(newfaces != NULL);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int i = 0; i < rp->num_faces; i++)
|
2024-06-15 18:12:48 +00:00
|
|
|
newfaces[i] = rp->faces[i];
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
mem_free(rp->faces);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
rp->faces = newfaces;
|
|
|
|
rp->num_faces += num_new_faces;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
if (rp->num_bbf_regions) {
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int i = 0; i < rp->num_bbf_regions; i++) {
|
2024-06-15 18:12:48 +00:00
|
|
|
mem_free(rp->bbf_list[i]);
|
|
|
|
}
|
|
|
|
mem_free(rp->bbf_list);
|
|
|
|
mem_free(rp->num_bbf);
|
|
|
|
mem_free(rp->bbf_list_min_xyz);
|
|
|
|
mem_free(rp->bbf_list_max_xyz);
|
|
|
|
mem_free(rp->bbf_list_sector);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
rp->num_bbf_regions = 0;
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
return (rp->num_faces - num_new_faces);
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Check if a point is inside, outside, or on an edge of a polygon
|
|
|
|
// Parameters: checkv - the point to be checked
|
2024-04-19 20:58:24 +00:00
|
|
|
// v1,v0 - the edge to check against. Two sequential verts in a clockwise polygon.
|
|
|
|
// normal - the surface normal of the polygon
|
2024-06-15 18:12:48 +00:00
|
|
|
// Returns: 1 if the point in inside the edge
|
2024-04-19 20:58:24 +00:00
|
|
|
// 0 if the point is on the edge
|
|
|
|
// -1 if the point is outside the edge
|
2024-06-15 18:12:48 +00:00
|
|
|
int CheckPointAgainstEdge(vector *checkv, vector *v0, vector *v1, vector *normal) {
|
|
|
|
int ii, jj;
|
|
|
|
float edge_i, edge_j, check_i, check_j;
|
|
|
|
float *vv0, *vv1, *checkvv;
|
|
|
|
float edge_mag, dot;
|
|
|
|
|
|
|
|
// Get the vertices for projection
|
|
|
|
GetIJ(normal, &ii, &jj);
|
|
|
|
|
|
|
|
// Get pointers to elements of our vectors
|
|
|
|
vv0 = (float *)v0;
|
|
|
|
vv1 = (float *)v1;
|
|
|
|
checkvv = (float *)checkv;
|
|
|
|
|
|
|
|
// Get 2d vector for edge
|
|
|
|
edge_i = vv1[ii] - vv0[ii];
|
|
|
|
edge_j = vv1[jj] - vv0[jj];
|
|
|
|
edge_mag = sqrt(edge_i * edge_i + edge_j * edge_j);
|
|
|
|
|
|
|
|
// Get 2d vector for check point
|
|
|
|
check_i = checkvv[ii] - vv0[ii];
|
|
|
|
check_j = checkvv[jj] - vv0[jj];
|
|
|
|
|
|
|
|
// Now do the dot product to see if the check point is on the front
|
|
|
|
dot = ((-edge_j * check_i) + (edge_i * check_j)) / edge_mag;
|
|
|
|
|
|
|
|
// Check dot value and return appropriate code
|
|
|
|
if (dot > POINT_TO_EDGE_EPSILON)
|
|
|
|
return -1;
|
|
|
|
else if (dot < -POINT_TO_EDGE_EPSILON)
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clips on edge of a polygon against another edge
|
|
|
|
// Parameters: normal - defines the plane in which these edgs lie
|
2024-04-19 20:58:24 +00:00
|
|
|
// v0,v1 - the edge to be clipped
|
|
|
|
// v2,v3 - is the edge clipped against
|
|
|
|
// newv - filled in with the intersection point
|
2024-06-15 18:12:48 +00:00
|
|
|
void ClipEdge(vector *normal, vertex *v0, vertex *v1, vector *v2, vector *v3, vertex *newv) {
|
|
|
|
float *vv0, *vv1, *vv2, *vv3;
|
|
|
|
float k;
|
|
|
|
int ii, jj;
|
|
|
|
|
|
|
|
// Get the vertices for projection
|
|
|
|
GetIJ(normal, &ii, &jj);
|
|
|
|
|
|
|
|
// Get pointers to elements of our vectors
|
|
|
|
vv0 = (float *)&v0->vec;
|
|
|
|
vv1 = (float *)&v1->vec;
|
|
|
|
vv2 = (float *)v2;
|
|
|
|
vv3 = (float *)v3;
|
|
|
|
|
|
|
|
k = ((vv2[jj] - vv0[jj]) * (vv3[ii] - vv2[ii]) - (vv2[ii] - vv0[ii]) * (vv3[jj] - vv2[jj])) /
|
|
|
|
((vv1[jj] - vv0[jj]) * (vv3[ii] - vv2[ii]) - (vv1[ii] - vv0[ii]) * (vv3[jj] - vv2[jj]));
|
|
|
|
|
|
|
|
// Deal w/ precision problems
|
|
|
|
if (k < 0.0) {
|
|
|
|
ASSERT((vm_VectorDistance(&v1->vec, &v0->vec) * -k) < POINT_TO_EDGE_EPSILON);
|
|
|
|
k = 0.0;
|
|
|
|
}
|
|
|
|
if (k > 1.0) {
|
|
|
|
ASSERT((vm_VectorDistance(&v1->vec, &v0->vec) * (k - 1.0)) < POINT_TO_EDGE_EPSILON);
|
|
|
|
k = 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for valid values of k
|
|
|
|
ASSERT((k >= 0) && (k <= 1.0));
|
|
|
|
|
|
|
|
newv->vec = v0->vec + (v1->vec - v0->vec) * k;
|
|
|
|
|
|
|
|
newv->uvl.u = v0->uvl.u + (v1->uvl.u - v0->uvl.u) * k;
|
|
|
|
newv->uvl.v = v0->uvl.v + (v1->uvl.v - v0->uvl.v) * k;
|
|
|
|
newv->uvl.alpha = v0->uvl.alpha + ((v1->uvl.alpha - v0->uvl.alpha) * k);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finds a shared edge, if one exists, between two faces in the same room
|
|
|
|
// Parameters: fp0,fp1 - pointers to the two faces
|
|
|
|
// vn0,vn1 - filled in with the vertex numbers of the edge. These vert numbers are
|
|
|
|
// relative to their own faces. The shared edge is
|
|
|
|
//verts <vn0,vn0+1> on face 0, and <vn1+1,vn1> on face 1 Returns: true if a shared edge was found, else
|
|
|
|
// false
|
|
|
|
bool FindSharedEdge(face *fp0, face *fp1, int *vn0, int *vn1) {
|
|
|
|
int i, j, a0, b0, a1, b1;
|
|
|
|
|
|
|
|
// Go through each edge in first face
|
|
|
|
for (i = 0; i < fp0->num_verts; i++) {
|
|
|
|
|
|
|
|
// Get edge verts - <a0,b0> is edge on first face
|
|
|
|
a0 = fp0->face_verts[i];
|
|
|
|
b0 = fp0->face_verts[(i + 1) % fp0->num_verts];
|
|
|
|
|
|
|
|
// Check against second face
|
|
|
|
for (j = 0; j < fp1->num_verts; j++) {
|
|
|
|
|
|
|
|
// Get edge verts - <a1,b1> is edge on second face
|
|
|
|
a1 = fp1->face_verts[j];
|
|
|
|
b1 = fp1->face_verts[(j + 1) % fp1->num_verts];
|
|
|
|
|
|
|
|
//@@if ((a0==a1) && (b0==b1))
|
|
|
|
//@@ Int3(); //If you hit this, you probably have a duplicate
|
|
|
|
//or overlapping face
|
|
|
|
|
|
|
|
if ((a0 == b1) && (b0 == a1)) { // found match!
|
|
|
|
*vn0 = i;
|
|
|
|
*vn1 = j;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Didn't find an edge, so return error
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finds a shared edge, if one exists, between two faces in different rooms
|
|
|
|
// Parameters: rp0,rp1 - pointers to the two rooms
|
2024-04-19 20:58:24 +00:00
|
|
|
// face0,face1 - the face numbers in rp0 & rp1, respectively
|
2024-06-15 18:12:48 +00:00
|
|
|
// vn0,vn1 - filled in with the vertex numbers of the edge. These vert numbers are
|
|
|
|
// relative to their own faces. The shared edge is
|
|
|
|
//verts <vn0,vn0+1> on face 0, and <vn1+1,vn1> on face 1 Returns: true if a shared edge was found, else
|
|
|
|
// false
|
|
|
|
bool FindSharedEdgeAcrossRooms(room *rp0, int face0, room *rp1, int face1, int *vn0, int *vn1) {
|
|
|
|
int i, j;
|
|
|
|
vector *va0, *vb0, *va1, *vb1;
|
|
|
|
face *fp0, *fp1;
|
|
|
|
|
|
|
|
// Get pointers to the two faces
|
|
|
|
fp0 = &rp0->faces[face0];
|
|
|
|
fp1 = &rp1->faces[face1];
|
|
|
|
|
|
|
|
// Go through each edge in first face
|
|
|
|
for (i = 0; i < fp0->num_verts; i++) {
|
|
|
|
|
|
|
|
// Get edge verts - <va0,vb0> is edge on first face
|
|
|
|
va0 = &rp0->verts[fp0->face_verts[i]];
|
|
|
|
vb0 = &rp0->verts[fp0->face_verts[(i + 1) % fp0->num_verts]];
|
|
|
|
|
|
|
|
// Check against second face
|
|
|
|
for (j = 0; j < fp1->num_verts; j++) {
|
|
|
|
|
|
|
|
// Get edge verts - <va1,vb1> is edge on second face
|
|
|
|
va1 = &rp1->verts[fp1->face_verts[j]];
|
|
|
|
vb1 = &rp1->verts[fp1->face_verts[(j + 1) % fp1->num_verts]];
|
|
|
|
|
|
|
|
if (PointsAreSame(va0, va1) && PointsAreSame(vb0, vb1)) // this shouldn't happen
|
|
|
|
Int3();
|
|
|
|
|
|
|
|
if (PointsAreSame(va0, vb1) && PointsAreSame(vb0, va1)) { // found match!
|
|
|
|
*vn0 = i;
|
|
|
|
*vn1 = j;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Didn't find an edge, so return error
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete a vertex from a room. Assumes the vertex is unused.
|
|
|
|
// Parameters: rp - pointer to room
|
2024-04-19 20:58:24 +00:00
|
|
|
// vertnum - the vertex to delete
|
2024-06-15 18:12:48 +00:00
|
|
|
void DeleteRoomVert(room *rp, int vertnum) {
|
|
|
|
int f, v;
|
|
|
|
face *fp;
|
|
|
|
vector *newverts;
|
|
|
|
|
|
|
|
// Remap vertices in faces
|
|
|
|
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] == vertnum)
|
|
|
|
Int3(); // THIS IS VERY BAD! DELETING A VERTEX STILL IN USE!
|
|
|
|
else if (fp->face_verts[v] > vertnum)
|
|
|
|
fp->face_verts[v]--;
|
|
|
|
|
|
|
|
// malloc new list
|
2024-09-08 11:29:41 +00:00
|
|
|
newverts = mem_rmalloc<vector>(rp->num_verts - 1);
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(newverts != NULL);
|
|
|
|
|
|
|
|
// Copy verts to new list
|
|
|
|
for (v = 0; v < vertnum; v++) {
|
|
|
|
newverts[v] = rp->verts[v];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; v < rp->num_verts - 1; v++) {
|
|
|
|
newverts[v] = rp->verts[v + 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete old list
|
|
|
|
mem_free(rp->verts);
|
|
|
|
|
|
|
|
// Use new list
|
|
|
|
rp->verts = newverts;
|
|
|
|
|
|
|
|
// Update count
|
|
|
|
rp->num_verts--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the vertices in a room & remove unused ones
|
|
|
|
// Parameters: rp - the room to check
|
|
|
|
// Returns: the number of vertices removed
|
|
|
|
int DeleteUnusedRoomVerts(room *rp) {
|
|
|
|
bool vert_used[MAX_VERTS_PER_ROOM];
|
|
|
|
int f, v;
|
|
|
|
face *fp;
|
|
|
|
int n_deleted = 0;
|
|
|
|
;
|
|
|
|
|
|
|
|
// Init all the flags to unused
|
|
|
|
for (v = 0; v < rp->num_verts; v++)
|
|
|
|
vert_used[v] = 0;
|
|
|
|
|
|
|
|
// Go through all the faces & flag the used verts
|
|
|
|
for (f = 0, fp = rp->faces; f < rp->num_faces; f++, fp++)
|
|
|
|
for (v = 0; v < fp->num_verts; v++)
|
|
|
|
vert_used[fp->face_verts[v]] = 1;
|
|
|
|
|
|
|
|
// Now delete the unused verts
|
|
|
|
for (v = rp->num_verts - 1; v >= 0; v--)
|
|
|
|
if (!vert_used[v]) {
|
|
|
|
DeleteRoomVert(rp, v);
|
|
|
|
n_deleted++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Done
|
|
|
|
return n_deleted;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete a face from a room
|
|
|
|
// Parameters: rp - the room the face is in
|
2024-04-19 20:58:24 +00:00
|
|
|
// facenum - the face to be deleted
|
2024-06-15 18:12:48 +00:00
|
|
|
void DeleteRoomFace(room *rp, int facenum, bool delete_unused_verts) {
|
|
|
|
int p, f, i, t;
|
|
|
|
face *newfaces;
|
|
|
|
|
|
|
|
// Check for trigger on this face
|
|
|
|
if (rp->faces[facenum].flags & FF_HAS_TRIGGER)
|
|
|
|
DeleteTrigger(ROOMNUM(rp), facenum);
|
|
|
|
|
|
|
|
// Adjust face numbers in portals
|
|
|
|
for (p = 0; p < rp->num_portals; p++) {
|
|
|
|
portal *pp = &rp->portals[p];
|
|
|
|
ASSERT(pp->portal_face != facenum);
|
|
|
|
if (pp->portal_face > facenum)
|
|
|
|
pp->portal_face--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Adjust face numbers in triggers
|
|
|
|
for (t = 0; t < Num_triggers; t++) {
|
|
|
|
trigger *tp = &Triggers[t];
|
|
|
|
if (tp->roomnum == ROOMNUM(rp)) {
|
|
|
|
ASSERT(tp->facenum != facenum);
|
|
|
|
if (tp->facenum > facenum)
|
|
|
|
tp->facenum--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate new face list
|
2024-09-08 11:29:41 +00:00
|
|
|
newfaces = mem_rmalloc<face>(rp->num_faces - 1);
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(newfaces != NULL);
|
|
|
|
|
|
|
|
// Copy faces over
|
|
|
|
for (f = 0; f < facenum; f++)
|
|
|
|
newfaces[f] = rp->faces[f];
|
|
|
|
for (f++; f < rp->num_faces; f++)
|
|
|
|
newfaces[f - 1] = rp->faces[f];
|
2024-04-19 20:58:24 +00:00
|
|
|
|
|
|
|
#ifdef NEWEDITOR
|
2024-06-15 18:12:48 +00:00
|
|
|
// Decrement texture usage
|
|
|
|
if (ROOMNUM(rp) < MAX_ROOMS) {
|
|
|
|
LevelTexDecrementTexture(rp->faces[facenum].tmap);
|
|
|
|
//@@RoomTexDecrementTexture(rp->faces[facenum].tmap,m_Textures_in_use,false);
|
|
|
|
} else {
|
|
|
|
// TODO: for room tab, replace second line with first
|
|
|
|
ned_MarkTextureInUse(rp->faces[facenum].tmap, false);
|
|
|
|
//@@RoomTexDecrementTexture(rp->faces[facenum].tmap,m_Textures_in_use);
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
#endif
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Free deleted face memeory
|
|
|
|
FreeRoomFace(&rp->faces[facenum]);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Free old face list
|
|
|
|
mem_free(rp->faces);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Use new face list
|
|
|
|
rp->faces = newfaces;
|
|
|
|
rp->num_faces--;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Adjust the current and marked faces if necessary
|
2024-04-19 20:58:24 +00:00
|
|
|
#ifdef NEWEDITOR
|
2024-06-15 18:12:48 +00:00
|
|
|
if (theApp.m_pLevelWnd != NULL)
|
2024-04-19 20:58:24 +00:00
|
|
|
#endif
|
2024-06-15 18:12:48 +00:00
|
|
|
if (rp == Curroomp) {
|
|
|
|
if (Curface == rp->num_faces)
|
|
|
|
Curface = rp->num_faces - 1;
|
|
|
|
else if (Curface > rp->num_faces)
|
|
|
|
Int3(); // this shouldn't be
|
|
|
|
if (Markedface == rp->num_faces)
|
|
|
|
Markedface = rp->num_faces - 1;
|
|
|
|
else if (Markedface > rp->num_faces)
|
|
|
|
Int3(); // this shouldn't be
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rp->num_bbf_regions) {
|
|
|
|
for (i = 0; i < rp->num_bbf_regions; i++) {
|
|
|
|
mem_free(rp->bbf_list[i]);
|
|
|
|
}
|
|
|
|
mem_free(rp->bbf_list);
|
|
|
|
mem_free(rp->num_bbf);
|
|
|
|
mem_free(rp->bbf_list_min_xyz);
|
|
|
|
mem_free(rp->bbf_list_max_xyz);
|
|
|
|
mem_free(rp->bbf_list_sector);
|
|
|
|
|
|
|
|
rp->num_bbf_regions = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get rid of any now-unused verts
|
|
|
|
if (delete_unused_verts)
|
|
|
|
DeleteUnusedRoomVerts(rp);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deletes a portal from a room. Does not delete this portal this connects to
|
|
|
|
// Parameters: rp - the room the portal is in
|
2024-04-19 20:58:24 +00:00
|
|
|
// portalnum - the portal to be deleted
|
2024-06-15 18:12:48 +00:00
|
|
|
void DeleteRoomPortal(room *rp, int portalnum) {
|
|
|
|
portal *pp = &rp->portals[portalnum];
|
|
|
|
face *fp = &rp->faces[pp->portal_face];
|
|
|
|
portal *newportals;
|
|
|
|
|
|
|
|
// Clear the portal field of the portal's face
|
|
|
|
ASSERT(fp->portal_num == portalnum);
|
|
|
|
fp->portal_num = -1;
|
|
|
|
|
|
|
|
// Renumber all portals that come after this one
|
|
|
|
for (int p = portalnum + 1; p < rp->num_portals; p++) {
|
|
|
|
portal *tp = &rp->portals[p];
|
|
|
|
|
|
|
|
// Renumber the face in this portal
|
|
|
|
ASSERT(rp->faces[tp->portal_face].portal_num == p);
|
|
|
|
rp->faces[tp->portal_face].portal_num--;
|
|
|
|
|
|
|
|
// Renumber the link back to this portal
|
|
|
|
if (tp->croom != -1)
|
|
|
|
Rooms[tp->croom].portals[tp->cportal].cportal--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alloc new portal list
|
|
|
|
if (rp->num_portals == 1)
|
|
|
|
newportals = NULL;
|
|
|
|
else {
|
2024-09-08 11:29:41 +00:00
|
|
|
newportals = mem_rmalloc<portal>(rp->num_portals - 1);
|
2024-06-15 18:12:48 +00:00
|
|
|
ASSERT(newportals != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy portals over
|
2024-06-29 09:28:14 +00:00
|
|
|
int p;
|
2024-06-15 18:12:48 +00:00
|
|
|
for (p = 0; p < portalnum; p++)
|
|
|
|
newportals[p] = rp->portals[p];
|
|
|
|
for (p++; p < rp->num_portals; p++)
|
|
|
|
newportals[p - 1] = rp->portals[p];
|
|
|
|
|
|
|
|
// Free old portal list
|
|
|
|
mem_free(rp->portals);
|
|
|
|
|
|
|
|
// Use the new list
|
|
|
|
rp->portals = newportals;
|
|
|
|
rp->num_portals--;
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
//Remove holes in the room list
|
|
|
|
void CompressRooms(void)
|
|
|
|
{
|
|
|
|
int hole,t,high_room;
|
|
|
|
|
|
|
|
t = Highest_room_index;
|
|
|
|
high_room=Highest_room_index;
|
|
|
|
|
|
|
|
|
|
|
|
for (hole=0; hole < t; hole++)
|
|
|
|
if (! Rooms[hole].used) {
|
|
|
|
|
|
|
|
// found an unused t which is a hole if a used t follows (not necessarily immediately) it.
|
|
|
|
while (! Rooms[t].used)
|
|
|
|
t--;
|
|
|
|
|
|
|
|
if (t > hole) {
|
|
|
|
room *rp;
|
|
|
|
int objnum;
|
|
|
|
|
|
|
|
// Ok, hole is the index of a hole, t is the index of a t which follows it.
|
|
|
|
// Copy t into hole, update pointers to it, update Curroomp, Markedroomp if necessary.
|
|
|
|
Rooms[hole] = Rooms[t];
|
|
|
|
Rooms[t].used = 0;
|
|
|
|
|
|
|
|
if (Curroomp == &Rooms[t])
|
|
|
|
Curroomp = &Rooms[hole];
|
|
|
|
|
|
|
|
if (Markedroomp == &Rooms[t])
|
|
|
|
Markedroomp = &Rooms[hole];
|
|
|
|
|
|
|
|
//Fix connections
|
|
|
|
rp = &Rooms[hole];
|
|
|
|
for (int p=0;p<rp->num_portals;p++) {
|
|
|
|
portal *pp = &rp->portals[p];
|
|
|
|
|
|
|
|
if (pp->croom == -1) { //a terrain connection
|
|
|
|
for (int l=0;l<MAX_LINK_TILES;l++)
|
|
|
|
if (Link_tile[l].terrain_seg != -1)
|
|
|
|
if (Link_tile[l].mine_seg == t) {
|
|
|
|
Link_tile[l].mine_seg = hole;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ASSERT(l < MAX_LINK_TILES);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ASSERT(Rooms[pp->croom].portals[pp->cportal].croom == t);
|
|
|
|
Rooms[pp->croom].portals[pp->cportal].croom = hole;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Update object room pointers
|
|
|
|
for (objnum = rp->objects; objnum != -1; objnum = Objects[objnum].next) {
|
|
|
|
ASSERT(Objects[objnum].roomnum == t);
|
|
|
|
Objects[objnum].roomnum = hole;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Fix triggers
|
|
|
|
for (p=0;p<Num_triggers;p++)
|
|
|
|
if (Triggers[p].roomnum == t)
|
|
|
|
Triggers[p].roomnum = hole;
|
|
|
|
|
|
|
|
//Fix selected list
|
|
|
|
for (int i=0;i<N_selected_rooms;i++)
|
|
|
|
if (Selected_rooms[i] == t)
|
|
|
|
Selected_rooms[i] = hole;
|
|
|
|
|
|
|
|
t--;
|
|
|
|
} // end if (t > hole)
|
|
|
|
} // end if
|
|
|
|
|
|
|
|
while (! Rooms[Highest_room_index].used)
|
|
|
|
Highest_room_index--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compress mine by getting rid of holes the the room array
|
|
|
|
void CompressMine()
|
|
|
|
{
|
|
|
|
//if (Do_duplicate_vertex_check) {
|
|
|
|
// med_combine_duplicate_vertices(Vertex_active);
|
|
|
|
// Do_duplicate_vertex_check = 0;
|
|
|
|
//}
|
|
|
|
|
|
|
|
CompressRooms();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Copies the contents of one face to another.
|
|
|
|
// Parameters: dfp - pointer to the destination face. This face should be uninitialized.
|
2024-04-19 20:58:24 +00:00
|
|
|
// sfp - pointer to the source face
|
2024-06-15 18:12:48 +00:00
|
|
|
void CopyPortal(face *dfp, face *sfp) {
|
|
|
|
InitRoomFace(dfp, sfp->num_verts);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
dfp->flags = sfp->flags;
|
|
|
|
dfp->portal_num = sfp->portal_num;
|
|
|
|
dfp->normal = sfp->normal;
|
|
|
|
dfp->tmap = sfp->tmap;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (int i = 0; i < sfp->num_verts; i++) {
|
|
|
|
dfp->face_verts[i] = sfp->face_verts[i];
|
|
|
|
dfp->face_uvls[i] = sfp->face_uvls[i];
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Copies the data from one room into another
|
|
|
|
// Note: Portals are not copied, and the new room will have zero portals
|
|
|
|
// Parameters: destp - the destination room of the copy
|
2024-04-19 20:58:24 +00:00
|
|
|
// srcp - the source room for the copy
|
2024-06-15 18:12:48 +00:00
|
|
|
void CopyRoom(room *destp, room *srcp) {
|
|
|
|
// Initialize the new room
|
|
|
|
InitRoom(destp, srcp->num_verts, srcp->num_faces, 0);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Copy over the faces
|
|
|
|
for (int i = 0; i < destp->num_faces; i++) {
|
|
|
|
CopyFace(&destp->faces[i], &srcp->faces[i]);
|
2024-04-19 20:58:24 +00:00
|
|
|
#ifdef NEWEDITOR
|
2024-06-15 18:12:48 +00:00
|
|
|
LevelTexIncrementTexture(srcp->faces[i].tmap);
|
2024-04-19 20:58:24 +00:00
|
|
|
#endif
|
2024-06-15 18:12:48 +00:00
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Copy over the verts
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int i = 0; i < destp->num_verts; i++)
|
2024-06-15 18:12:48 +00:00
|
|
|
destp->verts[i] = srcp->verts[i];
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Copy doorway info
|
|
|
|
if (srcp->doorway_data) {
|
2024-09-08 11:29:41 +00:00
|
|
|
destp->doorway_data = mem_rmalloc<doorway>();
|
2024-06-15 18:12:48 +00:00
|
|
|
*destp->doorway_data = *srcp->doorway_data;
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Copy over the rest of the data
|
|
|
|
destp->flags = srcp->flags;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Copy over room lighting
|
|
|
|
Room_multiplier[destp - Rooms] = Room_multiplier[srcp - Rooms];
|
|
|
|
Room_ambience_r[destp - Rooms] = Room_ambience_r[srcp - Rooms];
|
|
|
|
Room_ambience_g[destp - Rooms] = Room_ambience_g[srcp - Rooms];
|
|
|
|
Room_ambience_b[destp - Rooms] = Room_ambience_b[srcp - Rooms];
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Adds a new portal for this room. Returns the portal number.
|
|
|
|
// Initializes the flags
|
|
|
|
int AddPortal(room *rp) {
|
2024-09-08 11:29:41 +00:00
|
|
|
auto newlist = mem_rmalloc<portal>(rp->num_portals + 1);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Copy from old list to new list, and free old list
|
|
|
|
if (rp->num_portals) {
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int i = 0; i < rp->num_portals; i++)
|
2024-06-15 18:12:48 +00:00
|
|
|
newlist[i] = rp->portals[i];
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
mem_free(rp->portals);
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Point at new list
|
|
|
|
rp->portals = newlist;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Init flags
|
|
|
|
rp->portals[rp->num_portals].flags = 0;
|
|
|
|
rp->portals[rp->num_portals].bnode_index = -1;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
return rp->num_portals++;
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Links two rooms, creating portals in each room
|
|
|
|
// Parameters: roomlist - pointer to the array of rooms
|
2024-04-19 20:58:24 +00:00
|
|
|
// room0,face0 - the room & face numbers of the first room
|
|
|
|
// room1,face1 - the room & face numbers of the second room
|
2024-06-15 18:12:48 +00:00
|
|
|
void LinkRooms(room *roomlist, int room0, int face0, int room1, int face1) {
|
|
|
|
room *rp0, *rp1;
|
|
|
|
int pn0, pn1;
|
|
|
|
int nv0, nv1;
|
|
|
|
|
|
|
|
// Set some vars
|
|
|
|
rp0 = &roomlist[room0];
|
|
|
|
rp1 = &roomlist[room1];
|
|
|
|
nv0 = rp0->faces[face0].num_verts;
|
|
|
|
nv1 = rp1->faces[face1].num_verts;
|
|
|
|
|
|
|
|
// Make sure no portals already
|
|
|
|
ASSERT(rp0->faces[face0].portal_num == -1);
|
|
|
|
ASSERT(rp1->faces[face1].portal_num == -1);
|
|
|
|
|
|
|
|
// Check for match
|
|
|
|
if (nv0 != nv1)
|
|
|
|
Int3(); // You must get Matt if you hit this!
|
|
|
|
|
|
|
|
// Create portals
|
|
|
|
pn0 = AddPortal(rp0);
|
|
|
|
pn1 = AddPortal(rp1);
|
|
|
|
|
|
|
|
// Link portals
|
|
|
|
rp0->portals[pn0].croom = room1;
|
|
|
|
rp0->portals[pn0].cportal = pn1;
|
|
|
|
rp1->portals[pn1].croom = room0;
|
|
|
|
rp1->portals[pn1].cportal = pn0;
|
|
|
|
|
|
|
|
// Add faces to portals
|
|
|
|
rp0->portals[pn0].portal_face = face0;
|
|
|
|
rp1->portals[pn1].portal_face = face1;
|
|
|
|
|
|
|
|
// Mark faces as being part of portals
|
|
|
|
rp0->faces[face0].portal_num = pn0;
|
|
|
|
rp1->faces[face1].portal_num = pn1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finds the min and max x,y,z values of the vertices in a room
|
|
|
|
// Parameters: min,max - filled in with the minimum and maximum x,y, & z values, respectively
|
|
|
|
// rp = the room
|
|
|
|
void ComputeRoomMinMax(vector *min, vector *max, room *rp) {
|
|
|
|
min->x = min->y = min->z = FLT_MAX;
|
|
|
|
max->x = max->y = max->z = -FLT_MAX;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (int i = 0; i < rp->num_verts; i++) {
|
|
|
|
if (rp->verts[i].x > max->x)
|
|
|
|
max->x = rp->verts[i].x;
|
|
|
|
if (rp->verts[i].y > max->y)
|
|
|
|
max->y = rp->verts[i].y;
|
|
|
|
if (rp->verts[i].z > max->z)
|
|
|
|
max->z = rp->verts[i].z;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
if (rp->verts[i].x < min->x)
|
|
|
|
min->x = rp->verts[i].x;
|
|
|
|
if (rp->verts[i].y < min->y)
|
|
|
|
min->y = rp->verts[i].y;
|
|
|
|
if (rp->verts[i].z < min->z)
|
|
|
|
min->z = rp->verts[i].z;
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Builds a list of all the vertices in a room that are part of a portal
|
|
|
|
// Parameters: rp - the room to check
|
|
|
|
// list - filled in with the list of vert numbers. List should be
|
|
|
|
//MAX_VERTS_PER_ROOM big Returns: the number of verts in the list
|
|
|
|
int BuildListOfPortalVerts(room *rp, int *list) {
|
|
|
|
int i, t, j;
|
|
|
|
int count = 0;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (i = 0; i < rp->num_portals; i++) {
|
|
|
|
face *fp = &rp->faces[rp->portals[i].portal_face];
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (t = 0; t < fp->num_verts; t++) {
|
|
|
|
int v = fp->face_verts[t];
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (j = 0; j < count; j++)
|
|
|
|
if (list[j] == v)
|
|
|
|
break;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
if (j == count)
|
|
|
|
list[count++] = v;
|
|
|
|
}
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
return count;
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#define BUF_LEN 100000
|
|
|
|
|
|
|
|
char error_buf[BUF_LEN];
|
|
|
|
int error_buf_offset;
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Log an error in the room check process
|
|
|
|
void CheckError(const char *str, ...) {
|
|
|
|
std::va_list arglist;
|
|
|
|
int nchars;
|
|
|
|
|
|
|
|
va_start(arglist, str);
|
|
|
|
nchars = std::vsnprintf(error_buf + error_buf_offset, BUF_LEN - error_buf_offset, str, arglist);
|
|
|
|
va_end(arglist);
|
|
|
|
|
|
|
|
error_buf_offset += strlen(error_buf + error_buf_offset);
|
|
|
|
|
|
|
|
if (error_buf_offset >= BUF_LEN)
|
|
|
|
OutrageMessageBox("There has been a text buffer overflow in CheckError().");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks the normals in a room
|
|
|
|
// Returns the number of bad normals
|
|
|
|
int CheckNormals(room *rp) {
|
|
|
|
int errors = 0;
|
|
|
|
|
|
|
|
for (int f = 0; f < rp->num_faces; f++)
|
|
|
|
if (!ComputeFaceNormal(rp, f)) {
|
|
|
|
CheckError("Room %3d face %3d has a bad or low-precision normal\n", ROOMNUM(rp), f);
|
|
|
|
errors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks for concave faces
|
|
|
|
// Returns the number of concave faces
|
|
|
|
int CheckConcaveFaces(room *rp) {
|
|
|
|
int f;
|
|
|
|
face *fp;
|
|
|
|
int errors = 0;
|
|
|
|
|
|
|
|
// Go through all the faces
|
|
|
|
for (f = 0, fp = rp->faces; f < rp->num_faces; f++, fp++) {
|
|
|
|
|
|
|
|
int vert = CheckFaceConcavity(fp->num_verts, fp->face_verts, &fp->normal, rp->verts);
|
|
|
|
if (vert != -1) {
|
|
|
|
CheckError("Room %3d face %3d is concave at vertex %d\n", ROOMNUM(rp), f, vert);
|
|
|
|
errors++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CheckDegenerateFaces(room *rp) {
|
|
|
|
int errors = 0;
|
|
|
|
int f, v;
|
|
|
|
face *fp;
|
|
|
|
|
|
|
|
// Go through all the faces
|
|
|
|
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] == fp->face_verts[(v + 2) % fp->num_verts]) {
|
|
|
|
CheckError("Room %3d face %3d is degenerate\n", ROOMNUM(rp), f);
|
|
|
|
errors++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the two faces of a portal match
|
|
|
|
// Returns true if the portal is ok
|
|
|
|
bool CheckPortal(room *rp0, int p0) {
|
|
|
|
int i, j, p1;
|
|
|
|
room *rp1;
|
|
|
|
portal *pp0, *pp1;
|
|
|
|
face *fp0, *fp1;
|
|
|
|
|
|
|
|
// Get info
|
|
|
|
pp0 = &rp0->portals[p0];
|
|
|
|
rp1 = &Rooms[pp0->croom];
|
|
|
|
p1 = pp0->cportal;
|
|
|
|
pp1 = &rp1->portals[p1];
|
|
|
|
fp0 = &rp0->faces[pp0->portal_face];
|
|
|
|
fp1 = &rp1->faces[pp1->portal_face];
|
|
|
|
|
|
|
|
// Make sure faces have the same number of verts
|
|
|
|
if (fp0->num_verts != fp1->num_verts) {
|
|
|
|
CheckError("Room %d portal %d has a different number of vertices (%d) than room %d portal %d (%d)\n", ROOMNUM(rp0),
|
|
|
|
p0, fp0->num_verts, ROOMNUM(rp1), p1, fp1->num_verts);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
CheckError("Cannot find a single matching point between room %d portal %d and room %d portal %d\n", ROOMNUM(rp0),
|
|
|
|
p0, ROOMNUM(rp1), p1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
vector *v0, *v1, *prev0, *prev1;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
// Trace through verts in faces, making sure they match
|
|
|
|
prev0 = &rp0->verts[fp0->face_verts[i]];
|
|
|
|
prev1 = &rp1->verts[fp1->face_verts[j]];
|
|
|
|
for (n = 1; n < fp0->num_verts; n++) {
|
|
|
|
|
|
|
|
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)) {
|
|
|
|
CheckError("Vertex %d in room %d portal %d does not match vertex %d in room %d portal %d\n",
|
|
|
|
(i + n) % fp0->num_verts, ROOMNUM(rp0), p0, (j - n + fp1->num_verts) % fp1->num_verts, ROOMNUM(rp1),
|
|
|
|
p1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extern void DumpTextToClipboard(char *text);
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Checks for bad portal connections
|
|
|
|
// Returns the number of bad portals
|
|
|
|
int CheckPortals(room *rp) {
|
|
|
|
portal *pp;
|
|
|
|
int p;
|
|
|
|
int r = ROOMNUM(rp);
|
|
|
|
int errors = 0;
|
|
|
|
|
|
|
|
for (p = 0, pp = rp->portals; p < rp->num_portals; pp++, p++) {
|
|
|
|
|
|
|
|
// Make sure the connecting portal points back to this portal
|
|
|
|
if ((Rooms[pp->croom].portals[pp->cportal].croom != r) || (Rooms[pp->croom].portals[pp->cportal].cportal != p)) {
|
|
|
|
CheckError("Room %d, portal %d points at room %d portal %d, but the latter points at room %d portal %d\n", r, p,
|
|
|
|
pp->croom, pp->cportal, Rooms[pp->croom].portals[pp->cportal].croom,
|
|
|
|
Rooms[pp->croom].portals[pp->cportal].cportal);
|
|
|
|
errors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check to make sure the face in this portal points back to it
|
|
|
|
if (rp->faces[pp->portal_face].portal_num != p) {
|
|
|
|
CheckError("Room %d portal %d contains face %d, but that face points at portal %d\n", r, p, pp->portal_face,
|
|
|
|
rp->faces[pp->portal_face].portal_num);
|
|
|
|
errors++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks if the given face is a duplicate of another face
|
|
|
|
// Returns the face number of which this is a duplicate, or -1 if not a duplicate
|
|
|
|
int CheckForDuplicateFace(room *rp, int facenum) {
|
|
|
|
face *fp0 = &rp->faces[facenum];
|
|
|
|
int r = ROOMNUM(rp);
|
|
|
|
|
|
|
|
for (int j = 0; j < facenum; j++) {
|
|
|
|
face *fp1 = &rp->faces[j];
|
|
|
|
|
|
|
|
if (fp0->num_verts == fp1->num_verts)
|
|
|
|
for (int v = 0; v < fp1->num_verts; v++) // look for a shared vert
|
|
|
|
if (fp1->face_verts[v] == fp0->face_verts[0]) {
|
2024-06-29 09:28:14 +00:00
|
|
|
int t;
|
|
|
|
for (t = 0; t < fp0->num_verts; t++)
|
2024-06-15 18:12:48 +00:00
|
|
|
if (fp0->face_verts[t] != fp1->face_verts[(v + t) % fp1->num_verts])
|
|
|
|
break;
|
|
|
|
if (t == fp0->num_verts) {
|
|
|
|
return j;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Didn't find a duplicate
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Removes all the duplicate faces in a room
|
|
|
|
void RemoveDuplicateFaces(room *rp) {
|
|
|
|
int r = ROOMNUM(rp);
|
|
|
|
int removed = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < rp->num_faces; i++) {
|
|
|
|
int dup = CheckForDuplicateFace(rp, i);
|
|
|
|
|
|
|
|
if (dup != -1) {
|
|
|
|
DeleteRoomFace(rp, i);
|
|
|
|
mprintf(0, "Removed face %d (duplicate of face %d) from room %d\n", i, dup, r);
|
|
|
|
removed++;
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (removed) {
|
|
|
|
OutrageMessageBox("%d duplicate faces have been removed from room %d.", removed, r);
|
|
|
|
World_changed = 1;
|
|
|
|
} else
|
|
|
|
OutrageMessageBox("There are no duplicate faces in room %d.", r);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks for duplicate faces
|
|
|
|
// Returns the number of duplicate faces
|
|
|
|
int CheckDuplicateFaces(room *rp) {
|
|
|
|
int r = ROOMNUM(rp);
|
|
|
|
int errors = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < rp->num_faces; i++) {
|
|
|
|
int dup = CheckForDuplicateFace(rp, i);
|
|
|
|
|
|
|
|
if (dup != -1) {
|
|
|
|
CheckError("Room %d: face %d is the same as face %d\n", r, i, dup);
|
|
|
|
errors++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks for non-planar faces
|
|
|
|
// Returns the number of non-planar faces
|
|
|
|
int CheckNonPlanarFaces(room *rp) {
|
|
|
|
int r = ROOMNUM(rp);
|
|
|
|
int errors = 0;
|
|
|
|
|
|
|
|
for (int f = 0; f < rp->num_faces; f++)
|
|
|
|
if (!FaceIsPlanar(rp, f)) {
|
|
|
|
if (rp->faces[f].portal_num != -1)
|
|
|
|
CheckError("Room %d: face %d is not planar. FACE IS PORTAL!\n", r, f);
|
|
|
|
else
|
|
|
|
CheckError("Room %d: face %d is not planar\n", r, f);
|
|
|
|
errors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks for duplicate points
|
|
|
|
// Returns the number of duplicate points
|
|
|
|
int CheckDuplicatePoints(room *rp) {
|
|
|
|
int r = ROOMNUM(rp);
|
|
|
|
int errors = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < rp->num_verts; i++)
|
|
|
|
for (int j = 0; j < i; j++)
|
|
|
|
if (PointsAreSame(&rp->verts[i], &rp->verts[j])) {
|
|
|
|
float d = vm_VectorDistance(&rp->verts[i], &rp->verts[j]);
|
|
|
|
CheckError("Room %d: vert %3d is the same as vert %3d (d=%.2f)\n", r, i, j, d);
|
|
|
|
errors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find any unused points in a room
|
|
|
|
int CheckUnusedPoints(room *rp) {
|
|
|
|
int r = ROOMNUM(rp);
|
|
|
|
int errors = 0;
|
|
|
|
bool vert_used[MAX_VERTS_PER_ROOM];
|
|
|
|
int f, v;
|
|
|
|
face *fp;
|
|
|
|
|
|
|
|
// Init all the flags to unused
|
|
|
|
for (v = 0; v < rp->num_verts; v++)
|
|
|
|
vert_used[v] = 0;
|
|
|
|
|
|
|
|
// Go through all the faces & flag the used verts
|
|
|
|
for (f = 0, fp = rp->faces; f < rp->num_faces; f++, fp++)
|
|
|
|
for (v = 0; v < fp->num_verts; v++)
|
|
|
|
vert_used[fp->face_verts[v]] = 1;
|
|
|
|
|
|
|
|
// Now delete the unused verts
|
|
|
|
for (v = 0; v < rp->num_verts; v++)
|
|
|
|
if (!vert_used[v]) {
|
|
|
|
CheckError("Room %d: point %d is unused\n", r, v);
|
|
|
|
errors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Done
|
|
|
|
return errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks for duplicate points in faces
|
|
|
|
// Returns the number of duplicate face points
|
|
|
|
int CheckDuplicateFacePoints(room *rp) {
|
|
|
|
int r = ROOMNUM(rp);
|
|
|
|
int f, v, errors = 0;
|
|
|
|
face *fp;
|
|
|
|
|
|
|
|
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] == fp->face_verts[(v + 1) % fp->num_verts]) {
|
|
|
|
CheckError("Room %d, face %d: vert #%d (%d) is the same as #%d\n", r, f, (v + 1) % fp->num_verts,
|
|
|
|
fp->face_verts[v], v);
|
|
|
|
errors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if faces match exactly, else false
|
|
|
|
int CheckPortalFaces(room *rp0, int facenum0, room *rp1, int facenum1) {
|
|
|
|
face *fp0 = &rp0->faces[facenum0];
|
|
|
|
face *fp1 = &rp1->faces[facenum1];
|
|
|
|
vector *v0, *v1, *prev_v0, *prev_v1;
|
|
|
|
int n, i, j, prev_vn0, prev_vn1;
|
|
|
|
int points_added = 0;
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
prev_vn0 = fp0->face_verts[i];
|
|
|
|
prev_vn1 = fp1->face_verts[j];
|
|
|
|
prev_v0 = &rp0->verts[prev_vn0];
|
|
|
|
prev_v1 = &rp1->verts[prev_vn1];
|
|
|
|
|
|
|
|
if (fp0->num_verts != fp1->num_verts) {
|
|
|
|
CheckError("Room %d: portal face %d does not match %d:%d\n", ROOMNUM(rp0), facenum0, ROOMNUM(rp1), facenum1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Trace through faces, adding points where needed
|
|
|
|
for (n = 1; n < fp0->num_verts; n++) {
|
|
|
|
int vn0, vn1;
|
|
|
|
|
|
|
|
vn0 = fp0->face_verts[(i + n) % fp0->num_verts];
|
|
|
|
vn1 = fp1->face_verts[(j - n + fp1->num_verts) % fp1->num_verts];
|
|
|
|
|
|
|
|
v0 = &rp0->verts[vn0];
|
|
|
|
v1 = &rp1->verts[vn1];
|
|
|
|
|
|
|
|
if (!PointsAreSame(v0, v1)) { // Points are at least very close.
|
|
|
|
CheckError("Room %d: portal face %d does not match %d:%d\n", ROOMNUM(rp0), facenum0, ROOMNUM(rp1), facenum1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
prev_vn0 = vn0;
|
|
|
|
prev_vn1 = vn1;
|
|
|
|
|
|
|
|
prev_v0 = v0;
|
|
|
|
prev_v1 = v1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1; // no errors found, so faces ok
|
|
|
|
}
|
|
|
|
|
|
|
|
int CheckRoomPortalFaces(room *rp) {
|
|
|
|
int errors = 0;
|
|
|
|
|
|
|
|
for (int p = 0; p < rp->num_portals; p++) {
|
|
|
|
portal *pp = &rp->portals[p];
|
|
|
|
if (!CheckPortalFaces(rp, pp->portal_face, &Rooms[pp->croom], Rooms[pp->croom].portals[pp->cportal].portal_face))
|
|
|
|
errors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find t-joints in this room
|
|
|
|
int FindTJoints(room *rp) {
|
|
|
|
int f, f2, v, v2;
|
|
|
|
face *fp, *fp2;
|
|
|
|
int tjoints = 0;
|
|
|
|
int points_added = 0;
|
|
|
|
|
|
|
|
// Now search for and fix t-joints
|
|
|
|
for (f = 0, fp = rp->faces; f < rp->num_faces; f++, fp++) {
|
|
|
|
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
|
|
|
|
|
|
|
|
// 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 (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)) {
|
|
|
|
CheckError("Room %d: face %d edge %d has a T-joint\n", ROOMNUM(rp), f, v);
|
|
|
|
tjoints++;
|
|
|
|
goto next_face;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
next_face:;
|
|
|
|
}
|
|
|
|
|
|
|
|
return tjoints;
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Counts the number of unique textures in a level, plus gives names of textures used
|
2024-06-15 18:12:48 +00:00
|
|
|
void CountUniqueTextures() {
|
|
|
|
uint16_t *texture_tracking = (uint16_t *)mem_malloc(MAX_TEXTURES * 2);
|
|
|
|
ASSERT(texture_tracking);
|
|
|
|
memset(texture_tracking, 0, MAX_TEXTURES * 2);
|
|
|
|
|
|
|
|
for (int i = 0; i <= Highest_room_index; i++) {
|
|
|
|
room *rp = &Rooms[i];
|
|
|
|
if (rp->used == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (int t = 0; t < rp->num_faces; t++) {
|
|
|
|
face *fp = &rp->faces[t];
|
|
|
|
|
|
|
|
if (fp->portal_num != -1) {
|
|
|
|
if (!(rp->portals[fp->portal_num].flags & PF_RENDER_FACES))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fp->tmap != -1 && !(GameTextures[fp->tmap].flags & (TF_PROCEDURAL | TF_TEXTURE_64 | TF_TEXTURE_32))) {
|
|
|
|
texture_tracking[fp->tmap]++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now count totals
|
|
|
|
int total = 0, total_with_lights = 0;
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int i = 0; i < MAX_TEXTURES; i++) {
|
2024-06-15 18:12:48 +00:00
|
|
|
if (texture_tracking[i]) {
|
|
|
|
if (!(GameTextures[i].flags & TF_LIGHT)) {
|
|
|
|
total++;
|
|
|
|
total_with_lights++;
|
|
|
|
} else
|
|
|
|
total_with_lights++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CheckError("There are %d unique 128x128 textures (excluding lights) in this level:\n", total);
|
|
|
|
CheckError("There are %d unique 128x128 textures (including lights) in this level:\n", total_with_lights);
|
|
|
|
|
|
|
|
if (total > 60)
|
|
|
|
CheckError("ERROR: YOU HAVE MORE THAT 60 128x128 TEXTURES...YOU *MUST* FIX THIS!\n");
|
|
|
|
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int i = 0; i < MAX_TEXTURES; i++) {
|
2024-06-15 18:12:48 +00:00
|
|
|
if (texture_tracking[i] && !(GameTextures[i].flags & TF_LIGHT)) {
|
|
|
|
CheckError("%d : %s %s bmp=%s\n", texture_tracking[i], GameTextures[i].name,
|
|
|
|
(GameTextures[i].flags & TF_ANIMATED) ? "(Animated)" : "",
|
|
|
|
(GameTextures[i].flags & TF_ANIMATED) ? "" : GameBitmaps[GameTextures[i].bm_handle].name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int i = 0; i < MAX_TEXTURES; i++) {
|
2024-06-15 18:12:48 +00:00
|
|
|
if (texture_tracking[i] && (GameTextures[i].flags & TF_LIGHT)) {
|
|
|
|
CheckError("%d : %s %s %s bmp=%s\n", texture_tracking[i], GameTextures[i].name,
|
|
|
|
(GameTextures[i].flags & TF_ANIMATED) ? "(Animated)" : "",
|
|
|
|
(GameTextures[i].flags & TF_LIGHT) ? "(Light)" : "",
|
|
|
|
(GameTextures[i].flags & TF_ANIMATED) ? "" : GameBitmaps[GameTextures[i].bm_handle].name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mem_free(texture_tracking);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the number of soundsource objects that don't have a sound attached
|
|
|
|
int CheckSoundsourceObjects() {
|
|
|
|
int objnum, total = 0, named = 0, quiet = 0, quiet_unnamed = 0;
|
|
|
|
object *objp;
|
|
|
|
|
|
|
|
for (objnum = 0, objp = Objects; objnum <= Highest_object_index; objnum++, objp++) {
|
|
|
|
if (objp->type == OBJ_SOUNDSOURCE) {
|
|
|
|
ASSERT(objp->control_type == CT_SOUNDSOURCE);
|
|
|
|
total++;
|
|
|
|
if (objp->name)
|
|
|
|
named++;
|
|
|
|
if (objp->ctype.soundsource_info.sound_index == -1) {
|
|
|
|
CheckError("Soundsource object %d (\"%s\") has no sound specified.\n", objnum,
|
|
|
|
objp->name ? objp->name : "<no name>");
|
|
|
|
quiet++;
|
|
|
|
if (!objp->name)
|
|
|
|
quiet_unnamed++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CheckError(
|
|
|
|
"This level has %d soundsource objects; %d have names, %d don't have sounds, and %d have no sounds & no name.\n",
|
|
|
|
total, named, quiet, quiet_unnamed);
|
|
|
|
|
|
|
|
return quiet;
|
|
|
|
}
|
|
|
|
|
|
|
|
int BOAGetMineChecksum();
|
|
|
|
|
|
|
|
// Get rid of large whole parts of face UV coordinates
|
|
|
|
void NormalizeFaceUVs() {
|
|
|
|
int r;
|
|
|
|
room *rp;
|
|
|
|
|
|
|
|
for (r = 0, rp = Rooms; r <= Highest_room_index; r++, rp++) {
|
|
|
|
if (rp->used) {
|
|
|
|
int f;
|
|
|
|
face *fp;
|
|
|
|
|
|
|
|
for (f = 0, fp = rp->faces; f < rp->num_faces; f++, fp++) {
|
|
|
|
float base_u = floor(fp->face_uvls[0].u);
|
|
|
|
float base_v = floor(fp->face_uvls[0].v);
|
|
|
|
|
|
|
|
for (int v = 0; v < fp->num_verts; v++) {
|
|
|
|
fp->face_uvls[v].u -= base_u;
|
|
|
|
fp->face_uvls[v].v -= base_v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test the mine for validity
|
|
|
|
void VerifyMine() {
|
|
|
|
int r;
|
|
|
|
room *rp;
|
|
|
|
int errors = 0, bad_normals = 0, concave_faces = 0, degenerate_faces = 0, bad_portals = 0, duplicate_faces = 0,
|
|
|
|
duplicate_points = 0, duplicate_face_points = 0, unused_points = 0, nonplanar_faces = 0, mismatched_portals = 0,
|
|
|
|
tjoints = 0, bad_shells = 0, quiet_soundsource_objects = 0;
|
|
|
|
|
|
|
|
error_buf_offset = 0;
|
|
|
|
|
|
|
|
// Normalize all the UV coordinates in the level
|
|
|
|
NormalizeFaceUVs();
|
|
|
|
|
|
|
|
// Check normals, portals, & duplicate faces
|
|
|
|
for (r = 0, rp = Rooms; r <= Highest_room_index; r++, rp++)
|
|
|
|
if (rp->used) {
|
|
|
|
|
|
|
|
bad_normals += CheckNormals(rp);
|
|
|
|
errors += bad_normals;
|
|
|
|
|
|
|
|
concave_faces += CheckConcaveFaces(rp);
|
|
|
|
errors += concave_faces;
|
|
|
|
|
|
|
|
degenerate_faces += CheckDegenerateFaces(rp);
|
|
|
|
errors += degenerate_faces;
|
|
|
|
|
|
|
|
bad_portals += CheckPortals(rp);
|
|
|
|
errors += bad_portals;
|
|
|
|
|
|
|
|
duplicate_faces += CheckDuplicateFaces(rp);
|
|
|
|
errors += duplicate_faces;
|
|
|
|
|
|
|
|
nonplanar_faces += CheckNonPlanarFaces(rp);
|
|
|
|
errors += nonplanar_faces;
|
|
|
|
|
|
|
|
duplicate_points += CheckDuplicatePoints(rp);
|
|
|
|
errors += duplicate_points;
|
|
|
|
|
|
|
|
duplicate_face_points += CheckDuplicateFacePoints(rp);
|
|
|
|
errors += duplicate_face_points;
|
|
|
|
|
|
|
|
unused_points += CheckUnusedPoints(rp);
|
|
|
|
errors += unused_points;
|
|
|
|
|
|
|
|
mismatched_portals += CheckRoomPortalFaces(rp);
|
|
|
|
errors += mismatched_portals;
|
|
|
|
|
|
|
|
tjoints += FindTJoints(rp);
|
|
|
|
errors += tjoints;
|
|
|
|
|
|
|
|
if ((rp->num_portals > 0) && !(rp->flags & RF_EXTERNAL)) {
|
|
|
|
int shell_errors = ComputeRoomShell(rp);
|
|
|
|
if (shell_errors) {
|
|
|
|
bad_shells++;
|
|
|
|
errors++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
quiet_soundsource_objects = CheckSoundsourceObjects();
|
|
|
|
errors += quiet_soundsource_objects;
|
|
|
|
|
|
|
|
CountUniqueTextures();
|
|
|
|
|
|
|
|
bool terrain_occluded = false;
|
|
|
|
bool boa_ran = false;
|
|
|
|
bool terrain_volume = false;
|
|
|
|
|
|
|
|
if (Terrain_occlusion_checksum == (Terrain_checksum + 1))
|
|
|
|
terrain_occluded = true;
|
|
|
|
else
|
|
|
|
errors = 1;
|
|
|
|
|
|
|
|
if (BOAGetMineChecksum() == BOA_vis_checksum)
|
|
|
|
boa_ran = true;
|
|
|
|
else
|
|
|
|
errors = true;
|
|
|
|
|
|
|
|
// Check to see if dynamic terrain lighting is calculated
|
|
|
|
for (int i = 0; i < TERRAIN_DEPTH * TERRAIN_WIDTH && !terrain_volume; i++) {
|
|
|
|
if (Terrain_dynamic_table[i] != 255)
|
|
|
|
terrain_volume = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!terrain_volume)
|
|
|
|
errors = true;
|
|
|
|
|
|
|
|
// Show message if errors
|
|
|
|
if (errors) {
|
|
|
|
mprintf(0, "Error buf size = %d\n", strlen(error_buf));
|
|
|
|
DumpTextToClipboard(error_buf);
|
|
|
|
OutrageMessageBox("Mine check results:\n"
|
|
|
|
"\n"
|
|
|
|
" Bad portals:\t\t%d\n"
|
|
|
|
" Bad Normals:\t\t%d\n"
|
|
|
|
" Concave faces:\t\t%d\n"
|
|
|
|
" Degenerate faces:\t\t%d\n"
|
|
|
|
" Duplicate faces:\t\t%d\n"
|
|
|
|
" Non-planar faces:\t\t%d\n"
|
|
|
|
" Duplicate points:\t\t%d\n"
|
|
|
|
" Duplicate face points:\t%d\n"
|
|
|
|
" Unused points:\t\t%d\n"
|
|
|
|
" Unmatched portals:\t\t%d\n"
|
|
|
|
" T-Joints:\t\t\t%d\n"
|
|
|
|
" Bad shells:\t\t%d\n"
|
|
|
|
" Quiet soundsource objs:\t%d\n"
|
|
|
|
" Terrain occlusion:\t\t%s\n"
|
|
|
|
" BOA:\t\t\t%s\n"
|
|
|
|
" Terrain volume lighting:\t\t\t%s\n"
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
"For detailed info, see clipboard.",
|
|
|
|
bad_portals, bad_normals, concave_faces, degenerate_faces, duplicate_faces, nonplanar_faces,
|
|
|
|
duplicate_points, duplicate_face_points, unused_points, mismatched_portals, tjoints, bad_shells,
|
|
|
|
quiet_soundsource_objects, terrain_occluded ? "Valid" : "NOT VALID",
|
|
|
|
boa_ran ? "Valid" : "NOT VALID", terrain_volume ? "Valid" : "NOT VALID");
|
|
|
|
} else {
|
|
|
|
OutrageMessageBox("Mine has no errors, but check clipboard for texture counts.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Does CheckMine() stuff on one room only
|
|
|
|
void VerifyRoom(room *rp) {
|
|
|
|
int errors, bad_normals, concave_faces, duplicate_faces, duplicate_points, duplicate_face_points, unused_points,
|
|
|
|
nonplanar_faces;
|
|
|
|
|
|
|
|
// Reset error buffer
|
|
|
|
error_buf_offset = 0;
|
|
|
|
|
|
|
|
errors = bad_normals = CheckNormals(rp);
|
|
|
|
|
|
|
|
concave_faces = CheckConcaveFaces(rp);
|
|
|
|
errors += concave_faces;
|
|
|
|
|
|
|
|
duplicate_faces = CheckDuplicateFaces(rp);
|
|
|
|
errors += duplicate_faces;
|
|
|
|
|
|
|
|
nonplanar_faces = CheckNonPlanarFaces(rp);
|
|
|
|
errors += nonplanar_faces;
|
|
|
|
|
|
|
|
duplicate_points = CheckDuplicatePoints(rp);
|
|
|
|
errors += duplicate_points;
|
|
|
|
|
|
|
|
duplicate_face_points = CheckDuplicateFacePoints(rp);
|
|
|
|
errors += duplicate_face_points;
|
|
|
|
|
|
|
|
unused_points = CheckUnusedPoints(rp);
|
|
|
|
errors += unused_points;
|
|
|
|
|
|
|
|
// Show message if errors
|
|
|
|
if (errors) {
|
|
|
|
DumpTextToClipboard(error_buf);
|
|
|
|
OutrageMessageBox("Room check results:\n"
|
|
|
|
"\n"
|
|
|
|
" Bad Normals:\t\t%d\n"
|
|
|
|
" Concave faces:\t\t%d\n"
|
|
|
|
" Duplicate faces:\t\t%d\n"
|
|
|
|
" Non-planar faces:\t\t%d\n"
|
|
|
|
" Duplicate points:\t\t%d\n"
|
|
|
|
" Duplicate face points:\t%d\n"
|
|
|
|
" Unused points:\t\t%d\n"
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
"For detailed info, see clipboard.",
|
|
|
|
bad_normals, concave_faces, duplicate_faces, nonplanar_faces, duplicate_points,
|
|
|
|
duplicate_face_points, unused_points);
|
|
|
|
} else
|
|
|
|
OutrageMessageBox("Room has no errors.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy the flags from one face to another derrived from the first (by clipping or splitting)
|
|
|
|
// Only those flags which are safe to copy are copied
|
|
|
|
// Parameters: dfp - pointer to the destination face
|
2024-04-19 20:58:24 +00:00
|
|
|
// sfp - pointer to the source face
|
2024-06-15 18:12:48 +00:00
|
|
|
void CopyFaceFlags(face *dfp, face *sfp) {
|
|
|
|
dfp->flags = 0;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
//@@if (sfp->flags & FF_FORCEFIELD)
|
|
|
|
//@@ dfp->flags |= FF_FORCEFIELD;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
if (sfp->flags & FF_GOALFACE)
|
|
|
|
dfp->flags |= FF_GOALFACE;
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Inserts a vertex into a face
|
|
|
|
// Parameters: rp - the room for this face
|
2024-04-19 20:58:24 +00:00
|
|
|
// facenum - the face to which to add the vertex
|
|
|
|
// new_v - the number of the vertex to add
|
|
|
|
// after_v - the new vert is added *after* this vert (index into face verts)
|
2024-06-15 18:12:48 +00:00
|
|
|
void AddVertToFace(room *rp, int facenum, int new_v, int after_v) {
|
|
|
|
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 <= after_v; t++) {
|
|
|
|
fp->face_verts[t] = old_verts[t];
|
|
|
|
fp->face_uvls[t] = old_uvls[t];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert new vert
|
|
|
|
fp->face_verts[t++] = new_v;
|
|
|
|
|
|
|
|
// Copy rest of new verts
|
|
|
|
for (; t < fp->num_verts; t++) {
|
|
|
|
fp->face_verts[t] = old_verts[t - 1];
|
|
|
|
fp->face_uvls[t] = old_uvls[t - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the uv values for the new vert
|
|
|
|
vector *v0 = &rp->verts[fp->face_verts[after_v]], *v1 = &rp->verts[fp->face_verts[(after_v + 2) % fp->num_verts]],
|
|
|
|
*vn = &rp->verts[fp->face_verts[after_v + 1]];
|
|
|
|
roomUVL *uv0 = &fp->face_uvls[after_v], *uv1 = &fp->face_uvls[(after_v + 2) % fp->num_verts];
|
|
|
|
|
|
|
|
float k = vm_VectorDistance(vn, v0) / vm_VectorDistance(v1, v0);
|
|
|
|
|
|
|
|
fp->face_uvls[after_v + 1].u = uv0->u + k * (uv1->u - uv0->u);
|
|
|
|
fp->face_uvls[after_v + 1].v = uv0->v + k * (uv1->v - uv0->v);
|
|
|
|
fp->face_uvls[after_v + 1].u2 = uv0->u2 + k * (uv1->u2 - uv0->u2);
|
|
|
|
fp->face_uvls[after_v + 1].v2 = uv0->v2 + k * (uv1->v2 - uv0->v2);
|
|
|
|
fp->face_uvls[after_v + 1].alpha = uv0->alpha + k * (uv1->alpha - uv0->alpha);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Removes the duplicate points in a room
|
|
|
|
// Returns the number removed
|
|
|
|
int RemoveDuplicatePoints(room *rp) {
|
|
|
|
face *fp;
|
|
|
|
int f, v;
|
|
|
|
int n_fixed = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < rp->num_verts; i++)
|
|
|
|
for (int j = 0; j < i; j++)
|
|
|
|
if (PointsAreSame(&rp->verts[i], &rp->verts[j])) {
|
|
|
|
|
|
|
|
// Replace the higher-numbered point with the lower-numbered in all the faces in this room
|
|
|
|
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] == i)
|
|
|
|
fp->face_verts[v] = j;
|
|
|
|
|
|
|
|
// Delete the now-unused vert
|
|
|
|
DeleteRoomVert(rp, i);
|
|
|
|
|
|
|
|
n_fixed++;
|
|
|
|
|
|
|
|
i--; // back up, since the point we're checking is now gone
|
|
|
|
break; // don't keep checking for duplicates
|
|
|
|
}
|
|
|
|
|
|
|
|
return n_fixed;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove duplicate points in faces
|
|
|
|
// Returns the number of duplicate face points removed
|
|
|
|
int RemoveDuplicateFacePoints(room *rp) {
|
|
|
|
int r = ROOMNUM(rp);
|
|
|
|
int f, v, n_fixed = 0;
|
|
|
|
face *fp;
|
|
|
|
|
|
|
|
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] == fp->face_verts[(v + 1) % fp->num_verts]) {
|
|
|
|
int new_verts[MAX_VERTS_PER_FACE];
|
|
|
|
roomUVL new_uvls[MAX_VERTS_PER_FACE];
|
|
|
|
|
2024-06-29 09:28:14 +00:00
|
|
|
int t;
|
|
|
|
|
|
|
|
for (t = 0; t < v; t++) {
|
2024-06-15 18:12:48 +00:00
|
|
|
new_verts[t] = fp->face_verts[t];
|
|
|
|
new_uvls[t] = fp->face_uvls[t];
|
|
|
|
}
|
|
|
|
for (; t < fp->num_verts - 1; t++) {
|
|
|
|
new_verts[t] = fp->face_verts[t + 1];
|
|
|
|
new_uvls[t] = fp->face_uvls[t + 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
ReInitRoomFace(fp, fp->num_verts - 1);
|
|
|
|
|
|
|
|
for (t = 0; t < fp->num_verts; t++) {
|
|
|
|
fp->face_verts[t] = new_verts[t];
|
|
|
|
fp->face_uvls[t] = new_uvls[t];
|
|
|
|
}
|
|
|
|
|
|
|
|
n_fixed++;
|
|
|
|
f--;
|
|
|
|
fp--; // recheck this face
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return n_fixed;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Removes all the duplicate points in a level
|
|
|
|
void RemoveAllDuplicateAndUnusedPoints() {
|
|
|
|
int r;
|
|
|
|
room *rp;
|
|
|
|
int n_unused = 0, n_duplicate = 0, n_duplicate_face = 0;
|
|
|
|
|
|
|
|
for (r = 0, rp = Rooms; r <= Highest_room_index; r++, rp++)
|
|
|
|
if (rp->used) {
|
|
|
|
n_unused += DeleteUnusedRoomVerts(rp);
|
|
|
|
n_duplicate += RemoveDuplicatePoints(rp);
|
|
|
|
n_duplicate_face += RemoveDuplicateFacePoints(rp);
|
|
|
|
}
|
|
|
|
|
|
|
|
OutrageMessageBox(" %d Unused points deleted\n"
|
|
|
|
" %d Duplicate points deleted\n"
|
|
|
|
" %d Duplicate face points deleted\n",
|
|
|
|
n_unused, n_duplicate, n_duplicate_face);
|
|
|
|
|
|
|
|
if (n_unused || n_duplicate || n_duplicate_face)
|
|
|
|
World_changed = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the number fixed
|
|
|
|
void FixDegenerateFaces() {
|
|
|
|
int fixed = 0, deleted = 0;
|
|
|
|
int r, f, v;
|
|
|
|
face *fp;
|
|
|
|
room *rp;
|
|
|
|
|
|
|
|
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++) {
|
|
|
|
bool face_fixed = 0;
|
|
|
|
recheck_face:;
|
|
|
|
if (fp->num_verts < 3) {
|
|
|
|
DeleteRoomFace(rp, f);
|
|
|
|
deleted++;
|
|
|
|
face_fixed = 0; // deleted overrides fixed
|
|
|
|
f--;
|
|
|
|
fp = &rp->faces[f];
|
|
|
|
} else {
|
|
|
|
for (v = 0; v < fp->num_verts; v++) {
|
|
|
|
if (fp->face_verts[v] == fp->face_verts[(v + 2) % fp->num_verts]) {
|
|
|
|
int16_t tverts[MAX_VERTS_PER_FACE];
|
|
|
|
roomUVL tuvls[MAX_VERTS_PER_FACE];
|
|
|
|
|
|
|
|
for (int i = 0; i < fp->num_verts - 2; i++) {
|
|
|
|
tverts[i] = fp->face_verts[(v + 2 + i) % fp->num_verts];
|
|
|
|
tuvls[i] = fp->face_uvls[(v + 2 + i) % fp->num_verts];
|
|
|
|
}
|
2024-06-29 09:28:14 +00:00
|
|
|
for (int i = 0; i < fp->num_verts - 2; i++) {
|
2024-06-15 18:12:48 +00:00
|
|
|
fp->face_verts[i] = tverts[i];
|
|
|
|
fp->face_uvls[i] = tuvls[i];
|
|
|
|
}
|
|
|
|
fp->num_verts -= 2;
|
|
|
|
face_fixed = 1;
|
|
|
|
goto recheck_face;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (face_fixed)
|
|
|
|
fixed++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
OutrageMessageBox("%d Degenerate faces fixed\n%d Degenerate faces deleted", fixed, deleted);
|
|
|
|
|
|
|
|
if (fixed || deleted)
|
|
|
|
World_changed = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finda a face that connects to the specified face
|
|
|
|
// Starts looking at startface
|
|
|
|
// Returns the attached facenum, or -1 if found none
|
|
|
|
int FindConnectedFace(room *rp, int facenum, int edgenum, int startface) {
|
|
|
|
face *fp0 = &rp->faces[facenum], *fp1;
|
|
|
|
int a0, b0, a1, b1;
|
|
|
|
int f;
|
|
|
|
|
|
|
|
// Get edge verts - <a0,b0> is edge on first face
|
|
|
|
a0 = fp0->face_verts[edgenum];
|
|
|
|
b0 = fp0->face_verts[(edgenum + 1) % fp0->num_verts];
|
|
|
|
|
|
|
|
for (f = startface, fp1 = &rp->faces[startface]; f < rp->num_faces; f++, fp1++) {
|
|
|
|
|
|
|
|
if (f == facenum)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (int e = 0; e < fp1->num_verts; e++) {
|
|
|
|
|
|
|
|
// Get edge verts - <a1,b1> is edge on second face
|
|
|
|
a1 = fp1->face_verts[e];
|
|
|
|
b1 = fp1->face_verts[(e + 1) % fp1->num_verts];
|
|
|
|
|
|
|
|
//@@if ((a0==a1) && (b0==b1))
|
|
|
|
//@@ Int3(); //If you hit this, you probably have a duplicate
|
|
|
|
//or overlapping face
|
|
|
|
|
|
|
|
if ((a0 == b1) && (b0 == a1)) { // found match!
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1; // no match
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shell flags
|
|
|
|
#define SHELL_NONE 0
|
|
|
|
#define SHELL_UNCHECKED 1
|
|
|
|
#define SHELL_CLOSED 2
|
|
|
|
#define SHELL_ERROR 3
|
|
|
|
|
|
|
|
// Finds the shell for the specified room. If the shell is found with no errors, sets
|
|
|
|
// the non-shell flag for those faces not in the shell. If there are errors finding the
|
|
|
|
// shell, all faces have the non-shell flag cleared.
|
|
|
|
// Assumes all portals are part of the shell and starts checking from those faces
|
|
|
|
// Returns the number of shell errors (unconnected edges) in the room.
|
|
|
|
// Writes errors to the error buffer
|
|
|
|
int ComputeRoomShell(room *rp) {
|
|
|
|
uint8_t shell_flags[MAX_FACES_PER_ROOM];
|
|
|
|
bool done = 0;
|
|
|
|
int errors = 0;
|
|
|
|
int f;
|
|
|
|
|
|
|
|
for (f = 0; f < rp->num_faces; f++)
|
|
|
|
shell_flags[f] = SHELL_NONE;
|
|
|
|
|
|
|
|
// Start with the portal faces
|
|
|
|
ASSERT(rp->num_portals > 0);
|
|
|
|
for (int p = 0; p < rp->num_portals; p++)
|
|
|
|
shell_flags[rp->portals[p].portal_face] = SHELL_UNCHECKED;
|
|
|
|
|
|
|
|
// Check all the unchecked faces
|
|
|
|
while (!done) {
|
|
|
|
|
|
|
|
for (f = 0; f < rp->num_faces; f++) {
|
|
|
|
|
|
|
|
if (shell_flags[f] == SHELL_UNCHECKED) {
|
|
|
|
face *fp = &rp->faces[f];
|
|
|
|
|
|
|
|
for (int e = 0; e < fp->num_verts; e++) {
|
|
|
|
|
|
|
|
int t = FindConnectedFace(rp, f, e, 0);
|
|
|
|
|
|
|
|
if (t != -1) {
|
|
|
|
int t2;
|
|
|
|
|
|
|
|
// See if any additional faces
|
|
|
|
t2 = FindConnectedFace(rp, f, e, t + 1);
|
|
|
|
if (t2 != -1) {
|
|
|
|
// mprintf(0,"Room %d face %d: Found second connection for edge %d (face %d)\n",ROOMNUM(rp),f,e,t);
|
|
|
|
// CheckError("Room %d face %d: Found second connection for edge %d (face %d)\n",ROOMNUM(rp),f,e,t);
|
|
|
|
} else { // No double-edge, so add connected face
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
if (shell_flags[t] == SHELL_NONE)
|
|
|
|
shell_flags[t] = SHELL_UNCHECKED;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// mprintf(0,"Room %d face %d: No connection for edge %d\n",ROOMNUM(rp),f,e);
|
|
|
|
CheckError("Room %d face %d: No connection for edge %d\n", ROOMNUM(rp), f, e);
|
|
|
|
shell_flags[f] = SHELL_ERROR;
|
|
|
|
errors++;
|
|
|
|
}
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
if (shell_flags[f] == SHELL_UNCHECKED)
|
|
|
|
shell_flags[f] = SHELL_CLOSED;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
done = (f == rp->num_faces);
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Clear flags
|
|
|
|
for (f = 0; f < rp->num_faces; f++) {
|
|
|
|
ASSERT(shell_flags[f] != SHELL_UNCHECKED);
|
|
|
|
rp->faces[f].flags &= ~FF_NOT_SHELL;
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// If no errors, set flags for room
|
|
|
|
if (errors == 0) {
|
|
|
|
for (f = 0; f < rp->num_faces; f++) {
|
|
|
|
if (shell_flags[f] == SHELL_NONE)
|
|
|
|
rp->faces[f].flags |= FF_NOT_SHELL;
|
|
|
|
}
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
return errors;
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Finds shells for all rooms.
|
|
|
|
// Returns the number of rooms with bad shells
|
|
|
|
int ComputeAllRoomShells() {
|
|
|
|
int r, bad_shells = 0;
|
|
|
|
room *rp;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
error_buf_offset = 0;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
mprintf(0, "Computing room shells...");
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (r = 0, rp = Rooms; r <= Highest_room_index; r++, rp++) {
|
|
|
|
if (rp->used && (rp->num_portals > 0) && !(rp->flags & RF_EXTERNAL)) {
|
|
|
|
int errors = ComputeRoomShell(rp);
|
|
|
|
if (errors)
|
|
|
|
bad_shells++;
|
|
|
|
}
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
mprintf(0, "Done\n");
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
mprintf(0, "Error buf size = %d\n", strlen(error_buf));
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
DumpTextToClipboard(error_buf);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
return bad_shells;
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
#include "objinfo.h"
|
|
|
|
#include "mission.h"
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
void ListObjectsInLevel(int *object_counts) {
|
|
|
|
int i;
|
|
|
|
object *objp;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (i = 0; i < MAX_OBJECT_IDS; i++)
|
|
|
|
object_counts[i] = 0;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
for (i = 0, objp = Objects; i <= Highest_object_index; i++, objp++) {
|
|
|
|
if (IS_GENERIC(objp->type))
|
|
|
|
object_counts[objp->id]++;
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
CheckError("\n Robots:\n");
|
|
|
|
for (i = 0; i < MAX_OBJECT_IDS; i++) {
|
|
|
|
if (object_counts[i] && (Object_info[i].type == OBJ_ROBOT))
|
|
|
|
CheckError(" %2d\t%s\n", object_counts[i], Object_info[i].name);
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
CheckError("\n Powerups:\n");
|
|
|
|
for (i = 0; i < MAX_OBJECT_IDS; i++) {
|
|
|
|
if (object_counts[i] && (Object_info[i].type == OBJ_POWERUP))
|
|
|
|
CheckError(" %2d\t%s\n", object_counts[i], Object_info[i].name);
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
CheckError("\n Buildings:\n");
|
|
|
|
for (i = 0; i < MAX_OBJECT_IDS; i++) {
|
|
|
|
if (object_counts[i] && (Object_info[i].type == OBJ_BUILDING))
|
|
|
|
CheckError(" %2d\t%s\n", object_counts[i], Object_info[i].name);
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
CheckError("\n Clutter:\n");
|
|
|
|
for (i = 0; i < MAX_OBJECT_IDS; i++) {
|
|
|
|
if (object_counts[i] && (Object_info[i].type == OBJ_CLUTTER))
|
|
|
|
CheckError(" %2d\t%s\n", object_counts[i], Object_info[i].name);
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#include "loadlevel.h"
|
|
|
|
|
|
|
|
extern bool Disable_editor_rendering;
|
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
#define NUM_LEVELS 15
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
void ListObjectsInAllLevels() {
|
|
|
|
int object_counts[NUM_LEVELS + 1][MAX_OBJECT_IDS];
|
|
|
|
char levelname[25];
|
|
|
|
int i, l;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
error_buf_offset = 0;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Clear totals
|
|
|
|
for (i = 0; i < MAX_OBJECT_IDS; i++)
|
|
|
|
object_counts[0][i] = 0;
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Get totals for each level
|
|
|
|
for (l = 1; l < NUM_LEVELS + 1; l++) {
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
sprintf(levelname, "Level%d.d3l", l);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
LoadLevel(levelname);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
CheckError("\n\nLevel: %s\n", levelname);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
ListObjectsInLevel(&object_counts[l][0]);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Accumulate totals
|
|
|
|
for (i = 0; i < MAX_OBJECT_IDS; i++)
|
|
|
|
object_counts[0][i] += object_counts[l][i];
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Print headers
|
|
|
|
CheckError("\n\nName\tType\tTotal");
|
|
|
|
for (l = 1; l < NUM_LEVELS + 1; l++)
|
|
|
|
CheckError("\t%d", l + 1);
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
// Print table
|
|
|
|
for (i = 0; i < MAX_OBJECT_IDS; i++) {
|
|
|
|
if (Object_info[i].type != OBJ_NONE) {
|
|
|
|
CheckError("\n%s\t%c", Object_info[i].name, Object_type_names[Object_info[i].type][0]);
|
|
|
|
for (int l = 0; l < NUM_LEVELS + 1; l++) {
|
|
|
|
CheckError("\t%d", object_counts[l][i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-04-19 20:58:24 +00:00
|
|
|
|
2024-06-15 18:12:48 +00:00
|
|
|
DumpTextToClipboard(error_buf);
|
2024-04-27 19:30:57 +00:00
|
|
|
}
|