/* * Descent 3 * Copyright (C) 2024 Parallax Software * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . --- HISTORICAL COMMENTS FOLLOW --- * $Logfile: /DescentIII/Main/editor/HView.cpp $ * $Revision: 1.1.1.1 $ * $Date: 2003-08-26 03:57:38 $ * $Author: kevinb $ * * Code to implement view menu functions * * $Log: not supported by cvs2svn $ * * 24 4/30/99 11:18p Matt * Limit viewer to terrain bounds when moving the viewer to an external * room. * * 23 4/29/99 11:51p Matt * Improved viewer move functions. * * 22 4/17/99 2:42p Matt * Set Viewer_moved when a new viewer is selected. * * 21 2/28/99 8:11p Matt * Fixed problems when at max viewers * * 20 1/15/99 7:52p Chris * Updated ObjSetPos() to include a f_update_attach_children flag * * 19 5/12/98 2:49p Matt * Link the room viewer to the terrain, since there's not always a room to * put him in. * * 18 3/12/98 7:30p Chris * Added ObjSetOrient * * 17 2/09/98 10:55a Matt * When searching for rooms to put the viewer in, don't consider external * rooms. * * 16 2/05/98 3:11p Matt * Don't move the viewer to an external room. * * 15 2/05/98 2:57p Matt * Changed code to use ObjSetPos() * * 14 2/04/98 6:23p Matt * Changed object room number to indicate a terrain cell via a flag. Got * rid of the object flag which used to indicate terrain. * * 13 1/29/98 5:50p Matt * Changed old camera object type to be viewer object (for editor), and * now camera objects will just be for game cameras. * * 12 1/21/98 1:08p Matt * Made SetViewMode() not take new_viewer parm, since it's not needed * after my revamp of the editor view system. * * 11 1/21/98 12:33p Matt * Revamped viewer system * * 10 1/19/98 10:04a Matt * Removed unused function * * 9 11/14/97 1:01p Matt * When can't find camera, don't just make one in the center of room 0. * Instead, find the first room that's used. * * 8 9/17/97 11:51a Samir * BIG SEGMENT RIPOUT * * 7 8/25/97 2:50p Chris * Improve AABB/object movement code. * * 6 8/04/97 7:39p Matt * Pass new room number to ObjSetPos() * * 5 8/04/97 12:43p Matt * Added SetViewerFromRoomFace() * * 4 7/22/97 7:08p Matt * Cleaned up D3EditState, moving some vars in and some out, and renaming * and changing a few others * * 4 4/01/97 11:00p Matt * Changed editor to keep a viewer object (type camera) seperate from the * player object. This camera, and not the player, is now moved by * slewing, the C key, etc. When going into the game, the viewer position * & orientation are copied to the player. When going back to the editor, * the player position is copied to the viewer, and the player object is * reset to its start location. * * 3 3/31/97 5:58p Matt * Revamped mine update flags * * 2 3/17/97 2:26p Matt * Added function to set player view from curseg/curside * * 1 3/17/97 1:47p Matt * * $NoKeywords: $ */ #include "mainfrm.h" #include "HView.h" #include "d3edit.h" #include "terrain.h" #include "erooms.h" #include "editor.h" #define FACE_VIEW_DIST 5.0 //Set the viewer in the specified room facing the specified face //If room_center is true, put viewer at the center of the room facing the face //If room_center is false, put the viewer directly in front of the selected face //If the room is external, put the viewer a distance away from the room, //facing either the center (if room_center is true) or the specified face void SetViewerFromRoomFace(room *roomp,int facenum,bool room_center) { vector vp,newpos; matrix orient; int roomnum; bool outside_mine = 0; ComputeCenterPointOnFace(&vp,roomp,facenum); roomnum = ROOMNUM(roomp); if (room_center) { //Get position ComputeRoomCenter(&newpos,roomp); if (roomp->flags & RF_EXTERNAL) { vector t; float rad = ComputeRoomBoundingSphere(&t,roomp); newpos.z -= rad * 1.5; if(newpos.x < 1.0) newpos.x = 1.0; if(newpos.x > TERRAIN_WIDTH * TERRAIN_SIZE - 1.0) newpos.x = TERRAIN_WIDTH * TERRAIN_SIZE - 1.0; if(newpos.z < 1.0) newpos.z = 1.0; if(newpos.z > TERRAIN_DEPTH * TERRAIN_SIZE - 1.0) newpos.z = TERRAIN_WIDTH * TERRAIN_SIZE - 1.0; orient = Identity_matrix; roomnum = GetTerrainRoomFromPos(&newpos); } else { //Get orientation vp -= newpos; //vector from center of room to face vm_VectorToMatrix(&orient,&vp,NULL,NULL); } } else { face *fp = &roomp->faces[facenum]; newpos = vp + fp->normal * FACE_VIEW_DIST; vector t = -fp->normal; vm_VectorToMatrix(&orient,&t,NULL,NULL); if (roomp->flags & RF_EXTERNAL) { if(newpos.x < 1.0) newpos.x = 1.0; if(newpos.x > TERRAIN_WIDTH * TERRAIN_SIZE - 1.0) newpos.x = TERRAIN_WIDTH * TERRAIN_SIZE - 1.0; if(newpos.z < 1.0) newpos.z = 1.0; if(newpos.z > TERRAIN_DEPTH * TERRAIN_SIZE - 1.0) newpos.z = TERRAIN_WIDTH * TERRAIN_SIZE - 1.0; roomnum = GetTerrainRoomFromPos(&newpos); } else { int new_roomnum = FindPointRoom(&newpos); if (new_roomnum == -1) outside_mine = 1; else roomnum = new_roomnum; } } //Reset viewer if (Editor_view_mode == VM_ROOM) { Viewer_object->pos = newpos; Viewer_object->orient = orient; } else MoveViewer(&newpos,roomnum,&orient); if (outside_mine) Viewer_object->flags |= OF_OUTSIDE_MINE; Viewer_moved = 1; } //max viewers for each type #define MAX_VIEWERS 20 //The viewer in room mode has this ID #define ROOM_VIEWER_ID MAX_VIEWERS //Finds a specific viewer object it one exists //Parameters: id - which viewer id //Returns: object number of a viewer object, or -1 if none int FindViewerObject(int id) { object *objp; int objnum=-1; //trace through all objects looking for viewer for (objnum=0,objp=Objects;objnum<=Highest_object_index;objnum++,objp++) if ((objp->type == OBJ_VIEWER) && (objp->id == id)) break; if (objnum <= Highest_object_index) //found one return objnum; else return -1; } //Finds a viewer object if one exists. //Starts looking at the specified id and searches through all possible ids //Parameters: id - which viewer id // view_mode - if -1, find any viewer, else find one that matches view mode //Returns: object number of a viewer object, or -1 if none int FindNextViewerObject(int id,int view_mode) { int i; int objnum; int terrain_flag; if (id == -1) id = 0; ASSERT((id >= 0) && (id <= MAX_VIEWERS)); //Get flags terrain_flag = (view_mode == VM_TERRAIN); //Try all viewer id's, starting at the one passed in for (i=0;itype == OBJ_VIEWER) && (objp->id == id)) return -1; //this one already used } else { //for each id, loop through all objects to see if it's used for (id=0;idtype == OBJ_VIEWER) && (objp->id == id)) break; //this one already used if (objnum > Highest_object_index) //didn't find objec twith this id break; } if (id == MAX_VIEWERS) //no unused viewer id's return -1; } //Create the new object objnum = ObjCreate(OBJ_VIEWER,id,roomnum,pos,NULL); if (objnum == -1) { Int3(); return -1; } return objnum; } //Returns the number (not the id) of the current viewer, in the range 0..MAX_VIEWERS int GetViewerNum() { ASSERT(Editor_view_mode != VM_ROOM); return Viewer_object->id; } //Set the specified object to be the viewer void SetViewer(int objnum) { Viewer_object = &Objects[objnum]; if (Editor_view_mode != VM_ROOM) Editor_viewer_id = Viewer_object->id; if ((Editor_view_mode == VM_MINE) && OBJECT_OUTSIDE(Viewer_object)) SetViewMode(VM_TERRAIN); if ((Editor_view_mode == VM_TERRAIN) && !(OBJECT_OUTSIDE(Viewer_object))) SetViewMode(VM_MINE); State_changed = Viewer_moved = 1; } //Creates a new viewer object. Copies position & orientation from the current viewer void CreateNewViewer() { int objnum; objnum = CreateViewerObject(Editor_view_mode,&Viewer_object->pos,Viewer_object->roomnum); if (objnum == -1) { OutrageMessageBox("Cannot create new viewer: you already have the maximum number of viewers."); return; } ObjSetOrient(&Objects[objnum], &Viewer_object->orient); SetViewer(objnum); EditorStatus("Viewer %d created.",GetViewerNum()); World_changed = 1; } //Select next viewer void SelectNextViewer() { int objnum; if (Editor_view_mode == VM_ROOM) { EditorStatus("There is only one viewer in Room View"); return; } ASSERT(Viewer_object != NULL); objnum = FindNextViewerObject(Viewer_object->id + 1,-1); ASSERT(objnum != -1); if (objnum == OBJNUM(Viewer_object)) { EditorStatus("No other viewers."); return; } SetViewer(objnum); EditorStatus("Viewer %d selected.",GetViewerNum()); } //Deletes the current viewer object void DeleteViewer() { int objnum,old_num; if (Viewer_object->id == ROOM_VIEWER_ID) { OutrageMessageBox("You cannot delete the Room viewer."); return; } objnum = FindNextViewerObject(Viewer_object->id + 1,-1); ASSERT(objnum != -1); if (objnum == OBJNUM(Viewer_object)) { //couldn't find another viewer OutrageMessageBox("You cannot delete your only viewer."); return; } old_num = GetViewerNum(); //Kill the old viewer ObjDelete(OBJNUM(Viewer_object)); //Set the new viewer SetViewer(objnum); EditorStatus("Viewer %d deleted. Viewer %d selected.",old_num,GetViewerNum()); World_changed = 1; } //Sets the viewer object for the editor, creating if not already in mine //Keeps seperate viewer objects for mine & terrain views void SetEditorViewer() { int objnum,roomnum; //First, see if a camera object already exists in the level if (Editor_view_mode == VM_ROOM) objnum = FindViewerObject(ROOM_VIEWER_ID); else objnum = FindNextViewerObject(Editor_viewer_id,Editor_view_mode); //If no viewer object, create one if (objnum == -1) { vector pos; //get position for viewer if (Editor_view_mode == VM_TERRAIN) { //if terrain, put viewer at center of world pos.x = TERRAIN_SIZE * TERRAIN_WIDTH/2; pos.y = Terrain_seg[0].y+30; pos.z = TERRAIN_SIZE*TERRAIN_DEPTH/2; roomnum = MAKE_ROOMNUM(0); //any value ok, so long as it has terrain flag } else if (Editor_view_mode == VM_MINE) { //if mine, put in center of any room for (roomnum=0;roomnum<=Highest_room_index;roomnum++) if (Rooms[roomnum].used && !(Rooms[roomnum].flags & RF_EXTERNAL)) { ComputeRoomCenter(&pos,&Rooms[roomnum]); break; } ASSERT(roomnum <= Highest_room_index); } else if (Editor_view_mode == VM_ROOM) { //if room, put at 0,0,0 vm_MakeZero(&pos); roomnum = MAKE_ROOMNUM(0); } else Int3(); //unknown view mode objnum = CreateViewerObject(Editor_view_mode,&pos,roomnum); //If no free viewer slots, grab any viewer and move it if (objnum == -1) { ASSERT(Viewer_object != NULL); if (Viewer_object->type == OBJ_VIEWER) objnum = OBJNUM(Viewer_object); else { objnum = FindNextViewerObject(Editor_viewer_id,-1); ASSERT(objnum != -1); } ObjSetPos(&Objects[objnum],&pos,roomnum,NULL,true); } } ASSERT(objnum != -1); SetViewer(objnum); } //Moves the room viewer to the origin, if there is a room viewer void ResetRoomViewer() { int objnum = FindViewerObject(ROOM_VIEWER_ID); if (objnum != -1) { vm_MakeZero(&Objects[objnum].pos); ObjSetOrient(&Objects[objnum], &Identity_matrix); } }