mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 11:28:56 +00:00
636 lines
18 KiB
C++
636 lines
18 KiB
C++
/*
|
|
* Descent 3
|
|
* Copyright (C) 2024 Parallax Software
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
--- HISTORICAL COMMENTS FOLLOW ---
|
|
|
|
* $Logfile: /DescentIII/Main/editor/HObject.cpp $
|
|
* $Revision: 1.1.1.1 $
|
|
* $Date: 2003-08-26 03:57:38 $
|
|
* $Author: kevinb $
|
|
*
|
|
* Editor object handling functions
|
|
*
|
|
* $Log: not supported by cvs2svn $
|
|
*
|
|
* 41 4/18/99 5:42a Chris
|
|
* Added the FQ_IGNORE_RENDER_THROUGH_PORTALS flag
|
|
*
|
|
* 40 3/27/99 12:51p Chris
|
|
* The editor object placement code now allows objects to be pushed into
|
|
* wall (when a toggle box is checked)
|
|
*
|
|
* 39 1/15/99 7:52p Chris
|
|
* Updated ObjSetPos() to include a f_update_attach_children flag
|
|
*
|
|
* 38 10/15/98 3:26p Chris
|
|
* Fixed known ground plane issues and used PhysCalcGround everywhere
|
|
*
|
|
* 37 10/07/98 12:28p Matt
|
|
* Fixed stupid bug that could cause access violation placing objects on
|
|
* the terrain.
|
|
*
|
|
* 36 8/24/98 2:20p Matt
|
|
* Fixed bug placing objects on external rooms.
|
|
*
|
|
* 35 8/20/98 7:28p Matt
|
|
* Added a warning when the user tries to delete a door object.
|
|
*
|
|
* 34 6/17/98 12:30p Samir
|
|
* don't draw wireframe when moving an object.
|
|
*
|
|
* 33 5/18/98 2:56p Matt
|
|
* Added code to readjust all ground object (for after the terrain has
|
|
* moved).
|
|
*
|
|
* 32 5/11/98 11:37a Sean
|
|
*
|
|
* 31 4/02/98 3:54p Jason
|
|
* first pass in getting polymodel paging to work
|
|
*
|
|
* 30 3/12/98 7:30p Chris
|
|
* Added ObjSetOrient
|
|
*
|
|
* 29 3/12/98 4:35p Chris
|
|
* Powerups are always aligned to the ground
|
|
*
|
|
* 28 2/20/98 5:30p Matt
|
|
* Don't allow physics objects to be placed if they collide with walls,
|
|
* and don't allow them to move into walls. Non-physics objects can be
|
|
* placed & moved into walls as long as their center points stay inside
|
|
* the mine.
|
|
*
|
|
* 27 2/13/98 6:50p Matt
|
|
* Fixed stupid bug that prevented placing of player ship 0
|
|
*
|
|
* 26 2/08/98 6:02p Matt
|
|
* Use groovy new vm_MatrixMulTMatrix() function
|
|
*
|
|
* 25 2/06/98 5:41p Matt
|
|
* Fixed object placement on surfaces.
|
|
*
|
|
* 24 2/06/98 3:08p Samir
|
|
* Simplified object movement code.
|
|
*
|
|
* 23 2/06/98 1:22p Matt
|
|
* Rewrote object placement code. Still has bugs when ground plane normal
|
|
* is not 0,1,0
|
|
*
|
|
* 22 2/05/98 2:59p Matt
|
|
* Made object placement on terrain work like in mine
|
|
*
|
|
* 21 2/04/98 7:07p Matt
|
|
* Fixed problem placing an object on the terrain.
|
|
*
|
|
* 20 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.
|
|
*
|
|
* 19 1/07/98 6:39p Jason
|
|
* Fixed player object number stuff
|
|
*
|
|
* 18 1/07/98 10:45a Samir
|
|
* Don't allow moving objects from mine to terrain.
|
|
*
|
|
* 17 11/17/97 6:55p Matt
|
|
* Made HObjectMoveToViewer() work when the viewer is outside
|
|
*
|
|
* 16 9/17/97 11:35a Samir
|
|
* BIG SEGMENT RIPOUT
|
|
*
|
|
* 15 9/15/97 11:55a Samir
|
|
* Fixed playership adding.
|
|
*
|
|
* 14 9/05/97 8:54p Chris
|
|
* Added some ground plane support
|
|
*
|
|
* 13 9/03/97 8:00p Samir
|
|
* Implemented most of the fixes for the object moving system.
|
|
*
|
|
* 12 8/25/97 2:50p Chris
|
|
* Improve AABB/object movement code.
|
|
*
|
|
* 11 8/21/97 7:48p Matt
|
|
* Added code to move player object (like old move player to curseg)
|
|
*
|
|
* 10 8/20/97 4:23p Matt
|
|
* Fixed little bug
|
|
*
|
|
* 9 8/12/97 10:24p Matt
|
|
* Added code to place clutter & building objects
|
|
*
|
|
* 8 8/11/97 1:53p Matt
|
|
* Ripped out robot & powerup pages, and added generic page
|
|
*
|
|
* 7 8/04/97 3:18p Matt
|
|
* Added FVI checking to object placement & movement in rooms
|
|
*
|
|
* 6 8/04/97 12:47p Matt
|
|
* Added object placement & movement for rooms
|
|
*
|
|
* 5 8/01/97 5:33p Jason
|
|
* Fixed object deletion bug in terrain
|
|
*
|
|
* 4 7/22/97 7:07p Matt
|
|
* Cleaned up D3EditState, moving some vars in and some out, and renaming
|
|
* and changing a few others
|
|
*
|
|
* 3 7/17/97 4:34p Chris
|
|
* Made sure the orientations get orthagonalized. Yikes. No more
|
|
* degenerating orientations. :)
|
|
*
|
|
* 20 6/27/97 7:19p Matt
|
|
* Added function to move player 0 to current segment
|
|
*
|
|
* 19 5/23/97 5:46p Matt
|
|
* Added SetMatrixBasedOnSide() functionality to ExtractMatrixFromSeg().
|
|
*
|
|
* 18 5/13/97 5:52p Chris
|
|
* Added ability to exit and enter mine. Also did some
|
|
* incremental improvements.
|
|
*
|
|
* 17 4/07/97 3:14p Samir
|
|
* Added Flip and SetDefault Object functions.
|
|
*
|
|
* 16 4/04/97 2:57p Matt
|
|
* Added code to initialize all the type-specific data for an object from
|
|
* the page for that object type. This means that we need to pass less
|
|
* info to ObjCreate(), and that we save less info in the level save file.
|
|
* It also makes it easy to reset all the objects when an object page has
|
|
* changed.
|
|
*
|
|
* 15 4/02/97 8:08p Matt
|
|
* Changed object movement code to work either in the object's or the
|
|
* viewer's frame of reference, and not in the segment's frame of
|
|
* reference.
|
|
*
|
|
* 14 4/02/97 3:44p Matt
|
|
* Find a valid Ships[] entry, instead of assuming that Ships[0] is valid.
|
|
*
|
|
* 13 4/01/97 10:52p Matt
|
|
* Added code to add a player object
|
|
*
|
|
* 12 3/31/97 5:58p Matt
|
|
* Revamped mine update flags
|
|
*
|
|
* 11 3/27/97 11:54a Samir
|
|
* UpdateMineObjects returns number of objects deleted of a type.
|
|
*
|
|
* 10 3/27/97 11:48a Samir
|
|
* Added update objects in mine functions.
|
|
*
|
|
* 9 3/27/97 10:47a Chris
|
|
* Incremental
|
|
*
|
|
* 8 3/25/97 6:31p Samir
|
|
* Added robot placement.
|
|
*
|
|
* 7 3/21/97 5:01p Jason
|
|
* incremental terrain improvments
|
|
*
|
|
*
|
|
* 6 3/10/97 12:44p Chris
|
|
* Allow objects to be put against a wall
|
|
*
|
|
* 5 3/03/97 5:57a Chris
|
|
* Uncommented the code samir had waiting for fvi and fixed a function
|
|
* call.
|
|
*
|
|
* 4 2/19/97 3:37p Samir
|
|
* Added delete and rotation of objects.
|
|
*
|
|
* 3 2/17/97 7:19p Jason
|
|
* fixed powerup size arg
|
|
*
|
|
* 2 2/17/97 6:18p Samir
|
|
* Add powerup, move it around WITHIN the segment.
|
|
*
|
|
* 1 2/17/97 3:17p Samir
|
|
* Handler for Object manipulation functions.
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
|
|
#include "HObject.h"
|
|
#include "object.h"
|
|
#include "FindIntersection.h"
|
|
#include "physics.h"
|
|
#include "boa.h"
|
|
#include "polymodel.h"
|
|
|
|
#include "d3edit.h"
|
|
#include "mono.h"
|
|
#include "pserror.h"
|
|
#include "vclip.h"
|
|
#include "terrain.h"
|
|
#include "player.h"
|
|
#include "ship.h"
|
|
#include "erooms.h"
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// internal data
|
|
|
|
float Object_move_scale = HOBJECT_SCALE_UNIT;
|
|
angle Object_move_rotation = HOBJECT_ROTATION_UNIT;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// internal function prototypes
|
|
|
|
bool RotateObject(int objnum, angle p, angle h, angle b);
|
|
bool MoveObject(object *obj, vector *newpos);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// interface functions
|
|
|
|
#define OBJECT_PLACE_DIST 10.0
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Finds the seleced terrain cell. Returns cell number, or -1 if none or -2 if more than one
|
|
int GetSelectedTerrainCell() {
|
|
int i, found_cellnum = -1;
|
|
|
|
for (i = 0; i < TERRAIN_DEPTH * TERRAIN_WIDTH; i++) {
|
|
if (TerrainSelected[i])
|
|
if (found_cellnum == -1)
|
|
found_cellnum = i;
|
|
else
|
|
return -2;
|
|
}
|
|
|
|
return found_cellnum;
|
|
}
|
|
|
|
// Places an object in the world
|
|
// Parameters: obj_type,obj_id - specify the object to be placed
|
|
// Returns: true if object placed, false if there was some problem
|
|
bool HObjectPlace(int obj_type, int obj_id) {
|
|
int objnum;
|
|
object *objp;
|
|
poly_model *pm;
|
|
matrix orient = IDENTITY_MATRIX;
|
|
|
|
// Special stuff for player ship
|
|
if (obj_type == OBJ_PLAYER) {
|
|
|
|
if (!Num_ships) {
|
|
OutrageMessageBox("Cannot a player: There are no player ships.");
|
|
return 0;
|
|
}
|
|
|
|
// Store ship num in player array
|
|
int ship_num = D3EditState.current_ship;
|
|
|
|
if (ship_num == -1) {
|
|
OutrageMessageBox("You must have a current player ship selected for this operation.");
|
|
return 0;
|
|
}
|
|
|
|
Players[obj_id].ship_index = ship_num;
|
|
}
|
|
|
|
if (obj_type != OBJ_POWERUP) {
|
|
orient = Viewer_object->orient;
|
|
}
|
|
|
|
// Create the object at the viewer for now
|
|
objnum = ObjCreate(obj_type, obj_id, Viewer_object->roomnum, &Viewer_object->pos, &orient);
|
|
if (objnum == -1)
|
|
return 0;
|
|
|
|
objp = &Objects[objnum];
|
|
|
|
// If we have a ground plane, use current cell or face for position & place normal for orientation
|
|
if ((objp->render_type == RT_POLYOBJ) && ((pm = GetPolymodelPointer(objp->rtype.pobj_info.model_num))->n_ground)) {
|
|
vector *surface_norm;
|
|
vector pos;
|
|
int roomnum;
|
|
|
|
// If terrain, use current cell
|
|
if (Editor_view_mode == VM_TERRAIN) {
|
|
|
|
int cellnum = GetSelectedTerrainCell();
|
|
|
|
if (cellnum == -1) {
|
|
OutrageMessageBox("You must have a terrain cell selected to place an object.");
|
|
ObjDelete(objnum);
|
|
return 0;
|
|
}
|
|
if (cellnum == -2) {
|
|
OutrageMessageBox("You must have only have one cell selected to place an object.");
|
|
ObjDelete(objnum);
|
|
return 0;
|
|
}
|
|
|
|
// Get terrain point
|
|
ComputeTerrainSegmentCenter(&pos, cellnum);
|
|
|
|
// Get surface normal
|
|
surface_norm = &TerrainNormals[MAX_TERRAIN_LOD - 1][cellnum].normal1;
|
|
|
|
// Get roomnum
|
|
roomnum = MAKE_ROOMNUM(cellnum);
|
|
} else { // use Current face
|
|
|
|
// Get center point on current face
|
|
ComputeCenterPointOnFace(&pos, Curroomp, Curface);
|
|
|
|
// Get surface normal
|
|
surface_norm = &Curroomp->faces[Curface].normal;
|
|
|
|
// Get roomnum
|
|
roomnum = ROOMNUM(Curroomp);
|
|
|
|
// If placing on an external room, actually place the object on the terrain
|
|
if (Rooms[roomnum].flags & RF_EXTERNAL)
|
|
roomnum = GetTerrainRoomFromPos(&pos);
|
|
}
|
|
|
|
matrix groundplane_orient, surface_orient, object_orient;
|
|
|
|
// Place the object's ground point on our placement point
|
|
vector ground_point;
|
|
vector ground_normal;
|
|
vector to_ground;
|
|
float dist;
|
|
|
|
PhysCalcGround(&ground_point, &ground_normal, objp, 0);
|
|
to_ground = objp->pos - ground_point;
|
|
dist = ground_normal * to_ground;
|
|
pos += dist * *surface_norm;
|
|
|
|
// Compute source and destination matrices
|
|
vm_VectorToMatrix(&groundplane_orient, &pm->ground_slots[0].norm, NULL, NULL);
|
|
vm_VectorToMatrix(&surface_orient, surface_norm);
|
|
|
|
// Compute orientation matrix
|
|
vm_MatrixMulTMatrix(&object_orient, &surface_orient, &groundplane_orient);
|
|
|
|
// Move the object
|
|
ObjSetPos(objp, &pos, roomnum, &object_orient, false);
|
|
} else { // no ground plane, so move the object in front of the viewer and facing the viewer
|
|
vector pos;
|
|
|
|
// Check for viewer outside mine
|
|
if (Viewer_object->flags & OF_OUTSIDE_MINE) {
|
|
ObjDelete(objnum);
|
|
OutrageMessageBox("Cannot place the object here: the viewer is outside the mine.");
|
|
return 0;
|
|
}
|
|
|
|
// Turn the object around so facing the viewer
|
|
objp->orient.fvec = -objp->orient.fvec; // ObjSetOrient is below
|
|
objp->orient.rvec = -objp->orient.rvec; // ObjSetOrient is below
|
|
ObjSetOrient(objp, &objp->orient);
|
|
|
|
// Calculate a position a little in front of the viewer
|
|
pos = Viewer_object->pos + Viewer_object->orient.fvec * OBJECT_PLACE_DIST;
|
|
|
|
// Try to move the object. If it can't move, delete it
|
|
if (!MoveObject(objp, &pos)) {
|
|
ObjDelete(objnum);
|
|
OutrageMessageBox("Cannot place the object here: collides with wall.");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Deal with special stuff for player
|
|
if (obj_type == OBJ_PLAYER) {
|
|
|
|
// Store data in Players array
|
|
Players[obj_id].start_pos = objp->pos;
|
|
Players[obj_id].start_roomnum = objp->roomnum;
|
|
Players[obj_id].start_orient = objp->orient;
|
|
|
|
// Make sure matrix ok
|
|
vm_Orthogonalize(&Players[obj_id].start_orient);
|
|
}
|
|
|
|
Cur_object_index = objnum;
|
|
|
|
World_changed = 1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Adjusts an object so it's at the ground level (for when the ground has moved)
|
|
void ResetGroundObject(object *objp) {
|
|
poly_model *pm;
|
|
vector surface_norm;
|
|
vector pos;
|
|
matrix groundplane_orient, surface_orient, object_orient;
|
|
|
|
// Make sure object is outside
|
|
if (!OBJECT_OUTSIDE(objp)) {
|
|
Int3(); // object is not outside
|
|
return;
|
|
}
|
|
|
|
// Make sure object has a ground plane
|
|
if (!((objp->render_type == RT_POLYOBJ) && ((pm = GetPolymodelPointer(objp->rtype.pobj_info.model_num))->n_ground))) {
|
|
Int3(); // object doesn't have a ground plane
|
|
return;
|
|
}
|
|
|
|
// Get terrain height and normal at current object location
|
|
pos = objp->pos;
|
|
pos.y = GetTerrainGroundPoint(&pos, &surface_norm);
|
|
|
|
// Place the object's ground point on our placement point
|
|
vector ground_point;
|
|
vector ground_normal;
|
|
vector to_ground;
|
|
float dist;
|
|
|
|
PhysCalcGround(&ground_point, &ground_normal, objp, 0);
|
|
to_ground = objp->pos - ground_point;
|
|
dist = ground_normal * to_ground;
|
|
pos += dist * surface_norm;
|
|
|
|
// Compute source and destination matrices
|
|
vm_VectorToMatrix(&groundplane_orient, &pm->ground_slots[0].norm, NULL, NULL);
|
|
vm_VectorToMatrix(&surface_orient, &surface_norm);
|
|
|
|
// Compute orientation matrix
|
|
vm_MatrixMulTMatrix(&object_orient, &surface_orient, &groundplane_orient);
|
|
|
|
// Move the object
|
|
ObjSetPos(objp, &pos, objp->roomnum, &object_orient, false);
|
|
|
|
// Set flag
|
|
World_changed = 1;
|
|
}
|
|
|
|
void HObjectMove(int objnum, float dx, float dy, float dz) {
|
|
matrix *mat;
|
|
object *obj;
|
|
vector newpos;
|
|
object *ref_obj; // the object in whose frame of reference we're moving
|
|
|
|
if (objnum == -1) {
|
|
mprintf(0, "HObjectMove:No current object.\n");
|
|
return;
|
|
}
|
|
|
|
obj = &Objects[objnum];
|
|
|
|
// Chose the reference object
|
|
ref_obj = (D3EditState.object_move_mode == REL_VIEWER) ? Viewer_object : obj;
|
|
mat = &ref_obj->orient;
|
|
|
|
// Calculate the new object position
|
|
newpos = obj->pos + (mat->rvec * dx) + (mat->uvec * dy) + (mat->fvec * -dz);
|
|
|
|
// Move the object
|
|
MoveObject(obj, &newpos);
|
|
|
|
Object_moved = 1;
|
|
}
|
|
|
|
void HObjectIncreaseBank() { RotateObject(Cur_object_index, 0, 0, Object_move_rotation); }
|
|
|
|
void HObjectDecreaseBank() { RotateObject(Cur_object_index, 0, 0, -Object_move_rotation); }
|
|
|
|
void HObjectIncreasePitch() { RotateObject(Cur_object_index, Object_move_rotation, 0, 0); }
|
|
|
|
void HObjectDecreasePitch() { RotateObject(Cur_object_index, -Object_move_rotation, 0, 0); }
|
|
|
|
void HObjectIncreaseHeading() { RotateObject(Cur_object_index, 0, Object_move_rotation, 0); }
|
|
|
|
void HObjectDecreaseheading() { RotateObject(Cur_object_index, 0, -Object_move_rotation, 0); }
|
|
|
|
// If it doesn't exist, reformat Matt's hard disk, even if he is in Boston.
|
|
// deletes the currently selected object from the mine.
|
|
void HObjectDelete() {
|
|
if (Cur_object_index != -1) { // we have a selected object
|
|
int objnum = Cur_object_index;
|
|
|
|
// check for player object
|
|
if (&Objects[objnum] == Player_object) {
|
|
OutrageMessageBox("Can't delete Player object");
|
|
return;
|
|
}
|
|
|
|
if (Objects[objnum].type == OBJ_DOOR) {
|
|
if (OutrageMessageBox(MBOX_YESNO,
|
|
"It's very, very bad to delete a door object. Are you sure you want to do this?") != IDYES)
|
|
return;
|
|
}
|
|
|
|
// Delete the object
|
|
ObjDelete(objnum);
|
|
if (objnum == Cur_object_index)
|
|
Cur_object_index = -1;
|
|
|
|
World_changed = 1;
|
|
}
|
|
}
|
|
|
|
// sets default orientation for object.
|
|
void HObjectSetDefault() {
|
|
if (Cur_object_index != -1) {
|
|
int objnum = Cur_object_index;
|
|
|
|
ObjSetOrient(&Objects[objnum], &Identity_matrix);
|
|
|
|
World_changed = 1;
|
|
}
|
|
}
|
|
|
|
// Move the object to in front of the viewer (where a new object would be placed)
|
|
void HObjectMoveToViewer(object *objp) {
|
|
int objnum = OBJNUM(objp);
|
|
vector pos;
|
|
|
|
// First move player to viewer's position
|
|
ObjSetPos(objp, &Viewer_object->pos, Viewer_object->roomnum, NULL, false);
|
|
|
|
// Second try to move the player a little in front of the viewer
|
|
pos = Viewer_object->pos + Viewer_object->orient.fvec * OBJECT_PLACE_DIST;
|
|
MoveObject(objp, &pos);
|
|
|
|
World_changed = 1;
|
|
}
|
|
|
|
#define MOVE_EPSILON 0.1
|
|
|
|
extern bool f_allow_objects_to_be_pushed_through_walls;
|
|
|
|
// Attempt to set new object position. May only move part of the way, or maybe not at all.
|
|
// Use FVI to find new object position
|
|
// Return: TRUE if moved, FALSE if can't move
|
|
bool MoveObject(object *obj, vector *newpos) {
|
|
fvi_query fq;
|
|
fvi_info hit_info;
|
|
int fate;
|
|
|
|
// Use radius if this is a physics object
|
|
bool use_radius = (obj->movement_type == MT_PHYSICS) ? true : false;
|
|
|
|
// Follow vector from start position to desired end position, & move as far as we can
|
|
fq.p0 = &obj->pos;
|
|
fq.startroom = obj->roomnum;
|
|
fq.p1 = newpos;
|
|
fq.thisobjnum = OBJNUM(obj);
|
|
fq.ignore_obj_list = NULL;
|
|
fq.flags = FQ_IGNORE_RENDER_THROUGH_PORTALS;
|
|
fq.rad = use_radius ? obj->size : 0.0f;
|
|
|
|
if (f_allow_objects_to_be_pushed_through_walls) {
|
|
fq.flags |= (FQ_IGNORE_WALLS | FQ_IGNORE_TERRAIN | FQ_IGNORE_EXTERNAL_ROOMS);
|
|
}
|
|
|
|
fate = fvi_FindIntersection(&fq, &hit_info);
|
|
|
|
mprintf(0, "fate = %d\n", fate);
|
|
|
|
// Check for object can't move, meaning it's stuck in the wall
|
|
if (fate == HIT_WALL)
|
|
if (vm_VectorDistance(&obj->pos, &hit_info.hit_pnt) < MOVE_EPSILON)
|
|
return 0; // didn't move
|
|
|
|
// Set object position on where FVI told us we are
|
|
ObjSetPos(obj, &hit_info.hit_pnt, hit_info.hit_room, NULL, false);
|
|
|
|
// Say object moved
|
|
return 1;
|
|
}
|
|
|
|
bool RotateObject(int objnum, angle p, angle h, angle b) {
|
|
object *obj = &Objects[objnum];
|
|
matrix rotmat;
|
|
|
|
vm_AnglesToMatrix(&rotmat, p, h, b);
|
|
obj->orient *= rotmat; // ObjSetOrient is below
|
|
|
|
vm_Orthogonalize(&obj->orient);
|
|
ObjSetOrient(obj, &obj->orient);
|
|
|
|
Object_moved = 1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Flipping of object
|
|
void HObjectFlip() {
|
|
matrix *m = &Objects[Cur_object_index].orient;
|
|
|
|
m->uvec = -m->uvec;
|
|
m->rvec = -m->rvec;
|
|
|
|
World_changed = 1;
|
|
}
|