mirror of
https://github.com/Relintai/godot-lportal.git
synced 2024-11-11 10:52:09 +01:00
607 lines
17 KiB
C++
607 lines
17 KiB
C++
// Copyright (c) 2019 Lawnjelly
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
|
|
#include "ltrace.h"
|
|
#include "ldebug.h"
|
|
#include "ldob.h"
|
|
#include "lportal.h"
|
|
#include "lbitfield_dynamic.h"
|
|
#include "lroom_manager.h"
|
|
|
|
#define LMAN m_pManager
|
|
|
|
|
|
|
|
//void LTrace::Trace_Prepare(LRoomManager &manager, const LCamera &cam, Lawn::LBitField_Dynamic &BF_SOBs, Lawn::LBitField_Dynamic &BF_DOBs, Lawn::LBitField_Dynamic &BF_Rooms, LVector<int> &visible_SOBs, LVector<int> &visible_DOBs, LVector<int> &visible_Rooms)
|
|
void LTrace::Trace_Prepare(LRoomManager &manager, const LSource &cam, Lawn::LBitField_Dynamic &BF_SOBs, Lawn::LBitField_Dynamic &BF_Rooms, LVector<int> &visible_SOBs, LVector<int> &visible_Rooms)
|
|
{
|
|
m_pManager = &manager;
|
|
m_pCamera = &cam;
|
|
|
|
// default
|
|
m_TraceFlags = CULL_SOBS | CULL_DOBS | TOUCH_ROOMS | MAKE_ROOM_VISIBLE;
|
|
|
|
m_pBF_SOBs = &BF_SOBs;
|
|
// m_pBF_DOBs = &BF_DOBs;
|
|
m_pBF_Rooms = &BF_Rooms;
|
|
m_pVisible_SOBs = &visible_SOBs;
|
|
// m_pVisible_DOBs = &visible_DOBs;
|
|
m_pVisible_Rooms = &visible_Rooms;
|
|
}
|
|
|
|
void LTrace::CullSOBs(LRoom &room, const LVector<Plane> &planes)
|
|
{
|
|
// clip all objects in this room to the clipping planes
|
|
int last_sob = room.m_iFirstSOB + room.m_iNumSOBs;
|
|
for (int n=room.m_iFirstSOB; n<last_sob; n++)
|
|
{
|
|
LSob &sob = LMAN->m_SOBs[n];
|
|
|
|
//LPRINT_RUN(2, "sob " + itos(n) + " " + sob.GetSpatial()->get_name());
|
|
|
|
// already determined to be visible through another portal
|
|
if (m_pBF_SOBs->GetBit(n))
|
|
{
|
|
//LPRINT_RUN(2, "\talready visible");
|
|
continue;
|
|
}
|
|
|
|
bool bShow = true;
|
|
|
|
|
|
// estimate the radius .. for now
|
|
const AABB &bb = sob.m_aabb;
|
|
|
|
// print("\t\t\tculling object " + pObj->get_name());
|
|
|
|
for (int p=0; p<planes.size(); p++)
|
|
{
|
|
// float dist = planes[p].distance_to(pt);
|
|
// print("\t\t\t\t" + itos(p) + " : dist " + String(Variant(dist)));
|
|
|
|
float r_min, r_max;
|
|
bb.project_range_in_plane(planes[p], r_min, r_max);
|
|
|
|
// print("\t\t\t\t" + itos(p) + " : r_min " + String(Variant(r_min)) + ", r_max " + String(Variant(r_max)));
|
|
|
|
|
|
if (r_min > 0.0f)
|
|
{
|
|
//LPRINT_RUN(2, "\tout of view");
|
|
bShow = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bShow)
|
|
{
|
|
// sob is renderable and visible (not shadow only)
|
|
//LPRINT_RUN(2, "\tin view");
|
|
m_pBF_SOBs->SetBit(n, true);
|
|
m_pVisible_SOBs->push_back(n);
|
|
}
|
|
|
|
} // for through sobs
|
|
|
|
}
|
|
|
|
void LTrace::CullDOBs(LRoom &room, const LVector<Plane> &planes)
|
|
{
|
|
// NYI this isn't efficient, there may be more than 1 portal to the same room
|
|
|
|
// cull DOBs
|
|
int nDOBs = room.m_DOBs.size();
|
|
|
|
for (int n=0; n<nDOBs; n++)
|
|
{
|
|
LDob &dob = room.m_DOBs[n];
|
|
|
|
Spatial * pObj = dob.GetSpatial();
|
|
|
|
if (pObj)
|
|
{
|
|
bool bShow = true;
|
|
const Vector3 &pt = pObj->get_global_transform().origin;
|
|
|
|
float radius = dob.m_fRadius;
|
|
|
|
for (int p=0; p<planes.size(); p++)
|
|
{
|
|
float dist = planes[p].distance_to(pt);
|
|
//print("\t\t\t\t" + itos(p) + " : dist " + String(Variant(dist)));
|
|
|
|
if (dist > radius)
|
|
{
|
|
bShow = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bShow)
|
|
{
|
|
LPRINT_RUN(1, "\tDOB " + pObj->get_name() + " visible");
|
|
dob.m_bVisible = true;
|
|
}
|
|
else
|
|
{
|
|
LPRINT_RUN(1, "\tDOB " + pObj->get_name() + " culled");
|
|
}
|
|
}
|
|
} // for through dobs
|
|
|
|
}
|
|
|
|
|
|
bool LTrace::Trace_Light(LRoomManager &manager, const LLight &light, eLightRun eRun)
|
|
{
|
|
m_pManager = &manager;
|
|
|
|
|
|
LRoom * pRoom;
|
|
|
|
// non area light
|
|
if (light.m_iArea == -1)
|
|
{
|
|
// can only deal with lights in rooms for now
|
|
if (light.m_Source.m_RoomID == -1)
|
|
{
|
|
WARN_PRINT_ONCE("LTrace::Trace_Light can only trace lights in rooms");
|
|
return true;
|
|
}
|
|
|
|
pRoom = manager.GetRoom(light.m_Source.m_RoomID);
|
|
if (!pRoom)
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// area light
|
|
pRoom = 0;
|
|
}
|
|
|
|
const LSource &cam = light.m_Source;
|
|
|
|
unsigned int pool_member = manager.m_Pool.Request();
|
|
assert (pool_member != -1);
|
|
|
|
LVector<Plane> &planes = manager.m_Pool.Get(pool_member);
|
|
planes.clear();
|
|
|
|
// we now need to trace either just DOBs (in the case of static lights)
|
|
// or SOBs and DOBs (in the case of dynamic lights)
|
|
LRoomManager::LLightRender &lr = manager.m_LightRender;
|
|
lr.m_BF_Temp_SOBs.Blank();
|
|
lr.m_Temp_Visible_SOBs.clear();
|
|
lr.m_BF_Temp_Visible_Rooms.Blank();
|
|
lr.m_Temp_Visible_Rooms.clear();
|
|
|
|
bool bLightInView = true;
|
|
|
|
switch (eRun)
|
|
{
|
|
// finding all shadow casters at runtime
|
|
case LR_ALL:
|
|
{
|
|
//Trace_Prepare(manager, cam, lr.m_BF_Temp_SOBs, manager.m_BF_visible_rooms, lr.m_Temp_Visible_SOBs, *manager.m_pCurr_VisibleRoomList);
|
|
Trace_Prepare(manager, cam, lr.m_BF_Temp_SOBs, lr.m_BF_Temp_Visible_Rooms, lr.m_Temp_Visible_SOBs, lr.m_Temp_Visible_Rooms);
|
|
|
|
Trace_SetFlags(CULL_SOBS | CULL_DOBS | MAKE_ROOM_VISIBLE);
|
|
|
|
// create subset planes of light frustum and camera frustum
|
|
bLightInView = manager.m_MainCamera.AddCameraLightPlanes(manager, cam, planes);
|
|
}
|
|
break;
|
|
// finding only visible rooms at runtime
|
|
case LR_ROOMS:
|
|
{
|
|
Trace_Prepare(manager, cam, lr.m_BF_Temp_SOBs, lr.m_BF_Temp_Visible_Rooms, lr.m_Temp_Visible_SOBs, lr.m_Temp_Visible_Rooms);
|
|
|
|
// we ONLY want a list of rooms hit
|
|
Trace_SetFlags(MAKE_ROOM_VISIBLE);
|
|
}
|
|
break;
|
|
// finding all in preconversion
|
|
case LR_CONVERT:
|
|
{
|
|
Trace_Prepare(manager, cam, lr.m_BF_Temp_SOBs, lr.m_BF_Temp_Visible_Rooms, lr.m_Temp_Visible_SOBs, lr.m_Temp_Visible_Rooms);
|
|
|
|
// we want sobs but not to touch rooms
|
|
m_TraceFlags = CULL_SOBS | MAKE_ROOM_VISIBLE; // | CULL_DOBS | TOUCH_ROOMS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
if (bLightInView)
|
|
{
|
|
// non area light
|
|
if (pRoom)
|
|
{
|
|
Trace_Begin(*pRoom, planes);
|
|
}
|
|
else
|
|
{
|
|
// area light
|
|
|
|
// area lights don't go through portals, e.g. coming from above like sunlight
|
|
// they instead have a predefined list of rooms governed by the area
|
|
m_TraceFlags |= DONT_TRACE_PORTALS;
|
|
|
|
// new .. trace according to area, not affected rooms, as affected rooms has a limit
|
|
assert (light.m_iArea != -1);
|
|
const LArea &area = LMAN->m_Areas[light.m_iArea];
|
|
|
|
int last_room = area.m_iFirstRoom + area.m_iNumRooms;
|
|
|
|
for (int r=area.m_iFirstRoom; r<last_room; r++)
|
|
{
|
|
int room_id = LMAN->m_AreaRooms[r];
|
|
LRoom * pRoom = manager.GetRoom(room_id);
|
|
|
|
// should not happen, assert?
|
|
assert (pRoom);
|
|
|
|
// trace as usual but don't go through the portals
|
|
Trace_Recursive(0, *pRoom, planes, 0);
|
|
}
|
|
|
|
/*
|
|
// go through each affected room
|
|
for (int r=0; r<light.m_NumAffectedRooms; r++)
|
|
{
|
|
int room_id = light.m_AffectedRooms[r];
|
|
LRoom * pRoom = manager.GetRoom(room_id);
|
|
|
|
// should not happen, assert?
|
|
assert (pRoom);
|
|
|
|
// trace as usual but don't go through the portals
|
|
Trace_Recursive(0, *pRoom, planes, 0);
|
|
}
|
|
*/
|
|
} // if area light
|
|
} // if light in view
|
|
|
|
// we no longer need these planes
|
|
manager.m_Pool.Free(pool_member);
|
|
|
|
return bLightInView;
|
|
}
|
|
|
|
|
|
void LTrace::AddSpotlightPlanes(LVector<Plane> &planes) const
|
|
{
|
|
Plane p(m_pCamera->m_ptPos, -m_pCamera->m_ptDir);
|
|
planes.push_back(p);
|
|
|
|
// this is kinda crappy, because ideally we'd want a cone, but instead we'll fake a frustum
|
|
Vector3 pts[4];
|
|
|
|
// assuming here that d is normalized!
|
|
const Vector3 &d = m_pCamera->m_ptDir;
|
|
const Vector3 &ptCam = m_pCamera->m_ptPos;
|
|
|
|
assert (d.length_squared() < 1.1f);
|
|
assert (d.length_squared() > 0.9f);
|
|
|
|
// spotlight has no 'up' vector, as it is regular shape around direction axis
|
|
// so we can use anything for side vector
|
|
|
|
// this might balls up with a light pointing straight up
|
|
Vector3 ptSide = Vector3(0, 1, 0).cross(d);
|
|
|
|
float l = ptSide.length();
|
|
if (l < 0.1f)
|
|
{
|
|
// special case straight up, lets cross against something else
|
|
ptSide = d.cross(Vector3(1, 0, 0));
|
|
l = ptSide.length();
|
|
assert (l);
|
|
}
|
|
|
|
// unitize side
|
|
ptSide *= 1.0 / l;
|
|
|
|
Vector3 ptUp = ptSide.cross(d);
|
|
ptUp.normalize();
|
|
|
|
// now we've got the vecs, lets create some planes
|
|
|
|
// spotlight spread definition (light.cpp, line 146)
|
|
//float size = Math::tan(Math::deg2rad(param[PARAM_SPOT_ANGLE])) * len;
|
|
|
|
// this is the size at distance 1 .. it would be more efficient to calc distance at which sides were 1, but whatever...
|
|
float size = Math::tan(Math::deg2rad(m_pCamera->m_fSpread));
|
|
|
|
ptSide *= size; // or half size? not sure yet
|
|
ptUp *= -size;
|
|
|
|
// pts will be bot left, top left, top right, bot right
|
|
Vector3 ptEx = ptCam + d;
|
|
|
|
pts[0] = ptEx - ptSide - ptUp;
|
|
pts[1] = ptEx - ptSide + ptUp;
|
|
pts[2] = ptEx + ptSide + ptUp;
|
|
pts[3] = ptEx + ptSide - ptUp;
|
|
|
|
Plane left(ptCam, pts[0], pts[1], COUNTERCLOCKWISE);
|
|
Plane top(ptCam, pts[1], pts[2], COUNTERCLOCKWISE);
|
|
Plane right(ptCam, pts[2], pts[3], COUNTERCLOCKWISE);
|
|
Plane bottom(ptCam, pts[3], pts[0], COUNTERCLOCKWISE);
|
|
|
|
planes.push_back(left);
|
|
planes.push_back(top);
|
|
planes.push_back(right);
|
|
planes.push_back(bottom);
|
|
|
|
// debug
|
|
if (LMAN->m_bDebugFrustums)
|
|
{
|
|
for (int n=0; n<4; n++)
|
|
{
|
|
LMAN->m_DebugFrustums.push_back(ptCam);
|
|
LMAN->m_DebugFrustums.push_back(pts[n]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LTrace::Trace_Begin(LRoom &room, LVector<Plane> &planes)
|
|
{
|
|
int first_plane = 0;
|
|
|
|
switch (m_pCamera->m_eType)
|
|
{
|
|
case LSource::ST_SPOTLIGHT:
|
|
{
|
|
// special cases of spotlight, add some extra planes to define the cone
|
|
AddSpotlightPlanes(planes);
|
|
}
|
|
break;
|
|
case LSource::ST_CAMERA:
|
|
first_plane = 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
LPRINT_RUN(2, "TRACE BEGIN");
|
|
LPRINT_RUN(2, m_pCamera->MakeDebugString());
|
|
|
|
|
|
Trace_Recursive(0, room, planes, first_plane);
|
|
}
|
|
|
|
void LTrace::Trace_Recursive(int depth, LRoom &room, const LVector<Plane> &planes, int first_portal_plane)
|
|
{
|
|
// prevent too much depth
|
|
if (depth > 8)
|
|
{
|
|
LPRINT_RUN(2, "\t\t\tDEPTH LIMIT REACHED");
|
|
WARN_PRINT_ONCE("LPortal Depth Limit reached (seeing through > 8 portals)");
|
|
return;
|
|
}
|
|
|
|
// for debugging
|
|
Lawn::LDebug::m_iTabDepth = depth;
|
|
LPRINT_RUN(2, "");
|
|
|
|
LPRINT_RUN(2, "ROOM '" + itos(room.m_RoomID) + " : " + room.get_name() + "' planes " + itos(planes.size()) + " portals " + itos(room.m_iNumPortals) );
|
|
|
|
// only handle one touch per frame so far (one portal into room)
|
|
//assert (manager.m_uiFrameCounter > m_uiFrameTouched);
|
|
|
|
// first touch
|
|
DetectFirstTouch(room);
|
|
|
|
if (m_TraceFlags & CULL_SOBS)
|
|
CullSOBs(room, planes);
|
|
|
|
if (m_TraceFlags & CULL_DOBS)
|
|
CullDOBs(room, planes);
|
|
|
|
// portals
|
|
if (m_TraceFlags & DONT_TRACE_PORTALS)
|
|
return;
|
|
|
|
// look through portals
|
|
int nPortals = room.m_iNumPortals;
|
|
|
|
for (int port_num=0; port_num<nPortals; port_num++)
|
|
{
|
|
int port_id = room.m_iFirstPortal + port_num;
|
|
|
|
const LPortal &port = LMAN->m_Portals[port_id];
|
|
|
|
// have we already handled the room on this frame?
|
|
// get the room pointed to by the portal
|
|
LRoom * pLinkedRoom = &LMAN->Portal_GetLinkedRoom(port);
|
|
|
|
|
|
|
|
// cull by portal angle to camera.
|
|
|
|
// NEW! I've come up with a much better way of culling portals by direction to camera...
|
|
// instead of using dot product with a varying view direction, we simply find which side of the portal
|
|
// plane the camera is on! If it is behind, the portal can be seen through, if in front, it can't! :)
|
|
float dist_cam = port.m_Plane.distance_to(m_pCamera->m_ptPos);
|
|
LPRINT_RUN(2, "\tPORTAL " + itos (port_num) + " (" + itos(port_id) + ") " + port.get_name());
|
|
if (dist_cam >= 0.0f) // was >
|
|
{
|
|
LPRINT_RUN(2, "\t\tCULLED (back facing)");
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
// Note we need to deal with 'side on' portals, and the camera has a spreading view, so we cannot simply dot
|
|
// the portal normal with camera direction, we need to take into account angle to the portal itself.
|
|
const Vector3 &portal_normal = port.m_Plane.normal;
|
|
LPRINT_RUN(2, "\tPORTAL " + itos (port_num) + " (" + itos(port_id) + ") " + port.get_name() + " normal " + portal_normal);
|
|
|
|
// we will dot the portal angle with a ray from the camera to the portal centre
|
|
// (there might be an even better ray direction but this will do for now)
|
|
Vector3 dir_portal = port.m_ptCentre - m_pCamera->m_ptPos;
|
|
|
|
// doesn't actually need to be normalized?
|
|
float dot = dir_portal.dot(portal_normal);
|
|
|
|
if (dot <= -0.0f) // 0.0
|
|
{
|
|
//LPRINT_RUN(2, "\t\tCULLED (wrong direction) dot is " + String(Variant(dot)) + ", dir_portal is " + dir_portal);
|
|
LPRINT_RUN(2, "\t\tCULLED (wrong direction)");
|
|
continue;
|
|
}
|
|
*/
|
|
|
|
// is it culled by the planes?
|
|
LPortal::eClipResult overall_res = LPortal::eClipResult::CLIP_INSIDE;
|
|
|
|
// while clipping to the planes we maintain a list of partial planes, so we can add them to the
|
|
// recursive next iteration of planes to check
|
|
static LVector<int> partial_planes;
|
|
partial_planes.clear();
|
|
|
|
// for portals, we want to ignore the near clipping plane, as we might be right on the edge of a doorway
|
|
// and still want to look through the portal.
|
|
// So we are starting this loop from 1, ASSUMING that plane zero is the near clipping plane.
|
|
// If it isn't we would need a different strategy
|
|
// Note that now this only occurs for the first portal out of the current room. After that,
|
|
// 0 is passed as first_portal_plane, because the near plane will probably be irrelevant,
|
|
// and we are now not necessarily copying the camera planes.
|
|
for (int l=first_portal_plane; l<planes.size(); l++)
|
|
{
|
|
LPortal::eClipResult res = port.ClipWithPlane(planes[l]);
|
|
|
|
switch (res)
|
|
{
|
|
case LPortal::eClipResult::CLIP_OUTSIDE:
|
|
overall_res = res;
|
|
break;
|
|
case LPortal::eClipResult::CLIP_PARTIAL:
|
|
overall_res = res;
|
|
partial_planes.push_back(l);
|
|
break;
|
|
default: // suppress warning
|
|
break;
|
|
}
|
|
|
|
if (overall_res == LPortal::eClipResult::CLIP_OUTSIDE)
|
|
break;
|
|
}
|
|
|
|
// this portal is culled
|
|
if (overall_res == LPortal::eClipResult::CLIP_OUTSIDE)
|
|
{
|
|
LPRINT_RUN(2, "\t\tCULLED (outside planes)");
|
|
continue;
|
|
}
|
|
|
|
// else recurse into that portal
|
|
unsigned int uiPoolMem = LMAN->m_Pool.Request();
|
|
if (uiPoolMem != -1)
|
|
{
|
|
// get a vector of planes from the pool
|
|
LVector<Plane> &new_planes = LMAN->m_Pool.Get(uiPoolMem);
|
|
new_planes.clear();
|
|
|
|
// NEW!! if portal is totally inside the planes, don't copy the old planes
|
|
if (overall_res != LPortal::eClipResult::CLIP_INSIDE)
|
|
{
|
|
// copy the existing planes
|
|
//new_planes.copy_from(planes);
|
|
|
|
// new .. only copy the partial planes that the portal cuts through
|
|
for (int n=0; n<partial_planes.size(); n++)
|
|
new_planes.push_back(planes[partial_planes[n]]);
|
|
}
|
|
|
|
// add the planes for the portal
|
|
// NOTE that we can also optimize by not adding portal planes for edges that
|
|
// were behind a partial plane. NYI
|
|
port.AddPlanes(*LMAN, m_pCamera->m_ptPos, new_planes);
|
|
|
|
|
|
if (pLinkedRoom)
|
|
{
|
|
Trace_Recursive(depth+1, *pLinkedRoom, new_planes, 0);
|
|
//pLinkedRoom->DetermineVisibility_Recursive(manager, depth + 1, cam, new_planes, 0);
|
|
// for debugging need to reset tab depth
|
|
Lawn::LDebug::m_iTabDepth = depth;
|
|
}
|
|
|
|
// we no longer need these planes
|
|
LMAN->m_Pool.Free(uiPoolMem);
|
|
}
|
|
else
|
|
{
|
|
// planes pool is empty!
|
|
// This will happen if the view goes through shedloads of portals
|
|
// The solution is either to increase the plane pool size, or build levels
|
|
// with views through multiple portals. Looking through multiple portals is likely to be
|
|
// slow anyway because of the number of planes to test.
|
|
WARN_PRINT_ONCE("Planes pool is empty");
|
|
}
|
|
|
|
} // for p through portals
|
|
|
|
}
|
|
|
|
void LTrace::DetectFirstTouch(LRoom &room)
|
|
{
|
|
// mark if not reached yet on this trace
|
|
if (!m_pBF_Rooms->GetBit(room.m_RoomID))
|
|
{
|
|
m_pBF_Rooms->SetBit(room.m_RoomID, true);
|
|
|
|
if (m_TraceFlags & MAKE_ROOM_VISIBLE)
|
|
{
|
|
// keep track of which rooms are shown this trace
|
|
m_pVisible_Rooms->push_back(room.m_RoomID);
|
|
}
|
|
|
|
// camera and light traces
|
|
if (m_TraceFlags & TOUCH_ROOMS)
|
|
{
|
|
if (room.m_uiFrameTouched < LMAN->m_uiFrameCounter)
|
|
FirstTouch(room);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void LTrace::FirstTouch(LRoom &room)
|
|
{
|
|
// set the frame counter
|
|
room.m_uiFrameTouched = LMAN->m_uiFrameCounter;
|
|
|
|
// show this room and add to visible list of rooms
|
|
room.Room_MakeVisible(true);
|
|
|
|
// m_pBF_Rooms->SetBit(room.m_RoomID, true);
|
|
|
|
// keep track of which rooms are shown this frame
|
|
// m_pVisible_Rooms->push_back(room.m_RoomID);
|
|
|
|
// hide all dobs
|
|
for (int n=0; n<room.m_DOBs.size(); n++)
|
|
room.m_DOBs[n].m_bVisible = false;
|
|
}
|