thug/Code/Gfx/NxLightMan.cpp
2016-02-14 08:39:12 +11:00

1363 lines
44 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// NxLightMan.cpp
// start autoduck documentation
// @DOC NxLightMan
// @module NxLightMan | None
// @subindex Scripting Database
// @index script | NxLightMan
#include <core/defines.h>
#include <gel/Scripting/script.h>
#include <gel/scripting/struct.h>
#include <gfx/debuggfx.h> // for AddDebugLine( )
#include <sk/objects/skater.h>
#include <sk/gamenet/gamenet.h>
#include <sk/scripting/nodearray.h>
#include <core/math/math.h>
// This should eventually be removed, only needed for a call to GamePaused, which should not be
// called from here anyway.
#include <sk/modules/frontend/frontend.h>
#include <gfx/nx.h>
#include <gfx/NxGeom.h>
#include "gfx/NxLightMan.h"
namespace Nx
{
/////////////////////////////////////////////////////////////////////////////
// CLightManager
// World lights
CLightManager::SLightSet CLightManager::s_world_lights =
{
Image::RGBA(72, 72, 72, 0x80), // Ambient RGBA
{ Mth::Vector(0.5f, 0.8660254f, 0.0f, 0.0f), Mth::Vector(0.0f, 1.0f, 0.0f, 0.0f) }, // Light direction
{ Image::RGBA(75, 75, 75, 0x80), Image::RGBA(0, 0, 0, 0x80) }, // Diffuse RGBA
0.5f, // Ambient modulation
{ 0.7f, 1.0f } // Diffuse modulation
};
CLightManager::SLightSet CLightManager::s_world_lights_stack[WORLD_LIGHTS_STACK_SIZE];
int CLightManager::s_world_lights_stack_index = -1;
// The following are temp variables. These should be done per model
float CLightManager::s_brightness = 1.0f;
float CLightManager::s_brightness_target = 1.0f;
float CLightManager::s_min_brightness = 0.0f;
float CLightManager::s_ambient_brightness = 1.0f;
float CLightManager::s_diffuse_brightness[MAX_LIGHTS] = { 1.0f, 1.0f };
CSceneLight CLightManager::s_scene_lights[MAX_SCENE_LIGHTS];
int CLightManager::s_num_scene_lights = 0;
SVCLight CLightManager::sp_vc_lights[MAX_VC_LIGHTS];
int CLightManager::s_num_vc_lights = 0;
uint16 * CLightManager::sp_fake_lights_nodes=NULL;
int CLightManager::s_num_fake_lights_nodes=0;
int CLightManager::s_next_fake_light_node_to_process=0;
int CLightManager::s_fake_lights_period=0;
int CLightManager::s_fake_lights_current_time=0;
Script::CStruct * CLightManager::sp_fake_lights_params=NULL;
/////////////////////////////////////////////////////////////////////////////
// These functions are the platform independent part of the interface.
/******************************************************************/
/* */
/* */
/******************************************************************/
void CLightManager::sUpdateEngine( void )
{
s_plat_update_engine();
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CLightManager::sClearLights()
{
s_ambient_brightness = 1.0f;
s_world_lights.m_light_ambient_rgba = Image::RGBA(0, 0, 0, 0x80);
for (int i = 0; i < MAX_LIGHTS; i++)
{
s_world_lights.m_light_direction[i] = Mth::Vector(0.0f, 1.0f, 0.0f, 0.0f);
s_world_lights.m_light_diffuse_rgba[i] = Image::RGBA(0, 0, 0, 0x80);
s_diffuse_brightness[i] = 1.0f;
}
s_plat_update_lights();
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CLightManager::sSetLightAmbientColor(const Image::RGBA rgba)
{
s_world_lights.m_light_ambient_rgba = rgba;
return s_plat_set_light_ambient_color();
}
/******************************************************************/
/* */
/* */
/******************************************************************/
Image::RGBA CLightManager::sGetLightAmbientColor()
{
//return s_light_ambient_rgba;
return s_plat_get_light_ambient_color();
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CLightManager::sSetLightDirection(int light_index, const Mth::Vector &direction)
{
Dbg_Assert(light_index < MAX_LIGHTS);
s_world_lights.m_light_direction[light_index] = direction;
return s_plat_set_light_direction(light_index);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
const Mth::Vector & CLightManager::sGetLightDirection(int light_index)
{
Dbg_Assert(light_index < MAX_LIGHTS);
//return s_light_direction[light_index];
return s_plat_get_light_direction(light_index);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CLightManager::sSetLightDiffuseColor(int light_index, const Image::RGBA rgba)
{
s_world_lights.m_light_diffuse_rgba[light_index] = rgba;
return s_plat_set_light_diffuse_color(light_index);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
Image::RGBA CLightManager::sGetLightDiffuseColor(int light_index)
{
Dbg_Assert(light_index < MAX_LIGHTS);
//return s_light_diffuse_rgba[light_index];
return s_plat_get_light_diffuse_color(light_index);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CLightManager::sSetAmbientLightModulationFactor(float factor)
{
s_world_lights.m_ambient_light_modulation_factor = factor;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CLightManager::sSetDiffuseLightModulationFactor(int light_index, float factor)
{
Dbg_Assert(light_index < MAX_LIGHTS);
s_world_lights.m_diffuse_light_modulation_factor[light_index] = factor;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
float CLightManager::sGetAmbientLightModulationFactor()
{
return s_world_lights.m_ambient_light_modulation_factor;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
float CLightManager::sGetDiffuseLightModulationFactor(int light_index)
{
Dbg_Assert(light_index < MAX_LIGHTS);
return s_world_lights.m_diffuse_light_modulation_factor[light_index];
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CLightManager::sPushWorldLights()
{
if (++s_world_lights_stack_index < WORLD_LIGHTS_STACK_SIZE)
{
// Put on top of stack
s_world_lights_stack[s_world_lights_stack_index] = s_world_lights;
}
else
{
--s_world_lights_stack_index;
Dbg_MsgAssert(0, ("World light stack full"));
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CLightManager::sPopWorldLights()
{
if (s_world_lights_stack_index >= 0)
{
// Get from top of stack and update the engine
s_world_lights = s_world_lights_stack[s_world_lights_stack_index--];
s_plat_update_lights();
return true;
}
else
{
Dbg_MsgAssert(0, ("World light stack empty"));
return false;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CModelLights * CLightManager::sCreateModelLights()
{
return s_plat_create_model_lights();
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CLightManager::sFreeModelLights(CModelLights *p_model_lights)
{
return s_plat_free_model_lights(p_model_lights);
}
/******************************************************************/
/* */
/* This will eventually be done through CModel */
/* */
/******************************************************************/
bool CLightManager::sSetBrightness(float brightness)
{
#if 0
// Don't do anything now
return true;
#endif
s_brightness_target = brightness;
// We've found the target brightness, so move actual brightness towards this to avoid sudden unnatural transitions.
const float BRIGHTNESS_SPEED = 0.02f;
if( s_brightness < s_brightness_target )
{
s_brightness += BRIGHTNESS_SPEED;
if( s_brightness > s_brightness_target )
{
s_brightness = s_brightness_target;
}
}
else if( s_brightness > s_brightness_target )
{
s_brightness -= BRIGHTNESS_SPEED;
if( s_brightness < s_brightness_target )
{
s_brightness = s_brightness_target;
}
}
s_ambient_brightness = 1.0f + ((2.0f * s_brightness) - 1.0f) * s_world_lights.m_ambient_light_modulation_factor;
if (s_ambient_brightness < s_min_brightness)
{
s_ambient_brightness = s_min_brightness;
}
for (int i = 0; i < MAX_LIGHTS; i++)
{
s_diffuse_brightness[i] = 1.0f + ((2.0f * s_brightness) - 1.0f) * s_world_lights.m_diffuse_light_modulation_factor[i];
if (s_diffuse_brightness[i] < s_min_brightness)
{
s_diffuse_brightness[i] = s_min_brightness;
}
}
s_plat_update_colors();
return true;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CSceneLight * CLightManager::sAddSceneLight( void )
{
if( s_num_scene_lights < MAX_SCENE_LIGHTS )
{
++s_num_scene_lights;
return &s_scene_lights[s_num_scene_lights - 1];
}
return NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CLightManager::sClearSceneLights( void )
{
s_num_scene_lights = 0;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CLightManager::sClearVCLights()
{
// Need to flush the entries from each sector list before we "ignore" the light
// otherwise the hash items from one level hang around for another level, confusing us.
for( int i = 0; i < s_num_vc_lights; ++i )
{
sp_vc_lights[i].ResetSectorList();
}
s_num_vc_lights=0;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CLightManager::sAddVCLight(uint32 name, Mth::Vector &pos, float intensity,
Image::RGBA col, float outer_radius)
{
Dbg_MsgAssert(s_num_vc_lights < MAX_VC_LIGHTS, ("Too many level lights that affect geometry (%d max)",MAX_VC_LIGHTS));
if (s_num_vc_lights < MAX_VC_LIGHTS)
{
SVCLight *p_new_light = &sp_vc_lights[s_num_vc_lights++];
p_new_light->SetNameChecksum( name );
p_new_light->SetPosition( pos );
p_new_light->SetIntensity( intensity * 0.01f );
p_new_light->SetColor( col );
// Setting the radius will implicitly calculate the sector list for this light.
p_new_light->SetRadius( outer_radius );
// Calculate which sectors will be affected.
p_new_light->CalculateSectorList();
if( intensity > 0.0f )
{
s_apply_lighting( Nx::CEngine::sGetMainScene(), name );
}
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CLightManager::sClearCurrentFakeLightsCommand()
{
if (sp_fake_lights_nodes)
{
Mem::Free(sp_fake_lights_nodes);
sp_fake_lights_nodes=NULL;
}
s_num_fake_lights_nodes=0;
s_next_fake_light_node_to_process=0;
s_fake_lights_period=0;
s_fake_lights_current_time=0;
if (sp_fake_lights_params)
{
delete sp_fake_lights_params;
sp_fake_lights_params=NULL;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// Gets called from ScriptFakeLights in cfuncs.cpp
void CLightManager::sFakeLights(const uint16 *p_nodes, int numNodes, int period, Script::CStruct *pParams)
{
sClearCurrentFakeLightsCommand();
sp_fake_lights_params=new Script::CStruct;
sp_fake_lights_params->AppendStructure(pParams);
if (numNodes)
{
sp_fake_lights_nodes=(uint16*)Mem::Malloc(numNodes*sizeof(uint16));
for (int i=0; i<numNodes; ++i)
{
sp_fake_lights_nodes[i]=p_nodes[i];
}
}
s_num_fake_lights_nodes=numNodes;
s_fake_lights_period=period;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// This gets called every frame from CEngine::sRenderWorld()
// It calls sFakeLight for each of the nodes in the array sp_fake_lights_nodes, but
// staggers the calls across frames so that they don't cause a CPU time glitch.
// Once sFakeLight ahs been called for all of them it calls sClearCurrentFakeLightsCommand()
void CLightManager::sUpdateVCLights()
{
if (!sp_fake_lights_nodes)
{
return;
}
// TODO: At some point all the calls to GamePaused need to be removed from
// the logic functions that are currently called from CEngine::sRenderWorld()
if (Mdl::FrontEnd::Instance()->GamePaused())
{
return;
}
// If finished, clean up & stop.
if (s_fake_lights_current_time > s_fake_lights_period)
{
sClearCurrentFakeLightsCommand();
return;
}
// Calculate how many nodes ought to have been processed by now, based on the ratio of s_fake_lights_current_time
// to s_fake_lights_period
int required_num_nodes_processed;
if (s_fake_lights_period)
{
required_num_nodes_processed = (s_num_fake_lights_nodes * s_fake_lights_current_time) / s_fake_lights_period;
}
else
{
required_num_nodes_processed = s_num_fake_lights_nodes;
}
// Call sFakeLight for each node starting from where it got to last time, until the required number have
// been processed.
for (; s_next_fake_light_node_to_process<required_num_nodes_processed; ++s_next_fake_light_node_to_process)
{
Script::CStruct *p_node=SkateScript::GetNode(sp_fake_lights_nodes[s_next_fake_light_node_to_process]);
uint32 id=0;
p_node->GetChecksum(CRCD(0xa1dc81f9,"Name"),&id);
sFakeLight(id,sp_fake_lights_params);
}
++s_fake_lights_current_time;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CSceneLight *CLightManager::sGetSceneLight( uint32 checksum )
{
for( int l = 0; l < s_num_scene_lights; ++l )
{
if( s_scene_lights[l].GetNameChecksum() == checksum )
{
return &s_scene_lights[l];
}
}
return NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CSceneLight * CLightManager::sGetOptimumSceneLight( Mth::Vector & pos )
{
if( s_num_scene_lights > 0 )
{
float best_ratio = 1.0f;
int best_index = -1;
for( int l = 0; l < s_num_scene_lights; ++l )
{
// Only interested in lights that are active.
if( s_scene_lights[l].GetLightIntensity() > 0.0f )
{
float dist = Mth::Distance( pos, s_scene_lights[l].GetLightPosition());
if( dist < s_scene_lights[l].GetLightRadius())
{
// Potentially a usable light.
float ratio = dist * s_scene_lights[l].GetLightReciprocalRadius();
if( ratio < best_ratio )
{
// I wonder whether we should also consider intensity here? A further light, with a higher
// intensity may have more effect than a nearer light with smaller intensity.
best_ratio = ratio;
best_index = l;
}
}
}
}
if( best_index >= 0 )
{
return &s_scene_lights[best_index];
}
}
return NULL;
}
/////////////////////////////////////////////////////////////////////////////
// The script functions
// @script | SetLightAmbientColor | Sets the global light ambient color
// @parm int | r | Red
// @parm int | g | Green
// @parm int | b | Blue
bool ScriptSetLightAmbientColor(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
int r, g, b;
if (!pParams->GetInteger("r", &r))
{
Dbg_MsgAssert(0, ("Can't find 'r' color"));
}
if (!pParams->GetInteger("g", &g))
{
Dbg_MsgAssert(0, ("Can't find 'g' color"));
}
if (!pParams->GetInteger("b", &b))
{
Dbg_MsgAssert(0, ("Can't find 'b' color"));
}
Image::RGBA rgb(r, g, b, 0x80);
return CLightManager::sSetLightAmbientColor(rgb);
}
// @script | SetLightDirection | Sets the unit direction vector of a global light
// @parm int | index | Light number
// @parm vector | direction | Unit direction vector (overrides heading and pitch)
// @parm float | heading | Heading angle (in degrees)
// @parm float | pitch | Pitch angle (in degrees)
bool ScriptSetLightDirection(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
int index;
if (!pParams->GetInteger(CRCD(0x7f8c98fe,"index"), &index))
{
Dbg_MsgAssert(0, ("Can't find 'index' of light"));
}
float heading, pitch;
Mth::Vector direction(0, 0, 1, 0);
if (pParams->GetVector(CRCD(0xc1b52e4c,"direction"), &direction))
{
direction[W] = 0.0f; // This is the only way to force this to be a vector (as opposed to a point)
//Dbg_Message("************ direction (%f, %f, %f)", direction[X], direction[Y], direction[Z]);
} else if (pParams->GetFloat(CRCD(0xfd4bc03e,"heading"), &heading) && pParams->GetFloat(CRCD(0xd8604126,"pitch"), &pitch)) {
direction.RotateX(Mth::DegToRad(pitch));
direction.RotateY(Mth::DegToRad(heading));
//Dbg_Message("************ heading and pitch direction (%f, %f, %f)", direction[X], direction[Y], direction[Z]);
} else {
Dbg_MsgAssert(0, ("Can't find 'direction' or 'heading' and 'pitch' of light"));
}
return CLightManager::sSetLightDirection(index, direction);
}
// @script | SetLightDiffuseColor | Sets the diffuse color of a global light
// @parm int | index | Light number
// @parm int | r | Red
// @parm int | g | Green
// @parm int | b | Blue
bool ScriptSetLightDiffuseColor(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
int index;
if (!pParams->GetInteger(CRCD(0x7f8c98fe,"index"), &index))
{
Dbg_MsgAssert(0, ("Can't find 'index' of light"));
}
int r, g, b;
if (!pParams->GetInteger(CRCD(0x93f60062,"r"), &r))
{
Dbg_MsgAssert(0, ("Can't find 'r' color"));
}
if (!pParams->GetInteger(CRCD(0xfe2be489,"g"), &g))
{
Dbg_MsgAssert(0, ("Can't find 'g' color"));
}
if (!pParams->GetInteger(CRCD(0x8e411006,"b"), &b))
{
Dbg_MsgAssert(0, ("Can't find 'b' color"));
}
Image::RGBA rgb(r, g, b, 0x80);
return CLightManager::sSetLightDiffuseColor(index, rgb);
}
// @script | GetLightCurrentColor | Gets the rgb values for ambient and directional lights
bool ScriptGetLightCurrentColor(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
int r=0, g=0, b=0;
Image::RGBA rgb(r, g, b, 0x80);
rgb = CLightManager::sGetLightAmbientColor();
pScript->GetParams()->AddInteger(CRCD(0xed664897,"ambient_red"), rgb.r);
pScript->GetParams()->AddInteger(CRCD(0x908bf647,"ambient_green"), rgb.g);
pScript->GetParams()->AddInteger(CRCD(0x5f4fba78,"ambient_blue"), rgb.b);
rgb = CLightManager::sGetLightDiffuseColor(0);
pScript->GetParams()->AddInteger(CRCD(0xe213aeb7,"red_0"), rgb.r);
pScript->GetParams()->AddInteger(CRCD(0x6ef19631,"green_0"), rgb.g);
pScript->GetParams()->AddInteger(CRCD(0x5ec44213,"blue_0"), rgb.b);
rgb = CLightManager::sGetLightDiffuseColor(1);
pScript->GetParams()->AddInteger(CRCD(0x95149e21,"red_1"), rgb.r);
pScript->GetParams()->AddInteger(CRCD(0x19f6a6a7,"green_1"), rgb.g);
pScript->GetParams()->AddInteger(CRCD(0x29c37285,"blue_1"), rgb.b);
/*float heading=0, pitch=0;
Mth::Vector direction(0, 0, 1, 0);
direction = CLightManager::sGetLightDirection(0);
//printf("x=%i y=%i z=%i", direction[0], direction[1], direction[2]);
//Do the opposite of these two lines
//direction.RotateX(Mth::DegToRad(pitch));
//direction.RotateY(Mth::DegToRad(heading));
pScript->GetParams()->AddInteger("pitch_0", pitch);
pScript->GetParams()->AddInteger("heading_0", heading);
*/
return true;
}
// @script | PushWorldLights | Pushes current world lights onto stack, so they can be restored later.
bool ScriptPushWorldLights(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
CLightManager::sPushWorldLights();
return true;
}
// @script | PopWorldLights | Restores last pushed world lights to the current world lights.
bool ScriptPopWorldLights(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
return CLightManager::sPopWorldLights();
}
// @script | DrawDirectionalLightLines | Draws two lines to represent the directional lights
bool ScriptDrawDirectionalLightLines(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
float heading;
pParams->GetFloat( CRCD(0xfd4bc03e,"heading"), &heading, Script::ASSERT );
float pitch;
pParams->GetFloat( CRCD(0xd8604126,"pitch"), &pitch, Script::ASSERT );
pitch = -pitch * Mth::PI / 180.0f;
heading = ((-heading+90) * Mth::PI / 180.0f);
int r, g, b;
pParams->GetInteger( CRCD(0x93f60062,"r"), &r, Script::ASSERT );
pParams->GetInteger( CRCD(0xfe2be489,"g"), &g, Script::ASSERT );
pParams->GetInteger( CRCD(0x8e411006,"b"), &b, Script::ASSERT );
Image::RGBA rgb(r, g, b, 0x80);
//rgb = CLightManager::sGetLightDiffuseColor(0);
Obj::CSkater *pSkater = Mdl::Skate::Instance()->GetLocalSkater();
Mth::Vector pos = pSkater->GetPos();
float x = pos.GetX();
float y = ( pos.GetY() + 75 );
float z = pos.GetZ();
float hypyh = 50;
float y0 = ( (sin(pitch) * hypyh) + y );
float hypxz = (cos(pitch) * hypyh);
float x0 = ( (cos(heading) * hypxz) + x );
float z0 = ( (sin(heading) * hypxz) + z );
Mth::Vector skaterpos(x, y, z, 0);
Mth::Vector direction0(x0, y0, z0, 0);
//printf("x=%i y=%i z=%i\n", direction0[0], direction0[1], direction0[2]);
Gfx::AddDebugLine( skaterpos, direction0, MAKE_RGB( rgb.r, rgb.g, rgb.b ), MAKE_RGB( rgb.r, rgb.g, rgb.b ), 1 );
return true;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
SVCLight *CLightManager::s_get_vclight_from_checksum( uint32 cs )
{
for( int i = 0; i < s_num_vc_lights; ++i )
{
if( sp_vc_lights[i].m_checksum == cs )
{
return &sp_vc_lights[i];
}
}
return NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
SVCLight::SVCLight( void )
{
mp_sector_list = new Lst::HashTable< Nx::CSector >( 4 );
m_radius = 0.0f;
m_intensity = 0.0f;
// Default the color to pure white.
m_color.r = 0x80;
m_color.g = 0x80;
m_color.b = 0x80;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
SVCLight::~SVCLight( void )
{
ResetSectorList();
delete mp_sector_list;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void SVCLight::SetRadius( float radius )
{
Dbg_Assert( radius >= 0.0f );
if( radius != m_radius )
{
m_radius = radius;
// Deal with the SceneLight that may have been created from this node.
Nx::CSceneLight *p_light = Nx::CLightManager::sGetSceneLight( m_checksum );
if( p_light )
{
p_light->SetLightRadius( radius );
}
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void SVCLight::SetIntensity( float i )
{
Dbg_Assert(( i >= 0.0f ) && ( i <= 1.0f ));
if( i != m_intensity )
{
m_intensity = i;
// Deal with the SceneLight that may have been created from this node.
Nx::CSceneLight *p_light = Nx::CLightManager::sGetSceneLight( m_checksum );
if( p_light )
{
p_light->SetLightIntensity( i );
}
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void SVCLight::SetColor( Image::RGBA col )
{
m_color = col;
// Deal with the SceneLight that may have been created from this node.
Nx::CSceneLight *p_light = Nx::CLightManager::sGetSceneLight( m_checksum );
if( p_light )
{
p_light->SetLightColor( col );
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
Lst::HashTable< Nx::CSector > *SVCLight::GetSectorList( void )
{
return mp_sector_list;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void SVCLight::ResetSectorList( void )
{
mp_sector_list->FlushAllItems();
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool SVCLight::AffectsSector( Nx::CSector *p_sector )
{
Nx::CSector *p_match = mp_sector_list->GetItem((uint32)p_sector );
return ( p_match != NULL );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void SVCLight::CalculateSectorList( void )
{
// First reset the sector list.
ResetSectorList();
Nx::CScene *p_scene = Nx::CEngine::sGetMainScene();
if( p_scene )
{
Lst::HashTable< Nx::CSector > *p_sector_list = p_scene->GetSectorList();
if( p_sector_list )
{
p_sector_list->IterateStart();
Nx::CSector *p_sector = p_sector_list->IterateNext();
while( p_sector )
{
// Only consider this sector if it is not in the "NoLevelLights" lightgroup.
// and not in the Indoor group
if( p_sector->GetLightGroup() != CRCD( 0xed82767b, "NoLevelLights" )
&& p_sector->GetLightGroup() != CRCD(0xec6542d3,"indoor"))
{
Nx::CGeom *p_geom = p_sector->GetGeom();
if( p_geom )
{
// See of the radius of the light will intersect the radius of the bounding box of the geometry.
Mth::CBBox bbox = p_sector->GetBoundingBox();
Mth::Vector mid = ( bbox.GetMax() + bbox.GetMin()) / 2.0f;
float r = ( bbox.GetMax() - mid ).Length();
float dist = ( mid - m_pos ).Length();
if( dist < ( r + m_radius ))
{
mp_sector_list->PutItem((uint32)p_sector, p_sector );
}
}
}
p_sector = p_sector_list->IterateNext();
}
}
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CLightManager::s_recalculate_sector_lighting( Nx::CSector *p_sector )
{
Nx::CGeom *p_geom = p_sector->GetGeom();
if( p_geom == NULL )
{
return;
}
const int MAX_LIGHTS_AFFECTING_SECTOR = 8;
int current_lights_affecting_sector = 0;
int light_index[MAX_LIGHTS_AFFECTING_SECTOR];
// Build a list of lights that affect this sector, to the maximum of MAX_LIGHTS_AFFECTING_SECTOR.
for( int l = 0; l < s_num_vc_lights; ++l )
{
if(( sp_vc_lights[l].m_intensity > 0.0f ) && sp_vc_lights[l].AffectsSector( p_sector ))
{
light_index[current_lights_affecting_sector] = l;
if( ++current_lights_affecting_sector >= MAX_LIGHTS_AFFECTING_SECTOR )
{
break;
}
}
}
if( current_lights_affecting_sector == 0 )
{
// No lights are affecting this sector, which can happen in the situation where the last remaining
// light affecting a sector is turned off. In this case we just want to restore the original
// vertex colors.
int verts = p_geom->GetNumRenderVerts();
if( verts > 0 )
{
Image::RGBA *p_colors = new Image::RGBA[verts];
p_geom->GetOrigRenderColors( p_colors );
p_geom->SetRenderColors( p_colors );
delete [] p_colors;
}
}
else if( current_lights_affecting_sector > 0 )
{
int verts = p_geom->GetNumRenderVerts();
if( verts > 0 )
{
Mth::Vector *p_verts = new Mth::Vector[verts];
Image::RGBA *p_colors = new Image::RGBA[verts];
p_geom->GetRenderVerts( p_verts );
p_geom->GetOrigRenderColors( p_colors );
Image::RGBA *p_color = p_colors;
Mth::Vector *p_vert = p_verts;
for( int i = 0; i < verts; ++i )
{
float ir = 0.0f;
float ig = 0.0f;
float ib = 0.0f;
for( int l = 0; l < current_lights_affecting_sector; ++l )
{
SVCLight *p_current_light = &sp_vc_lights[light_index[l]];
float dist_sqr = ( *p_vert - p_current_light->m_pos ).LengthSqr();
if( dist_sqr < ( p_current_light->m_radius * p_current_light->m_radius ))
{
float dist = sqrtf( dist_sqr );
float intensity = p_current_light->m_intensity * (( p_current_light->m_radius - dist ) / p_current_light->m_radius );
ir += intensity * (float)p_current_light->m_color.r;
ig += intensity * (float)p_current_light->m_color.g;
ib += intensity * (float)p_current_light->m_color.b;
}
}
// Apply to each r,g,b idividually, so we don't loose original color.
Image::RGBA rgb = *p_color;
float v = (float)rgb.r + ir;
rgb.r = (uint8)(( v > 255.0f ) ? 255.0f : v );
v = (float)rgb.g + ig;
rgb.g = (uint8)(( v > 255.0f ) ? 255.0f : v );
v = (float)rgb.b + ib;
rgb.b = (uint8)(( v > 255.0f ) ? 255.0f : v );
*p_color = rgb;
++p_color;
++p_vert;
}
p_geom->SetRenderColors( p_colors );
delete [] p_verts;
delete [] p_colors;
}
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CLightManager::s_apply_lighting( Nx::CScene * p_scene, uint32 checksum )
{
// Now requires a checksum.
if( checksum == 0 )
return;
// Now deal with geometry.
SVCLight *p_scene_light = s_get_vclight_from_checksum( checksum );
if( p_scene_light == NULL )
{
return;
}
Lst::HashTable< Nx::CSector > * p_sector_list = p_scene_light->GetSectorList();
if( p_sector_list )
{
p_sector_list->IterateStart();
Nx::CSector *p_sector = p_sector_list->IterateNext();
while( p_sector )
{
Nx::CGeom *p_geom = p_sector->GetGeom();
if( p_geom )
{
s_recalculate_sector_lighting( p_sector );
}
p_sector = p_sector_list->IterateNext();
}
}
# if 0
// Set up an array of lights, (eventually) culled from the node array.
s_num_vc_lights = 0;
Script::CArray *p_nodearray=Script::GetArray("NodeArray");
for (uint32 i=0; i<p_nodearray->GetSize(); ++i)
{
Script::CStruct *p_node_struct=p_nodearray->GetStructure(i);
Dbg_MsgAssert(p_node_struct,("Error getting node from node array for rail generation"));
uint32 class_checksum = 0;
p_node_struct->GetChecksum( CRCD(0x12b4e660, "Class"), &class_checksum );
if (class_checksum == CRCD(0xa0e52802,"LevelLight"))
{
SkateScript::GetPosition(p_node_struct,&sp_vc_lights[s_num_vc_lights].m_pos);
sp_vc_lights[s_num_vc_lights].m_value = 100.0f; // default values
sp_vc_lights[s_num_vc_lights].m_radius = 300.0f;
p_node_struct->GetFloat(CRCD(0x2689291c,"Brightness"),&sp_vc_lights[s_num_vc_lights].m_value);
p_node_struct->GetFloat(CRCD(0xc48391a5,"Radius"),&sp_vc_lights[s_num_vc_lights].m_radius);
s_num_vc_lights++;
}
}
Dbg_Message( "Found %d LevelLights\n", s_num_vc_lights );
if( s_num_vc_lights == 0 )
{
Dbg_Message( "Did not find any LevelLight nodes, so using Pedestrian nodes....\n" );
// Scan through the node array creating all things that need to be created.
Script::CArray *p_nodearray=Script::GetArray("NodeArray");
for (uint32 i=0; i<p_nodearray->GetSize(); ++i)
{
Script::CStruct *p_node_struct=p_nodearray->GetStructure(i);
Dbg_MsgAssert(p_node_struct,("Error getting node from node array for rail generation"));
uint32 class_checksum = 0;
p_node_struct->GetChecksum( CRCD(0x12b4e660, "Class"), &class_checksum );
if (class_checksum == CRCD(0xa0dfac98,"Pedestrian"))
{
SkateScript::GetPosition(p_node_struct,&sp_vc_lights[s_num_vc_lights].m_pos);
sp_vc_lights[s_num_vc_lights].m_value = 100.0f;
sp_vc_lights[s_num_vc_lights].m_radius = 300.0f;
s_num_vc_lights++;
}
}
}
// Go through each object, find the nearest light that intersects the object
// and the apply lighting from that
Lst::HashTable< Nx::CSector > * p_sector_list = p_scene->GetSectorList();
Image::RGBA rgb;
if (p_sector_list)
{
p_sector_list->IterateStart();
Nx::CSector *p_sector = p_sector_list->IterateNext();
while(p_sector)
{
Nx::CGeom *p_geom = p_sector->GetGeom();
if (p_geom)
{
// Need to find a light (if any) such that the radius of the light
// will intersect the radius of the bounding box of the geometry
Mth::CBBox bbox = p_sector->GetBoundingBox();
Mth::Vector mid = (bbox.GetMax() + bbox.GetMin())/2.0f;
float radius = (bbox.GetMax() - bbox.GetMin()).Length();
int best_light = -1;
float best_dist = 100000000000.0f;
for (int light = 0; light < s_num_vc_lights; light++)
{
float dist = (mid - sp_vc_lights[light].m_pos).Length();
if (dist < best_dist && dist < (radius+sp_vc_lights[light].m_radius))
{
best_dist = dist;
best_light = light;
}
}
if (best_light != -1)
{
Mth::Vector light_pos = sp_vc_lights[best_light].m_pos;
float light_radius = sp_vc_lights[best_light].m_radius;
float light_radius_squared = light_radius * light_radius;
float light_value = sp_vc_lights[best_light].m_value * amount;
//
// Do renderable geometry
int verts = p_geom->GetNumRenderVerts();
if (verts)
{
Mth::Vector *p_verts = new Mth::Vector[verts];
Image::RGBA *p_colors = new Image::RGBA[verts];
p_geom->GetRenderVerts(p_verts);
// Note: getting the original render colors will allocate lots of memory
// to store all the origianl colors the firs tiem it is called
//p_geom->GetOrigRenderColors(p_colors);
// For lighting, we get the current colors, so needs to be applied in conjunction with a compressVC
p_geom->GetRenderColors(p_colors);
Image::RGBA *p_color = p_colors;
Mth::Vector *p_vert = p_verts;
for (int i = 0; i < verts; i++)
{
// CalculateVertexLighting(*p_vert, *p_color);
float dist_sqr = (*p_vert - light_pos).LengthSqr();
float intensity = (light_radius_squared-dist_sqr)/light_radius_squared * light_value;
if (intensity<0.0f)
{
intensity = 0.0f;
}
rgb = *p_color;
float v;
// Apply to each r,g,b idividually, so we don't loose original color
v = (float) rgb.r + intensity;
if (v > 255.0f) v = 255.0f;
rgb.r = (uint8) v;
v = (float) rgb.g + intensity;
if (v > 255.0f) v = 255.0f;
rgb.g = (uint8) v;
v = (float) rgb.b + intensity;
if (v > 255.0f) v = 255.0f;
rgb.b = (uint8) v;
*p_color = rgb; //(*p_color & 0xff000000) | color;
//*(uint32*)p_color = Mth::Rnd(32767); //(*p_color & 0xff000000) | color;
p_color++;
p_vert++;
} // end for
p_geom->SetRenderColors(p_colors);
delete [] p_verts;
delete [] p_colors;
} // end if
else
{
// debuggery
//p_geom->SetColor(Image::RGBA(0,0,100,0));
}
}
}
p_sector = p_sector_list->IterateNext();
}
}
# endif
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// K: Factored this out so that the FakeLights command could also adjust a set of prefixed nodes.
void CLightManager::sFakeLight(uint32 id, Script::CStruct *pParams)
{
Nx::CScene *p_scene = Nx::CEngine::sGetMainScene();
float amount, radius;
int red, grn, blu;
// Get the SVCLight created from this node.
SVCLight *p_light = s_get_vclight_from_checksum( id );
if( p_light )
{
// Determine the intensity required.
if( pParams->GetFloat( CRCD(0x9e497fc6,"percent"), &amount ))
{
// Want to adjust the intensity. Convert from percentage to [0.0, 1.0].
amount = amount * 0.01f;
// Adjust the light intensity for this amount.
p_light->SetIntensity( amount );
}
if( pParams->GetFloat( CRCD(0x999151b,"outer_radius"), &radius ))
{
p_light->SetRadius( radius );
// Changing the radius requires that the sector list be rebuilt.
p_light->CalculateSectorList();
}
if( pParams->GetInteger( CRCD(0x59ea070,"red"), &red ))
{
// We must have a green and blue value too.
pParams->GetInteger( CRCD(0x2f6511de,"green"), &grn, true );
pParams->GetInteger( CRCD(0x61c9354b,"blue"), &blu, true );
Image::RGBA col( red, grn, blu, 0xFF );
// Adjust the light color.
p_light->SetColor( col );
}
s_apply_lighting( p_scene, id );
}
else
{
// Perhaps there is just a light for objects?
Nx::CSceneLight *p_obj_light = Nx::CLightManager::sGetSceneLight( id );
if( p_obj_light )
{
// Determine the intensity required.
if( pParams->GetFloat( CRCD(0x9e497fc6,"percent"), &amount ))
{
// Want to adjust the intensity. Convert from percentage to [0.0, 1.0].
amount = amount * 0.01f;
// Adjust the light intensity for this amount.
p_obj_light->SetLightIntensity( amount );
}
if( pParams->GetFloat( CRCD(0x999151b,"outer_radius"), &radius ))
{
p_obj_light->SetLightRadius( radius );
}
if( pParams->GetInteger( CRCD(0x59ea070,"red"), &red ))
{
// We must have a green and blue value too.
pParams->GetInteger( CRCD(0x2f6511de,"green"), &grn, true );
pParams->GetInteger( CRCD(0x61c9354b,"blue"), &blu, true );
Image::RGBA col( red, grn, blu, 0xFF );
// Adjust the light color.
p_obj_light->SetLightColor( col );
}
}
}
}
}