Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
906 lines
26 KiB
C++
906 lines
26 KiB
C++
//===========================================================================//
|
|
// File: Map.cpp //
|
|
// Project: MUNGA Brick: Interest Manager //
|
|
// Contents: Interface specifications for interest manager //
|
|
//---------------------------------------------------------------------------//
|
|
// Date Who Modification //
|
|
// -------- --- -----------------------------------------------------------//
|
|
// 11/04/94 ECH Initial coding. //
|
|
// 11/29/94 JMA Changed Identities to IDs //
|
|
// 02/10/95 CPB Added GaugeInterestType //
|
|
// 08/25/97 ECH Infrastructure changes. //
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc. //
|
|
// All Rights reserved worldwide //
|
|
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
|
//===========================================================================//
|
|
|
|
#include "AdeptHeaders.hpp"
|
|
#include "Tile.hpp"
|
|
#include <ElementRenderer\GridElement.hpp>
|
|
#include "Zone.hpp"
|
|
#include "Map.hpp"
|
|
#include "CollisionVolume.hpp"
|
|
#include "LightManager.hpp"
|
|
#include <ElementRenderer\StateChange.hpp>
|
|
#include <ElementRenderer\CameraElement.hpp>
|
|
#include <MLR\MLRInfiniteLightWithFalloff.hpp>
|
|
#include "Application.hpp"
|
|
|
|
#if defined(BSP_BUG)
|
|
bool Adept::debug_bsp=true;
|
|
#endif
|
|
|
|
bool
|
|
TerrainBSP::TestLine(
|
|
CollisionQuery *query,
|
|
Scalar enter,
|
|
Scalar leave
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(query);
|
|
|
|
//
|
|
//-----------------------------------------
|
|
// Set up the recursion avoidance variables
|
|
//-----------------------------------------
|
|
//
|
|
Check_Object(query->m_line);
|
|
Line3D *line = query->m_line;
|
|
Check_Object(query->m_zone);
|
|
Zone *zone = query->m_zone;
|
|
Check_Object(query->m_tile);
|
|
Tile *tile = query->m_tile;
|
|
TerrainBSP *bsp = this;
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// Here is the real start of the loop. We first want to find which side of
|
|
// the plane we are on and which direction we are headed
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
Start_Over:
|
|
Check_Object(bsp);
|
|
Verify(enter >= 0.0f && enter <= leave);
|
|
Verify(bsp->m_planeIndex < zone->m_planeCount);
|
|
const Plane *plane = &zone->m_planes[bsp->m_planeIndex];
|
|
Scalar dot = plane->normal * line->m_direction;
|
|
Scalar separation = plane->GetDistanceTo(line->m_origin);
|
|
|
|
//
|
|
//----------------------------------------------------------
|
|
// We are heading into the plane, so see when we will hit it
|
|
//----------------------------------------------------------
|
|
//
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
{
|
|
SPEW((
|
|
BSP_BUG,
|
|
"### %sBSP %d",
|
|
(plane->m_normal.y > SMALL) ? "" : "pseudo ",
|
|
bsp-tile->m_BSP
|
|
));
|
|
SPEW((
|
|
BSP_BUG,
|
|
" enter: %f, leave: %f, dot: %f, sep: %f",
|
|
enter,
|
|
leave,
|
|
dot,
|
|
separation
|
|
));
|
|
}
|
|
#endif
|
|
if (dot < -SMALL)
|
|
{
|
|
Scalar distance = -separation/dot;
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((
|
|
BSP_BUG,
|
|
" distance into plane = %f",
|
|
distance
|
|
));
|
|
#endif
|
|
|
|
//
|
|
//------------------------------------------------------------------
|
|
// See if we are still on the outside of the plane when we take into
|
|
// account the distance already moved along the line. If we are,
|
|
// look to see if the line will strike the plane. If not, and there
|
|
// is no tree on the outer halfspace of the plane, we won't hit
|
|
// anything so just return
|
|
//------------------------------------------------------------------
|
|
//
|
|
if (distance > enter)
|
|
{
|
|
if (distance > leave)
|
|
{
|
|
if (!bsp->m_outerIndex)
|
|
{
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " won't reach plane and outside is empty"));
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------
|
|
// There is a tree to check on the outside of our plane, so set
|
|
// it as the current tree and start over
|
|
//-------------------------------------------------------------
|
|
//
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " won't reach plane, jumping to outer halfspace"));
|
|
#endif
|
|
Verify(bsp->m_outerIndex < tile->m_BSPCount);
|
|
bsp = &tile->m_BSP[bsp->m_outerIndex];
|
|
goto Start_Over;
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------
|
|
// We will cross into the plane space, so first check the outer
|
|
// half via recursion, as we will need to continue into the inner
|
|
// space. We will shorten the line for his test to make sure
|
|
// that any plane hits happen only within our outer halfspace.
|
|
// If our child hits something, stop the traversal. Note that if
|
|
// there is no tree on the outer space, it is empty and can just
|
|
// be ignored
|
|
//---------------------------------------------------------------
|
|
//
|
|
if (bsp->m_outerIndex)
|
|
{
|
|
Verify(bsp->m_outerIndex < tile->m_BSPCount);
|
|
Verify(enter <= distance);
|
|
Verify(distance <= leave);
|
|
Scalar new_leave = leave;
|
|
Max_Clamp(new_leave, distance);
|
|
if (tile->m_BSP[bsp->m_outerIndex].TestLine(query, enter, new_leave))
|
|
{
|
|
//
|
|
// I'm commenting out this line because I have never beaten the case where
|
|
// rounding error causes the line to exit one tile and enter the next tile
|
|
// under the terrain...
|
|
//
|
|
// Verify(!line->m_length || query->m_normal->y > SMALL);
|
|
return true;
|
|
}
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, "### returning to BSP %d", bsp-tile->m_BSP));
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------
|
|
// We have struck the plane, so this is the normal of impact unless
|
|
// we go through another plane. Bump the enter to be on our plane
|
|
// surface to make sure that nothing tries to check against the
|
|
// outer space of the plane
|
|
//-----------------------------------------------------------------
|
|
//
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " crossing <%f,%f,%f>", plane->m_normal.x, plane->m_normal.y, plane->m_normal.z));
|
|
#endif
|
|
*query->m_normal = plane->normal;
|
|
enter = distance;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// At this point we need only concern ourself with the inner space of
|
|
// the plane. If there is no tree there, then it is solid space. We
|
|
// set the line length to the amount already traveled and then stop with
|
|
// a successful hit
|
|
//----------------------------------------------------------------------
|
|
//
|
|
if (!bsp->m_innerIndex)
|
|
{
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " inside is solid"));
|
|
#endif
|
|
line->m_length = enter;
|
|
//
|
|
// I'm commenting out this line because I have never beaten the case where
|
|
// rounding error causes the line to exit one tile and enter the next tile
|
|
// under the terrain...
|
|
//
|
|
// Verify(!line->m_length || query->m_normal->y > SMALL);
|
|
return true;
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------------------
|
|
// Set our inner space tree as the current tree and start over
|
|
//------------------------------------------------------------
|
|
//
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " jumping to inner halfspace"));
|
|
#endif
|
|
Verify(bsp->m_innerIndex < tile->m_BSPCount);
|
|
bsp = &tile->m_BSP[bsp->m_innerIndex];
|
|
goto Start_Over;
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------
|
|
// If we are heading out of the plane, see when we will strike it
|
|
//---------------------------------------------------------------
|
|
//
|
|
else if (dot > SMALL)
|
|
{
|
|
Scalar distance = -separation/dot;
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((
|
|
BSP_BUG,
|
|
" distance out of plane = %f",
|
|
distance
|
|
));
|
|
#endif
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// If we are still inside the plane when we take into account the
|
|
// distance already travelled, see if the inside is solid. If so,
|
|
// set the line length to that already travelled and stop the
|
|
// traversal with a successful hit
|
|
//----------------------------------------------------------------
|
|
//
|
|
if (distance > enter)
|
|
{
|
|
if (!bsp->m_innerIndex)
|
|
{
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " inside is solid"));
|
|
#endif
|
|
line->m_length = enter;
|
|
//
|
|
// I'm commenting out this line because I have never beaten the case where
|
|
// rounding error causes the line to exit one tile and enter the next tile
|
|
// under the terrain...
|
|
//
|
|
// Verify(!line->m_length || query->m_normal->y > SMALL);
|
|
return true;
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------
|
|
// We now know there is a tree inside our plane halfspace. If we
|
|
// won't hit the surface of the plane, set it as the current tree
|
|
// and start over
|
|
//---------------------------------------------------------------
|
|
//
|
|
if (distance > leave)
|
|
{
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " jumping into inner halfspace"));
|
|
#endif
|
|
Verify(bsp->m_innerIndex < tile->m_BSPCount);
|
|
bsp = &tile->m_BSP[bsp->m_innerIndex];
|
|
goto Start_Over;
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------
|
|
// We will cross into the plane space, so first check the inner
|
|
// half via recursion, as we will need to continue into the outer
|
|
// space. We will shorten the line for his test to make sure
|
|
// that any plane hits happen only within our inner halfspace.
|
|
// If our child hits something, stop the traversal
|
|
//---------------------------------------------------------------
|
|
//
|
|
Verify(bsp->m_innerIndex < tile->m_BSPCount);
|
|
Verify(enter <= distance);
|
|
Verify(distance <= leave);
|
|
Scalar new_leave = leave;
|
|
Max_Clamp(new_leave, distance);
|
|
if (tile->m_BSP[bsp->m_innerIndex].TestLine(query, enter, new_leave))
|
|
{
|
|
//
|
|
// I'm commenting out this line because I have never beaten the case where
|
|
// rounding error causes the line to exit one tile and enter the next tile
|
|
// under the terrain...
|
|
//
|
|
// Verify(!line->m_length || query->m_normal->y > SMALL);
|
|
return true;
|
|
}
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, "### returning to BSP %d", bsp-tile->m_BSP));
|
|
#endif
|
|
enter = distance;
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// If we get here, we only need concern ourselves with the outer space.
|
|
// If it is empty, stop the traversal with a miss
|
|
//---------------------------------------------------------------------
|
|
//
|
|
if (!bsp->m_outerIndex)
|
|
{
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " outside is empty"));
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------------------
|
|
// Set the outer space tree as the current tree and start over
|
|
//------------------------------------------------------------
|
|
//
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " jumping to outer halfspace"));
|
|
#endif
|
|
Verify(bsp->m_outerIndex < tile->m_BSPCount);
|
|
bsp = &tile->m_BSP[bsp->m_outerIndex];
|
|
goto Start_Over;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// We are moving parallel to the plane, so check only the inside of the
|
|
// plane if that's where we are
|
|
//----------------------------------------------------------------------
|
|
//
|
|
else if (separation < SMALL)
|
|
{
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " Inside plane"));
|
|
#endif
|
|
if (!bsp->m_innerIndex)
|
|
{
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " inside is solid"));
|
|
#endif
|
|
line->m_length = enter;
|
|
//
|
|
// I'm commenting out this line because I have never beaten the case where
|
|
// rounding error causes the line to exit one tile and enter the next tile
|
|
// under the terrain...
|
|
//
|
|
// Verify(!line->m_length || query->m_normal->y > SMALL);
|
|
return true;
|
|
}
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " jumping to inner halfspace"));
|
|
#endif
|
|
Verify(bsp->m_innerIndex < tile->m_BSPCount);
|
|
bsp = &tile->m_BSP[bsp->m_innerIndex];
|
|
goto Start_Over;
|
|
}
|
|
|
|
//
|
|
//------------------------
|
|
// Check outside the plane
|
|
//------------------------
|
|
//
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " Outside plane"));
|
|
#endif
|
|
if (!bsp->m_outerIndex)
|
|
{
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " outside is empty"));
|
|
#endif
|
|
return false;
|
|
}
|
|
#if defined(BSP_BUG)
|
|
if (debug_bsp)
|
|
SPEW((BSP_BUG, " jumping to outer halfspace"));
|
|
#endif
|
|
Verify(bsp->m_outerIndex < tile->m_BSPCount);
|
|
bsp = &tile->m_BSP[bsp->m_outerIndex];
|
|
goto Start_Over;
|
|
}
|
|
|
|
//#############################################################################
|
|
//########################### Zone #################################
|
|
//#############################################################################
|
|
|
|
Tile::ClassData
|
|
*Tile::DefaultData = NULL;
|
|
|
|
const Stuff::RGBAColor
|
|
*Tile::s_GroundColor = NULL;
|
|
|
|
DECLARE_TIMER(static, Ray_Vs_BSP);
|
|
DECLARE_TIMER(static, Ray_Vs_OBB);
|
|
|
|
static bool Hide_Ground=false;
|
|
static bool __stdcall CheckHideGround() {return Hide_Ground;}
|
|
static void __stdcall EnableHideGround() {Hide_Ground = !Hide_Ground;}
|
|
|
|
static bool Hide_TileEntities=false;
|
|
static bool __stdcall CheckHideTileEntities() {return Hide_TileEntities;}
|
|
static void __stdcall EnableHideTileEntities() {Hide_TileEntities = !Hide_TileEntities;}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Tile::InitializeClass()
|
|
{
|
|
Verify(!DefaultData);
|
|
DefaultData =
|
|
new ClassData(
|
|
TileClassID,
|
|
"Adept::Tile",
|
|
ElementRenderer::GroupElement::DefaultData,
|
|
reinterpret_cast<Factory>(&Make)
|
|
);
|
|
Register_Object(DefaultData);
|
|
|
|
s_GroundColor = NULL;
|
|
Initialize_Timer(Ray_Vs_BSP, "Ray vs BSP");
|
|
Initialize_Timer(Ray_Vs_OBB, "Ray vs OBB");
|
|
|
|
AddDebuggerMenuItem("Libraries\\Adept\\Hide Ground", CheckHideGround, EnableHideGround, 0 );
|
|
AddDebuggerMenuItem("Libraries\\Adept\\Hide Tile Entities", CheckHideTileEntities, EnableHideTileEntities, 0 );
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Tile::TerminateClass()
|
|
{
|
|
Unregister_Object(DefaultData);
|
|
delete DefaultData;
|
|
DefaultData = NULL;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Tile::TestInstance()
|
|
{
|
|
Verify(IsDerivedFrom(DefaultData));
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Tile::Tile(
|
|
Zone *zone,
|
|
BYTE row,
|
|
BYTE column
|
|
):
|
|
GroupElement(DefaultData),
|
|
m_collidees(NULL),
|
|
m_colliders(NULL),
|
|
m_lights(NULL),
|
|
damagableEntityList(NULL)
|
|
{
|
|
Check_Pointer(this);
|
|
|
|
m_draw = static_cast<DrawMethod>(&Tile::Draw);
|
|
AdoptStateChange(new ElementRenderer::StateChange);
|
|
m_row = row;
|
|
m_column = column;
|
|
m_zone = zone;
|
|
m_ground = NULL;
|
|
m_BSP = NULL;
|
|
m_BSPCount = 0;
|
|
m_lightsChanged = false;
|
|
|
|
|
|
m_DamageBitStart = -1;
|
|
m_damagableEntityCount = 0;
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Tile::~Tile()
|
|
{
|
|
Check_Object(this);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Tile::Save(MemoryStream *stream)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(stream);
|
|
|
|
//
|
|
//--------------------------
|
|
// Write stuff to the stream
|
|
//--------------------------
|
|
//
|
|
*stream << static_cast<int>(ElementRenderer::GroupElementClassID) << m_state;
|
|
if (m_stateChange)
|
|
{
|
|
Check_Object(m_stateChange);
|
|
Verify(m_state & PropertyChangeFlag);
|
|
m_stateChange->Save(stream);
|
|
}
|
|
if (!IsLocalToParentIdentity())
|
|
*stream << m_localToParent;
|
|
|
|
//
|
|
//--------------------------
|
|
// Write the bounding volume
|
|
//--------------------------
|
|
//
|
|
if (IsBoundedByOBB())
|
|
{
|
|
*stream << m_localOBB.localToParent << m_localOBB.axisExtents
|
|
<< m_localOBB.sphereRadius;
|
|
}
|
|
else
|
|
{
|
|
Point3D center(m_localOBB.localToParent);
|
|
*stream << center;
|
|
*stream << m_localOBB.sphereRadius;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------
|
|
// Write out the child count and then the children themselves
|
|
//-----------------------------------------------------------
|
|
//
|
|
ChainIteratorOf<ElementRenderer::Element*> elements(&m_group);
|
|
ElementRenderer::Element *element;
|
|
*stream << elements.GetSize();
|
|
while ((element = elements.ReadAndNext()) != NULL)
|
|
{
|
|
Check_Object(element);
|
|
element->Save(stream);
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void*
|
|
Tile::TestTile(
|
|
ElementRenderer::GridElement *grid,
|
|
WORD cell,
|
|
ElementRenderer::CollisionQuery *query,
|
|
Scalar enter,
|
|
Scalar leave
|
|
)
|
|
{
|
|
Check_Object(grid);
|
|
Check_Object(query);
|
|
Verify(query->m_line->m_length >= 0.0f);
|
|
Entity::CollisionQuery *masked_query =
|
|
Cast_Pointer(Entity::CollisionQuery*, query);
|
|
|
|
//
|
|
//-------------------------------
|
|
// Figure out what tile we are in
|
|
//-------------------------------
|
|
//
|
|
Tile *tile = Cast_Object(Tile*, grid->GetIndexedElement(cell));
|
|
Entity *result = NULL;
|
|
|
|
//
|
|
//-----------------------------------------------
|
|
// Find the zone - it should be the grid's parent
|
|
//-----------------------------------------------
|
|
//
|
|
if (masked_query->m_collisionMask&Entity::WalkerMask)
|
|
{
|
|
Zone *zone = tile->m_zone;
|
|
Check_Object(zone);
|
|
Normal3D normal;
|
|
Check_Object(masked_query->m_line);
|
|
normal.Vector3D::Negate(masked_query->m_line->m_direction);
|
|
TerrainBSP::CollisionQuery
|
|
bsp_query(masked_query->m_line, &normal, zone, tile);
|
|
|
|
//
|
|
//-------------------------
|
|
// Try out our new BSP code
|
|
//-------------------------
|
|
//
|
|
bool hit;
|
|
Check_Object(tile->m_BSP);
|
|
{
|
|
RAYCAST_LOGIC("vs. BSPs");
|
|
Start_Timer(Ray_Vs_BSP);
|
|
hit = tile->m_BSP->TestLine(&bsp_query, enter, leave);
|
|
Stop_Timer(Ray_Vs_BSP);
|
|
}
|
|
if (hit)
|
|
{
|
|
*masked_query->m_normal = normal;
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// This is turned off for now because I couldn't figure out the case where
|
|
// a ray exits one tile and enters another tile underneath the terrain - I
|
|
// suspect it has something to do with roundoff errors
|
|
//------------------------------------------------------------------------
|
|
//
|
|
Verify(*query->m_normal * query->m_line->m_direction < -SMALL);
|
|
Point3D impact;
|
|
query->m_line->FindEnd(&impact);
|
|
query->m_material = zone->GetMaterial(impact);
|
|
result = Map::GetInstance();
|
|
}
|
|
}
|
|
|
|
//
|
|
//---------------------------
|
|
// Check each collidee entity
|
|
//---------------------------
|
|
//
|
|
ChainIteratorOf<Entity*> collidees(&tile->m_collidees);
|
|
Entity *entity;
|
|
while ((entity = collidees.ReadAndNext()) != NULL)
|
|
{
|
|
Check_Object(entity);
|
|
Verify(!entity->IsACollider());
|
|
Verify(entity->GetInterestLevel() != Entity::DormantInterestLevel);
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// If the ray is coming from us, don't check. Also don't bother if the
|
|
// masks don't match
|
|
//---------------------------------------------------------------------
|
|
//
|
|
if (
|
|
entity == masked_query->m_raySource
|
|
|| !entity->CouldCollideWith(masked_query->m_collisionMask)
|
|
)
|
|
continue;
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// If there is a hit on this element, set the return value and figure out
|
|
// the relative position of the hit to the target entity
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
CollisionVolume *bounds = entity->GetHierarchicalVolume();
|
|
if (!bounds)
|
|
bounds = entity->GetSolidVolume();
|
|
Check_Object(bounds);
|
|
{
|
|
RAYCAST_LOGIC("vs. Collidees");
|
|
Start_Timer(Ray_Vs_OBB);
|
|
Verify(query->m_line->m_length >= 0.0f);
|
|
bounds = bounds->CastRay(masked_query, entity, entity->GetLocalToWorld());
|
|
Stop_Timer(Ray_Vs_OBB);
|
|
}
|
|
if (bounds)
|
|
{
|
|
result = bounds->m_entity;
|
|
query->m_data = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
//---------------------------
|
|
// Check each collider entity
|
|
//---------------------------
|
|
//
|
|
ChainIteratorOf<Entity*> colliders(&tile->m_colliders);
|
|
while ((entity = colliders.ReadAndNext()) != NULL)
|
|
{
|
|
Check_Object(entity);
|
|
Verify(entity->IsACollider());
|
|
Verify(entity->GetInterestLevel() != Entity::DormantInterestLevel);
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// If the ray is coming from us, don't check. Also don't bother if the
|
|
// masks don't match
|
|
//---------------------------------------------------------------------
|
|
//
|
|
if (
|
|
entity == masked_query->m_raySource
|
|
|| !entity->CouldCollideWith(masked_query->m_collisionMask)
|
|
)
|
|
continue;
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// If there is a hit on this element, set the return value and figure out
|
|
// the relative position of the hit to the target entity
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
CollisionVolume *bounds = entity->GetHierarchicalVolume();
|
|
if (!bounds)
|
|
bounds = entity->GetSolidVolume();
|
|
Check_Object(bounds);
|
|
{
|
|
RAYCAST_LOGIC("vs. Colliders");
|
|
Start_Timer(Ray_Vs_OBB);
|
|
bounds = bounds->CastRay(masked_query, entity, entity->GetLocalToWorld());
|
|
Stop_Timer(Ray_Vs_OBB);
|
|
}
|
|
if (bounds)
|
|
{
|
|
result = bounds->m_entity;
|
|
query->m_data = NULL;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Tile::SetDrawState()
|
|
{
|
|
Check_Object(this);
|
|
m_draw = static_cast<DrawMethod>(&Tile::Draw);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Tile::Draw(
|
|
ElementRenderer::CameraElement *camera,
|
|
const ElementRenderer::StateChange *inherited_state,
|
|
int culling_state
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(inherited_state);
|
|
Check_Object(camera);
|
|
Verify(culling_state != -1);
|
|
|
|
//
|
|
//--------------------------
|
|
// Construct our light array
|
|
//--------------------------
|
|
//
|
|
if (m_lightsChanged)
|
|
UpdateLights();
|
|
|
|
//
|
|
//-------------------
|
|
// Mix the properties
|
|
//-------------------
|
|
//
|
|
Check_Object(m_stateChange);
|
|
ElementRenderer::StateChange mixed;
|
|
mixed.Mix(*inherited_state, *m_stateChange);
|
|
MixLights(
|
|
mixed.m_lights,
|
|
inherited_state->m_lights,
|
|
m_stateChange->m_lights
|
|
);
|
|
|
|
//
|
|
//--------------------------------------
|
|
// Draw our bounds if we are supposed to
|
|
//--------------------------------------
|
|
//
|
|
#if defined(LAB_ONLY)
|
|
Callback *callback = GetCallbackSet();
|
|
unsigned index = GetCallbackIndex();
|
|
if (callback && callback[index])
|
|
{
|
|
Check_Pointer(callback[index]);
|
|
(*callback[index])(camera, inherited_state, culling_state, this);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
//------------------
|
|
// Draw our children
|
|
//------------------
|
|
//
|
|
ChainIteratorOf<ElementRenderer::Element*> children(&m_group);
|
|
ElementRenderer::Element *child;
|
|
while ((child = children.ReadAndNext()) != NULL)
|
|
{
|
|
Check_Object(child);
|
|
|
|
//
|
|
//---------------------
|
|
// Deal with the ground
|
|
//---------------------
|
|
//
|
|
if (child==m_ground)
|
|
{
|
|
#if defined(LAB_ONLY)
|
|
if (Hide_Ground)
|
|
continue;
|
|
#endif
|
|
if (s_GroundColor)
|
|
{
|
|
RGBAColor old(Element::FadeColor);
|
|
Element::FadeColor = *s_GroundColor;
|
|
camera->DrawElement(child, &mixed);
|
|
Element::FadeColor = old;
|
|
}
|
|
else
|
|
camera->DrawElement(child, &mixed);
|
|
}
|
|
|
|
//
|
|
//------------------------
|
|
// Draw our other children
|
|
//------------------------
|
|
//
|
|
else
|
|
{
|
|
#if defined(LAB_ONLY)
|
|
if (Hide_TileEntities)
|
|
continue;
|
|
#endif
|
|
camera->DrawElement(child, &mixed);
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Tile::UpdateLights()
|
|
{
|
|
Check_Object(this);
|
|
Verify(m_lightsChanged);
|
|
|
|
ChainIteratorOf<FiniteLight*> light_chain(&m_lights);
|
|
gosFX::Light *light;
|
|
Check_Object(m_stateChange);
|
|
int i=0;
|
|
while (
|
|
(light = light_chain.ReadAndNext()) != NULL
|
|
&& i<MidLevelRenderer::Limits::Max_Number_Of_Lights_Per_Primitive
|
|
)
|
|
{
|
|
Check_Object(light);
|
|
if(light->m_light->GetIntensity()>Stuff::SMALL)
|
|
{
|
|
m_stateChange->m_lights[i++] = light->m_light;
|
|
}
|
|
}
|
|
if (i < MidLevelRenderer::Limits::Max_Number_Of_Lights_Per_Primitive)
|
|
m_stateChange->m_lights[i] = NULL;
|
|
Verify(i <= MidLevelRenderer::Limits::Max_Number_Of_Lights_Per_Primitive);
|
|
m_lightsChanged = false;
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void
|
|
Tile::AddEntityToDamagableList(Entity* entity)
|
|
{
|
|
|
|
if (!entity->IsDestroyable())
|
|
return;
|
|
|
|
|
|
if (Application::GetInstance()->GetApplicationState() != ApplicationStateEngine::LoadingGameState)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
if (m_damagableEntityCount >= damagableEntityList.GetLength())
|
|
{
|
|
damagableEntityList.SetLength(m_damagableEntityCount + 10);
|
|
}
|
|
|
|
|
|
damagableEntityList[m_damagableEntityCount] = entity;
|
|
++m_damagableEntityCount;
|
|
|
|
|
|
}
|