Compare commits
5 Commits
d805f2ba86
...
x64_lulu
| Author | SHA1 | Date | |
|---|---|---|---|
| 0efa4ee6f7 | |||
| c0f3eeb00d | |||
| 7ac00458df | |||
| 5131a4b09c | |||
| 162d5940f0 |
@ -30,7 +30,7 @@ target_include_directories(webcam PRIVATE
|
||||
|
||||
target_compile_definitions(webcam PRIVATE
|
||||
__PLATFORM_WINDOWS__
|
||||
WIN32
|
||||
WIN64
|
||||
_USRDLL
|
||||
_GS_DLL_EXPORT
|
||||
)
|
||||
@ -42,8 +42,7 @@ target_link_libraries(webcam PRIVATE
|
||||
platform
|
||||
engine
|
||||
framework
|
||||
opencv_world300
|
||||
opencv_ts300
|
||||
opencv_world345
|
||||
extern
|
||||
|
||||
Ws2_32
|
||||
|
||||
BIN
bin/opencv_world345.dll
Normal file
BIN
bin/opencv_world345.dll
Normal file
Binary file not shown.
23
include/engine/automation/automated_property_provider.cpp
Normal file
23
include/engine/automation/automated_property_provider.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "automation/automated_property_provider.h"
|
||||
#include "math/quaternion.h"
|
||||
|
||||
using namespace GS::Automation;
|
||||
using GS::Core::MotionChannel;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool IPropertyProvider::GetProperty(MotionChannel::Type, float &)
|
||||
{ return false; }
|
||||
bool IPropertyProvider::SetProperty(MotionChannel::Type, float)
|
||||
{ return false; }
|
||||
GS::Quaternion IPropertyProvider::GetRotation() const
|
||||
{ return Quaternion(); }
|
||||
bool IPropertyProvider::SetRotation(const GS::Quaternion &)
|
||||
{ return false; }
|
||||
//------------------------------------------------------------------------------
|
||||
152
include/engine/automation/automation_player.cpp
Normal file
152
include/engine/automation/automation_player.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#ifndef __PLATFORM_IOS__
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include "automation/automation_player.h"
|
||||
#include "automation/automation_source.h"
|
||||
#include "platform_config.h"
|
||||
|
||||
using namespace GS::Automation;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Motion *Player::GetMotion(const char *id) const
|
||||
{
|
||||
ListForeachPtr(Motion *, i, motions)
|
||||
if (!String::Compare(i->name, id))
|
||||
return i;
|
||||
return NULL;
|
||||
}
|
||||
Motion *Player::GetMotionFromIndex(uint index) const
|
||||
{ return motions.ObjectAt(index); }
|
||||
bool Player::AddMotion(Motion *motion)
|
||||
{ return asbool(motions.Add(motion)); }
|
||||
void Player::RemoveMotion(Motion *motion)
|
||||
{ motions.Remove(motion); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Source *Player::StartAutomation(Source *source, float _blend, AddSourceMode mode, float _weight)
|
||||
{
|
||||
if (!source)
|
||||
return NULL;
|
||||
|
||||
// Source blend.
|
||||
switch (mode)
|
||||
{
|
||||
case SourceSet: // Dispose of all current sources.
|
||||
ListForeachPtr(Source *, s, sources)
|
||||
s->Dispose(_blend);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Blend in source.
|
||||
source->SetWeight(_weight, _blend);
|
||||
sources.Add(source);
|
||||
|
||||
switch (source->GetType())
|
||||
{
|
||||
case Source::TypeMotion:
|
||||
source->relative = flags.IsSet(FlagRelativeMotion);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
void Player::Evaluate(const GS::Time &dt_time)
|
||||
{
|
||||
if (!sources.GetCount() || property_provider.IsNull())
|
||||
return;
|
||||
|
||||
// Allocate weight table from stack.
|
||||
weight_table = (float *)alloca(sizeof(float) * MotionChannel::Last);
|
||||
for (int n = 0; n < MotionChannel::Last; ++n)
|
||||
weight_table[n] = 0;
|
||||
|
||||
// Update sources.
|
||||
ListForeachPtr(Source *, s, sources)
|
||||
{
|
||||
s->Update(dt_time);
|
||||
if (s->CanDispose())
|
||||
sources.Remove(s);
|
||||
}
|
||||
|
||||
// Mix rotation.
|
||||
float w = 0;
|
||||
Quaternion q, source_q = property_provider->GetRotation();
|
||||
|
||||
ListForeachPtr(Source *, s, sources)
|
||||
if (s->GetWeight() > 0.01)
|
||||
if (s->EvaluateRotation(source_q))
|
||||
{
|
||||
if (w > 0.01)
|
||||
q = Quaternion::Slerp(w / (w + s->GetWeight()), source_q, q);
|
||||
else q = source_q;
|
||||
|
||||
w += s->GetWeight();
|
||||
}
|
||||
|
||||
if (w > 0)
|
||||
property_provider->SetRotation(q);
|
||||
|
||||
// Mix sources.
|
||||
ListForeachPtr(Source *, s, sources)
|
||||
if (s->GetWeight())
|
||||
s->Evaluate(*this);
|
||||
|
||||
// Drop weight table.
|
||||
weight_table = NULL;
|
||||
}
|
||||
void Player::BlendAutomatedProperty(MotionChannel::Type type, float _v, float _w)
|
||||
{
|
||||
if (!_w || !weight_table)
|
||||
return;
|
||||
|
||||
float v, *w = &weight_table[(uint)type];
|
||||
|
||||
if (property_provider->GetProperty(type, v))
|
||||
{
|
||||
v = (v * *w + _v * _w) / (*w + _w);
|
||||
*w += _w;
|
||||
property_provider->SetProperty(type, v);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Player::IsAutomated() const
|
||||
{ return asbool(sources.GetCount()); }
|
||||
bool Player::IsAutomationDone() const
|
||||
{
|
||||
ListForeachPtr(Source *, s, GetSourceList())
|
||||
if (!s->IsDone())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool Player::GetAutomationActive() const
|
||||
{ return active; }
|
||||
void Player::SetAutomationActive(bool _active)
|
||||
{ active = _active; }
|
||||
void Player::DisposeAutomationSources()
|
||||
{ sources.Clear(); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Player::Player()
|
||||
{
|
||||
active = true;
|
||||
weight_table = NULL;
|
||||
}
|
||||
Player::~Player()
|
||||
{
|
||||
DisposeAutomationSources();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
60
include/engine/automation/automation_player_nml.cpp
Normal file
60
include/engine/automation/automation_player_nml.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "automation/automation_player.h"
|
||||
#include "motion/motion.h"
|
||||
#include "memory/memory.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::Automation;
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Player::FromMetaTag(Tag &tag)
|
||||
{
|
||||
if (tag.name != "MotionPlayer")
|
||||
__ERR__(__LOG_E__ << "Could not parse motion player, incorrect root tag (" << tag.name << ").\n", false)
|
||||
|
||||
NMLTagForeach(pt, tag)
|
||||
{
|
||||
if (pt->name == "Motions")
|
||||
{
|
||||
NMLTagForeach(pm, *pt)
|
||||
if (pm->name == "Motion")
|
||||
{
|
||||
if (Motion *m = new Motion)
|
||||
{
|
||||
m->FromMetaTag(*pm);
|
||||
motions.Add(m);
|
||||
}
|
||||
}
|
||||
else __LOG_W__ << "Unknown tag '" << pm->name << "' in <Motions>.\n";
|
||||
}
|
||||
#if 1
|
||||
else if (pt->name == "Flag")
|
||||
;
|
||||
#endif
|
||||
else __LOG_W__ << "Unknown tag '" << pt->name << "' in <MotionPlayer>.\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Tag *Player::AsMetaTag()
|
||||
{
|
||||
Tag *root = new Tag("MotionPlayer");
|
||||
if (!root)
|
||||
__ERR__(__LOG_E__ << "Could not create root tag to serialize.\n", NULL)
|
||||
|
||||
// Dump all motions.
|
||||
if (Tag *mtag = root->AddChild("Motions"))
|
||||
ListForeachPtr(Motion *, m, motions)
|
||||
mtag->AddChild(m->AsMetaTag());
|
||||
|
||||
if (!root->GetChildCount())
|
||||
_safe_delete(root);
|
||||
return root;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
81
include/engine/automation/automation_source.cpp
Normal file
81
include/engine/automation/automation_source.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "automation/automation_source.h"
|
||||
|
||||
using namespace GS::Automation;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Source::Dispose(float blend)
|
||||
{
|
||||
if (!dispose)
|
||||
{
|
||||
SetWeight(0, blend);
|
||||
dispose = true;
|
||||
}
|
||||
}
|
||||
bool Source::CanDispose() const
|
||||
{ return asbool(dispose && (weight == 0)); }
|
||||
void Source::Update(const GS::Time &dt_time)
|
||||
{
|
||||
const Time scaled_dt(dt_time * time_scale);
|
||||
|
||||
// Update weight.
|
||||
if (weight_step)
|
||||
{
|
||||
weight += weight_step * dt_time.toSec();
|
||||
|
||||
if (weight_step > 0)
|
||||
{
|
||||
if (weight > weight_target)
|
||||
{
|
||||
weight = weight_target;
|
||||
weight_step = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (weight < weight_target)
|
||||
{
|
||||
weight = weight_target;
|
||||
weight_step = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
time += scaled_dt;
|
||||
}
|
||||
void Source::SetWeight(float _weight, float _blend)
|
||||
{
|
||||
if (_weight < 0)
|
||||
_weight = 0;
|
||||
if (_blend < 0)
|
||||
_blend = 0;
|
||||
|
||||
if (_blend)
|
||||
{
|
||||
weight_target = _weight;
|
||||
weight_step = (_weight - weight) / _blend;
|
||||
}
|
||||
else
|
||||
{
|
||||
weight = _weight;
|
||||
weight_target = _weight;
|
||||
weight_step = 0;
|
||||
}
|
||||
}
|
||||
Source::Source()
|
||||
{
|
||||
weight = 0;
|
||||
weight_target = 1;
|
||||
weight_step = 0;
|
||||
|
||||
time_scale = 1;
|
||||
|
||||
dispose = false;
|
||||
}
|
||||
Source::~Source() {}
|
||||
//------------------------------------------------------------------------------
|
||||
41
include/engine/automation/automation_source_group.cpp
Normal file
41
include/engine/automation/automation_source_group.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "automation/automation_source_group.h"
|
||||
|
||||
using namespace GS::Automation;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void SourceGroup::Dispose(float blend)
|
||||
{ ListForeachPtr(Source *, s, source_list)
|
||||
s->Dispose(blend); }
|
||||
void SourceGroup::SetTime(const GS::Time &t)
|
||||
{ ListForeachPtr(Source *, s, source_list)
|
||||
s->time = t; }
|
||||
void SourceGroup::SetTimeScale(float scale)
|
||||
{ ListForeachPtr(Source *, s, source_list)
|
||||
s->time_scale = scale; }
|
||||
void SourceGroup::SetWeight(float weight, float blend)
|
||||
{ ListForeachPtr(Source *, s, source_list)
|
||||
s->SetWeight(weight, blend); }
|
||||
void SourceGroup::SetRelative(bool relative)
|
||||
{ ListForeachPtr(Source *, s, source_list)
|
||||
s->relative = relative; }
|
||||
void SourceGroup::SetLoop(const GS::Time &start, const GS::Time &end)
|
||||
{ ListForeachPtr(Source *, s, source_list)
|
||||
s->SetLoop(start, end); }
|
||||
void SourceGroup::SetLoopMode(GS::Curve::LoopMode mode)
|
||||
{ ListForeachPtr(Source *, s, source_list)
|
||||
s->SetLoopMode(mode); }
|
||||
bool SourceGroup::IsDone() const
|
||||
{
|
||||
ListForeachPtr(Source *, s, source_list)
|
||||
if (!s->IsDone())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
173
include/engine/core/ace.cpp
Normal file
173
include/engine/core/ace.cpp
Normal file
@ -0,0 +1,173 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/ace.h"
|
||||
#include "ascii/parser.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::ACE;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Manager::UpdateACEUnit(Unit *u, float dt)
|
||||
{
|
||||
if (!u->pc)
|
||||
u->is_done = true;
|
||||
if (u->is_done)
|
||||
return;
|
||||
|
||||
// Execute sequence until next exec opcode.
|
||||
bool has_exec_left = false, looped = false;
|
||||
|
||||
List <Command> ::Item *cpc;
|
||||
for (cpc = u->pc; cpc; cpc = cpc->Next())
|
||||
{
|
||||
Command *cmd = &cpc->Object();
|
||||
if (cmd->code == AceCommandExec)
|
||||
{
|
||||
cpc = cpc->Next();
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd->duration_left > 0.00001)
|
||||
{
|
||||
float exec_dt = cmd->duration_left;
|
||||
if (dt < cmd->duration_left)
|
||||
{
|
||||
exec_dt = dt;
|
||||
has_exec_left = true;
|
||||
}
|
||||
|
||||
u->ExecCommand(cmd, exec_dt);
|
||||
cmd->duration_left -= exec_dt;
|
||||
|
||||
if (cmd->code == AceCommandNext)
|
||||
looped = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Sequence done.
|
||||
if (looped)
|
||||
{
|
||||
// We need to reset the looping block commands duration.
|
||||
for (List <Command> ::Item *lpc = u->loop_pc; lpc != cpc; lpc = lpc->Next())
|
||||
lpc->Object().duration_left = lpc->Object().duration;
|
||||
}
|
||||
else
|
||||
if (!has_exec_left)
|
||||
u->pc = cpc;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static bool DefineNameCompare(const Define *o, const GS::String &name) { return o->id == name; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int Manager::LoadACECommandList(const char *s, Unit *u) const
|
||||
{
|
||||
if (!s)
|
||||
__ERR__(__LOG_E__ << "Cannot load a null command list.\n", -1);
|
||||
|
||||
u->ResetCommandList();
|
||||
|
||||
using namespace AsciiParser;
|
||||
|
||||
// Parse command list.
|
||||
const char *e = s + String::strlen(s);
|
||||
s = SkipSpace(s, e);
|
||||
|
||||
while (s < e)
|
||||
{
|
||||
Command &cc = u->list.Add(Command())->Object();
|
||||
|
||||
// Read in code.
|
||||
String command(s, SkipEntry(s, e));
|
||||
|
||||
Define *d = ArrayListFindEx(define_list, DefineNameCompare, command);
|
||||
if (!d)
|
||||
__ERR__(__LOG_E__ << "Undefined ACE command '" << command << "'.\n", -1);
|
||||
cc.code = d->code;
|
||||
|
||||
// Read in duration.
|
||||
s = NextEntry(s, e);
|
||||
if (s == e)
|
||||
__ERR__(__LOG_E__ << "No duration for ACE command '" << command << "'.\n", -1);
|
||||
|
||||
cc.duration = String(s, SkipEntry(s, e)).Float();
|
||||
if (cc.duration < 0.001f)
|
||||
cc.duration = 0.001f; // Force minimum duration to 1ms.
|
||||
cc.duration_left = cc.duration;
|
||||
|
||||
// Read up to 3 parameters.
|
||||
s = NextEntry(s, e);
|
||||
uint nparm = 0;
|
||||
|
||||
while (s < e)
|
||||
{
|
||||
if ((s[0] == '+') || (s[0] == ';'))
|
||||
break;
|
||||
|
||||
if (nparm == 3)
|
||||
__ERR__(__LOG_E__ << "ACE compiled with " << max_command_param << " command parameter(s) maximum.\n", -1);
|
||||
if (s[0] != ',')
|
||||
__ERR__(__LOG_E__ << "Expected ',' in ACE list declaration.\n", -1);
|
||||
s = SkipSpace(s + 1, e);
|
||||
if (s == e)
|
||||
__ERR__(__LOG_E__ << "Expected ACE command parameter following ','.\n", -1);
|
||||
|
||||
String parm(s, SkipEntry(s, e, true));
|
||||
cc.parm[nparm++] = parm.Float();
|
||||
s = SkipSpace(SkipEntry(s, e, true), e);
|
||||
}
|
||||
if (s == e)
|
||||
__ERR__(__LOG_E__ << "Mangled ACE command list.\n", -1);
|
||||
if ((uint)d->nparm != nparm)
|
||||
__ERR__(__LOG_E__ << "ACE command '" << command << "' takes " << d->nparm << " parameter(s), found " << nparm << ".\n", -1);
|
||||
|
||||
// Output exec command.
|
||||
if (s[0] == ';')
|
||||
u->list.Add(Command())->Object().code = AceCommandExec;
|
||||
|
||||
s = SkipSpace(s + 1, e); // Skip comma.
|
||||
}
|
||||
|
||||
// Set PC to list root command.
|
||||
u->is_done = false;
|
||||
u->pc = u->list.GetRoot();
|
||||
return u->list.GetCount();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Manager::DefineACECommand(const char *command, int code, int nparm, bool user_code)
|
||||
{
|
||||
if (user_code && (code < 0))
|
||||
__ERR__(__LOG_E__ << "Negative command codes are reserved.\n", false)
|
||||
|
||||
Define *d = new Define;
|
||||
if (!d)
|
||||
__ERR__(__LOG_E__ << "Failed to allocate new container to define ACE command '" << command << "'.\n", false)
|
||||
|
||||
d->id = command;
|
||||
d->code = code;
|
||||
d->nparm = nparm;
|
||||
|
||||
define_list.Add(d);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Manager::Manager()
|
||||
{
|
||||
DefineACECommand("nop", AceCommandNop, 0, false);
|
||||
DefineACECommand("loop", AceCommandLoop, 0, false);
|
||||
DefineACECommand("next", AceCommandNext, 0, false);
|
||||
}
|
||||
Manager::~Manager()
|
||||
{
|
||||
ArrayListDeleteAllPtr(Define *, define_list)
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
63
include/engine/core/ace_unit.cpp
Normal file
63
include/engine/core/ace_unit.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/ace.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::ACE;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Unit::ResetCommandList()
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
loop_pc = NULL;
|
||||
pc = NULL;
|
||||
|
||||
is_done = true;
|
||||
}
|
||||
void Unit::DumpCommandList()
|
||||
{
|
||||
for (uint n = 0; n < list.GetCount(); ++n)
|
||||
{
|
||||
Command &cmd = list.ObjectAt(n);
|
||||
__LOG__ << "Command [" << cmd.code << "] : " << cmd.duration << "s, parm = {" << cmd.parm[0] << ", " << cmd.parm[1] << ", " << cmd.parm[2] << "}.\n";
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Unit::ExecCommand(Command *cmd, float dt)
|
||||
{
|
||||
if (!cmd || !dt)
|
||||
return false;
|
||||
|
||||
switch (cmd->code)
|
||||
{
|
||||
case AceCommandNop:
|
||||
break;
|
||||
case AceCommandExec:
|
||||
__LOG_W__ << "ACE unit is executing exec opcode (?!).\n";
|
||||
break;
|
||||
|
||||
case AceCommandLoop:
|
||||
cmd->SetDone();
|
||||
loop_pc = pc;
|
||||
break;
|
||||
case AceCommandNext:
|
||||
cmd->SetDone();
|
||||
pc = loop_pc;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Unit::Unit()
|
||||
{
|
||||
ResetCommandList();
|
||||
}
|
||||
94
include/engine/core/cached_graphic_resource_factory.cpp
Normal file
94
include/engine/core/cached_graphic_resource_factory.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/cached_graphic_resource_factory.h"
|
||||
#include "core/resource_geometry_generator.h"
|
||||
#include "picture/pict_io.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static bool DoLoadResource(const char *name, Geometry &g)
|
||||
{
|
||||
if (GeometryGenerator::IsGenerated(name))
|
||||
return GeometryGenerator::Generate(name, g);
|
||||
return NML::LoadFromFile(g, name);
|
||||
}
|
||||
static bool DoLoadResource(const char *name, Picture &p)
|
||||
{ return PictureIO::Get().Load(p, name); }
|
||||
template <class T> bool DoLoadResource(const char *name, T &t)
|
||||
{ return NML::LoadFromFile(t, name); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T> T *LoadResourceCommonSeq(const char *name, SharedList <T *> &list)
|
||||
{
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
ListForeachPtr(T *, t, list)
|
||||
if (t->name == name)
|
||||
return t;
|
||||
|
||||
AutoPtr <T> t(new T);
|
||||
if (t.IsNull())
|
||||
return NULL;
|
||||
|
||||
t->name = name;
|
||||
if (!DoLoadResource(name, *t))
|
||||
return NULL;
|
||||
|
||||
list.Add(t);
|
||||
|
||||
return t.Detach();
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Picture *CachedResourceFactory::LoadPicture(const char *name)
|
||||
{ return LoadResourceCommonSeq(name, pictures); }
|
||||
Geometry *CachedResourceFactory::LoadGeometry(const char *name)
|
||||
{ return LoadResourceCommonSeq(name, geometries); }
|
||||
Material *CachedResourceFactory::LoadMaterial(const char *name)
|
||||
{ return LoadResourceCommonSeq(name, materials); }
|
||||
Shader *CachedResourceFactory::LoadShader(const char *name)
|
||||
{ return LoadResourceCommonSeq(name, shaders); }
|
||||
ParticleModel *CachedResourceFactory::LoadParticleModel(const char *name)
|
||||
{ return LoadResourceCommonSeq(name, particle_models); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint CachedResourceFactory::GetCachedResourceCount()
|
||||
{
|
||||
uint count = 0;
|
||||
count += geometries.GetCount();
|
||||
count += materials.GetCount();
|
||||
count += shaders.GetCount();
|
||||
count += particle_models.GetCount();
|
||||
count += pictures.GetCount();
|
||||
return count;
|
||||
}
|
||||
uint CachedResourceFactory::PurgeCache()
|
||||
{
|
||||
uint purged = 0;
|
||||
forever
|
||||
{
|
||||
uint pass_purged = 0;
|
||||
purged += PurgeSharedList(geometries);
|
||||
purged += PurgeSharedList(materials);
|
||||
purged += PurgeSharedList(shaders);
|
||||
purged += PurgeSharedList(particle_models);
|
||||
purged += PurgeSharedList(pictures);
|
||||
if (pass_purged == 0)
|
||||
break;
|
||||
|
||||
purged += pass_purged;
|
||||
}
|
||||
return purged;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
30
include/engine/core/cached_mixer_resource_factory.cpp
Normal file
30
include/engine/core/cached_mixer_resource_factory.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/cached_mixer_resource_factory.h"
|
||||
#include "core/mixer.h"
|
||||
|
||||
using namespace GS::Audio;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Sound *CachedResourceFactory::LoadSound(const char *name)
|
||||
{
|
||||
ListForeachPtr(Sound *, s, sounds)
|
||||
if (s->name == name)
|
||||
return s;
|
||||
|
||||
Sound *s = new Sound(mixer);
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
s->name = name;
|
||||
s->mixer_data = mixer.LoadSound(name);
|
||||
|
||||
sounds.Add(s);
|
||||
return s;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
116
include/engine/core/cached_renderer_resource_factory.cpp
Normal file
116
include/engine/core/cached_renderer_resource_factory.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/cached_renderer_resource_factory.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Render;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T> static bool ResourceNameCompare(const T o, const String &name) { return String::Compare(o->name, name) == 0; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T> T CheckCachedResourceCommonSeq(const char *uri, const SharedList <T> *cache)
|
||||
{
|
||||
if (!cache)
|
||||
return NULL;
|
||||
|
||||
String name(uri);
|
||||
name.FileCleanName();
|
||||
|
||||
return ListFindEx(*cache, ResourceNameCompare <T>, name);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Geometry *CachedRendererResourceFactory::LoadGeometry(const char *name, bool bypass_cache, Geometry *g)
|
||||
{
|
||||
if (!bypass_cache && !g)
|
||||
if (Geometry *r = CheckCachedResourceCommonSeq <Geometry *> (name, &geometries))
|
||||
return r;
|
||||
Geometry *r = RendererResourceFactory::LoadGeometry(name, bypass_cache, g);
|
||||
if (!bypass_cache && !g)
|
||||
if (r)
|
||||
geometries.Add(r);
|
||||
return r;
|
||||
}
|
||||
Material *CachedRendererResourceFactory::LoadMaterial(const char *name, bool bypass_cache, Material *m)
|
||||
{
|
||||
if (!bypass_cache && !m)
|
||||
if (Material *r = CheckCachedResourceCommonSeq <Material *> (name, &materials))
|
||||
return r;
|
||||
Material *r = RendererResourceFactory::LoadMaterial(name, bypass_cache, m);
|
||||
if (!bypass_cache && !m)
|
||||
if (r)
|
||||
materials.Add(r);
|
||||
return r;
|
||||
}
|
||||
Texture *CachedRendererResourceFactory::LoadTexture(const char *name, bool bypass_cache, Texture *t)
|
||||
{
|
||||
if (!bypass_cache && !t)
|
||||
if (Texture *r = CheckCachedResourceCommonSeq <Texture *> (name, &textures))
|
||||
return r;
|
||||
Texture *r = RendererResourceFactory::LoadTexture(name, bypass_cache, t);
|
||||
if (!bypass_cache && !t)
|
||||
if (r)
|
||||
textures.Add(r);
|
||||
return r;
|
||||
}
|
||||
Shader *CachedRendererResourceFactory::LoadShader(const char *name, bool bypass_cache, Shader *s)
|
||||
{
|
||||
if (!bypass_cache && !s)
|
||||
if (Shader *r = CheckCachedResourceCommonSeq <Shader *> (name, &shaders))
|
||||
return r;
|
||||
Shader *r = RendererResourceFactory::LoadShader(name, bypass_cache, s);
|
||||
if (!bypass_cache && !s)
|
||||
if (r)
|
||||
shaders.Add(r);
|
||||
return r;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CachedRendererResourceFactory::ListCachedResources()
|
||||
{
|
||||
ListForeachPtr(Geometry *, g, geometries)
|
||||
__LOG_V__ << "- " << g->name << " (refcount=" << g->GetRefCount() << ")\n";
|
||||
ListForeachPtr(Material *, m, materials)
|
||||
__LOG_V__ << "- " << m->name << " (refcount=" << m->GetRefCount() << ")\n";
|
||||
ListForeachPtr(Texture *, t, textures)
|
||||
__LOG_V__ << "- " << t->name << " (refcount=" << t->GetRefCount() << ")\n";
|
||||
ListForeachPtr(Shader *, s, shaders)
|
||||
__LOG_V__ << "- " << s->name << " (refcount=" << s->GetRefCount() << ")\n";
|
||||
}
|
||||
uint CachedRendererResourceFactory::GetCachedResourceCount()
|
||||
{
|
||||
uint count = 0;
|
||||
count += geometries.GetCount();
|
||||
count += materials.GetCount();
|
||||
count += textures.GetCount();
|
||||
count += shaders.GetCount();
|
||||
return count;
|
||||
}
|
||||
uint CachedRendererResourceFactory::PurgeCache()
|
||||
{
|
||||
uint purged = 0;
|
||||
forever
|
||||
{
|
||||
uint pass_purged = 0;
|
||||
pass_purged += PurgeSharedList(geometries);
|
||||
pass_purged += PurgeSharedList(materials);
|
||||
pass_purged += PurgeSharedList(textures);
|
||||
pass_purged += PurgeSharedList(shaders);
|
||||
if (pass_purged == 0)
|
||||
break;
|
||||
|
||||
purged += pass_purged;
|
||||
}
|
||||
return purged;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
224
include/engine/core/camera.cpp
Normal file
224
include/engine/core/camera.cpp
Normal file
@ -0,0 +1,224 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/camera.h"
|
||||
#include "core/light.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Camera::AlignTo(const Light &l)
|
||||
{
|
||||
aspect_ratio = 1;
|
||||
SnapshotTransformation(l.GetMatrix());
|
||||
|
||||
SetNearClippingPlane(l.GetNearClippingPlane());
|
||||
SetFarClippingPlane(l.GetFarClippingPlane());
|
||||
|
||||
float fov = l.cone_angle + l.edge_angle;
|
||||
SetZoomFactor(Math::Cos(fov) / Math::Sin(fov));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Camera::WorldToScreen(const fRect &v, const Vector4 &in, Vector4 &out, bool normalize)
|
||||
{
|
||||
Vector4 dp = in * GetInverseMatrix();
|
||||
if (dp.z <= 0)
|
||||
return false;
|
||||
|
||||
dp.x /= dp.z / zoom_factor;
|
||||
dp.y /= dp.z / zoom_factor;
|
||||
|
||||
if (normalize)
|
||||
{
|
||||
if (aspect_ratio_ref_yaxis)
|
||||
dp.x /= v.GetWidth() / v.GetHeight();
|
||||
else dp.y /= v.GetWidth() / v.GetHeight();
|
||||
}
|
||||
|
||||
out.Set((1 + dp.x) * 0.5f, (1 - dp.y) * 0.5f, 0);
|
||||
return true;
|
||||
}
|
||||
Vector4 Camera::ScreenToWorld(const fRect &v, float x, float y, float z, float ar)
|
||||
{
|
||||
Vector4 sw((x - 0.5f) * 2.f, -(y - 0.5f) * 2.f, zoom_factor);
|
||||
sw *= z / sw.z;
|
||||
|
||||
if (ar < 0)
|
||||
{
|
||||
if (aspect_ratio_ref_yaxis)
|
||||
sw.x *= v.GetWidth() / v.GetHeight();
|
||||
else sw.y *= v.GetWidth() / v.GetHeight();
|
||||
}
|
||||
else
|
||||
sw.x *= ar;
|
||||
|
||||
return sw * GetMatrix();
|
||||
}
|
||||
Vector4 Camera::ComputeAspectRatioCorrection(const fRect &v, float global_ar) const
|
||||
{
|
||||
float k_ar = aspect_ratio;
|
||||
|
||||
if (k_ar <= 0.f) // Square AR.
|
||||
k_ar = v.GetHeight() / v.GetWidth();
|
||||
k_ar *= global_ar;
|
||||
|
||||
if (aspect_ratio_ref_yaxis)
|
||||
return Vector4(k_ar, 1, 1);
|
||||
return Vector4(1, 1.f / k_ar, 1);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Camera::ComputeProjectionMatrix(const fRect &v, Matrix4 &m) const
|
||||
{
|
||||
#if __PLATFORM_NINTENDO_WII__
|
||||
const float za = z_far;
|
||||
#else
|
||||
const float za = z_near;
|
||||
#endif
|
||||
|
||||
if (is_occulus_camera)
|
||||
{
|
||||
m = custom_projection;
|
||||
/* // create scale and offset
|
||||
float projXScale = 2.0f / (tan_left + tan_right);
|
||||
float projXOffset = (tan_left - tan_right) * projXScale * 0.5f;
|
||||
float projYScale = 2.0f / (tan_up + tan_down);
|
||||
float projYOffset = (tan_up - tan_down) * projYScale * 0.5f;
|
||||
|
||||
//result.Scale = GS::Vector2(projXScale, projYScale);
|
||||
//result.Offset = GS::Vector2(projXOffset, projYOffset);
|
||||
// Hey - why is that Y.Offset negated?
|
||||
// It's because a projection matrix transforms from world coords with Y=up,
|
||||
// whereas this is from NDC which is Y=down.
|
||||
|
||||
m.Set
|
||||
(
|
||||
projXScale, 0, 0, 0,
|
||||
0, projYScale, 0, 0,
|
||||
0, 0, z_far / (z_far - z_near), 1,
|
||||
projXOffset, -projYOffset, -(z_far * z_near) / (z_far - z_near), 0
|
||||
);
|
||||
/*
|
||||
float idx = 1.0f / (tan_right - tan_left);
|
||||
float idy = 1.0f / (tan_down - tan_up);
|
||||
float idz = 1.0f / (z_far - z_near);
|
||||
float sx = tan_right + tan_left;
|
||||
float sy = tan_down + tan_up;
|
||||
|
||||
m.Set
|
||||
(
|
||||
2 * idx, 0, 0, 0,
|
||||
0, 2 * idy, 0, 0,
|
||||
0, 0, z_far / (z_far - z_near), 1,
|
||||
sx*idx, -sy*idy, -(z_far * z_near) / (z_far - z_near), 0
|
||||
);
|
||||
*/
|
||||
/*
|
||||
float(*p)[4] = pmProj->m;
|
||||
p[0][0] = 2 * idx; p[0][1] = 0; p[0][2] = sx*idx; p[0][3] = 0;
|
||||
p[1][0] = 0; p[1][1] = 2 * idy; p[1][2] = sy*idy; p[1][3] = 0;
|
||||
p[2][0] = 0; p[2][1] = 0; p[2][2] = -zFar*idz; p[2][3] = -zFar*zNear*idz;
|
||||
p[3][0] = 0; p[3][1] = 0; p[3][2] = -1.0f; p[3][3] = 0;
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_orthographic)
|
||||
{
|
||||
const float q = 1.f / (z_far - z_near);
|
||||
|
||||
m.Set
|
||||
(
|
||||
2.f / ortho_w, 0, 0, 0,
|
||||
0, 2.f / ortho_h, 0, 0,
|
||||
0, 0, q, 0,
|
||||
0, 0, -q * za, 1
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
m.Set
|
||||
(
|
||||
zoom_factor, 0, 0, 0,
|
||||
0, zoom_factor, 0, 0,
|
||||
0, 0, z_far / (z_far - z_near), 1,
|
||||
0, 0, -(z_far * z_near) / (z_far - z_near), 0
|
||||
);
|
||||
}
|
||||
|
||||
Vector4 ar = ComputeAspectRatioCorrection(v);
|
||||
m.m[0][0] *= ar.x;
|
||||
m.m[1][1] *= ar.y;
|
||||
}
|
||||
}
|
||||
void Camera::ComputeFrustum(Frustum &f, const fRect &viewport, float zn, float zf) const
|
||||
{
|
||||
Vector4 k_ar = ComputeAspectRatioCorrection(viewport);
|
||||
|
||||
if (is_orthographic)
|
||||
f.SetOrthographic(ortho_w, ortho_h, zn != -1 ? zn : z_near, zf != -1 ? zf : z_far, &GetMatrix(), k_ar.x, k_ar.y);
|
||||
else f.SetPerspective(GetFov(), zn != -1 ? zn : z_near, zf != -1 ? zf : z_far, &GetMatrix(), k_ar.x, k_ar.y);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Camera::ComputeMatrix()
|
||||
{
|
||||
bool update_world = item_flags.IsSet(ItemFlagWorldMatrixDirty);
|
||||
|
||||
Item::ComputeMatrix();
|
||||
|
||||
// Remove scale from world matrix.
|
||||
if (update_world)
|
||||
{
|
||||
Vector4 u = matrix.GetRow(0).Normalized();
|
||||
matrix.m[0][0] = u.x; matrix.m[1][0] = u.y; matrix.m[2][0] = u.z;
|
||||
Vector4 v = matrix.GetRow(1).Normalized();
|
||||
matrix.m[0][1] = v.x; matrix.m[1][1] = v.y; matrix.m[2][1] = v.z;
|
||||
Vector4 w = matrix.GetRow(2).Normalized();
|
||||
matrix.m[0][2] = w.x; matrix.m[1][2] = w.y; matrix.m[2][2] = w.z;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
float Camera::GetFov() const
|
||||
{ return Math::ATan(1.f / zoom_factor) * 2.f; }
|
||||
void Camera::SetZoomFactor(float z)
|
||||
{ zoom_factor = z; /*Types::Max(0.1f, z);*/ }
|
||||
void Camera::SetFov(float fov)
|
||||
{
|
||||
fov = Types::Clamp(fov, Units::Deg(0.5f), Units::Deg(179.95f));
|
||||
SetZoomFactor(1.f / Math::Tan(fov * 0.5f));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Camera::Camera()
|
||||
{
|
||||
using namespace Units;
|
||||
|
||||
SetZoomFactor(3.2f);
|
||||
|
||||
is_orthographic = false;
|
||||
ortho_w = Mtr(1.f);
|
||||
ortho_h = Mtr(1.f);
|
||||
|
||||
z_near = Cm(10.f);
|
||||
z_far = Km(50.f);
|
||||
|
||||
aspect_ratio = -1.f;
|
||||
aspect_ratio_ref_yaxis = true;
|
||||
|
||||
is_occulus_camera = false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
90
include/engine/core/camera_nml.cpp
Normal file
90
include/engine/core/camera_nml.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/camera.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Camera::FromMetaTag(Tag &tag)
|
||||
{
|
||||
if (tag.name != "Camera")
|
||||
__ERR__(__LOG_E__ << "Could not parse light, incorrect root tag (" << tag.name << ").\n", false)
|
||||
|
||||
registry.DeleteKey("PostProcess:Dof;");
|
||||
|
||||
// Parse root tags.
|
||||
NMLTagForeach(pt, tag)
|
||||
{
|
||||
if (pt->name == "Item")
|
||||
Item::FromMetaTag(*pt);
|
||||
|
||||
else if (pt->name == "ZNear")
|
||||
z_near = Types::Max(pt->GetReal(), 0.2f);
|
||||
else if (pt->name == "ZFar")
|
||||
z_far = pt->GetReal();
|
||||
else if (pt->name == "ZoomFactor")
|
||||
zoom_factor = pt->GetReal();
|
||||
|
||||
else if (pt->name == "Orthographic")
|
||||
is_orthographic = true;
|
||||
else if (pt->name == "OrthographicWidth")
|
||||
ortho_w = pt->GetReal();
|
||||
else if (pt->name == "OrthographicHeight")
|
||||
ortho_h = pt->GetReal();
|
||||
|
||||
// Legacy support.
|
||||
#if 1
|
||||
else if (pt->name == "FStop")
|
||||
registry.CreateKey("PostProcess:Dof:FStop", pt->GetReal());
|
||||
else if (pt->name == "FocalDistance")
|
||||
registry.CreateKey("PostProcess:Dof:FDist", pt->GetReal());
|
||||
|
||||
else if (pt->name == "ViewportOrigin");
|
||||
else if (pt->name == "ViewportSize");
|
||||
#endif
|
||||
|
||||
else if (pt->name == "AspectRatioRefYAxis")
|
||||
aspect_ratio_ref_yaxis = pt->GetBool();
|
||||
|
||||
else __LOG_W__ << "Unknown tag '" << pt->name << "' in <Camera>.\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tag *Camera::AsMetaTag()
|
||||
{
|
||||
Tag *root = new Tag("Camera");
|
||||
if (!root)
|
||||
__ERR__(__LOG_E__ << "Could not serialize camera. Failed to create root tag.\n", NULL)
|
||||
|
||||
// Store item.
|
||||
root->AddChild(Item::AsMetaTag());
|
||||
|
||||
// Misc.
|
||||
if (z_near != Units::Cm(10))
|
||||
root->AddChild("ZNear", z_near);
|
||||
if (z_far != Units::Km(100))
|
||||
root->AddChild("ZFar", z_far);
|
||||
if (zoom_factor != 3.2f)
|
||||
root->AddChild("ZoomFactor", zoom_factor);
|
||||
|
||||
if (is_orthographic)
|
||||
root->AddChild("Orthographic");
|
||||
if (ortho_w != Units::Mtr(1.f))
|
||||
root->AddChild("OrthographicWidth", ortho_w);
|
||||
if (ortho_h != Units::Mtr(1.f))
|
||||
root->AddChild("OrthographicHeight", ortho_h);
|
||||
|
||||
root->AddChild("AspectRatioRefYAxis", aspect_ratio_ref_yaxis);
|
||||
return root;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
92
include/engine/core/clock.cpp
Normal file
92
include/engine/core/clock.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/clock.h"
|
||||
#include "platform.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
float Clock::Getf() const
|
||||
{ return (float)gtick / Platform::Get().GetClockFrequency(); }
|
||||
float Clock::GetDeltaf() const
|
||||
{ return (float)dt_clock / Platform::Get().GetClockFrequency(); }
|
||||
void Clock::SetScalef(float k)
|
||||
{ SetScale((int)(k * 1000.f)); }
|
||||
float Clock::GetScalef() const
|
||||
{ return (float)GetScale() / 1000.f; }
|
||||
void Clock::SetFixedDeltaFramef(float dt)
|
||||
{ fixed_delta = (dt >= 0.0) ? int(dt * Platform::Get().GetClockFrequency()) : -1; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Clock::Pause(bool _pause)
|
||||
{ pause = _pause; }
|
||||
void Clock::Reset()
|
||||
{
|
||||
_gtick = Platform::Get().GetClock(); // CU
|
||||
dt_clock_filter.Reset();
|
||||
}
|
||||
void Clock::Update()
|
||||
{
|
||||
// Clock can run up to 49 days before looping.
|
||||
if (pause)
|
||||
{
|
||||
_gtick = 0;
|
||||
dt_clock = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int tmp = Platform::Get().GetClock(); // CU
|
||||
int raw_dt = tmp - _gtick; // CU
|
||||
int dt = (raw_dt * clock_scale) / 1000; // CU
|
||||
|
||||
// Fixed delta clock.
|
||||
if (fixed_delta > 0)
|
||||
dt = (fixed_delta * clock_scale) / 1000; // CU
|
||||
|
||||
// Update tick.
|
||||
if (_gtick != 0)
|
||||
{
|
||||
gtick += dt;
|
||||
dt_clock = dt;
|
||||
}
|
||||
else
|
||||
dt_clock = 0;
|
||||
|
||||
_gtick = tmp;
|
||||
if (dt_clock < 0)
|
||||
dt_clock = 0;
|
||||
|
||||
// Filter dt_clock.
|
||||
dt_clock_filter.LogValue(dt_clock);
|
||||
int filtered_dt_clock = dt_clock_filter.GetMedian();
|
||||
|
||||
dt_error += dt_clock - filtered_dt_clock;
|
||||
int dt_error_correction = dt_error >> 2;
|
||||
dt_error -= dt_error_correction;
|
||||
|
||||
dt_clock = filtered_dt_clock + dt_error_correction;
|
||||
}
|
||||
}
|
||||
void Clock::EatDeltaClock()
|
||||
{ _gtick = Platform::Get().GetClock(); }
|
||||
|
||||
Clock::Clock()
|
||||
{
|
||||
pause = false;
|
||||
|
||||
_gtick = 0;
|
||||
gtick = 0;
|
||||
|
||||
dt_clock = 0;
|
||||
dt_error = 0;
|
||||
clock_scale = 1000;
|
||||
|
||||
fixed_delta = -1;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
143
include/engine/core/core_profiler.cpp
Normal file
143
include/engine/core/core_profiler.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/core_profiler.h"
|
||||
#include "core/raster_font.h"
|
||||
#include "core/renderer.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
#if __ENABLE_ALLOCATION_STAT__
|
||||
static Time time_last;
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void Core::DrawAllocProfilerText(Render::Renderer &render, Render::RasterFont *font[2], float &x, float &y, float w, float h)
|
||||
{
|
||||
#if __ENABLE_ALLOCATION_STAT__
|
||||
|
||||
Time time = Platform::Get().GetTime();
|
||||
Time time_elapsed = Types::Max(nTime(1), time - Platform::Get().GetStartTime());
|
||||
|
||||
// Update averages.
|
||||
Time time_delta = time - time_last;
|
||||
|
||||
if (time_delta.toSec() > 1)
|
||||
{
|
||||
for (uint n = 0; n < nIAlloc::SystemCount; ++n)
|
||||
{
|
||||
nIAlloc::Stat &stat = nIAlloc::system_stat[n];
|
||||
stat.alloc_avg = stat.alloc_count / time_delta.toSec();
|
||||
stat.alloc_count = 0;
|
||||
}
|
||||
time_last = time;
|
||||
}
|
||||
|
||||
//
|
||||
nColor title_color(1.f, 0.9f, 0);
|
||||
nRenderer::WriterConfig config(false);
|
||||
|
||||
render.Write(*font[1], "Allocation System\n\n", x, y, config, 1, &title_color);
|
||||
float col, _col;
|
||||
|
||||
// Output legend.
|
||||
#define MOVE_COL(EXP) { EXP; _col = col; }
|
||||
MOVE_COL(col = x)
|
||||
|
||||
render.Write(*font[0], "System", _col, y, config);
|
||||
MOVE_COL(col += 120)
|
||||
|
||||
render.Write(*font[0], "| Commit", _col, y, config);
|
||||
MOVE_COL(col += 90)
|
||||
render.Write(*font[0], "| Commit Peak", _col, y, config);
|
||||
MOVE_COL(col += 90)
|
||||
|
||||
render.Write(*font[0], "| Alive", _col, y, config);
|
||||
MOVE_COL(col += 90)
|
||||
render.Write(*font[0], "| Alive Peak", _col, y, config);
|
||||
|
||||
MOVE_COL(col += 90)
|
||||
render.Write(*font[0], "| Pressure (a/s)", _col, y, config);
|
||||
|
||||
y += 16 + 8;
|
||||
|
||||
// Output per system stats.
|
||||
String current_group;
|
||||
|
||||
for (uint n = 0; n < nIAlloc::SystemCount; ++n)
|
||||
{
|
||||
// Output allocation group on change.
|
||||
if (current_group != nIAlloc::system_desc[n].group)
|
||||
{
|
||||
// y += 4;
|
||||
// MOVE_COL(col = x)
|
||||
// render.Write(*font[0], nIAlloc::system_desc[n].group, _col, y, config, 1, &title_color);
|
||||
// y += 16 + 8;
|
||||
current_group = nIAlloc::system_desc[n].group;
|
||||
}
|
||||
|
||||
// Output statistics.
|
||||
nIAlloc::Stat &stat = nIAlloc::system_stat[n];
|
||||
MOVE_COL(col = x)
|
||||
|
||||
render.Write(*font[0], nIAlloc::system_desc[n].name, _col, y, config);
|
||||
MOVE_COL(col += 120)
|
||||
|
||||
render.Write(*font[0], String::Format("| %s", FormatNumber((float)stat.size, MemorySize).c_str()), _col, y, config);
|
||||
MOVE_COL(col += 90)
|
||||
render.Write(*font[0], String::Format("| %s", FormatNumber((float)stat.size_peak, MemorySize).c_str()), _col, y, config);
|
||||
MOVE_COL(col += 90)
|
||||
|
||||
render.Write(*font[0], String::Format("| %d", stat.alive_count), _col, y, config);
|
||||
MOVE_COL(col += 90)
|
||||
render.Write(*font[0], String::Format("| %d", stat.alive_count_peak), _col, y, config);
|
||||
|
||||
MOVE_COL(col += 90)
|
||||
render.Write(*font[0], String::Format("| %d", stat.alloc_avg), _col, y, config);
|
||||
|
||||
y += 16;
|
||||
}
|
||||
|
||||
// Totals.
|
||||
size_t total_size = 0,
|
||||
total_size_peak = 0;
|
||||
uint total_count = 0,
|
||||
total_count_peak = 0,
|
||||
total_pressure = 0;
|
||||
|
||||
for (uint n = 0; n < nIAlloc::SystemCount; ++n)
|
||||
{
|
||||
nIAlloc::Stat &stat = nIAlloc::system_stat[n];
|
||||
|
||||
total_size += stat.size;
|
||||
total_size_peak += stat.size_peak;
|
||||
total_count += stat.alive_count;
|
||||
total_count_peak += stat.alive_count_peak;
|
||||
total_pressure += stat.alloc_avg;
|
||||
}
|
||||
|
||||
y += 8;
|
||||
|
||||
MOVE_COL(col = x)
|
||||
|
||||
render.Write(*font[0], "Total", _col, y, config);
|
||||
MOVE_COL(col += 120)
|
||||
|
||||
render.Write(*font[0], String::Format("| %s", FormatNumber((float)total_size, MemorySize).c_str()), _col, y, config);
|
||||
MOVE_COL(col += 90)
|
||||
render.Write(*font[0], String::Format("| %s", FormatNumber((float)total_size_peak, MemorySize).c_str()), _col, y, config);
|
||||
MOVE_COL(col += 90)
|
||||
|
||||
render.Write(*font[0], String::Format("| %d", total_count), _col, y, config);
|
||||
MOVE_COL(col += 90)
|
||||
render.Write(*font[0], String::Format("| %d", total_count_peak), _col, y, config);
|
||||
|
||||
MOVE_COL(col += 90)
|
||||
render.Write(*font[0], String::Format("| %d", total_pressure), _col, y, config);
|
||||
|
||||
#endif
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
71
include/engine/core/embedded_resource_extractor.cpp
Normal file
71
include/engine/core/embedded_resource_extractor.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/embedded_resource_extractor.h"
|
||||
#include "metafile/nml.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "filesystem/io_memory.h"
|
||||
#include "platform.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static bool FormatExtractionPath(String &out, const char *context, const char *suffix, bool to_ram)
|
||||
{
|
||||
__LOG_V__ << "Formatting extraction path for a resource embedded in '" << context << "'.\n";
|
||||
if (!context)
|
||||
__ERR__(__LOG_W__ << "Cannot extract embedded resource, context is empty.\n", false)
|
||||
|
||||
String extract_out = String(context).CutFileExtension();
|
||||
|
||||
if (to_ram)
|
||||
{
|
||||
extract_out = Platform::Get().io->StripRootPath(extract_out);
|
||||
|
||||
if (extract_out.IsAbsolutePath())
|
||||
__ERR__(__LOG_W__ << "Extraction context represents an absolute path, cannot extract to ramdisk.\n", false)
|
||||
else
|
||||
{
|
||||
if (!extract_out.StartsWith("@embedded"))
|
||||
extract_out = String("@embedded/") + extract_out;
|
||||
}
|
||||
}
|
||||
|
||||
out = extract_out + suffix;
|
||||
return true;
|
||||
}
|
||||
static bool SaveExtractedResource(const char *out, NML::Tag &t)
|
||||
{
|
||||
if (Platform::Get().io->Exists(out))
|
||||
return true; // Do not export resource more than once.
|
||||
|
||||
__LOG_H__ << "Saving extracted resource to '" << out << "'.\n";
|
||||
|
||||
NML::File file;
|
||||
file.AddRoot(&t);
|
||||
|
||||
bool r = NML::Parser::Save(out, file);
|
||||
file.UnlinkRoot(&t); // Unlink root tag so that the meta file destructor does not free it.
|
||||
|
||||
return r;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool EmbeddedResourceExtractor::ExtractEmbeddedMaterial(String &out, NML::Tag &t, const char *context, int slot)
|
||||
{ return FormatExtractionPath(out, context, String::Format("-material-%d.nmm", slot), extract_to_ram) && SaveExtractedResource(out, t); }
|
||||
bool EmbeddedResourceExtractor::ExtractEmbeddedShaderTree(String &out, NML::Tag &t, const char *context)
|
||||
{ return FormatExtractionPath(out, context, "-shader-tree.nsa", extract_to_ram) && SaveExtractedResource(out, t); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
EmbeddedResourceExtractor::EmbeddedResourceExtractor(bool extract_to_ramdisk)
|
||||
{
|
||||
if ((extract_to_ram = extract_to_ramdisk) == true)
|
||||
Platform::Get().io->Mount(new IO::Memory, "@embedded/");
|
||||
}
|
||||
26
include/engine/core/embedded_resource_handler_interface.cpp
Normal file
26
include/engine/core/embedded_resource_handler_interface.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/embedded_resource_handler_interface.h"
|
||||
#include "memory/nauto_ptr.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
static GS::AutoPtr <IEmbeddedResourceHandler> embedded_resource_handler;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
IEmbeddedResourceHandler *IEmbeddedResourceHandler::Get()
|
||||
{
|
||||
if (embedded_resource_handler.IsNull())
|
||||
embedded_resource_handler = new IEmbeddedResourceHandler;
|
||||
return embedded_resource_handler.c_ptr();
|
||||
}
|
||||
void IEmbeddedResourceHandler::Set(IEmbeddedResourceHandler *h)
|
||||
{
|
||||
embedded_resource_handler = h;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
275
include/engine/core/emitter.cpp
Normal file
275
include/engine/core/emitter.cpp
Normal file
@ -0,0 +1,275 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/emitter.h"
|
||||
#include "core/camera.h"
|
||||
#include "core/resource_factories.h"
|
||||
#include "core/graphic_resource_factory.h"
|
||||
#include "core/render_resource_factory.h"
|
||||
#include "timing/benchmark.h"
|
||||
#include "rand/rand.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ParticleModel::ParticleModel()
|
||||
{
|
||||
gravity.Set(0, 0, 0);
|
||||
|
||||
SetColor(Color(1, 1, 1, 1));
|
||||
// size_curve.SetDefaultValue(1);
|
||||
|
||||
time_to_live.setSec(4.f);
|
||||
damping = 1.f;
|
||||
}
|
||||
void ParticleModel::SetColor(const Color &color)
|
||||
{
|
||||
// red_curve.SetDefaultValue(color.x);
|
||||
// green_curve.SetDefaultValue(color.y);
|
||||
// blue_curve.SetDefaultValue(color.z);
|
||||
// opacity_curve.SetDefaultValue(color.w);
|
||||
}
|
||||
void ParticleModel::AddColorPoint(const Time &t, const Color &color)
|
||||
{
|
||||
red_curve.Insert(CurvePoint(t, color.x));
|
||||
green_curve.Insert(CurvePoint(t, color.y));
|
||||
blue_curve.Insert(CurvePoint(t, color.z));
|
||||
opacity_curve.Insert(CurvePoint(t, color.w));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void ParticleModel::RenderSetup(ResourceFactories *f)
|
||||
{
|
||||
if ((render_data = new RenderData) != NULL)
|
||||
if (f && f->render)
|
||||
render_data->material = f->render->LoadMaterial(material);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Emitter::ComputeMinMax(MinMax &minmax) const
|
||||
{
|
||||
minmax.mn = minmax.mx = GetMatrix().GetRow(3);
|
||||
if (!GetParticleCount())
|
||||
return;
|
||||
|
||||
for (uint n = 0; n < GetParticleCount(); ++n)
|
||||
{
|
||||
Particle *p = GetParticle(n);
|
||||
minmax.Grow(MinMax(p->position - Vector4(p->size, p->size, p->size), p->position + Vector4(p->size, p->size, p->size)));
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Emitter::GetRenderablePrimitiveList(const Camera &view, const Camera &default_view, Stack <Render::Primitive *> &list, Renderable::Context context, bool cull)
|
||||
{
|
||||
if (cull)
|
||||
{
|
||||
MinMax minmax;
|
||||
ComputeMinMax(minmax);
|
||||
|
||||
if (view.frustum.ClassifyMinMax(minmax) == Frustum::Outside)
|
||||
return 1;
|
||||
}
|
||||
|
||||
is_seen = true;
|
||||
|
||||
const Matrix4 &vm = view.GetMatrix();
|
||||
list.Push(new Render::Primitive(this, this, vm.GetRow(2).Dot(GetMatrix().GetRow(3) - vm.GetRow(3))));
|
||||
|
||||
return 1;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Emitter::Sort(const Matrix4 &view)
|
||||
{
|
||||
if (!particle_pool || !is_seen)
|
||||
return;
|
||||
|
||||
Vector4 view_front = view.GetRow(2).Normalized(),
|
||||
view_pos = view.GetRow(3);
|
||||
|
||||
alive_count = 0;
|
||||
for (uint n = 0; n < particle_pool.GetCount(); ++n)
|
||||
if (particle_pool[n].IsAlive())
|
||||
{
|
||||
sort_array[alive_count].v = (particle_pool[n].position - view_pos).Dot(view_front);
|
||||
sort_array[alive_count].o = n;
|
||||
alive_count++;
|
||||
}
|
||||
|
||||
GS::Sort <float, uint> ::QuickSort(alive_count, sort_array);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Emitter::Update(const Time &dt)
|
||||
{
|
||||
if (!particle_pool || !is_seen)
|
||||
return;
|
||||
|
||||
// Spawn particles.
|
||||
uint n = 0;
|
||||
for (birth_time += dt * birth_rate * birth_rate_scale; birth_time.toSec() > 1; birth_time -= Time::fromSec(1))
|
||||
{
|
||||
// Seek next free particle.
|
||||
for ( ; n < particle_pool.GetCount(); ++n)
|
||||
if (particle_pool[n].time.toSec() < 0)
|
||||
break;
|
||||
|
||||
// Emitter pool exhausted.
|
||||
if (n == particle_pool.GetCount())
|
||||
break;
|
||||
|
||||
// Spawn particle.
|
||||
Particle &p = particle_pool[n];
|
||||
|
||||
p.time = birth_time / birth_rate;
|
||||
p.angle = 0;
|
||||
p.size = 0;
|
||||
p.color.Set(0, 0, 0, 0);
|
||||
|
||||
ModelParticle(p, *this, time);
|
||||
}
|
||||
|
||||
// Update running particles and drop dead ones.
|
||||
if (render_data.IsValid())
|
||||
if (ParticleModel *model = render_data->particle_model)
|
||||
{
|
||||
float damping = Math::Pow(model->damping, dt.toSec());
|
||||
|
||||
for (n = 0; n < particle_pool.GetCount(); ++n)
|
||||
{
|
||||
Particle &p = particle_pool[n];
|
||||
if (!p.IsAlive())
|
||||
continue;
|
||||
|
||||
// Kill particle.
|
||||
p.time += dt;
|
||||
|
||||
if (p.time >= model->time_to_live)
|
||||
p.time.setSec(-1);
|
||||
|
||||
else
|
||||
{
|
||||
float k_dt = dt.toSec();
|
||||
|
||||
p.position += p.velocity * k_dt;
|
||||
p.velocity += render_data->particle_model->gravity * k_dt;
|
||||
p.velocity *= damping;
|
||||
|
||||
model->size_curve.Evaluate(p.time, &p.size);
|
||||
model->angle_curve.Evaluate(p.time, &p.angle);
|
||||
|
||||
model->red_curve.Evaluate(p.time, &p.color.x);
|
||||
model->green_curve.Evaluate(p.time, &p.color.y);
|
||||
model->blue_curve.Evaluate(p.time, &p.color.z);
|
||||
model->opacity_curve.Evaluate(p.time, &p.color.w);
|
||||
|
||||
p.size *= p.size_scale;
|
||||
p.color.w *= p.opacity_scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is_seen = false;
|
||||
time += dt;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Emitter::RenderSetup(ResourceFactories *f)
|
||||
{
|
||||
if ((render_data = new RenderData) != NULL)
|
||||
if (f && f->graphic)
|
||||
if ((render_data->particle_model = f->graphic->LoadParticleModel(particle_model)) != NULL)
|
||||
render_data->particle_model->RenderSetup(f);
|
||||
}
|
||||
bool Emitter::Setup()
|
||||
{
|
||||
alive_count = 0;
|
||||
if (!particle_pool.Allocate(pool_size) || !sort_array.Allocate(pool_size))
|
||||
__ERR__(__LOG_E__ << "Failed to allocate emitter particle pool (size " << pool_size << ").\n", false)
|
||||
|
||||
for (uint n = 0; n < pool_size; ++n)
|
||||
particle_pool[n].time.setSec(-1);
|
||||
|
||||
time.setSec(0);
|
||||
birth_time.setSec(0);
|
||||
|
||||
is_seen = true;
|
||||
return true;
|
||||
}
|
||||
void Emitter::Free()
|
||||
{
|
||||
particle_pool.Free();
|
||||
sort_array.Free();
|
||||
alive_count = 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Emitter::SetSprayModel(float angle)
|
||||
{
|
||||
model = Model_Spray;
|
||||
spray_angle = angle;
|
||||
}
|
||||
void Emitter::ModelParticle(Particle &p, const Item &i, const Time &/*emitter_time*/)
|
||||
{
|
||||
Vector4 ip = i.GetMatrix().GetRow(3);
|
||||
|
||||
switch (model)
|
||||
{
|
||||
default:
|
||||
p.position = ip;
|
||||
p.velocity.Set(0, 0, 0);
|
||||
break;
|
||||
|
||||
case Model_Spray:
|
||||
{
|
||||
p.position = ip;
|
||||
|
||||
using namespace Random;
|
||||
|
||||
float a = (FRand(2.f) - 1.f) * spray_angle;
|
||||
Vector4 d(Math::Sin(a), 0, Math::Cos(a));
|
||||
Matrix3::RotationMatrixZAxis(FRand(Units::Deg(360.f))).Apply(&p.velocity, &d);
|
||||
p.velocity = (p.velocity.Normalized() * FRRand(birth_speed_min, birth_speed_max)) * birth_speed_scale * Matrix3::FromMatrix4(i.GetMatrix());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
p.size_scale = birth_size_scale;
|
||||
p.opacity_scale = birth_opacity_scale;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Emitter::Emitter()
|
||||
{
|
||||
is_seen = true;
|
||||
|
||||
birth_speed_min = 1;
|
||||
birth_speed_max = 1.5;
|
||||
pool_size = 500;
|
||||
birth_rate = (float)pool_size / Units::Sec(5.f);
|
||||
|
||||
birth_rate_scale = 1;
|
||||
birth_opacity_scale = 1;
|
||||
birth_speed_scale = 1;
|
||||
birth_size_scale = 1;
|
||||
|
||||
SetSprayModel(Units::Deg(45.f));
|
||||
|
||||
alive_count = 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
154
include/engine/core/emitter_nml.cpp
Normal file
154
include/engine/core/emitter_nml.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/emitter.h"
|
||||
#include "core/embedded_resource_handler_interface.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool ParticleModel::FromMetaTag(Tag &tag)
|
||||
{
|
||||
if (tag.name != "ParticleModel")
|
||||
__ERR__(__LOG_E__ << "Could not parse particle model, incorrect root tag (" << tag.name << ").\n", false)
|
||||
|
||||
NMLTagForeach(pt, tag)
|
||||
{
|
||||
if (pt->name == "Material")
|
||||
IEmbeddedResourceHandler::Get()->ExtractEmbeddedMaterial(material, *pt, name, 0);
|
||||
else if (pt->name == "MaterialRef")
|
||||
material = pt->GetString();
|
||||
|
||||
else if (pt->name == "TTL")
|
||||
time_to_live.setSec(pt->GetReal());
|
||||
else if (pt->name == "Damping")
|
||||
damping = pt->GetReal();
|
||||
else if (pt->name == "Gravity")
|
||||
gravity.FromMetaTag(*pt);
|
||||
|
||||
else if (pt->name == "AngleCurve" && pt->GetTag("Curve"))
|
||||
angle_curve.FromMetaTag(*pt->GetTag("Curve"));
|
||||
else if (pt->name == "SizeCurve" && pt->GetTag("Curve"))
|
||||
size_curve.FromMetaTag(*pt->GetTag("Curve"));
|
||||
|
||||
else if (pt->name == "RedCurve" && pt->GetTag("Curve"))
|
||||
red_curve.FromMetaTag(*pt->GetTag("Curve"));
|
||||
else if (pt->name == "GreenCurve" && pt->GetTag("Curve"))
|
||||
green_curve.FromMetaTag(*pt->GetTag("Curve"));
|
||||
else if (pt->name == "BlueCurve" && pt->GetTag("Curve"))
|
||||
blue_curve.FromMetaTag(*pt->GetTag("Curve"));
|
||||
else if (pt->name == "OpacityCurve" && pt->GetTag("Curve"))
|
||||
opacity_curve.FromMetaTag(*pt->GetTag("Curve"));
|
||||
|
||||
else __LOG_W__ << "Unknown tag '" << pt->name << "' in <ParticleModel>.\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Tag *ParticleModel::AsMetaTag() const
|
||||
{
|
||||
Tag *root = new Tag("ParticleModel");
|
||||
if (!root)
|
||||
__ERR__(__LOG_E__ << "Could not serialize particle model. Failed to create root tag.\n", NULL)
|
||||
|
||||
root->AddChild("MaterialRef", material.c_str());
|
||||
root->AddChild("TTL", time_to_live.toSec());
|
||||
root->AddChild("Damping", damping);
|
||||
root->AddChild(gravity.AsMetaTag("Gravity"));
|
||||
|
||||
Tag *ct;
|
||||
ct = root->AddChild("AngleCurve"); ct->AddChild(angle_curve.AsMetaTag());
|
||||
ct = root->AddChild("SizeCurve"); ct->AddChild(size_curve.AsMetaTag());
|
||||
|
||||
ct = root->AddChild("RedCurve"); ct->AddChild(red_curve.AsMetaTag());
|
||||
ct = root->AddChild("GreenCurve"); ct->AddChild(green_curve.AsMetaTag());
|
||||
ct = root->AddChild("BlueCurve"); ct->AddChild(blue_curve.AsMetaTag());
|
||||
ct = root->AddChild("OpacityCurve"); ct->AddChild(opacity_curve.AsMetaTag());
|
||||
return root;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Emitter::FromMetaTag(Tag &tag)
|
||||
{
|
||||
if (tag.name != "Emitter")
|
||||
__ERR__(__LOG_E__ << "Could not parse emitter, incorrect root tag (" << tag.name << ").\n", false)
|
||||
|
||||
birth_speed_min = birth_speed_max = 1;
|
||||
pool_size = 100;
|
||||
birth_rate = 1;
|
||||
|
||||
model = Model_Spray;
|
||||
spray_angle = Units::Deg(45.f);
|
||||
|
||||
particle_model.Clear();
|
||||
|
||||
NMLTagForeach(pt, tag)
|
||||
{
|
||||
if (pt->name == "Item")
|
||||
Item::FromMetaTag(*pt);
|
||||
|
||||
else if (pt->name == "SpeedMin")
|
||||
birth_speed_min = pt->GetReal();
|
||||
else if (pt->name == "SpeedMax")
|
||||
birth_speed_max = pt->GetReal();
|
||||
|
||||
else if (pt->name == "PoolSize")
|
||||
pool_size = pt->GetInteger();
|
||||
else if (pt->name == "BirthRate")
|
||||
birth_rate = pt->GetReal();
|
||||
|
||||
else if (pt->name == "Type")
|
||||
{
|
||||
String _type(pt->GetString());
|
||||
|
||||
if (_type == "Spray")
|
||||
model = Model_Spray;
|
||||
}
|
||||
|
||||
else if (pt->name == "SprayAngle")
|
||||
spray_angle = pt->GetReal();
|
||||
|
||||
else if (pt->name == "ParticleModel")
|
||||
particle_model = pt->GetString();
|
||||
|
||||
else __LOG_W__ << "Unknown tag '" << pt->name << "' in <Emitter>.\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Tag *Emitter::AsMetaTag() const
|
||||
{
|
||||
Tag *root = new Tag("Emitter");
|
||||
if (!root)
|
||||
__ERR__(__LOG_E__ << "Could not serialize emitter. Failed to create root tag.\n", NULL)
|
||||
|
||||
// Store item.
|
||||
root->AddChild(Item::AsMetaTag());
|
||||
|
||||
if (birth_speed_min != 1)
|
||||
root->AddChild("SpeedMin", birth_speed_min);
|
||||
if (birth_speed_max != 1)
|
||||
root->AddChild("SpeedMax", birth_speed_max);
|
||||
if (birth_rate != 1)
|
||||
root->AddChild("BirthRate", birth_rate);
|
||||
if (pool_size != 100)
|
||||
root->AddChild("PoolSize", (int)pool_size);
|
||||
|
||||
// switch (emitter_type)
|
||||
// {
|
||||
// case Type_Spray: root->AddChild("Type", "Spray");
|
||||
// }
|
||||
|
||||
root->AddChild("SprayAngle", spray_angle);
|
||||
|
||||
if (!particle_model.IsEmpty())
|
||||
root->AddChild("ParticleModel", particle_model.c_str());
|
||||
|
||||
return root;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
449
include/engine/core/geometry.cpp
Normal file
449
include/engine/core/geometry.cpp
Normal file
@ -0,0 +1,449 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/geometry.h"
|
||||
#include "math/matrix4.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
static const float HomogeneousDistance = Units::Mm(0.01f);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Core::ComputeVertexArrayMinMax(const Array <Vector4> &vtx, MinMax &mm, const Matrix4 *mtx)
|
||||
{
|
||||
if (!vtx.GetCount())
|
||||
return false;
|
||||
|
||||
Vector4 tvt = mtx ? vtx[0] * mtx[0] : vtx[0], mn = tvt, mx = tvt;
|
||||
|
||||
if (mtx)
|
||||
for (uint n = 0; n < vtx.GetCount(); ++n)
|
||||
{
|
||||
tvt = vtx[n] * mtx[0];
|
||||
if (tvt.x > mx.x) mx.x = tvt.x;
|
||||
if (tvt.y > mx.y) mx.y = tvt.y;
|
||||
if (tvt.z > mx.z) mx.z = tvt.z;
|
||||
if (tvt.x < mn.x) mn.x = tvt.x;
|
||||
if (tvt.y < mn.y) mn.y = tvt.y;
|
||||
if (tvt.z < mn.z) mn.z = tvt.z;
|
||||
}
|
||||
else
|
||||
for (uint n = 0; n < vtx.GetCount(); ++n)
|
||||
{
|
||||
tvt = vtx[n];
|
||||
if (tvt.x > mx.x) mx.x = tvt.x;
|
||||
if (tvt.y > mx.y) mx.y = tvt.y;
|
||||
if (tvt.z > mx.z) mx.z = tvt.z;
|
||||
if (tvt.x < mn.x) mn.x = tvt.x;
|
||||
if (tvt.y < mn.y) mn.y = tvt.y;
|
||||
if (tvt.z < mn.z) mn.z = tvt.z;
|
||||
}
|
||||
|
||||
mm.mn = mn;
|
||||
mm.mx = mx;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Geometry::GetUVCount() const
|
||||
{
|
||||
uint c = 0;
|
||||
for (uint n = 0; n < __UV_PER_GEOMETRY__; ++n)
|
||||
if (uv[n])
|
||||
++c;
|
||||
return c;
|
||||
}
|
||||
uint Geometry::GetBoneCount() const
|
||||
{ return bone_name.GetCount(); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Geometry::ComputeBoneBoundingVolumes(Array <MinMax> &bone_mm) const
|
||||
{
|
||||
uint bone_count = bone_bind_matrix.GetCount();
|
||||
|
||||
if (skin.IsNull() || !bone_count)
|
||||
return false;
|
||||
|
||||
if (!bone_mm.Allocate(bone_count))
|
||||
__ERR__(__LOG_E__ << "Failed to allocate bon minmax array.\n", false)
|
||||
|
||||
Array <bool> bone_mm_init(bone_count);
|
||||
for (uint n = 0; n < bone_count; ++n)
|
||||
bone_mm_init[n] = false;
|
||||
|
||||
for (uint v = 0; v < vtx.GetCount(); ++v)
|
||||
for (int b = 0; b < __PV_BONE_LIMIT__; ++b)
|
||||
if (skin[v].w[b] > 0.f)
|
||||
{
|
||||
int idx = skin[v].bone_index[b];
|
||||
|
||||
if (!bone_mm_init[idx])
|
||||
{
|
||||
bone_mm[idx].Set(vtx[v], vtx[v]);
|
||||
bone_mm_init[idx] = true;
|
||||
}
|
||||
else
|
||||
bone_mm[idx].Grow(vtx[v]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Geometry::ComputePolygonBindingCount() const
|
||||
{
|
||||
uint c = 0;
|
||||
for (uint n = 0; n < pol.GetCount(); ++n)
|
||||
c += pol[n].vtx_count;
|
||||
return c;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
MinMax Geometry::ComputeMinMax(const Matrix4 *mtx) const
|
||||
{
|
||||
MinMax mm;
|
||||
ComputeVertexArrayMinMax(vtx, mm, mtx);
|
||||
return mm;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Geometry::AllocateVertex(uint count)
|
||||
{ return vtx.Allocate(count); }
|
||||
bool Geometry::AllocatePolygon(uint count)
|
||||
{
|
||||
binding.Free();
|
||||
return pol.Allocate(count);
|
||||
}
|
||||
bool Geometry::AllocatePolygonBinding()
|
||||
{
|
||||
uint count = ComputePolygonBindingCount();
|
||||
|
||||
if (!binding.Allocate(count))
|
||||
return false;
|
||||
|
||||
count = 0;
|
||||
for (uint n = 0; n < pol.GetCount(); ++n)
|
||||
{
|
||||
pol[n].binding = &binding[count];
|
||||
count += pol[n].vtx_count;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool Geometry::AllocateBone(uint count)
|
||||
{ return bone_name.Allocate(count) && bone_bind_matrix.Allocate(count); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Geometry::ComputePolygonIndex(Array <uint> &i) const
|
||||
{
|
||||
if (i.Allocate(pol.GetCount()))
|
||||
for (uint pc = 0, ci = 0; pc < pol.GetCount(); ++pc)
|
||||
{
|
||||
i[pc] = ci;
|
||||
ci += pol[pc].vtx_count;
|
||||
}
|
||||
}
|
||||
void Geometry::ComputeVertexToPolygon(Array <VertexToPolygon> &vtx_to_pol) const
|
||||
{
|
||||
if (!pol || !vtx)
|
||||
return;
|
||||
|
||||
Array <uint> pol_per_vtx(vtx.GetCount());
|
||||
if (!pol_per_vtx)
|
||||
return;
|
||||
|
||||
Memory::Set(&pol_per_vtx[0], 0, pol_per_vtx.GetSize());
|
||||
|
||||
uint p, v;
|
||||
for (p = 0; p < pol.GetCount(); ++p)
|
||||
for (v = 0; v < pol[p].vtx_count; ++v)
|
||||
pol_per_vtx[pol[p].binding[v]]++;
|
||||
|
||||
vtx_to_pol.Allocate(vtx.GetCount());
|
||||
for (v = 0; v < vtx.GetCount(); ++v)
|
||||
{
|
||||
vtx_to_pol[v].pol_count = 0;
|
||||
vtx_to_pol[v].pol_index.Allocate(pol_per_vtx[v]);
|
||||
}
|
||||
|
||||
for (p = 0; p < pol.GetCount(); ++p)
|
||||
for (v = 0; v < pol[p].vtx_count; ++v)
|
||||
vtx_to_pol[pol[p].binding[v]].pol_index[vtx_to_pol[pol[p].binding[v]].pol_count++] = p;
|
||||
}
|
||||
void Geometry::ComputeVertexToVertex(Array <VertexToVertex> &vtx_to_vtx, const Array <VertexToPolygon> *vtx_to_pol) const
|
||||
{
|
||||
if (!pol || !vtx)
|
||||
return;
|
||||
|
||||
// Allocate vertex to vertex buffer.
|
||||
if (!vtx_to_vtx.Allocate(vtx.GetCount()))
|
||||
__ERRRAW__(__LOG_E__ << "Could not allocate memory.\n")
|
||||
|
||||
// Allocate work area.
|
||||
#define __VertexToVertexTempListSize 1024
|
||||
PolygonVertex tmp_vtx_to_vtx[__VertexToVertexTempListSize];
|
||||
|
||||
// Compute vertex to polygon if not provided.
|
||||
Array <VertexToPolygon> _vtx_to_pol;
|
||||
if (!vtx_to_pol)
|
||||
{
|
||||
vtx_to_pol = &_vtx_to_pol;
|
||||
ComputeVertexToPolygon(_vtx_to_pol);
|
||||
}
|
||||
|
||||
for (int pass = 0; pass < 2; ++pass)
|
||||
for (uint v = 0; v < vtx.GetCount(); ++v)
|
||||
{
|
||||
vtx_to_vtx[v].vtx_count = 0;
|
||||
|
||||
uint vtx_vtx_count = 0;
|
||||
for (uint p = 0; p < (*vtx_to_pol)[v].pol_count; ++p)
|
||||
{
|
||||
uint pol_index = (*vtx_to_pol)[v].pol_index[p];
|
||||
Polygon *poly = &pol[pol_index];
|
||||
|
||||
int ci;
|
||||
for (ci = 0; ci < poly->vtx_count; ++ci)
|
||||
if (poly->binding[ci] == v)
|
||||
break;
|
||||
|
||||
for (int _c = (ci - 1); _c <= (ci + 1); _c += 2)
|
||||
{
|
||||
int vtx_index = _c;
|
||||
if (vtx_index < 0)
|
||||
vtx_index += poly->vtx_count;
|
||||
if (vtx_index >= poly->vtx_count)
|
||||
vtx_index -= poly->vtx_count;
|
||||
|
||||
// Invalidate already registered candidate.
|
||||
bool insert = true;
|
||||
for (uint nl = 0; nl < vtx_vtx_count; ++nl)
|
||||
if ((tmp_vtx_to_vtx[nl].pol_index == pol_index) && (tmp_vtx_to_vtx[nl].vtx_index == (uint)vtx_index))
|
||||
{
|
||||
insert = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (insert)
|
||||
{
|
||||
tmp_vtx_to_vtx[vtx_vtx_count].pol_index = pol_index;
|
||||
tmp_vtx_to_vtx[vtx_vtx_count].vtx_index = vtx_index;
|
||||
|
||||
if (vtx_vtx_count == __VertexToVertexTempListSize)
|
||||
{
|
||||
__LOG_E__ << "Temporary list exceeded, vertex to vertex LUT corrupted.\n";
|
||||
vtx_vtx_count = __VertexToVertexTempListSize - 1;
|
||||
}
|
||||
|
||||
if (pass == 1)
|
||||
{
|
||||
vtx_to_vtx[v].vtx[vtx_vtx_count].pol_index = pol_index;
|
||||
vtx_to_vtx[v].vtx[vtx_vtx_count].vtx_index = vtx_index;
|
||||
}
|
||||
++vtx_vtx_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate vertex container for this vertex.
|
||||
if (pass == 0)
|
||||
vtx_to_vtx[v].vtx.Allocate(vtx_vtx_count);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Geometry::FlagHomogeneousVertex(Array <bool> &flag, const Array <uint> &pol_index, const Array <VertexToPolygon> &vtx_to_pol, int mat) const
|
||||
{
|
||||
if (!flag.Allocate(vtx.GetCount()))
|
||||
__ERRRAW__(__LOG_E__ << "Could not allocate homogeneous vertex table.\n")
|
||||
for (uint n = 0; n < vtx.GetCount(); ++n)
|
||||
flag[n] = true;
|
||||
|
||||
#if 0
|
||||
// Test for early exit in case no polygon uses this material.
|
||||
for (uint n = 0; n < pol.GetCount(); n++)
|
||||
if (pol[n].material == n)
|
||||
break;
|
||||
if (n == pol.GetCount())
|
||||
return;
|
||||
#endif
|
||||
|
||||
for (uint m = 0; m < vtx.GetCount(); ++m)
|
||||
{
|
||||
uint ngeo = vtx_to_pol[m].pol_count;
|
||||
for (uint ac = 0; ac < ngeo; ++ac)
|
||||
for (uint bc = 0; bc < ngeo; ++bc)
|
||||
{
|
||||
uint i_ac = vtx_to_pol[m].pol_index[ac],
|
||||
i_bc = vtx_to_pol[m].pol_index[bc];
|
||||
Polygon *apoly = &pol[i_ac], *bpoly = &pol[i_bc];
|
||||
|
||||
if (apoly == bpoly)
|
||||
continue;
|
||||
if (apoly->material == bpoly->material)
|
||||
{
|
||||
if ((mat == -1) || (apoly->material == (uint)mat))
|
||||
{
|
||||
uint _u, _v;
|
||||
for (_u = 0; _u < apoly->vtx_count; ++_u)
|
||||
if ( apoly->binding[_u] == m )
|
||||
break;
|
||||
for (_v = 0; _v < bpoly->vtx_count; ++_v)
|
||||
if ( bpoly->binding[_v] == m )
|
||||
break;
|
||||
|
||||
if (vtx_normal)
|
||||
if (Vector4::Dist2(vtx_normal[pol_index[i_ac] + _u], vtx_normal[pol_index[i_bc] + _v]) > HomogeneousDistance)
|
||||
flag[m] = false;
|
||||
/*
|
||||
if (vtx_tangent)
|
||||
if (
|
||||
(nVector::Dist2(vtx_tangent[pol_index[i_ac] + _u].B, vtx_tangent[pol_index[i_bc] + _v].B) > HomogeneousDistance) ||
|
||||
(nVector::Dist2(vtx_tangent[pol_index[i_ac] + _u].T, vtx_tangent[pol_index[i_bc] + _v].T) > HomogeneousDistance)
|
||||
)
|
||||
vtx_homogeneous[m] = false;
|
||||
*/
|
||||
if (rgb)
|
||||
if (Vector4::Dist2(rgb[pol_index[i_ac] + _u], rgb[pol_index[i_bc] + _v]) > HomogeneousDistance)
|
||||
flag[m] = false;
|
||||
|
||||
for (uint cuv = 0; cuv < __UV_PER_GEOMETRY__; ++cuv)
|
||||
if (uv[cuv])
|
||||
if (Vector2::Dist2(uv[cuv][pol_index[i_ac] + _u], uv[cuv][pol_index[i_bc] + _v]) > HomogeneousDistance)
|
||||
flag[m] = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
flag[m] = false;
|
||||
|
||||
if (!flag[m])
|
||||
goto nxth;
|
||||
}
|
||||
nxth:;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Geometry::MergeDuplicateMaterials()
|
||||
{
|
||||
if (material_table.GetCount() < 2)
|
||||
return 0;
|
||||
|
||||
__LOG_H__ << "Merging materials in geometry '" << name << "'...\n";
|
||||
|
||||
// Build the drop table.
|
||||
uint old_slot_count = material_table.GetCount();
|
||||
Array <bool> drop(material_table.GetCount());
|
||||
for (uint n = 0; n < material_table.GetCount(); ++n)
|
||||
drop[n] = false;
|
||||
|
||||
Array <uint> material_remap(material_table.GetCount());
|
||||
for (uint n = 0; n < material_table.GetCount(); ++n)
|
||||
material_remap[n] = n;
|
||||
|
||||
// Flag materials to drop.
|
||||
for (uint n = 0; n < material_table.GetCount(); ++n)
|
||||
{
|
||||
if (drop[n]) // Already dropped.
|
||||
continue;
|
||||
|
||||
for (uint m = n + 1; m < material_table.GetCount(); ++m)
|
||||
{
|
||||
// Check by material name.
|
||||
if (material_table[n].name != material_table[m].name)
|
||||
goto skip_material_drop;
|
||||
|
||||
// Drop material.
|
||||
drop[m] = true;
|
||||
material_remap[m] = n;
|
||||
|
||||
skip_material_drop:;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the new material array.
|
||||
uint new_material_slot_count = 0;
|
||||
for (uint n = 0; n < material_table.GetCount(); ++n)
|
||||
if (!drop[n])
|
||||
new_material_slot_count++;
|
||||
|
||||
Array <Geometry::MaterialSlot> new_material_slot(new_material_slot_count);
|
||||
new_material_slot_count = 0;
|
||||
for (uint n = 0; n < material_table.GetCount(); ++n)
|
||||
if (!drop[n])
|
||||
{
|
||||
for (uint m = 0; m < material_table.GetCount(); ++m)
|
||||
if (material_remap[m] == n)
|
||||
material_remap[m] = new_material_slot_count;
|
||||
|
||||
new_material_slot[new_material_slot_count].name = material_table[n].name;
|
||||
new_material_slot[new_material_slot_count].use_cache = material_table[n].use_cache;
|
||||
++new_material_slot_count;
|
||||
}
|
||||
|
||||
material_table.Transfer(new_material_slot);
|
||||
|
||||
// Remap polygon references.
|
||||
for (uint n = 0; n < pol.GetCount(); ++n)
|
||||
pol[n].material = (ushort)material_remap[pol[n].material];
|
||||
|
||||
uint merge_count = old_slot_count - material_table.GetCount();
|
||||
|
||||
__LOG__ << "Done, merged " << merge_count << " material slot(s).\n";
|
||||
return merge_count;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Geometry::Free()
|
||||
{
|
||||
bone_name.Free();
|
||||
skin.Free();
|
||||
bone_bind_matrix.Free();
|
||||
|
||||
vtx.Free();
|
||||
vtx_normal.Free();
|
||||
vtx_tangent.Free();
|
||||
pol_normal.Free();
|
||||
pol_tangent.Free();
|
||||
pol.Free();
|
||||
binding.Free();
|
||||
|
||||
for (uint n = 0; n < __UV_PER_GEOMETRY__; n++)
|
||||
uv[n].Free();
|
||||
rgb.Free();
|
||||
|
||||
material_table.Free();
|
||||
|
||||
lod_proxy = NULL;
|
||||
lod_distance = Units::Mtr(100.f);
|
||||
|
||||
shadow_proxy = NULL;
|
||||
|
||||
flag.Raise(FlagNullShadowProxy | FlagNullLodProxy, false);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Geometry::Geometry()
|
||||
{
|
||||
lod_distance = Units::Mtr(100.f);
|
||||
}
|
||||
Geometry::~Geometry()
|
||||
{
|
||||
Free();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
256
include/engine/core/geometry_bih.cpp
Normal file
256
include/engine/core/geometry_bih.cpp
Normal file
@ -0,0 +1,256 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/geometry_bih.h"
|
||||
#include "core/graphic_resource_factory.h"
|
||||
#include "metafile/nml_object.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool GeometryBIH::FastPolyTest(uint ip, Vector4 &s, Vector4 &d, float t_max)
|
||||
{
|
||||
Polygon &pol = geometry->pol[ip];
|
||||
Vector4 &pn = geometry->pol_normal[ip];
|
||||
|
||||
float dnv = d.Dot(pn);
|
||||
if (dnv >= 0.f)
|
||||
return false; // backface
|
||||
|
||||
float t = (geometry->vtx[pol.binding[0]].Dot(pn) - s.Dot(pn)) / dnv;
|
||||
if ((t < 0) || ((t_max > 0) && (t > t_max)))
|
||||
return false; // Behind origin or beyond ray length.
|
||||
|
||||
// Make sure point is in polygon.
|
||||
GeometryBIHAccel &ca = acc[ip];
|
||||
|
||||
// Note that the intersection point if offset toward the polygon origin.
|
||||
float pu = s[ca.cu] + d[ca.cu] * t - geometry->vtx[pol.binding[0]][ca.cu];
|
||||
float pv = s[ca.cv] + d[ca.cv] * t - geometry->vtx[pol.binding[0]][ca.cv];
|
||||
|
||||
const float ray_epsilon = -0.000001f;
|
||||
|
||||
float *k = ca.k;
|
||||
for (int m = 1; m < (pol.vtx_count - 1); ++m)
|
||||
{
|
||||
float u = pv * k[0] + pu * k[1], v, w;
|
||||
if (u < ray_epsilon)
|
||||
goto next;
|
||||
v = pu * k[2] + pv * k[3];
|
||||
if (v < ray_epsilon)
|
||||
goto next;
|
||||
w = 1 - u - v;
|
||||
if (w < ray_epsilon)
|
||||
goto next;
|
||||
|
||||
return true;
|
||||
next:;
|
||||
k += 4;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void GeometryBIH::TraceLeaf(BIH::Node *leaf, float tmin, float tmax, BIH::Trace &, void *parm)
|
||||
{
|
||||
GeometryTrace *trace = (GeometryTrace *)parm;
|
||||
|
||||
uint *leaf_indice = (uint *)leaf->p;
|
||||
Vector4 *vtx = geometry->vtx;
|
||||
|
||||
// Test each polygon in leaf.
|
||||
for (uint n = 0; n < leaf->count; ++n)
|
||||
{
|
||||
uint idx = leaf_indice[n];
|
||||
Vector4 &pn = geometry->pol_normal[idx];
|
||||
Polygon &pol = geometry->pol[idx];
|
||||
Material *material = material_table[pol.material].material;
|
||||
|
||||
trace->tri_test += pol.vtx_count - 2;
|
||||
|
||||
// Reject back facing polygons.
|
||||
bool backface = false;
|
||||
float dnv = trace->d.Dot(pn);
|
||||
|
||||
if (dnv >= 0.f)
|
||||
{
|
||||
if (material->renderword & Material::Render_DoubleSided)
|
||||
backface = true;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
float t = (vtx[pol.binding[0]].Dot(pn) - trace->s.Dot(pn)) / dnv;
|
||||
|
||||
// Reject intersections further away than the current best one.
|
||||
if (trace->has_i && (t >= trace->i_t))
|
||||
continue;
|
||||
// Plane is outside ray boundaries.
|
||||
if ((t < tmin) || (t > tmax))
|
||||
continue;
|
||||
|
||||
// Make sure point is in polygon.
|
||||
GeometryBIHAccel &ca = acc[idx];
|
||||
|
||||
// Note that the intersection point if offset toward the polygon origin.
|
||||
float pu = trace->s[ca.cu] + trace->d[ca.cu] * t - vtx[pol.binding[0]][ca.cu],
|
||||
pv = trace->s[ca.cv] + trace->d[ca.cv] * t - vtx[pol.binding[0]][ca.cv];
|
||||
|
||||
#define RAY_EPSILON -0.000001f
|
||||
|
||||
float *k = ca.k;
|
||||
for (int m = 1; m < (pol.vtx_count - 1); ++m)
|
||||
{
|
||||
float u = pv * k[0] + pu * k[1], v, w;
|
||||
if (u < RAY_EPSILON)
|
||||
goto next;
|
||||
v = pu * k[2] + pv * k[3];
|
||||
if (v < RAY_EPSILON)
|
||||
goto next;
|
||||
w = 1 - u - v;
|
||||
if (w < RAY_EPSILON)
|
||||
goto next;
|
||||
|
||||
trace->has_i = true;
|
||||
trace->i_t = t;
|
||||
|
||||
trace->ip = idx;
|
||||
trace->it = m - 1;
|
||||
trace->bi = pol_index[idx];
|
||||
trace->u = u;
|
||||
trace->v = v;
|
||||
trace->w = w;
|
||||
trace->g = geometry;
|
||||
trace->m = material;
|
||||
trace->st = material_table[pol.material].shader_tree;
|
||||
trace->backface = backface;
|
||||
|
||||
break; // No need to look further.
|
||||
next:;
|
||||
k += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
void GeometryBIH::RaytraceGeometry(GeometryTrace &trace, const Vector4 &s, const Vector4 &d, float l)
|
||||
{
|
||||
trace.ip = -1;
|
||||
trace.tri_test = 0;
|
||||
trace.has_i = false;
|
||||
trace.i_t = -1;
|
||||
trace.s = s;
|
||||
trace.d = d;
|
||||
|
||||
BIH::Trace bih_trace;
|
||||
Raytrace(bih_trace, s, d, l, (void *)&trace);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool GeometryBIH::BuildFromGeometry(ResourceFactory &gf, Geometry *g)
|
||||
{
|
||||
Free();
|
||||
|
||||
// Build geometry LUTs.
|
||||
geometry = g;
|
||||
geometry->ComputePolygonIndex(pol_index);
|
||||
|
||||
// Load geometry resources.
|
||||
material_table.Allocate(geometry->material_table.GetCount());
|
||||
for (uint n = 0; n < material_table.GetCount(); ++n)
|
||||
{
|
||||
sMaterial m(gf.LoadMaterial(geometry->material_table[n].name));
|
||||
|
||||
if (m.IsNull())
|
||||
return false;
|
||||
|
||||
if (!m->shader.IsEmpty())
|
||||
{
|
||||
sShaderTree tree(new ShaderTree);
|
||||
if (NML::LoadFromFile(*tree, m->shader))
|
||||
material_table[n].shader_tree = tree;
|
||||
}
|
||||
material_table[n].material = m;
|
||||
}
|
||||
|
||||
// Build volume array to build tree.
|
||||
Array <MinMax> varray(geometry->pol.GetCount());
|
||||
if (!varray)
|
||||
__ERR__(__LOG_E__ << "Failed to allocate volume objects.\n", false);
|
||||
|
||||
if (!acc.Allocate(geometry->pol.GetCount()))
|
||||
__ERR__(__LOG_E__ << "Failed to allocate point in polygon acceleration array.\n", false);
|
||||
|
||||
geometry->ComputePolygonNormal();
|
||||
|
||||
for (uint n = 0; n < geometry->pol.GetCount(); ++n)
|
||||
{
|
||||
Polygon &pol = geometry->pol[n];
|
||||
if (pol.vtx_count < 3)
|
||||
continue;
|
||||
|
||||
Vector4 *vtx = geometry->vtx;
|
||||
Vector4 u_edge = vtx[pol.binding[1]] - vtx[pol.binding[0]],
|
||||
v_edge = vtx[pol.binding[2]] - vtx[pol.binding[0]];
|
||||
geometry->pol_normal[n] = u_edge.Cross(v_edge).Normalized();
|
||||
|
||||
// Compute plane determinant.
|
||||
acc[n].d = -vtx[pol.binding[0]].Dot(geometry->pol_normal[n]);
|
||||
|
||||
// Determine most significant axis.
|
||||
Vector4 m = (vtx[pol.binding[1]] - vtx[pol.binding[0]]).Cross(vtx[pol.binding[2]] - vtx[pol.binding[0]]);
|
||||
float x = Types::Abs(m.x), y = Types::Abs(m.y), z = Types::Abs(m.z);
|
||||
|
||||
uint axis = 2;
|
||||
if ((x >= y) && (x >= z))
|
||||
axis = 0;
|
||||
else if ((y >= x) && (y >= z))
|
||||
axis = 1;
|
||||
|
||||
char cu = (axis + 1) % 3,
|
||||
cv = (axis + 2) % 3;
|
||||
acc[n].cu = cu; acc[n].cv = cv;
|
||||
|
||||
// Compute point in triangle fixed coefficients.
|
||||
if (acc[n].k.Allocate((pol.vtx_count - 2) * 4))
|
||||
{
|
||||
float *pk = acc[n].k.c_ptr();
|
||||
|
||||
for (int i = 1; i < (pol.vtx_count - 1); ++i)
|
||||
{
|
||||
Vector4 b = vtx[pol.binding[i + 1]] - vtx[pol.binding[0]],
|
||||
c = vtx[pol.binding[i]] - vtx[pol.binding[0]];
|
||||
|
||||
float k = 1.f / (b[cu] * c[cv] - b[cv] * c[cu]);
|
||||
*pk++ = b[cu] * k;
|
||||
*pk++ = -b[cv] * k;
|
||||
*pk++ = c[cv] * k;
|
||||
*pk++ = -c[cu] * k;
|
||||
}
|
||||
|
||||
// Build volume array.
|
||||
varray[n].mn = varray[n].mx = geometry->vtx[pol.binding[0]];
|
||||
for (uint m = 1; m < pol.vtx_count; ++m)
|
||||
{
|
||||
varray[n].mn.x = Types::Min(vtx[pol.binding[m]].x, varray[n].mn.x);
|
||||
varray[n].mn.y = Types::Min(vtx[pol.binding[m]].y, varray[n].mn.y);
|
||||
varray[n].mn.z = Types::Min(vtx[pol.binding[m]].z, varray[n].mn.z);
|
||||
varray[n].mx.x = Types::Max(vtx[pol.binding[m]].x, varray[n].mx.x);
|
||||
varray[n].mx.y = Types::Max(vtx[pol.binding[m]].y, varray[n].mx.y);
|
||||
varray[n].mx.z = Types::Max(vtx[pol.binding[m]].z, varray[n].mx.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Build(geometry->pol.GetCount(), &varray[0]);
|
||||
}
|
||||
void GeometryBIH::Free()
|
||||
{
|
||||
geometry = NULL;
|
||||
material_table.Free();
|
||||
|
||||
acc.Free();
|
||||
Tree::Free();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
1141
include/engine/core/geometry_kdtree.cpp
Normal file
1141
include/engine/core/geometry_kdtree.cpp
Normal file
File diff suppressed because it is too large
Load Diff
853
include/engine/core/geometry_nml.cpp
Normal file
853
include/engine/core/geometry_nml.cpp
Normal file
@ -0,0 +1,853 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/geometry.h"
|
||||
#include "core/embedded_resource_handler_interface.h"
|
||||
#include "timing/benchmark.h"
|
||||
#include "metafile/nml.h"
|
||||
#include "math/matrix4.h"
|
||||
#include "ascii/parser.h"
|
||||
#include "memory/endian.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
using namespace GS::AsciiParser;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Geometry::ParseVertex(const Tag *pt)
|
||||
{
|
||||
NMLTagForeach(vt, *pt)
|
||||
{
|
||||
if (vt->name == "Count")
|
||||
vtx.Allocate(vt->GetInteger());
|
||||
|
||||
else if (vt->name == "Data")
|
||||
{
|
||||
float *fbff = (float *)vt->GetValue().GetBinaryBuffer();
|
||||
|
||||
if (vtx)
|
||||
for (uint n = 0; n < vtx.GetCount(); ++n)
|
||||
{
|
||||
for (int m = 0; m < 3; ++m)
|
||||
Endian::ToHost(&fbff[n * 3 + m], 4, Endian::Intel);
|
||||
vtx[n].Set(fbff[n * 3 + 0], fbff[n * 3 + 1], fbff[n * 3 + 2]);
|
||||
}
|
||||
else __LOG_E__ << "Incomplete vertex chunk loading '" << name << "', expected vertex count integer tag.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
void Geometry::ParseAsciiVertex(const Tag *pt)
|
||||
{
|
||||
NMLTagForeach(vt, *pt)
|
||||
{
|
||||
if (vt->name == "Count")
|
||||
vtx.Allocate(vt->GetInteger());
|
||||
|
||||
else if (vt->name == "Data")
|
||||
{
|
||||
if (vtx)
|
||||
{
|
||||
uint cvtx = 0;
|
||||
|
||||
NMLTagForeach(vx, *vt)
|
||||
{
|
||||
if (cvtx == vtx.GetCount())
|
||||
{
|
||||
__LOG_E__ << "Corrupter <AVertex> tag, more vertices than expected.\n";
|
||||
break;
|
||||
}
|
||||
vtx[cvtx++].FromMetaTag(*vx);
|
||||
}
|
||||
}
|
||||
else __LOG_E__ << "Incomplete vertex chunk loading '" << name << "', expected vertex count integer tag.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Geometry::ParseSkin(const Tag *pt)
|
||||
{
|
||||
// Retrieve bone id list.
|
||||
Tag *bonetag = pt->GetTag("Bones"),
|
||||
*bindtag = pt->GetTag("Binds");
|
||||
|
||||
skin.Free();
|
||||
bone_name.Free();
|
||||
bone_bind_matrix.Free();
|
||||
|
||||
if (bonetag)
|
||||
{
|
||||
AllocateBone(bonetag->GetChildCount());
|
||||
|
||||
if (!bone_name)
|
||||
__LOG_E__ << "Failed to allocate bone id list.\n";
|
||||
|
||||
else
|
||||
{
|
||||
int n = 0;
|
||||
NMLTagForeach(b, *bonetag)
|
||||
bone_name[n++] = b->GetString();
|
||||
}
|
||||
|
||||
if (bindtag && (bindtag->GetChildCount() == bone_name.GetCount()))
|
||||
{
|
||||
int n = 0;
|
||||
NMLTagForeach(m, *bindtag)
|
||||
bone_bind_matrix[n++].FromMetaTag(*m);
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve skin weights.
|
||||
Tag *weighttag = pt->GetTag("Weights");
|
||||
|
||||
if (bone_name.GetCount() && weighttag)
|
||||
{
|
||||
if (weighttag->GetChildCount() != vtx.GetCount())
|
||||
__LOG_W__ << "Incoherent weight count (" << weighttag->GetChildCount() <<" for " << vtx.GetCount() << " vertice (is the skin being declared before the vertice?)).\n";
|
||||
|
||||
if (!skin.Allocate(weighttag->GetChildCount()))
|
||||
__LOG_E__ << "Failed to allocate skin weights array.\n";
|
||||
|
||||
else
|
||||
{
|
||||
int n = 0;
|
||||
NMLTagForeach(b, *weighttag)
|
||||
{
|
||||
const char *p = b->GetString(), *e = p + String::strlen(p);
|
||||
|
||||
skin[n].bone_index[0] = (ushort)String::atoi(p);
|
||||
p = NextEntry(p, e) + 1;
|
||||
skin[n].bone_index[1] = (ushort)String::atoi(p);
|
||||
p = NextEntry(p, e) + 1;
|
||||
skin[n].bone_index[2] = (ushort)String::atoi(p);
|
||||
p = NextEntry(p, e) + 1;
|
||||
skin[n].bone_index[3] = (ushort)String::atoi(p);
|
||||
p = NextEntry(p, e) + 1;
|
||||
|
||||
skin[n].w[0] = ((float)String::atoi(p)) / 255.f;
|
||||
p = NextEntry(p, e) + 1;
|
||||
skin[n].w[1] = ((float)String::atoi(p)) / 255.f;
|
||||
p = NextEntry(p, e) + 1;
|
||||
skin[n].w[2] = ((float)String::atoi(p)) / 255.f;
|
||||
p = NextEntry(p, e) + 1;
|
||||
skin[n].w[3] = ((float)String::atoi(p)) / 255.f;
|
||||
|
||||
// Re-normalize weights.
|
||||
float tw = 0.f;
|
||||
for (int w = 0; w < 4; ++w)
|
||||
tw += skin[n].w[w];
|
||||
|
||||
float k = 1.f / tw;
|
||||
for (int w = 0; w < 4; ++w)
|
||||
skin[n].w[w] *= k;
|
||||
|
||||
++n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Geometry::ParsePolygon(const Tag *pt)
|
||||
{
|
||||
NMLTagForeach(vt, *pt)
|
||||
{
|
||||
if (vt->name == "Count")
|
||||
pol.Allocate(vt->GetInteger());
|
||||
|
||||
else if (vt->name == "BindingCount")
|
||||
binding.Allocate(vt->GetInteger());
|
||||
|
||||
else if (vt->name == "Data")
|
||||
{
|
||||
if (pol && binding)
|
||||
{
|
||||
uint *pbuffer = (uint *)vt->GetValue().GetBinaryBuffer();
|
||||
uint *pbinding = binding;
|
||||
|
||||
for (uint m = 0; m < pol.GetCount(); m++)
|
||||
{
|
||||
Endian::ToHost(pbuffer, 4, Endian::Intel);
|
||||
pol[m].vtx_count = (ushort)*pbuffer++;
|
||||
|
||||
pol[m].binding = pbinding;
|
||||
for (uint j = 0; j < pol[m].vtx_count; j++)
|
||||
{
|
||||
Endian::ToHost(pbuffer, 4, Endian::Intel);
|
||||
*pbinding++ = *pbuffer++;
|
||||
}
|
||||
|
||||
Endian::ToHost(pbuffer, 4, Endian::Intel);
|
||||
pol[m].material = (ushort)*pbuffer++;
|
||||
}
|
||||
}
|
||||
else __LOG_E__ << "Could not allocate polygon/binding memory.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
void Geometry::ParseAsciiPolygon(const Tag *pt)
|
||||
{
|
||||
NMLTagForeach(vt, *pt)
|
||||
{
|
||||
if (vt->name == "Count")
|
||||
pol.Allocate(vt->GetInteger());
|
||||
|
||||
else if (vt->name == "BindingCount")
|
||||
binding.Allocate(vt->GetInteger());
|
||||
|
||||
else if (vt->name == "Data")
|
||||
{
|
||||
if (binding && pol)
|
||||
{
|
||||
static String _PolyIndex("Index");
|
||||
|
||||
uint *pbinding = binding;
|
||||
uint cpoly = 0;
|
||||
|
||||
NMLTagForeach(poly, *vt)
|
||||
{
|
||||
if (cpoly == pol.GetCount())
|
||||
{
|
||||
__LOG_E__ << "Corrupted <APolygon> tag, too many polygons.\n";
|
||||
break;
|
||||
}
|
||||
|
||||
Tag *count_tag = poly->GetTag("Count;"),
|
||||
*material_tag = poly->GetTag("Material;");
|
||||
|
||||
pol[cpoly].vtx_count = 0;
|
||||
pol[cpoly].binding = pbinding;
|
||||
pol[cpoly].material = 0;
|
||||
|
||||
if (count_tag && material_tag)
|
||||
{
|
||||
pol[cpoly].vtx_count = (ushort)count_tag->GetInteger();
|
||||
pol[cpoly].material = (ushort)material_tag->GetInteger();
|
||||
|
||||
NMLTagForeach(tag, *poly)
|
||||
if (tag->name == _PolyIndex)
|
||||
*pbinding++ = tag->GetInteger();
|
||||
}
|
||||
else __LOG_E__ << "Corrupt <Polygon> tag in <APolygon>.\n";
|
||||
|
||||
++cpoly;
|
||||
}
|
||||
}
|
||||
else __LOG_E__ << "Could not allocate polygon/binding memory.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Geometry::ParsePolygonNormal(const Tag *pt)
|
||||
{
|
||||
NMLTagForeach(vt, *pt)
|
||||
{
|
||||
if (vt->name == "Count")
|
||||
pol_normal.Allocate(vt->GetInteger());
|
||||
|
||||
else if (vt->name == "Data")
|
||||
{
|
||||
if (pol_normal)
|
||||
{
|
||||
char *pbuffer = (char *)vt->GetValue().GetBinaryBuffer();
|
||||
|
||||
for (uint m = 0; m < pol.GetCount(); m++)
|
||||
{
|
||||
pol_normal[m].x = (float)(pbuffer[0]) / 127.f;
|
||||
pol_normal[m].y = (float)(pbuffer[1]) / 127.f;
|
||||
pol_normal[m].z = (float)(pbuffer[2]) / 127.f;
|
||||
pol_normal[m].Normalize();
|
||||
pbuffer += 3;
|
||||
}
|
||||
}
|
||||
else __LOG_E__ << "Incomplete polygon normal chunk loading '" << name << "'.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
void Geometry::ParseVertexNormal(const Tag *pt)
|
||||
{
|
||||
Tag *vt = pt->GetTag("Count;");
|
||||
if (!vt)
|
||||
__ERRRAW__(__LOG_E__ << "No count tag in <VNormal>.\n")
|
||||
vtx_normal.Allocate(vt->GetInteger());
|
||||
|
||||
vt = pt->GetTag("Data;");
|
||||
if (!vt)
|
||||
__ERRRAW__(__LOG_E__ << "No data tag in <VNormal>.\n")
|
||||
|
||||
if (vtx_normal)
|
||||
{
|
||||
signed char *pbuffer = (signed char *)vt->GetValue().GetBinaryBuffer();
|
||||
|
||||
for (uint m = 0; m < vtx_normal.GetCount(); ++m)
|
||||
{
|
||||
vtx_normal[m].x = ((float)pbuffer[0]) / 127.f;
|
||||
vtx_normal[m].y = ((float)pbuffer[1]) / 127.f;
|
||||
vtx_normal[m].z = ((float)pbuffer[2]) / 127.f;
|
||||
|
||||
vtx_normal[m].Normalize();
|
||||
pbuffer += 3;
|
||||
}
|
||||
}
|
||||
else __LOG_E__ << "Incomplete vertex normal chunk loading '" << name << "'.\n";
|
||||
}
|
||||
void Geometry::ParseVertexTangent(const Tag *pt)
|
||||
{
|
||||
Tag *vt = pt->GetTag("Count;");
|
||||
if (!vt)
|
||||
__ERRRAW__(__LOG_E__ << "No count tag in <VTangent>.\n")
|
||||
vtx_tangent.Allocate(vt->GetInteger());
|
||||
|
||||
vt = pt->GetTag("Data;");
|
||||
if (!vt)
|
||||
__ERRRAW__(__LOG_E__ << "No data tag in <VTangent>.\n")
|
||||
|
||||
// Allocate vertex normals.
|
||||
if (vtx_tangent)
|
||||
{
|
||||
signed char *pbuffer = (signed char *)vt->GetValue().GetBinaryBuffer();
|
||||
|
||||
for (uint m = 0; m < vtx_tangent.GetCount(); ++m)
|
||||
{
|
||||
Vector4 &T = vtx_tangent[m].T, &B = vtx_tangent[m].B;
|
||||
|
||||
T.x = ((float)pbuffer[0]) / 127.f;
|
||||
T.y = ((float)pbuffer[1]) / 127.f;
|
||||
T.z = ((float)pbuffer[2]) / 127.f;
|
||||
T.Normalize();
|
||||
|
||||
B.x = ((float)pbuffer[3]) / 127.f;
|
||||
B.y = ((float)pbuffer[4]) / 127.f;
|
||||
B.z = ((float)pbuffer[5]) / 127.f;
|
||||
B.Normalize();
|
||||
|
||||
pbuffer += 6;
|
||||
}
|
||||
}
|
||||
else __LOG_E__ << "Incomplete vertex tangent chunk loading '" << name << "'.\n";
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Geometry::ParseAsciiRGB(const Tag *pt)
|
||||
{
|
||||
Tag *vt = pt->GetTag("Count;");
|
||||
if (!vt)
|
||||
__ERRRAW__(__LOG_E__ << "No count tag in <ARGB>.\n")
|
||||
rgb.Allocate(vt->GetInteger());
|
||||
|
||||
vt = pt->GetTag("Data;");
|
||||
if (!vt)
|
||||
__ERRRAW__(__LOG_E__ << "No data tag in <ARGB>.\n")
|
||||
|
||||
if (rgb)
|
||||
{
|
||||
uint rgb_count = 0;
|
||||
|
||||
NMLTagForeach(rt, *vt)
|
||||
{
|
||||
if (rgb_count == rgb.GetCount())
|
||||
{
|
||||
__LOG_E__ << "Too many <Value> tags in RGB list of geometry '" << name << "'.\n";
|
||||
break;
|
||||
}
|
||||
Tag *r_tag = rt->GetTypedTag("R;", Variant::VariantFloat),
|
||||
*g_tag = rt->GetTypedTag("G;", Variant::VariantFloat),
|
||||
*b_tag = rt->GetTypedTag("B;", Variant::VariantFloat),
|
||||
*a_tag = rt->GetTypedTag("A;", Variant::VariantFloat);
|
||||
|
||||
rgb[rgb_count].x = r_tag ? r_tag->GetReal() : 0;
|
||||
rgb[rgb_count].y = g_tag ? g_tag->GetReal() : 0;
|
||||
rgb[rgb_count].z = b_tag ? b_tag->GetReal() : 0;
|
||||
rgb[rgb_count].w = a_tag ? a_tag->GetReal() : 1;
|
||||
++rgb_count;
|
||||
}
|
||||
|
||||
if (rgb_count != rgb.GetCount())
|
||||
__LOG_W__ << "Not enough <Value> tags in RGB list of geometry '" << name << "'.\n";
|
||||
}
|
||||
else __LOG_E__ << "Could not allocate RGB for geometry '" << name << "'.\n";
|
||||
}
|
||||
void Geometry::ParseRGB(const Tag *pt)
|
||||
{
|
||||
if (Tag *vt = pt->GetTag("Data"))
|
||||
{
|
||||
float *pbuffer = (float *)vt->GetValue().GetBinaryBuffer();
|
||||
size_t size = vt->GetValue().GetBinarySize();
|
||||
|
||||
if (rgb.Allocate(binding.GetCount()))
|
||||
{
|
||||
uint components = (size == binding.GetCount() * 4) ? 4 : 3;
|
||||
|
||||
for (uint m = 0; m < rgb.GetCount(); m++)
|
||||
{
|
||||
for (uint n = 0; n < components; ++n)
|
||||
Endian::ToHost(&pbuffer[n], 4, Endian::Intel);
|
||||
|
||||
rgb[m].Set(pbuffer[0], pbuffer[1], pbuffer[2]);
|
||||
rgb[m].w = components == 4 ? pbuffer[3] : 1.f;
|
||||
|
||||
pbuffer += components;
|
||||
}
|
||||
}
|
||||
else __LOG_E__ << "Could not allocate RGB channel for geometry '" << name << "'.\n";
|
||||
}
|
||||
else __LOG_E__ << "Expected <Data> tag under <RGB> tag.\n";
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Geometry::ParseUV(const Tag *pt)
|
||||
{
|
||||
uint current_uv = 0;
|
||||
|
||||
NMLTagForeach(vt, *pt)
|
||||
{
|
||||
if (vt->name != "Data")
|
||||
continue;
|
||||
|
||||
if (current_uv == __UV_PER_GEOMETRY__)
|
||||
{
|
||||
__LOG_W__ << "Geometry '" << name << "' requires more UV channels than this build can handle.\n";
|
||||
__LOG_W__ << "Please increase nUVMapCount and recompile to fully import this object.\n";
|
||||
break;
|
||||
}
|
||||
|
||||
float *pbuffer = (float *)vt->GetValue().GetBinaryBuffer();
|
||||
|
||||
if (uv[current_uv].Allocate(binding.GetCount()))
|
||||
for (uint m = 0; m < binding.GetCount(); ++m)
|
||||
{
|
||||
for (int n = 0; n < 2; ++n)
|
||||
Endian::ToHost(&pbuffer[n], 4, Endian::Intel);
|
||||
uv[current_uv][m].x = pbuffer[0];
|
||||
uv[current_uv][m].y = pbuffer[1];
|
||||
pbuffer += 2;
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "could not allocate UV channel for geometry '" << name << "'.\n";
|
||||
|
||||
++current_uv;
|
||||
}
|
||||
}
|
||||
void Geometry::ParseAsciiUV(const Tag *pt)
|
||||
{
|
||||
uint current_uv = 0;
|
||||
|
||||
NMLTagForeach(vt, *pt)
|
||||
{
|
||||
if (vt->name != "Data")
|
||||
continue;
|
||||
|
||||
if (uv[current_uv].Allocate(binding.GetCount()))
|
||||
{
|
||||
uint uv_index = 0;
|
||||
|
||||
NMLTagForeach(uvt, *vt)
|
||||
{
|
||||
if (uv_index == binding.GetCount())
|
||||
{
|
||||
__LOG_E__ << "Too many <Value> tags in UV channel " << current_uv << " of geometry '" << name << "'.\n";
|
||||
break;
|
||||
}
|
||||
Tag *u_tag = uvt->GetTypedTag("U;", Variant::VariantFloat),
|
||||
*v_tag = uvt->GetTypedTag("V;", Variant::VariantFloat);
|
||||
|
||||
uv[current_uv][uv_index].x = u_tag ? u_tag->GetReal() : 0;
|
||||
uv[current_uv][uv_index].y = v_tag ? v_tag->GetReal() : 0;
|
||||
uv_index++;
|
||||
}
|
||||
}
|
||||
else __LOG_E__ << "could not allocate UV channel for geometry '" << name << "'.\n";
|
||||
|
||||
if (++current_uv == __UV_PER_GEOMETRY__)
|
||||
{
|
||||
__LOG_W__ << "'" << name << "' requires more UV channels than this build can handle.\n";
|
||||
__LOG_W__ << " Please increase nUVMapCount and recompile nEngine to fully import this object.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Geometry::ParseMaterials(const Tag *pt)
|
||||
{
|
||||
// First pass, material count.
|
||||
uint slot_count = 0;
|
||||
NMLTagForeach(mt, *pt)
|
||||
if ((mt->name == "Material") || (mt->name == "MaterialRef") || (mt->name == "MaterialRefEx"))
|
||||
slot_count++;
|
||||
|
||||
// Allocate slots.
|
||||
if (material_table.Allocate(slot_count))
|
||||
{
|
||||
slot_count = 0;
|
||||
|
||||
NMLTagForeach(mt, *pt)
|
||||
{
|
||||
String mref;
|
||||
bool use_cache = true;
|
||||
|
||||
if (mt->name == "Material")
|
||||
IEmbeddedResourceHandler::Get()->ExtractEmbeddedMaterial(mref, *mt, name, slot_count);
|
||||
else if (mt->name == "MaterialRef")
|
||||
mref = mt->GetString();
|
||||
else if (mt->name == "MaterialRefEx")
|
||||
{
|
||||
if (Tag *t = mt->GetTypedTag("Name", Variant::VariantString))
|
||||
mref = t->GetString();
|
||||
if (Tag *t = mt->GetTypedTag("UseCache", Variant::VariantBool))
|
||||
use_cache = t->GetBool();
|
||||
}
|
||||
|
||||
material_table[slot_count].name = mref;
|
||||
material_table[slot_count].use_cache = use_cache;
|
||||
|
||||
++slot_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Geometry::ParseMisc(const Tag &tag)
|
||||
{
|
||||
NMLTagForeach(pt, tag)
|
||||
{
|
||||
if (pt->name == "LodDistance")
|
||||
lod_distance = pt->GetReal();
|
||||
else if (pt->name == "LodNull")
|
||||
flag.Raise(FlagNullLodProxy, pt->GetBool());
|
||||
|
||||
else if (pt->name == "ShadowNull")
|
||||
flag.Raise(FlagNullShadowProxy, pt->GetBool());
|
||||
|
||||
else if (pt->name == "CopyLock")
|
||||
copy_lock = pt->GetString();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Geometry::FromMetaTag(const Tag &tag)
|
||||
{
|
||||
Benchmark bench(true);
|
||||
|
||||
// Build binary objects base name.
|
||||
if (tag.name != "Geometry")
|
||||
__ERR__(__LOG_E__ << "Could not parse material, incorrect root tag (" << tag.name << ")...\n", false)
|
||||
Free();
|
||||
|
||||
// Parse root tags.
|
||||
Tag *pt;
|
||||
|
||||
if ((pt = tag.GetTag("Materials")) != NULL)
|
||||
ParseMaterials(pt);
|
||||
|
||||
if ((pt = tag.GetTag("Vertex")) != NULL)
|
||||
ParseVertex(pt);
|
||||
else
|
||||
if ((pt = tag.GetTag("AVertex")) != NULL)
|
||||
ParseAsciiVertex(pt);
|
||||
|
||||
if ((pt = tag.GetTag("Polygon")) != NULL)
|
||||
ParsePolygon(pt);
|
||||
else
|
||||
if ((pt = tag.GetTag("APolygon")) != NULL)
|
||||
ParseAsciiPolygon(pt);
|
||||
|
||||
if ((pt = tag.GetTag("PNormal")) != NULL)
|
||||
ParsePolygonNormal(pt);
|
||||
|
||||
if ((pt = tag.GetTag("VNormal")) != NULL)
|
||||
ParseVertexNormal(pt);
|
||||
|
||||
if ((pt = tag.GetTag("VTangent")) != NULL)
|
||||
ParseVertexTangent(pt);
|
||||
|
||||
if ((pt = tag.GetTag("Skin")) != NULL)
|
||||
ParseSkin(pt);
|
||||
|
||||
if ((pt = tag.GetTag("RGB")) != NULL)
|
||||
ParseRGB(pt);
|
||||
else
|
||||
if ((pt = tag.GetTag("ARGB")) != NULL)
|
||||
ParseAsciiRGB(pt);
|
||||
|
||||
if ((pt = tag.GetTag("UV")) != NULL)
|
||||
ParseUV(pt);
|
||||
else
|
||||
if ((pt = tag.GetTag("AUV")) != NULL)
|
||||
ParseAsciiUV(pt);
|
||||
|
||||
if ((pt = tag.GetTag("LodProxy")) != NULL)
|
||||
lod_proxy = pt->GetString();
|
||||
|
||||
if ((pt = tag.GetTag("ShadowProxy")) != NULL)
|
||||
shadow_proxy = pt->GetString();
|
||||
|
||||
ParseMisc(tag);
|
||||
|
||||
// Quick integrity check.
|
||||
if (!vtx.GetCount())
|
||||
__ERR__(__LOG_E__ << "No vertice in geometry '" << name << "'.\n", false)
|
||||
if (!pol.GetCount())
|
||||
__ERR__(__LOG_E__ << "No polygon in geometry '" << name << "'.\n", false)
|
||||
|
||||
if (vtx_tangent.IsNull())
|
||||
ComputeVertexTangent();
|
||||
|
||||
bench.Stop();
|
||||
__LOG__ << "Geometry::FromMetaTag(Tag &tag) done in " << bench.GetMs() << "ms. " << vtx.GetCount() << " vertice, " << pol.GetCount() << " polygon(s), " << material_table.GetCount() << " material(s).\n";
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tag *Geometry::AsMetaTag() const
|
||||
{
|
||||
// Integrity check.
|
||||
if (!vtx.GetCount() || !pol.GetCount() || !material_table.GetCount())
|
||||
__ERR__(__LOG_E__ << "Cannot serialize '" << name << "', geometry is empty.\n", NULL)
|
||||
|
||||
Tag *root = new Tag("Geometry");
|
||||
if (!root)
|
||||
__ERR__(__LOG_E__ << "Could not create root tag to serialize geometry '" << name << "'.\n", NULL)
|
||||
|
||||
uint n;
|
||||
|
||||
// Vertice.
|
||||
if (Tag *vertex = root->AddChild("Vertex"))
|
||||
{
|
||||
vertex->AddChild("Count", (int)vtx.GetCount());
|
||||
|
||||
if (float *vtx_rl = new float[vtx.GetCount() * 3])
|
||||
{
|
||||
for (n = 0; n < vtx.GetCount(); n++)
|
||||
{
|
||||
vtx_rl[n * 3 + 0] = vtx[n].x;
|
||||
vtx_rl[n * 3 + 1] = vtx[n].y;
|
||||
vtx_rl[n * 3 + 2] = vtx[n].z;
|
||||
}
|
||||
vertex->AddChild("Data", (uchar *)vtx_rl, vtx.GetCount() * 3 * sizeof(float));
|
||||
_safe_delete_array(vtx_rl);
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "Failed to allocate raw vertice buffer.\n";
|
||||
}
|
||||
|
||||
// Polygons.
|
||||
if (Tag *polygon = root->AddChild("Polygon"))
|
||||
{
|
||||
polygon->AddChild("Count", (int)pol.GetCount());
|
||||
polygon->AddChild("BindingCount", (int)binding.GetCount());
|
||||
|
||||
if (uint *pol_u4 = new uint[binding.GetCount() + pol.GetCount() * 2])
|
||||
{
|
||||
uint *ptr = pol_u4;
|
||||
for (n = 0; n < pol.GetCount(); n++)
|
||||
{
|
||||
*ptr++ = pol[n].vtx_count;
|
||||
for (uint m = 0; m < pol[n].vtx_count; m++)
|
||||
*ptr++ = pol[n].binding[m];
|
||||
*ptr++ = (uint)pol[n].material;
|
||||
}
|
||||
polygon->AddChild("Data", (uchar *)pol_u4, (binding.GetCount() + pol.GetCount() * 2) * sizeof(uint));
|
||||
_safe_delete_array(pol_u4);
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "Failed to allocate raw polygon buffer.\n";
|
||||
}
|
||||
|
||||
// Polygon normals.
|
||||
if (pol_normal)
|
||||
if (Tag *pnormal = root->AddChild("PNormal"))
|
||||
{
|
||||
pnormal->AddChild("Count", (int)pol.GetCount());
|
||||
|
||||
if (char *pnrm = new char[pol.GetCount() * 3])
|
||||
{
|
||||
char *ptr = pnrm;
|
||||
for (n = 0; n < pol.GetCount(); n++)
|
||||
{
|
||||
*ptr++ = (char)(pol_normal[n].x * 127.f);
|
||||
*ptr++ = (char)(pol_normal[n].y * 127.f);
|
||||
*ptr++ = (char)(pol_normal[n].z * 127.f);
|
||||
}
|
||||
pnormal->AddChild("Data", (uchar *)pnrm, pol.GetCount() * 3);
|
||||
_safe_delete_array(pnrm);
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "Failed to allocate raw normal buffer.\n";
|
||||
}
|
||||
|
||||
// Vertex normals.
|
||||
if (vtx_normal)
|
||||
if (Tag *vnormal = root->AddChild("VNormal"))
|
||||
{
|
||||
vnormal->AddChild("Count", (int)binding.GetCount());
|
||||
|
||||
if (char *vnrm = new char[binding.GetCount() * 3])
|
||||
{
|
||||
char *ptr = vnrm;
|
||||
for (n = 0; n < binding.GetCount(); n++)
|
||||
{
|
||||
*ptr++ = (char)(vtx_normal[n].x * 127.f);
|
||||
*ptr++ = (char)(vtx_normal[n].y * 127.f);
|
||||
*ptr++ = (char)(vtx_normal[n].z * 127.f);
|
||||
}
|
||||
vnormal->AddChild("Data", (uchar *)vnrm, binding.GetCount() * 3);
|
||||
_safe_delete_array(vnrm);
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "Failed to allocate raw normal buffer.\n";
|
||||
}
|
||||
|
||||
// Vertex tangent frames.
|
||||
if (vtx_tangent)
|
||||
if (Tag *vtangent = root->AddChild("VTangent"))
|
||||
{
|
||||
vtangent->AddChild("Count", (int)binding.GetCount());
|
||||
|
||||
if (char *vfrm = new char[binding.GetCount() * 6])
|
||||
{
|
||||
char *ptr = vfrm;
|
||||
for (n = 0; n < binding.GetCount(); n++)
|
||||
{
|
||||
*ptr++ = (char)(vtx_tangent[n].T.x * 127.f);
|
||||
*ptr++ = (char)(vtx_tangent[n].T.y * 127.f);
|
||||
*ptr++ = (char)(vtx_tangent[n].T.z * 127.f);
|
||||
*ptr++ = (char)(vtx_tangent[n].B.x * 127.f);
|
||||
*ptr++ = (char)(vtx_tangent[n].B.y * 127.f);
|
||||
*ptr++ = (char)(vtx_tangent[n].B.z * 127.f);
|
||||
}
|
||||
vtangent->AddChild("Data", (uchar *)vfrm, binding.GetCount() * 6);
|
||||
_safe_delete_array(vfrm);
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "Failed to allocate raw tangent frame buffer.\n";
|
||||
}
|
||||
|
||||
// Output RGB.
|
||||
if (rgb)
|
||||
if (Tag *rgbtag = root->AddChild("RGB"))
|
||||
{
|
||||
// Test if we need to output alpha.
|
||||
uint num_comp = 3;
|
||||
for (uint m = 0; m < binding.GetCount(); ++m)
|
||||
if (rgb[m].w < 1.f)
|
||||
{
|
||||
num_comp = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
// Output buffer.
|
||||
if (float *rgbbuf = new float[binding.GetCount() * num_comp])
|
||||
{
|
||||
float *prgb = rgbbuf;
|
||||
for (uint m = 0; m < binding.GetCount(); ++m)
|
||||
{
|
||||
prgb[0] = rgb[m].x;
|
||||
prgb[1] = rgb[m].y;
|
||||
prgb[2] = rgb[m].z;
|
||||
if (num_comp == 4)
|
||||
prgb[3] = rgb[m].w;
|
||||
prgb += num_comp;
|
||||
}
|
||||
rgbtag->AddChild("Data", (uchar *)rgbbuf, binding.GetCount() * num_comp * sizeof(float));
|
||||
_safe_delete_array(rgbbuf);
|
||||
}
|
||||
}
|
||||
|
||||
// Output UVs.
|
||||
if (GetUVCount())
|
||||
if (Tag *uvtag = root->AddChild("UV"))
|
||||
{
|
||||
uvtag->AddChild("Count", (int)GetUVCount()); // LEGACY
|
||||
|
||||
if (float *uvbuf = new float[binding.GetCount() * 2])
|
||||
{
|
||||
for (n = 0; n < __UV_PER_GEOMETRY__; ++n)
|
||||
if (uv[n])
|
||||
{
|
||||
for (uint m = 0; m < binding.GetCount(); m++)
|
||||
{
|
||||
uvbuf[m * 2 + 0] = uv[n][m].x;
|
||||
uvbuf[m * 2 + 1] = uv[n][m].y;
|
||||
}
|
||||
uvtag->AddChild("Data", (uchar *)uvbuf, binding.GetCount() * 2 * sizeof(float));
|
||||
}
|
||||
_safe_delete_array(uvbuf);
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "Failed to allocate raw UVs buffer.\n";
|
||||
}
|
||||
|
||||
// Output vertex weights.
|
||||
if (bone_name.GetCount())
|
||||
{
|
||||
Tag *skintag = root->AddChild("Skin");
|
||||
|
||||
// Save bone id list.
|
||||
if (Tag *bonetag = skintag->AddChild("Bones"))
|
||||
for (uint n = 0; n < bone_name.GetCount(); ++n)
|
||||
bonetag->AddChild("Id", bone_name[n]);
|
||||
else
|
||||
__LOG_E__ << "Failed to allocate bone id tag.\n";
|
||||
|
||||
// Save bone binding matrix.
|
||||
if (Tag *bindtag = skintag->AddChild("Binds"))
|
||||
for (uint n = 0; n < bone_name.GetCount(); ++n)
|
||||
bindtag->AddChild(bone_bind_matrix[n].AsMetaTag("Matrix"));
|
||||
else
|
||||
__LOG_E__ << "Failed to allocate bone binding tag.\n";
|
||||
|
||||
// Save bone association/weights.
|
||||
Tag *weighttag = skintag->AddChild("Weights");
|
||||
for (uint n = 0; n < vtx.GetCount(); ++n)
|
||||
weighttag->AddChild
|
||||
(
|
||||
"W", String::Format
|
||||
( "%d,%d,%d,%d:%d,%d,%d,%d",
|
||||
skin[n].bone_index[0], skin[n].bone_index[1], skin[n].bone_index[2], skin[n].bone_index[3],
|
||||
(int)(skin[n].w[0] * 255.f), (int)(skin[n].w[1] * 255.f), (int)(skin[n].w[2] * 255.f), (int)(skin[n].w[3] * 255.f)).toUtf8()
|
||||
);
|
||||
}
|
||||
|
||||
// Proxies.
|
||||
if (!lod_proxy.IsEmpty())
|
||||
root->AddChild("LodProxy", lod_proxy.c_str());
|
||||
if (lod_distance != Units::Mtr(100.f))
|
||||
root->AddChild("LodDistance", lod_distance);
|
||||
if (flag.IsSet(FlagNullLodProxy))
|
||||
root->AddChild("LodNull", true);
|
||||
if (!shadow_proxy.IsEmpty())
|
||||
root->AddChild("ShadowProxy", shadow_proxy.c_str());
|
||||
if (flag.IsSet(FlagNullShadowProxy))
|
||||
root->AddChild("ShadowNull", true);
|
||||
|
||||
// Serialize materials.
|
||||
if (Tag *mtag = root->AddChild("Materials"))
|
||||
for (n = 0; n < material_table.GetCount(); n++)
|
||||
if (Tag *extag = mtag->AddChild("MaterialRefEx"))
|
||||
{
|
||||
extag->AddChild("Name", material_table[n].name.c_str());
|
||||
if (!material_table[n].use_cache)
|
||||
extag->AddChild("UseCache", material_table[n].use_cache);
|
||||
}
|
||||
|
||||
// Copyright lock.
|
||||
if (!copy_lock.IsEmpty())
|
||||
root->AddChild("CopyLock", copy_lock);
|
||||
|
||||
return root;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
81
include/engine/core/geometry_normal.cpp
Normal file
81
include/engine/core/geometry_normal.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/geometry.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Geometry::ComputePolygonNormal(bool force)
|
||||
{
|
||||
if (!pol.GetCount() || !vtx.GetCount())
|
||||
return false;
|
||||
|
||||
if (!force && (pol_normal.GetCount() == pol.GetCount()))
|
||||
return true;
|
||||
|
||||
if (!pol_normal.Allocate(pol.GetCount()))
|
||||
__ERR__(__LOG_E__ << "Geometry::ComputePolygonNormal() failed to allocate buffer.\n", false)
|
||||
|
||||
for (uint c = 0; c < pol.GetCount(); ++c)
|
||||
if (pol[c].vtx_count > 2)
|
||||
{
|
||||
Vector4 va = vtx[pol[c].binding[2]] - vtx[pol[c].binding[0]],
|
||||
vb = vtx[pol[c].binding[1]] - vtx[pol[c].binding[0]];
|
||||
pol_normal[c] = vb.Cross(va).Normalized();
|
||||
}
|
||||
else
|
||||
pol_normal[c].Set();
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Geometry::ComputeVertexNormal(Array <Vector4> &buffer, float msa)
|
||||
{
|
||||
if (!pol.GetCount() || !vtx.GetCount())
|
||||
return false;
|
||||
|
||||
if (!ComputePolygonNormal())
|
||||
return false;
|
||||
|
||||
Array <VertexToPolygon> vtx_to_pol;
|
||||
ComputeVertexToPolygon(vtx_to_pol);
|
||||
|
||||
// Allocate full blown edge normal array.
|
||||
__LOG__ << "Compute vertex normal for " << name << ".\n";
|
||||
|
||||
if (!binding.GetCount())
|
||||
__ERR__(__LOG_E__ << "The total binding count is wrong. Check that the importer did properly update this flag.\n", false)
|
||||
if (!vtx_normal.Allocate(binding.GetCount()))
|
||||
__ERR__(__LOG_E__ << "Failed to allocate vertex normal buffer.\n", false)
|
||||
|
||||
for (uint cp = 0, ttp = 0; cp < pol.GetCount(); cp++)
|
||||
for (uint cv = 0; cv < pol[cp].vtx_count; cv++)
|
||||
{
|
||||
uint gv = pol[cp].binding[cv];
|
||||
|
||||
Vector4 normal(0, 0, 0);
|
||||
for (uint cg = 0; cg < vtx_to_pol[gv].pol_count; cg++)
|
||||
if (pol_normal[cp].Dot(pol_normal[vtx_to_pol[gv].pol_index[cg]]) > msa) // MSA test.
|
||||
normal += pol_normal[vtx_to_pol[gv].pol_index[cg]];
|
||||
|
||||
vtx_normal[ttp++] = normal.Normalized();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool Geometry::ComputeVertexNormal(float msa, bool force)
|
||||
{
|
||||
if (!force && (vtx_normal.GetCount() == binding.GetCount()))
|
||||
return true;
|
||||
return ComputeVertexNormal(vtx_normal, msa);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
589
include/engine/core/geometry_reducer.cpp
Normal file
589
include/engine/core/geometry_reducer.cpp
Normal file
@ -0,0 +1,589 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/geometry_reducer.h"
|
||||
#include "core/geometry.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void GeometryReducer::RemoveVertex (uint v)
|
||||
{
|
||||
vlist[v].active = false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
float GeometryReducer::ComputeEdgeCost(Geometry *sg, nLLEDGE *edg)
|
||||
{
|
||||
#define nMAXTRIPEREDGE 512 // FIXME
|
||||
|
||||
nLLTRI *sidetri[nMAXTRIPEREDGE];
|
||||
nTENTRY *te;
|
||||
|
||||
uint nsidetri = 0, n;
|
||||
|
||||
te = tlist.lut[edg->a];
|
||||
while (te)
|
||||
{
|
||||
if (te->tri->UseVertex(edg->b))
|
||||
sidetri[nsidetri++] = te->tri;
|
||||
te = te->n;
|
||||
}
|
||||
|
||||
float curvature = 0.f;
|
||||
|
||||
te = tlist.lut[edg->a];
|
||||
|
||||
while (te)
|
||||
{
|
||||
float mincurv = 1.f;
|
||||
|
||||
for (n = 0; n < nsidetri; ++n)
|
||||
{
|
||||
float dot = te->tri->normal.Dot(sidetri[n]->normal);
|
||||
dot = (1.f - dot) / 2.f;
|
||||
if (dot < mincurv)
|
||||
mincurv = dot;
|
||||
}
|
||||
if (mincurv > curvature)
|
||||
curvature = mincurv;
|
||||
te = te->n;
|
||||
}
|
||||
|
||||
return curvature * Vector4::Dist(sg->vtx[edg->a], sg->vtx[edg->b]);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void GeometryReducer::ComputeVertexCost(Geometry *sg, uint v)
|
||||
{
|
||||
nEENTRY *te = elist.lut[v];
|
||||
vlist[v].cost = -1.f;
|
||||
|
||||
// cheapest collapse target for this vertex...
|
||||
while (te)
|
||||
{
|
||||
float ecost = ComputeEdgeCost (sg, te->edge);
|
||||
if ( (vlist[v].cost == -1.f) || (ecost < vlist[v].cost) )
|
||||
{
|
||||
vlist[v].tgtcollapse = te->edge->b;
|
||||
vlist[v].cost = ecost;
|
||||
}
|
||||
te = te->n;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void nLLTRI::ReplaceVertex(uint f, uint t)
|
||||
{
|
||||
if ( a == f ) a = t;
|
||||
else if ( b == f ) b = t;
|
||||
else if ( c == f ) c = t;
|
||||
}
|
||||
char nLLTRI::UseVertex(uint i)
|
||||
{
|
||||
if ((a == i) || (b == i) || (c == i))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
char GeometryReducer::IsBorder(uint v)
|
||||
{
|
||||
/*
|
||||
If any of the edge going trough the vertex owns only one polygon then
|
||||
the vertex is on a border...
|
||||
`*/
|
||||
for (nEENTRY *pedg = elist.lut[v]; pedg; pedg = pedg->n)
|
||||
{
|
||||
uint ecnt = 0;
|
||||
for (nTENTRY *ptri = tlist.lut[v]; ptri; ptri = ptri->n)
|
||||
if (ptri->tri->UseVertex (pedg->edge->b))
|
||||
++ecnt;
|
||||
|
||||
if (ecnt < 2)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Geometry *GeometryReducer::Reduce(Geometry *sg, float k)
|
||||
{
|
||||
if (!sg->pol.GetCount()) return NULL;
|
||||
if (k <= 0.f) return NULL;
|
||||
if (k >= 1.f) return NULL;
|
||||
|
||||
#ifdef DEBUG_COMPILATION
|
||||
__LOG__ << "Geometry reducer invoked: " << k << "...\n";
|
||||
#endif
|
||||
|
||||
// Allocate
|
||||
Geometry *ng = new Geometry;
|
||||
if (!ng)
|
||||
return NULL;
|
||||
|
||||
// Triangulate geometry.
|
||||
uint n, m;
|
||||
nLLTRI *ctri;
|
||||
|
||||
elist.SetVertexCount (sg->vtx.GetCount());
|
||||
tlist.SetGeo (sg);
|
||||
|
||||
for (n = 0; n < sg->pol.GetCount(); ++n)
|
||||
{
|
||||
const Polygon &p = sg->pol[n];
|
||||
|
||||
for (m = 1; m < (uint)(p.vtx_count - 1); ++m)
|
||||
{
|
||||
ctri = tlist.Add(p.binding[0], p.binding[m], p.binding[m+1]);
|
||||
ctri->m = p.material;
|
||||
tlist.ComputeNormal(ctri);
|
||||
|
||||
// insert edges
|
||||
elist.Add(p.binding[0], p.binding[m]);
|
||||
elist.Add(p.binding[m], p.binding[m+1]);
|
||||
|
||||
elist.Add(p.binding[m], p.binding[0]);
|
||||
elist.Add(p.binding[m+1], p.binding[m]);
|
||||
}
|
||||
|
||||
elist.Add(p.binding[0], p.binding[m]);
|
||||
elist.Add(p.binding[m], p.binding[0]);
|
||||
}
|
||||
|
||||
// create vertice list
|
||||
vlist = new nLVERTEX[sg->vtx.GetCount()];
|
||||
for ( n = 0; n < sg->vtx.GetCount(); n++ )
|
||||
{
|
||||
vlist[n].active = true;
|
||||
vlist[n].locked = IsBorder (n);
|
||||
vlist[n].tgtcollapse = n;
|
||||
ComputeVertexCost (sg, n);
|
||||
}
|
||||
|
||||
// collapse until we reach target...
|
||||
uint _tgt = (uint)((float)tlist.ntri * k);
|
||||
#ifdef DEBUG_COMPILATION
|
||||
__LOG__ << "Collapsing from " << tlist.ntri << " to " << _tgt << "...\n";
|
||||
float pcttri = (float)(tlist.ntri - _tgt) * 0.01f;
|
||||
#endif
|
||||
|
||||
while ( tlist.ntri > _tgt )
|
||||
{
|
||||
// get the cheapest vertex to collapse
|
||||
int vtx = -1;
|
||||
float cst = 10000000.f;
|
||||
for ( n = 0; n < sg->vtx.GetCount(); n++ )
|
||||
{
|
||||
if ( vlist[n].active && (!vlist[n].locked) && (vlist[n].cost < cst) )
|
||||
{
|
||||
vtx = n;
|
||||
cst = vlist[n].cost;
|
||||
}
|
||||
}
|
||||
|
||||
/* // move vertex
|
||||
if ( !vlist[vlist[vtx].tgtcollapse].locked )
|
||||
{
|
||||
VEC_INC (sg->vtx[vlist[vtx].tgtcollapse], sg->vtx[vtx]);
|
||||
VEC_SCALEK (sg->vtx[vlist[vtx].tgtcollapse], 0.5f);
|
||||
}
|
||||
*/
|
||||
// remap triangles
|
||||
tlist.ReplaceVertex (vtx, vlist[vtx].tgtcollapse);
|
||||
elist.RemapEdges (vtx, vlist[vtx].tgtcollapse);
|
||||
|
||||
// recompute cost for all modified vertice
|
||||
ComputeVertexCost (sg, vlist[vtx].tgtcollapse);
|
||||
|
||||
nEENTRY *pedg = elist.lut[vlist[vtx].tgtcollapse];
|
||||
while ( pedg )
|
||||
{
|
||||
ComputeVertexCost (sg, pedg->edge->b);
|
||||
pedg = pedg->n;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_COMPILATION
|
||||
if (!(tlist.ntri & 4095))
|
||||
__LOG__ << (float)(100.f - ((float)(tlist.ntri - _tgt) / pcttri)) << "%%...\n";
|
||||
#endif
|
||||
// Invalidate vertex.
|
||||
vlist[vtx].active = false;
|
||||
}
|
||||
|
||||
_safe_delete_array(vlist);
|
||||
|
||||
#ifdef DEBUG_COMPILATION
|
||||
__LOG__ << "Geometry reduction done.\n";
|
||||
#endif
|
||||
|
||||
// Convert to mesh datas...
|
||||
char *usevtx = new char[sg->vtx.GetCount()];
|
||||
GS::Memory::Set(usevtx, 0, sg->vtx.GetCount());
|
||||
|
||||
ctri = tlist.root;
|
||||
while ( ctri )
|
||||
{
|
||||
usevtx[ctri->a] = true;
|
||||
usevtx[ctri->b] = true;
|
||||
usevtx[ctri->c] = true;
|
||||
ctri = ctri->n;
|
||||
}
|
||||
|
||||
m = 0;
|
||||
for ( n = 0; n < sg->vtx.GetCount(); n++ )
|
||||
if ( usevtx[n] )
|
||||
m++;
|
||||
|
||||
// Fill new geometry.
|
||||
ng->pol.Allocate(tlist.ntri);
|
||||
ng->binding.Allocate(ng->pol.GetCount() * 3);
|
||||
ng->vtx.Allocate(m);
|
||||
uint *rmpvtx = new uint[sg->vtx.GetCount()];
|
||||
|
||||
// Copy vertice.
|
||||
m = 0;
|
||||
for ( n = 0; n < sg->vtx.GetCount(); n++ )
|
||||
if ( usevtx[n] )
|
||||
{
|
||||
rmpvtx[n] = m;
|
||||
ng->vtx[m++] = sg->vtx[n];
|
||||
}
|
||||
_safe_delete_array(usevtx);
|
||||
|
||||
// Setup polygons.
|
||||
m = 0;
|
||||
ctri = tlist.root;
|
||||
for ( n = 0; n < ng->pol.GetCount(); n++ )
|
||||
{
|
||||
ng->pol[n].vtx_count = 3;
|
||||
ng->pol[n].binding = &ng->binding[m];
|
||||
ng->binding[m++] = rmpvtx[ctri->a];
|
||||
ng->binding[m++] = rmpvtx[ctri->b];
|
||||
ng->binding[m++] = rmpvtx[ctri->c];
|
||||
ng->pol[n].material = ctri->m;
|
||||
ctri = ctri->n;
|
||||
}
|
||||
_safe_delete_array(rmpvtx);
|
||||
|
||||
// Copy material.
|
||||
if (!ng->material_table.Allocate(sg->material_table.GetCount()))
|
||||
return NULL;
|
||||
for (n = 0; n < ng->material_table.GetCount(); n++)
|
||||
ng->material_table[n] = sg->material_table[n];
|
||||
|
||||
// Setup geometry.
|
||||
ng->ComputeVertexNormal();
|
||||
|
||||
return ng;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------
|
||||
void nLTRILIST::ComputeNormal (nLLTRI *ctri)
|
||||
//---------------------------------------------------
|
||||
{
|
||||
Vector4 va = sg->vtx[ctri->c] - sg->vtx[ctri->a];
|
||||
Vector4 vb = sg->vtx[ctri->b] - sg->vtx[ctri->a];
|
||||
ctri->normal = vb.Cross(va).Normalized();
|
||||
}
|
||||
|
||||
//--------------------------------------------
|
||||
void nLTRILIST::SetGeo (Geometry *g)
|
||||
//--------------------------------------------
|
||||
{
|
||||
sg = g;
|
||||
lut = new pnTENTRY[g->vtx.GetCount()];
|
||||
for ( uint c = 0; c < g->vtx.GetCount(); c++ )
|
||||
lut[c] = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
nLLTRI *nLTRILIST::Add (uint a, uint b, uint c)
|
||||
//-----------------------------------------------------------
|
||||
{
|
||||
nLLTRI *tri = new nLLTRI;
|
||||
tri->a = a;
|
||||
tri->b = b;
|
||||
tri->c = c;
|
||||
|
||||
tri->n = root;
|
||||
tri->p = NULL;
|
||||
if ( root )
|
||||
root->p = tri;
|
||||
root = tri;
|
||||
|
||||
// Register in vertex to poly.
|
||||
AddTriToVertex(tri, a);
|
||||
AddTriToVertex(tri, b);
|
||||
AddTriToVertex(tri, c);
|
||||
ntri++;
|
||||
|
||||
return tri;
|
||||
}
|
||||
|
||||
//--------------------------------------------
|
||||
void nLTRILIST::Remove(nLLTRI *t)
|
||||
//--------------------------------------------
|
||||
{
|
||||
RemoveTriFromVertex(t, t->a);
|
||||
RemoveTriFromVertex(t, t->b);
|
||||
RemoveTriFromVertex(t, t->c);
|
||||
|
||||
if (t->p)
|
||||
t->p->n = t->n;
|
||||
else root = t->n;
|
||||
if (t->n)
|
||||
t->n->p = t->p;
|
||||
delete t;
|
||||
ntri--;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
void nLTRILIST::RemoveTriFromVertex(nLLTRI *t, uint v)
|
||||
//--------------------------------------------------------------------
|
||||
{
|
||||
nTENTRY *n = lut[v], *p = NULL;
|
||||
while (n)
|
||||
{
|
||||
if (n->tri == t)
|
||||
break;
|
||||
p = n;
|
||||
n = n->n;
|
||||
}
|
||||
if (!n)
|
||||
return;
|
||||
if (!p)
|
||||
lut[v] = n->n;
|
||||
else p->n = n->n;
|
||||
_safe_delete(n);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
void nLTRILIST::AddTriToVertex (nLLTRI *t, uint v)
|
||||
//---------------------------------------------------------------
|
||||
{
|
||||
nTENTRY *n = lut[v];
|
||||
while ( n && (n->tri != t ))
|
||||
n = n->n;
|
||||
if ( n ) return;
|
||||
|
||||
n = new nTENTRY;
|
||||
n->tri = t;
|
||||
n->n = lut[v];
|
||||
lut[v] = n;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
void nLTRILIST::ReplaceVertex (uint a, uint b)
|
||||
//----------------------------------------------------------
|
||||
{
|
||||
nTENTRY *s = lut[a], *n;
|
||||
while ( s )
|
||||
{
|
||||
n = s->n;
|
||||
|
||||
if ( s->tri->UseVertex (b) )
|
||||
Remove (s->tri);
|
||||
else
|
||||
{
|
||||
s->tri->ReplaceVertex (a, b);
|
||||
AddTriToVertex (s->tri, b);
|
||||
ComputeNormal (s->tri);
|
||||
RemoveTriFromVertex (s->tri, a);
|
||||
}
|
||||
|
||||
s = n;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------
|
||||
nLTRILIST::nLTRILIST ()
|
||||
//---------------------------
|
||||
{
|
||||
ntri = 0;
|
||||
root = NULL;
|
||||
}
|
||||
|
||||
//----------------------------
|
||||
nLTRILIST::~nLTRILIST ()
|
||||
//----------------------------
|
||||
{
|
||||
uint i;
|
||||
for ( i = 0; i < sg->vtx.GetCount(); i++ )
|
||||
{
|
||||
nTENTRY *s = lut[i], *n;
|
||||
while ( s )
|
||||
{
|
||||
n = s->n;
|
||||
delete s;
|
||||
s = n;
|
||||
}
|
||||
}
|
||||
delete [] lut;
|
||||
lut = NULL;
|
||||
|
||||
nLLTRI *s = root, *n;
|
||||
while ( s )
|
||||
{
|
||||
n = s->n;
|
||||
delete s;
|
||||
s = n;
|
||||
}
|
||||
root = NULL;
|
||||
ntri = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------
|
||||
void nEDGELIST::Add (uint a, uint b)
|
||||
//--------------------------------------------
|
||||
{
|
||||
nEENTRY *pedg;
|
||||
if ( a == b )
|
||||
return;
|
||||
|
||||
pedg = lut[a];
|
||||
while ( pedg )
|
||||
{
|
||||
if ( pedg->edge->b == b )
|
||||
return;
|
||||
pedg = pedg->n;
|
||||
}
|
||||
|
||||
nLLEDGE *edg = new nLLEDGE;
|
||||
edg->a = a;
|
||||
edg->b = b;
|
||||
|
||||
edg->n = root;
|
||||
edg->p = NULL;
|
||||
if ( root )
|
||||
root->p = edg;
|
||||
root = edg;
|
||||
|
||||
// update lut
|
||||
pedg = new nEENTRY;
|
||||
pedg->edge = edg;
|
||||
pedg->n = lut[a];
|
||||
lut[a] = pedg;
|
||||
nedg++;
|
||||
}
|
||||
|
||||
//--------------------------------------------
|
||||
void nEDGELIST::Remove (nLLEDGE *edg)
|
||||
//--------------------------------------------
|
||||
{
|
||||
if ( !edg )
|
||||
return;
|
||||
if ( edg->n )
|
||||
edg->n->p = edg->p;
|
||||
if ( edg->p )
|
||||
edg->p->n = edg->n;
|
||||
else root = edg->n;
|
||||
|
||||
// update lut
|
||||
nEENTRY *pedg = lut[edg->a], *ledg = NULL;
|
||||
while ( pedg )
|
||||
{
|
||||
if ( pedg->edge == edg )
|
||||
break;
|
||||
ledg = pedg;
|
||||
pedg = pedg->n;
|
||||
}
|
||||
if ( pedg )
|
||||
{
|
||||
if ( ledg )
|
||||
ledg->n = pedg->n;
|
||||
else lut[edg->a] = pedg->n;
|
||||
delete pedg;
|
||||
}
|
||||
delete edg;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
nLLEDGE *nEDGELIST::GetEdge (uint a, uint b)
|
||||
//-----------------------------------------------------
|
||||
{
|
||||
nEENTRY *pedg = lut[a];
|
||||
while ( pedg && (pedg->edge->b != b) )
|
||||
pedg = pedg->n;
|
||||
if ( !pedg )
|
||||
return NULL;
|
||||
return pedg->edge;
|
||||
}
|
||||
|
||||
//---------------------------------------------------
|
||||
void nEDGELIST::RemapEdges (uint a, uint b)
|
||||
//---------------------------------------------------
|
||||
{
|
||||
nEENTRY *pedg = lut[a], *nedg;
|
||||
|
||||
// remap all edges and wipe invalid ones
|
||||
while ( pedg )
|
||||
{
|
||||
uint ob = pedg->edge->b;
|
||||
nedg = pedg->n;
|
||||
|
||||
Remove (GetEdge (pedg->edge->b, pedg->edge->a));
|
||||
Remove (pedg->edge);
|
||||
|
||||
Add (b, ob);
|
||||
Add (ob, b);
|
||||
|
||||
pedg = nedg;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------
|
||||
void nEDGELIST::SetVertexCount (uint v)
|
||||
//---------------------------------------------
|
||||
{
|
||||
lut = new pnEENTRY[v];
|
||||
for ( uint n = 0; n < v; n++ )
|
||||
lut[n] = NULL;
|
||||
vtx_count = v;
|
||||
}
|
||||
|
||||
//---------------------------
|
||||
nEDGELIST::nEDGELIST ()
|
||||
//---------------------------
|
||||
{
|
||||
lut = NULL;
|
||||
root = NULL;
|
||||
nedg = 0;
|
||||
}
|
||||
|
||||
//----------------------------
|
||||
nEDGELIST::~nEDGELIST ()
|
||||
//----------------------------
|
||||
{
|
||||
uint n;
|
||||
for (n = 0; n < vtx_count; n++)
|
||||
{
|
||||
nEENTRY *pedg = lut[n], *nedg;
|
||||
while (pedg)
|
||||
{
|
||||
nedg = pedg->n;
|
||||
delete pedg;
|
||||
pedg = nedg;
|
||||
}
|
||||
}
|
||||
_safe_delete_array(lut);
|
||||
|
||||
nLLEDGE *pedg = root, *nedg;
|
||||
while (pedg)
|
||||
{
|
||||
nedg = pedg->n;
|
||||
delete pedg;
|
||||
pedg = nedg;
|
||||
}
|
||||
root = NULL;
|
||||
}
|
||||
51
include/engine/core/geometry_rgb.cpp
Normal file
51
include/engine/core/geometry_rgb.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/geometry.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Geometry::SmoothRGB(uint pass_count, float max_smooth_angle)
|
||||
{
|
||||
if (!rgb)
|
||||
return;
|
||||
|
||||
Array <uint> pol_index;
|
||||
ComputePolygonIndex(pol_index);
|
||||
|
||||
Array <VertexToVertex> vtx_to_vtx;
|
||||
ComputeVertexToVertex(vtx_to_vtx);
|
||||
|
||||
Array <Color> dst(binding.GetCount());
|
||||
|
||||
for (uint ns = 0; ns < pass_count; ++ns)
|
||||
{
|
||||
for (uint np = 0; np < pol.GetCount(); ++np)
|
||||
for (uint nv = 0; nv < pol[np].vtx_count; ++nv)
|
||||
{
|
||||
uint imv = pol[np].binding[nv],
|
||||
iv = pol_index[np] + nv;
|
||||
|
||||
dst[iv] = rgb[iv] * 4.f;
|
||||
uint nrgb = 4;
|
||||
|
||||
for (uint nvv = 0; nvv < vtx_to_vtx[imv].vtx_count; nvv++)
|
||||
if (pol[vtx_to_vtx[imv].vtx[nvv].pol_index].material == pol[np].material)
|
||||
{
|
||||
dst[iv] += rgb[pol_index[vtx_to_vtx[imv].vtx[nvv].pol_index] + vtx_to_vtx[imv].vtx[nvv].vtx_index];
|
||||
nrgb++;
|
||||
}
|
||||
|
||||
dst[iv] /= (float)nrgb;
|
||||
}
|
||||
|
||||
Array <Color>::Swap(rgb, dst);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
110
include/engine/core/geometry_tangent.cpp
Normal file
110
include/engine/core/geometry_tangent.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/geometry.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Geometry::ComputePolygonTangent(uint uv_index, bool force)
|
||||
{
|
||||
if (!pol.GetCount() || !vtx.GetCount())
|
||||
return false;
|
||||
|
||||
if (!force && (pol_tangent.GetCount() == pol.GetCount()))
|
||||
return true;
|
||||
|
||||
if (!ComputePolygonNormal())
|
||||
return false;
|
||||
|
||||
if ((uv_index >= __UV_PER_GEOMETRY__) || !uv[uv_index])
|
||||
return false;
|
||||
|
||||
// Allocate polygon tangents.
|
||||
if (!pol_tangent.Allocate(pol.GetCount()))
|
||||
__ERR__(__LOG_W__ << "Geometry::ComputePolygonTangent() failed to allocate buffer!\n", false)
|
||||
|
||||
Array <uint> pol_index;
|
||||
ComputePolygonIndex(pol_index);
|
||||
|
||||
for (uint c = 0; c < pol.GetCount(); c++)
|
||||
if (pol[c].vtx_count > 2)
|
||||
{
|
||||
// Compute tangent frame for this polygon.
|
||||
Vector4 side_0 = vtx[pol[c].binding[0]] - vtx[pol[c].binding[1]],
|
||||
side_1 = vtx[pol[c].binding[2]] - vtx[pol[c].binding[1]];
|
||||
|
||||
float delta_U_0 = uv[uv_index][pol_index[c] + 0].x - uv[uv_index][pol_index[c] + 1].x,
|
||||
delta_U_1 = uv[uv_index][pol_index[c] + 2].x - uv[uv_index][pol_index[c] + 1].x,
|
||||
delta_V_0 = uv[uv_index][pol_index[c] + 0].y - uv[uv_index][pol_index[c] + 1].y,
|
||||
delta_V_1 = uv[uv_index][pol_index[c] + 2].y - uv[uv_index][pol_index[c] + 1].y;
|
||||
|
||||
Vector4 T = (side_0 * delta_V_1 - side_1 * delta_V_0).Normalized().Reversed(),
|
||||
B = (side_0 * delta_U_1 - side_1 * delta_U_0).Normalized();
|
||||
|
||||
if (T.Cross(B).Dot(pol_normal[c]) < 0)
|
||||
{
|
||||
T.Reverse();
|
||||
B.Reverse();
|
||||
}
|
||||
|
||||
pol_tangent[c].B = B;
|
||||
pol_tangent[c].T = T;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool Geometry::ComputeVertexTangent(bool rev_t, bool rev_b, bool force)
|
||||
{
|
||||
if (!pol.GetCount() || !vtx.GetCount())
|
||||
return false;
|
||||
|
||||
if (!force && (vtx_tangent.GetCount() == binding.GetCount()))
|
||||
return true;
|
||||
|
||||
if (!ComputeVertexNormal() || !ComputePolygonTangent())
|
||||
return false;
|
||||
|
||||
Array <VertexToPolygon> vtx_to_pol;
|
||||
ComputeVertexToPolygon(vtx_to_pol);
|
||||
|
||||
// Allocate full blown edge normal array.
|
||||
if (vtx_tangent.Allocate(binding.GetCount()))
|
||||
for (uint cp = 0, ttp = 0; cp < pol.GetCount(); ++cp)
|
||||
for (uint cv = 0; cv < pol[cp].vtx_count; ++cv)
|
||||
{
|
||||
uint gv = pol[cp].binding[cv];
|
||||
|
||||
Vector4 T(0, 0, 0), B(0, 0, 0);
|
||||
for (uint cg = 0; cg < vtx_to_pol[gv].pol_count; cg++)
|
||||
{
|
||||
Vector4 _T = pol_tangent[vtx_to_pol[gv].pol_index[cg]].T,
|
||||
_B = pol_tangent[vtx_to_pol[gv].pol_index[cg]].B;
|
||||
|
||||
if (pol_tangent[cp].T.Dot(_T) < 0.f)
|
||||
_T = _T.Reversed();
|
||||
if (pol_tangent[cp].B.Dot(_B) < 0.f)
|
||||
_B = _B.Reversed();
|
||||
|
||||
T += _T;
|
||||
B += _B;
|
||||
}
|
||||
|
||||
T -= vtx_normal[ttp] * vtx_normal[ttp].Dot(T);
|
||||
T = T.Normalized();
|
||||
B = vtx_normal[ttp].Cross(T);
|
||||
|
||||
vtx_tangent[ttp].T = rev_t ? T.Reversed() : T;
|
||||
vtx_tangent[ttp].B = rev_b ? B.Reversed() : B;
|
||||
|
||||
++ttp;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
238
include/engine/core/geometry_template.cpp
Normal file
238
include/engine/core/geometry_template.cpp
Normal file
@ -0,0 +1,238 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/geometry_template.h"
|
||||
#include "sort/sort.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void GeometryTemplate::ClearMaterials()
|
||||
{ materials.Clear(); }
|
||||
void GeometryTemplate::PushMaterial(const char *uri)
|
||||
{ materials.Add(uri); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#define __AssertPolygon if (!polygon) __ERRRAW__(__LOG_E__ << "You must begin a polygon before pushing attributes.\n")
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void GeometryTemplate::BeginPolygon()
|
||||
{ polygon = new Polygon; }
|
||||
void GeometryTemplate::PushVertex(const Vector4 &v)
|
||||
{ __AssertPolygon
|
||||
polygon->vertex.Add(v); }
|
||||
void GeometryTemplate::PushNormal(const Vector4 &n)
|
||||
{ __AssertPolygon
|
||||
polygon->normal.Add(n); }
|
||||
void GeometryTemplate::PushColor(const Color &c)
|
||||
{ __AssertPolygon
|
||||
polygon->color.Add(c); }
|
||||
void GeometryTemplate::PushUV(uint channel, const Vector2 &uv)
|
||||
{ __AssertPolygon
|
||||
polygon->uv[channel].Add(uv); }
|
||||
void GeometryTemplate::EndPolygon(ushort material)
|
||||
{
|
||||
if (polygon)
|
||||
{
|
||||
polygon->material = material;
|
||||
polygons.Add(polygon.Detach());
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "You must begin a polygon before ending it.\n";
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Vector4 GeometryTemplate::GetVertex(const PolygonVertex &pv)
|
||||
{ return polygons[pv.ipoly]->vertex[pv.ivertex]; }
|
||||
Vector4 GeometryTemplate::GetNormal(const PolygonVertex &pv)
|
||||
{ return polygons[pv.ipoly]->normal[pv.ivertex]; }
|
||||
Vector2 GeometryTemplate::GetUV(uint channel, const PolygonVertex &pv)
|
||||
{ return polygons[pv.ipoly]->uv[channel][pv.ivertex]; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Geometry *GeometryTemplate::Instantiate(const char *name)
|
||||
{
|
||||
if (!materials.GetCount())
|
||||
__ERR__(__LOG_E__ << "Cannot instantiate a geometry with no material array.\n", NULL)
|
||||
|
||||
AutoPtr <Geometry> geo(new Geometry);
|
||||
if (geo.IsNull())
|
||||
__ERR__(__LOG_E__ << "Geometry allocation error.\n", NULL)
|
||||
geo->name = name;
|
||||
|
||||
// Build vertex merge table.
|
||||
uint vtx_per_poly_count = 0;
|
||||
ListForeachPtr(Polygon *, p, polygons)
|
||||
vtx_per_poly_count += p->vertex.GetCount();
|
||||
|
||||
typedef GS::Sort<float, uint> SortVPP;
|
||||
|
||||
Array <PolygonVertex> raw_v(vtx_per_poly_count);
|
||||
Array <SortVPP::Entry> qs_e(vtx_per_poly_count);
|
||||
|
||||
vtx_per_poly_count = 0;
|
||||
uint ipoly = 0;
|
||||
Vector4 &support = polygons[0]->vertex[0];
|
||||
|
||||
ListForeachPtr(Polygon *, p, polygons)
|
||||
{
|
||||
for (uint ivertex = 0; ivertex < p->vertex.GetCount(); ++ivertex)
|
||||
{
|
||||
// Track this vertex.
|
||||
raw_v[vtx_per_poly_count].ipoly = ipoly;
|
||||
raw_v[vtx_per_poly_count].ivertex = ivertex;
|
||||
|
||||
// Quick-sort entry.
|
||||
qs_e[vtx_per_poly_count].o = vtx_per_poly_count;
|
||||
qs_e[vtx_per_poly_count].v = Vector4::Dist2(GetVertex(raw_v[vtx_per_poly_count]), support);
|
||||
|
||||
++vtx_per_poly_count;
|
||||
}
|
||||
++ipoly;
|
||||
}
|
||||
|
||||
// Compute polygon start offsets.
|
||||
Array <int> poly_offset(polygons.GetCount());
|
||||
|
||||
int poly_binding_start_offset = 0;
|
||||
for (uint n = 0; n < polygons.GetCount(); ++n)
|
||||
{
|
||||
poly_offset[n] = poly_binding_start_offset;
|
||||
poly_binding_start_offset += polygons[n]->vertex.GetCount();
|
||||
}
|
||||
|
||||
// Count merged vertex.
|
||||
SortVPP::QuickSort(vtx_per_poly_count, qs_e);
|
||||
geo->binding.Allocate(vtx_per_poly_count);
|
||||
|
||||
uint packed_vtx_count = 0;
|
||||
for (uint n = 0; n < vtx_per_poly_count; )
|
||||
{
|
||||
PolygonVertex &pv = raw_v[qs_e[n].o];
|
||||
geo->binding[poly_offset[pv.ipoly] + pv.ivertex] = packed_vtx_count;
|
||||
|
||||
uint m = n + 1;
|
||||
for (; m < vtx_per_poly_count; ++m)
|
||||
{
|
||||
if (Vector4::Dist2(GetVertex(raw_v[qs_e[n].o]), GetVertex(raw_v[qs_e[m].o])) > merge_threshold)
|
||||
break;
|
||||
|
||||
PolygonVertex &pv = raw_v[qs_e[m].o];
|
||||
geo->binding[poly_offset[pv.ipoly] + pv.ivertex] = packed_vtx_count;
|
||||
}
|
||||
|
||||
++packed_vtx_count;
|
||||
n = m;
|
||||
}
|
||||
|
||||
// Pack vertices.
|
||||
geo->vtx.Allocate(packed_vtx_count);
|
||||
|
||||
packed_vtx_count = 0;
|
||||
for (uint n = 0; n < vtx_per_poly_count; )
|
||||
{
|
||||
uint m = n + 1;
|
||||
for (; m < vtx_per_poly_count; ++m)
|
||||
if (Vector4::Dist2(GetVertex(raw_v[qs_e[n].o]), GetVertex(raw_v[qs_e[m].o])) > merge_threshold)
|
||||
break;
|
||||
|
||||
geo->vtx[packed_vtx_count++] = GetVertex(raw_v[qs_e[n].o]);
|
||||
n = m;
|
||||
}
|
||||
|
||||
// Build polygons.
|
||||
geo->pol.Allocate(polygons.GetCount());
|
||||
|
||||
uint pol_count = 0, pol_bind = 0;
|
||||
ListForeachPtr(Polygon *, p, polygons)
|
||||
{
|
||||
if (p->vertex.GetCount() > 65535)
|
||||
__LOG_W__ << "Too many vertices in polygon " << pol_count << ".\n";
|
||||
|
||||
geo->pol[pol_count].vtx_count = (ushort)p->vertex.GetCount();
|
||||
geo->pol[pol_count].binding = &geo->binding[pol_bind];
|
||||
pol_bind += p->vertex.GetCount();
|
||||
|
||||
if (p->material > materials.GetCount())
|
||||
{
|
||||
p->material = 0;
|
||||
__LOG_W__ << "Polygon " << pol_count << " is referencing a material outside of the material table.\n";
|
||||
}
|
||||
geo->pol[pol_count].material = p->material;
|
||||
++pol_count;
|
||||
}
|
||||
|
||||
// Output remaining attributes.
|
||||
Polygon *poly = polygons[0];
|
||||
|
||||
bool has_normal = asbool(poly->normal.GetCount());
|
||||
bool has_color = asbool(poly->color.GetCount());
|
||||
|
||||
bool has_uv[__UV_PER_GEOMETRY__];
|
||||
for (uint n = 0; n < __UV_PER_GEOMETRY__; ++n)
|
||||
has_uv[n] = asbool(poly->uv[n].GetCount());
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
#define __CreateAttribute(__Destination, __Attrib)\
|
||||
{\
|
||||
if (__Destination.Allocate(vtx_per_poly_count))\
|
||||
{\
|
||||
uint cpol = 0, cvpl = 0;\
|
||||
ListForeachPtr(Polygon *, p, polygons)\
|
||||
{\
|
||||
if (p->vertex.GetCount() != __Attrib.GetCount())\
|
||||
__LOG_W__ << "Incoherent normal count in polygon " << cpol << ".\n";\
|
||||
else\
|
||||
for (uint n = 0; n < p->vertex.GetCount(); ++n)\
|
||||
__Destination[cvpl + n] = __Attrib[n];\
|
||||
\
|
||||
cvpl += p->vertex.GetCount();\
|
||||
++cpol;\
|
||||
}\
|
||||
}\
|
||||
else\
|
||||
__LOG_E__ << "Failed to allocate attribute array while creating geometry '" << name << "'.\n";\
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
if (has_normal)
|
||||
__CreateAttribute(geo->vtx_normal, p->normal)
|
||||
if (has_color)
|
||||
__CreateAttribute(geo->rgb, p->color)
|
||||
|
||||
for (uint i_uv = 0; i_uv < __UV_PER_GEOMETRY__; ++i_uv)
|
||||
if (has_uv[i_uv])
|
||||
__CreateAttribute(geo->uv[i_uv], p->uv[i_uv])
|
||||
|
||||
if (!geo->material_table.Allocate(materials.GetCount()))
|
||||
__ERR__(__LOG_E__ << "Failed to allocate material array when creating geometry '" << name << "'.\n", NULL)
|
||||
|
||||
uint slot_count = 0;
|
||||
ListForeach(String, n, materials)
|
||||
geo->material_table[slot_count++].name = n.Object();
|
||||
|
||||
return geo.Detach();
|
||||
}
|
||||
void GeometryTemplate::Clear()
|
||||
{
|
||||
polygon = NULL;
|
||||
polygons.Clear();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
GeometryTemplate::GeometryTemplate()
|
||||
{
|
||||
merge_threshold = 0.0001f;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
286
include/engine/core/geometry_to_triangle_list.cpp
Normal file
286
include/engine/core/geometry_to_triangle_list.cpp
Normal file
@ -0,0 +1,286 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/geometry_to_triangle_list.h"
|
||||
#include "core/triangle_list.h"
|
||||
#include "core/geometry.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
/*!
|
||||
Converter max vertex per polygon.
|
||||
*/
|
||||
#define PolygonMaxVertexMap 128
|
||||
/*
|
||||
@short Define a larger step to skip polygons during the conversion.
|
||||
For debug purpose only.
|
||||
*/
|
||||
#define TrilistStep 1
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
bool GeometryToTriangleList::Convert(const Geometry &g, AutoList <Trilist *> &trilist, uint mxtri, uint optimize_cache)
|
||||
{
|
||||
__LOG__ << "Building geometry triangle list...\n";
|
||||
|
||||
if (!g.pol.GetCount())
|
||||
__ERR__(__LOG_E__ << "Invalid geometry (" << g.name << ").\n", false)
|
||||
|
||||
Array <uint> pol_index;
|
||||
g.ComputePolygonIndex(pol_index);
|
||||
|
||||
Array <int> vmap(g.vtx.GetCount());
|
||||
if (!vmap)
|
||||
__ERR__(__LOG_E__ << "Could not allocate memory to remap vertice!\n", false)
|
||||
bool warn_vertex_count_exceeded = false;
|
||||
|
||||
Array <VertexToPolygon> vtx_to_pol;
|
||||
g.ComputeVertexToPolygon(vtx_to_pol);
|
||||
|
||||
// Triangle list are split by material.
|
||||
Array <bool> flag;
|
||||
|
||||
for (uint n = 0; n < g.material_table.GetCount(); n++)
|
||||
{
|
||||
uint _p = 0, p;
|
||||
|
||||
// Flag homogeneous vertex for this material.
|
||||
g.FlagHomogeneousVertex(flag, pol_index, vtx_to_pol, n);
|
||||
|
||||
while (_p < g.pol.GetCount())
|
||||
{
|
||||
ushort bone_map[256];
|
||||
int bone_count = 0;
|
||||
|
||||
// Count how many vertex will go in this list.
|
||||
uint tl_vtxc = 0, tl_tric = 0;
|
||||
for (p = 0; p < g.vtx.GetCount(); p++)
|
||||
vmap[p] = -1;
|
||||
|
||||
for (p = _p; p < g.pol.GetCount(); p += TrilistStep)
|
||||
if ((g.pol[p].vtx_count > 2) && (g.pol[p].material == n))
|
||||
{
|
||||
// Check limit constraints.
|
||||
if ((tl_tric + g.pol[p].vtx_count - 2) >= mxtri)
|
||||
break;
|
||||
if (bone_count >= (__PL_BONE_LIMIT__ - 4))
|
||||
break;
|
||||
|
||||
// Valid polygon.
|
||||
for (uint v = 0; v < g.pol[p].vtx_count; v++)
|
||||
{
|
||||
uint pol_crel = g.pol[p].binding[v];
|
||||
|
||||
// Keep track of the bone set for this list.
|
||||
if (g.skin)
|
||||
for (int n = 0; n < 4; ++n)
|
||||
{
|
||||
for (int b = 0; b < bone_count; ++b)
|
||||
if (bone_map[b] == g.skin[pol_crel].bone_index[n])
|
||||
goto bone_registered;
|
||||
|
||||
// Register bone.
|
||||
if (bone_count < __PL_BONE_LIMIT__)
|
||||
bone_map[bone_count++] = g.skin[pol_crel].bone_index[n];
|
||||
else __LOG_E__ << "Bone array safeguard exceeded!\n";
|
||||
|
||||
bone_registered:;
|
||||
}
|
||||
|
||||
if (flag[pol_crel])
|
||||
{
|
||||
if (vmap[pol_crel] == -1)
|
||||
vmap[pol_crel] = tl_vtxc++;
|
||||
}
|
||||
else
|
||||
tl_vtxc++;
|
||||
}
|
||||
tl_tric += g.pol[p].vtx_count - 2;
|
||||
}
|
||||
|
||||
uint break_at = p;
|
||||
|
||||
/*
|
||||
If we have a non-zero vertices triangle list, let's allocate it
|
||||
and its buffers then fill it.
|
||||
*/
|
||||
if (tl_vtxc)
|
||||
{
|
||||
// Allocate trilist.
|
||||
Trilist *ptrilist = new Trilist;
|
||||
if (!ptrilist)
|
||||
__ERR__(__LOG_E__ << "Couldn't allocate triangle list container objects.\n", false)
|
||||
trilist.Add(ptrilist);
|
||||
|
||||
// Allocate trilist buffers.
|
||||
ptrilist->vtx.Allocate(tl_vtxc);
|
||||
ptrilist->idx.Allocate(tl_tric * 3);
|
||||
if (g.skin)
|
||||
ptrilist->skin.Allocate(tl_vtxc);
|
||||
if (bone_count)
|
||||
{
|
||||
ptrilist->bone.Allocate(bone_count);
|
||||
Memory::Copy(&ptrilist->bone[0], bone_map, sizeof(ushort) * bone_count);
|
||||
}
|
||||
|
||||
if (g.vtx_normal)
|
||||
ptrilist->nrm.Allocate(tl_vtxc);
|
||||
if (g.vtx_tangent)
|
||||
ptrilist->tangent.Allocate(tl_vtxc);
|
||||
|
||||
for (uint u = 0; u < __UV_PER_GEOMETRY__; u++) // Note: never remap geometry UV channels.
|
||||
if (g.uv[u])
|
||||
ptrilist->uv[u].Allocate(tl_vtxc);
|
||||
|
||||
if (g.rgb)
|
||||
ptrilist->rgb.Allocate(tl_vtxc);
|
||||
|
||||
// Fill trilist.
|
||||
ptrilist->mat = n;
|
||||
for (p = 0; p < g.vtx.GetCount(); p++)
|
||||
vmap[p] = -1;
|
||||
|
||||
uint c_vtx = 0, c_tri = 0;
|
||||
for (p = _p; p < g.pol.GetCount(); p += TrilistStep)
|
||||
{
|
||||
if ((g.pol[p].vtx_count > 2) && (g.pol[p].material == n))
|
||||
{
|
||||
// Do not overrun.
|
||||
if ((c_tri + g.pol[p].vtx_count - 2) >= mxtri)
|
||||
break;
|
||||
if (p == break_at)
|
||||
break;
|
||||
|
||||
// STOP! Lists will break a few polygons before they are actually ended!
|
||||
#if 0
|
||||
if (ptrilist->vtx.GetCount() == tl_vtxc)
|
||||
break;
|
||||
#endif
|
||||
|
||||
// Append polygon.
|
||||
uint cpol_vmap[PolygonMaxVertexMap], v;
|
||||
|
||||
for (v = 0; v < g.pol[p].vtx_count; v++)
|
||||
{
|
||||
uint pol_crel = g.pol[p].binding[v];
|
||||
|
||||
// Map or insert vertex.
|
||||
bool do_insert_vtx = false;
|
||||
|
||||
if (flag[pol_crel])
|
||||
{
|
||||
if (vmap[pol_crel] == -1)
|
||||
{
|
||||
vmap[pol_crel] = c_vtx;
|
||||
do_insert_vtx = true;
|
||||
}
|
||||
if (v < PolygonMaxVertexMap)
|
||||
cpol_vmap[v] = vmap[pol_crel];
|
||||
else
|
||||
warn_vertex_count_exceeded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v < PolygonMaxVertexMap)
|
||||
{
|
||||
cpol_vmap[v] = c_vtx;
|
||||
do_insert_vtx = true;
|
||||
}
|
||||
else
|
||||
warn_vertex_count_exceeded = true;
|
||||
}
|
||||
|
||||
// Perform vertex insertion.
|
||||
if (do_insert_vtx)
|
||||
{
|
||||
// Position dump.
|
||||
ptrilist->vtx[c_vtx] = g.vtx[pol_crel];
|
||||
|
||||
// Normal dump.
|
||||
if (ptrilist->nrm)
|
||||
ptrilist->nrm[c_vtx] = g.vtx_normal[pol_index[p] + v];
|
||||
|
||||
// Tangent dump.
|
||||
if (ptrilist->tangent)
|
||||
ptrilist->tangent[c_vtx] = g.vtx_tangent[pol_index[p] + v];
|
||||
|
||||
// Skin dump.
|
||||
if (ptrilist->skin)
|
||||
for (int b = 0; b < 4; ++b)
|
||||
{
|
||||
// Resolve skin bone in local trilist bone map.
|
||||
int n;
|
||||
for (n = 0; n < bone_count; ++n)
|
||||
if (bone_map[n] == g.skin[pol_crel].bone_index[b])
|
||||
break;
|
||||
|
||||
// Failed to resolve, cancel this bone out.
|
||||
if (n == bone_count)
|
||||
{
|
||||
ptrilist->skin[c_vtx].w[b] = 0;
|
||||
ptrilist->skin[c_vtx].bone_index[b] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptrilist->skin[c_vtx].w[b] = g.skin[pol_crel].w[b];
|
||||
ptrilist->skin[c_vtx].bone_index[b] = (uchar)n;
|
||||
}
|
||||
}
|
||||
|
||||
// UV sets dump.
|
||||
for (uint u = 0; u < __UV_PER_GEOMETRY__; u++)
|
||||
if (ptrilist->uv[u] && g.uv[u])
|
||||
ptrilist->uv[u][c_vtx] = g.uv[u][pol_index[p] + v];
|
||||
|
||||
// RGB color.
|
||||
if (ptrilist->rgb)
|
||||
ptrilist->rgb[c_vtx] = g.rgb[pol_index[p] + v];
|
||||
|
||||
// Insertion done.
|
||||
++c_vtx;
|
||||
}
|
||||
}
|
||||
|
||||
// Remap and convert this polygon to triangle list index.
|
||||
for (v = 1; v < uint(g.pol[p].vtx_count - 1); v++)
|
||||
{
|
||||
if (v == PolygonMaxVertexMap)
|
||||
break;
|
||||
|
||||
uint itri = c_tri * 3;
|
||||
ptrilist->idx[itri + 0] = cpol_vmap[0];
|
||||
ptrilist->idx[itri + 1] = cpol_vmap[v];
|
||||
ptrilist->idx[itri + 2] = cpol_vmap[v + 1];
|
||||
++c_tri;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_p = p;
|
||||
}
|
||||
}
|
||||
|
||||
int ttri = 0, tbone = 0;
|
||||
ListForeachPtr(Trilist *, ptl, trilist)
|
||||
{
|
||||
ttri += ptl->GetTriangleCount();
|
||||
tbone += ptl->bone.GetCount();
|
||||
}
|
||||
|
||||
if (warn_vertex_count_exceeded)
|
||||
__LOG_E__ << "One or more polygon vertex count exceeded remapping capability (" << PolygonMaxVertexMap << ") in geometry '" << g.name << "'.\n";
|
||||
__LOG__ << "Done, average: " << (float)ttri / trilist.GetCount() << " tri/list, " << (float)tbone / trilist.GetCount() << " bone/list.\n";
|
||||
|
||||
#if 0 // Grabs a few FPS at the cost of much longer load time.
|
||||
if (optimize_cache > 0)
|
||||
Trilist::Optimize(trilist, optimize_cache);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
964
include/engine/core/iso_surface.cpp
Normal file
964
include/engine/core/iso_surface.cpp
Normal file
@ -0,0 +1,964 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include "core/iso_surface.h"
|
||||
#include "core/geometry.h"
|
||||
#include "rand/rand.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
const int Isosurface::EdgeArray[256]={0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
|
||||
0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
|
||||
0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
|
||||
0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
|
||||
0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c,
|
||||
0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
|
||||
0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac,
|
||||
0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
|
||||
0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c,
|
||||
0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
|
||||
0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc,
|
||||
0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
|
||||
0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c,
|
||||
0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
|
||||
0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc ,
|
||||
0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
|
||||
0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
|
||||
0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
|
||||
0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
|
||||
0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
|
||||
0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
|
||||
0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
|
||||
0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
|
||||
0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
|
||||
0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
|
||||
0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
|
||||
0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
|
||||
0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
|
||||
0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
|
||||
0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
|
||||
0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
|
||||
0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 };
|
||||
|
||||
const int Isosurface::TriTable[256][16]=
|
||||
{{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
|
||||
{3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||
{3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
|
||||
{3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||
{2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
|
||||
{8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||
{4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
|
||||
{3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
|
||||
{4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
|
||||
{4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||
{5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
|
||||
{2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
|
||||
{9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||
{2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
|
||||
{10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
|
||||
{4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
|
||||
{5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
|
||||
{5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
|
||||
{10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
|
||||
{8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
|
||||
{2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||
{7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
|
||||
{2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
|
||||
{11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
|
||||
{5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
|
||||
{11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
|
||||
{11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||
{5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
|
||||
{2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||
{5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
|
||||
{6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
|
||||
{3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
|
||||
{6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
|
||||
{5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||
{10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
|
||||
{6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
|
||||
{8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
|
||||
{7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
|
||||
{3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||
{5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
|
||||
{0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
|
||||
{9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
|
||||
{8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
|
||||
{5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
|
||||
{0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
|
||||
{6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
|
||||
{10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
|
||||
{10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
|
||||
{8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
|
||||
{1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
|
||||
{3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
|
||||
{0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||
{10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
|
||||
{3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
|
||||
{6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
|
||||
{9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
|
||||
{8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
|
||||
{3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
|
||||
{6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
|
||||
{10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
|
||||
{10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
|
||||
{2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
|
||||
{7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
|
||||
{7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
|
||||
{2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
|
||||
{1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
|
||||
{11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
|
||||
{8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
|
||||
{0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
|
||||
{7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||
{10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||
{2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||
{6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
|
||||
{7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
|
||||
{2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
|
||||
{10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||
{10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
|
||||
{0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
|
||||
{7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
|
||||
{6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||
{8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
|
||||
{6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
|
||||
{4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
|
||||
{10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
|
||||
{8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
|
||||
{1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||
{8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
|
||||
{10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
|
||||
{4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
|
||||
{10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||
{5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||
{11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
|
||||
{9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||
{6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
|
||||
{7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
|
||||
{3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
|
||||
{7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
|
||||
{3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
|
||||
{6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
|
||||
{9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
|
||||
{1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
|
||||
{4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
|
||||
{7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
|
||||
{6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
|
||||
{3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
|
||||
{0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
|
||||
{6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
|
||||
{0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
|
||||
{11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
|
||||
{6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
|
||||
{5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
|
||||
{9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
|
||||
{1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
|
||||
{10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
|
||||
{0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
|
||||
{5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
|
||||
{10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
|
||||
{11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
|
||||
{9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
|
||||
{7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
|
||||
{2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||
{8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
|
||||
{9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
|
||||
{9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
|
||||
{1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
|
||||
{5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
|
||||
{0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
|
||||
{10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
|
||||
{2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
|
||||
{0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
|
||||
{0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
|
||||
{9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
|
||||
{5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
|
||||
{3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
|
||||
{5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
|
||||
{8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
|
||||
{9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
|
||||
{1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
|
||||
{3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
|
||||
{4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
|
||||
{9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
|
||||
{11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
|
||||
{11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
|
||||
{2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
|
||||
{9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
|
||||
{3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
|
||||
{1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
|
||||
{4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
|
||||
{4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||
{3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
|
||||
{3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
|
||||
{0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
|
||||
{9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
|
||||
{1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};
|
||||
|
||||
/*static*/ uint Isosurface::m_MaxNbVertex = 3000;
|
||||
/*static*/ Isosurface::TVertex* Isosurface::m_TempVertexBuffer = NULL; // Vertex buffer containing the geometry
|
||||
/*static*/ int Isosurface::m_MaxIdx = 3000;
|
||||
/*static*/ int* Isosurface::m_TempIdxBuffer = NULL; // index buffer containing the geometry
|
||||
|
||||
//***************************************************
|
||||
|
||||
void Isosurface::CalcNormalX(float &_x, float &_y, float &_z, Vector4 &_Normal)
|
||||
{
|
||||
float l_Xplus1 = _x+1;
|
||||
float l_Yplus1 = _y+1;
|
||||
float l_Zplus1 = _z+1;
|
||||
float l_Xplus2 = _x+2;
|
||||
float l_Xmoins1 = _x-1;
|
||||
float l_Ymoins1 = _y-1;
|
||||
float l_Zmoins1 = _z-1;
|
||||
// so interpolation is on X, the composant x has a special treatment
|
||||
_Normal.x = interpolateVal(m_Grid[GridIndex(l_Xplus1,_y,_z)], m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(l_Xplus2,_y,_z)]- m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(l_Xplus1,_y,_z)]- m_Grid[GridIndex(l_Xmoins1,_y,_z)]);
|
||||
|
||||
_Normal.y = interpolateVal(m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(l_Xplus1,_y,_z)], m_Grid[GridIndex(_x,l_Yplus1,_z)], m_Grid[GridIndex(l_Xplus1,l_Yplus1,_z)]) -
|
||||
interpolateVal(m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(l_Xplus1,_y,_z)], m_Grid[GridIndex(_x,l_Ymoins1,_z)], m_Grid[GridIndex(l_Xplus1,l_Ymoins1,_z)]);
|
||||
|
||||
_Normal.z = interpolateVal(m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(l_Xplus1,_y,_z)], m_Grid[GridIndex(_x,_y,l_Zplus1)], m_Grid[GridIndex(l_Xplus1,_y,l_Zplus1)]) -
|
||||
interpolateVal(m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(l_Xplus1,_y,_z)], m_Grid[GridIndex(_x,_y,l_Zmoins1)], m_Grid[GridIndex(l_Xplus1,_y,l_Zmoins1)]);
|
||||
}
|
||||
|
||||
//***************************************************
|
||||
|
||||
void Isosurface::CalcNormalY(float &_x, float &_y, float &_z, Vector4 &_Normal)
|
||||
{
|
||||
float l_Xplus1 = _x+1;
|
||||
float l_Yplus1 = _y+1;
|
||||
float l_Zplus1 = _z+1;
|
||||
float l_Yplus2 = _y+2;
|
||||
float l_Xmoins1 = _x-1;
|
||||
float l_Ymoins1 = _y-1;
|
||||
float l_Zmoins1 = _z-1;
|
||||
// so interpolation is on Y, the composant Y has a special treatment
|
||||
_Normal.y = interpolateVal(m_Grid[GridIndex(_x,l_Yplus1,_z)], m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(_x,l_Yplus2,_z)]- m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(_x,l_Yplus1,_z)]- m_Grid[GridIndex(_x,l_Ymoins1,_z)]);
|
||||
|
||||
_Normal.x = interpolateVal(m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(_x,l_Yplus1,_z)], m_Grid[GridIndex(l_Xplus1,_y,_z)], m_Grid[GridIndex(l_Xplus1,l_Yplus1,_z)]) -
|
||||
interpolateVal(m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(_x,l_Yplus1,_z)], m_Grid[GridIndex(l_Xmoins1,_y,_z)], m_Grid[GridIndex(l_Xmoins1,l_Yplus1,_z)]);
|
||||
|
||||
_Normal.z = interpolateVal(m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(_x,l_Yplus1,_z)], m_Grid[GridIndex(_x,_y,l_Zplus1)], m_Grid[GridIndex(_x,l_Yplus1,l_Zplus1)]) -
|
||||
interpolateVal(m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(_x,l_Yplus1,_z)], m_Grid[GridIndex(_x,_y,l_Zmoins1)], m_Grid[GridIndex(_x,l_Yplus1,l_Zmoins1)]);
|
||||
}
|
||||
|
||||
//***************************************************
|
||||
|
||||
void Isosurface::CalcNormalZ(float &_x, float &_y, float &_z, Vector4 &_Normal)
|
||||
{
|
||||
float l_Xplus1 = _x+1;
|
||||
float l_Yplus1 = _y+1;
|
||||
float l_Zplus1 = _z+1;
|
||||
float l_Zplus2 = _z+2;
|
||||
float l_Xmoins1 = _x-1;
|
||||
float l_Ymoins1 = _y-1;
|
||||
float l_Zmoins1 = _z-1;
|
||||
// so interpolation is on Z, the composant Z has a special treatment
|
||||
_Normal.z = interpolateVal(m_Grid[GridIndex(_x,_y,l_Zplus1)], m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(_x,_y,l_Zplus2)]- m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(_x,_y,l_Zplus1)]- m_Grid[GridIndex(_x,_y,l_Zmoins1)]);
|
||||
|
||||
_Normal.y = interpolateVal(m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(_x,_y,l_Zplus1)], m_Grid[GridIndex(_x,l_Yplus1,_z)], m_Grid[GridIndex(_x,l_Yplus1,l_Zplus1)]) -
|
||||
interpolateVal(m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(_x,_y,l_Zplus1)], m_Grid[GridIndex(_x,l_Ymoins1,_z)], m_Grid[GridIndex(_x,l_Ymoins1,l_Zplus1)]);
|
||||
|
||||
_Normal.x = interpolateVal(m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(_x,_y,l_Zplus1)], m_Grid[GridIndex(l_Xplus1,_y,_z)], m_Grid[GridIndex(l_Xplus1,_y,l_Zplus1)]) -
|
||||
interpolateVal(m_Grid[GridIndex(_x,_y,_z)], m_Grid[GridIndex(_x,_y,l_Zplus1)], m_Grid[GridIndex(l_Xmoins1,_y,_z)], m_Grid[GridIndex(l_Xmoins1,_y,l_Zplus1)]);
|
||||
}
|
||||
|
||||
//***************************************************
|
||||
|
||||
// function permit to linear interpolate between vector
|
||||
void Isosurface::interpolateVect(Vector4 &_Vect1, Vector4 &_Vect2, float &_Val1, float &_Val2, Vector4&_Vect)
|
||||
{
|
||||
// _Vect1 and _Vect2 are extremities (with _val1 and _val2)
|
||||
// _Vect is the position to find
|
||||
|
||||
// if(fabsf(m_IsoValue - _Val1) < 0.00001)
|
||||
// {
|
||||
// // to don't have arround error
|
||||
// _Vect = _Vect1;
|
||||
// }
|
||||
//
|
||||
// if(fabsf(m_IsoValue - _Val2) < 0.00001)
|
||||
// {
|
||||
// // to don't have arround error
|
||||
// _Vect = _Vect2;
|
||||
// }
|
||||
//
|
||||
// if(fabsf(_Val1 - _Val2) < 0.00001)
|
||||
// {
|
||||
// // to don't have arround error
|
||||
// _Vect = _Vect1;
|
||||
// }
|
||||
|
||||
float l_Coef = (m_IsoValue - _Val1) / (_Val2 - _Val1);
|
||||
|
||||
_Vect.x = _Vect1.x + l_Coef * (_Vect2.x - _Vect1.x);
|
||||
_Vect.y = _Vect1.y + l_Coef * (_Vect2.y - _Vect1.y);
|
||||
_Vect.z = _Vect1.z + l_Coef * (_Vect2.z - _Vect1.z);
|
||||
|
||||
}
|
||||
|
||||
//***************************************************
|
||||
|
||||
// function permit to linear interpolate between val
|
||||
float Isosurface::interpolateVal(float &_Val1, float &_Val2, float _Val_cible1, float _Val_cible2)
|
||||
{
|
||||
if(fabsf(m_IsoValue - _Val1) < 0.00001)
|
||||
{
|
||||
return _Val_cible1;
|
||||
}
|
||||
|
||||
if(fabsf(m_IsoValue - _Val2) < 0.00001)
|
||||
{
|
||||
return _Val_cible2;
|
||||
}
|
||||
|
||||
if(fabsf(_Val1 - _Val2) < 0.00001)
|
||||
{
|
||||
return _Val_cible1;
|
||||
}
|
||||
|
||||
if(m_IsoValue - _Val1 != 0)
|
||||
{
|
||||
float l_Coef = (m_IsoValue - _Val1) / (_Val2 - _Val1);
|
||||
|
||||
return _Val_cible1 + l_Coef * (_Val_cible2 - _Val_cible1);
|
||||
}
|
||||
else
|
||||
return _Val_cible1;
|
||||
}
|
||||
|
||||
//***************************************************
|
||||
|
||||
// Set the value of the vertex
|
||||
void Isosurface::EvalPos(float &_x, float &_y, float &_z, Vector4 &_CasePosition)
|
||||
{
|
||||
_CasePosition.x = (_x/* - m_NbGridCase.x*0.5f*/) * m_CaseSizeDivNbCase.x /*+m_Pos.x*/;
|
||||
_CasePosition.y = (_y/* - m_NbGridCase.y*0.5f*/) * m_CaseSizeDivNbCase.y /*+m_Pos.y*/;
|
||||
_CasePosition.z = (_z/* - m_NbGridCase.z*0.5f*/) * m_CaseSizeDivNbCase.z /*+m_Pos.z*/;
|
||||
}
|
||||
|
||||
//***************************************************
|
||||
|
||||
// return the good float with the 3d parameter in the grid
|
||||
int Isosurface::GridIndex(float &_x, float &_y, float &_z)const
|
||||
{
|
||||
return (int)(_x+_y*m_NbGridCase.x+_z*m_NbGridCase.x*m_NbGridCase.y);
|
||||
}
|
||||
|
||||
//***************************************************
|
||||
|
||||
/// calcul the polygon for one case, and return the number of triangle for the polygon
|
||||
int Isosurface::CalculPolygon(float &_x, float &_y, float &_z, STriangle* _TriangleList)
|
||||
{
|
||||
float l_GridValues[8]; // values for each vertex of the case
|
||||
Vector4 l_GridPositions[8]; // case vertex position
|
||||
|
||||
// vertex List for the vertex in the final polygon
|
||||
Vector4 l_ListVertex[12];
|
||||
|
||||
float l_Xplus1 = _x+1;
|
||||
float l_Yplus1 = _y+1;
|
||||
float l_Zplus1 = _z+1;
|
||||
|
||||
// calcul values of the vertex of the case
|
||||
l_GridValues[0] = m_Grid[GridIndex(_x,_y,_z)];
|
||||
l_GridValues[1] = m_Grid[GridIndex(l_Xplus1,_y,_z)];
|
||||
l_GridValues[2] = m_Grid[GridIndex(l_Xplus1,_y,l_Zplus1)];
|
||||
l_GridValues[3] = m_Grid[GridIndex(_x,_y,l_Zplus1)];
|
||||
l_GridValues[4] = m_Grid[GridIndex(_x,l_Yplus1,_z)];
|
||||
l_GridValues[5] = m_Grid[GridIndex(l_Xplus1,l_Yplus1,_z)];
|
||||
l_GridValues[6] = m_Grid[GridIndex(l_Xplus1,l_Yplus1,l_Zplus1)];
|
||||
l_GridValues[7] = m_Grid[GridIndex(_x,l_Yplus1,l_Zplus1)];
|
||||
|
||||
// Calcul vertex Position
|
||||
EvalPos(_x, _y, _z, l_GridPositions[0]);
|
||||
EvalPos(l_Xplus1, _y, _z, l_GridPositions[1]);
|
||||
EvalPos(l_Xplus1, _y, l_Zplus1, l_GridPositions[2]);
|
||||
EvalPos(_x, _y, l_Zplus1, l_GridPositions[3]);
|
||||
EvalPos(_x, l_Yplus1, _z, l_GridPositions[4]);
|
||||
EvalPos(l_Xplus1, l_Yplus1, _z, l_GridPositions[5]);
|
||||
EvalPos(l_Xplus1, l_Yplus1, l_Zplus1, l_GridPositions[6]);
|
||||
EvalPos(_x, l_Yplus1, l_Zplus1, l_GridPositions[7]);
|
||||
|
||||
// find the index in the edge Array to know wich side the surface intersect
|
||||
int l_Index = 0;
|
||||
if(l_GridValues[0] < m_IsoValue)
|
||||
l_Index |= 1; // put the bit 0 at 1
|
||||
|
||||
if(l_GridValues[1] < m_IsoValue)
|
||||
l_Index |= 2;
|
||||
if(l_GridValues[2] < m_IsoValue)
|
||||
l_Index |= 4;
|
||||
if(l_GridValues[3] < m_IsoValue)
|
||||
l_Index |= 8;
|
||||
if(l_GridValues[4] < m_IsoValue)
|
||||
l_Index |= 16;
|
||||
if(l_GridValues[5] < m_IsoValue)
|
||||
l_Index |= 32;
|
||||
if(l_GridValues[6] < m_IsoValue)
|
||||
l_Index |= 64;
|
||||
if(l_GridValues[7] < m_IsoValue)
|
||||
l_Index |= 128;
|
||||
|
||||
// Calcul vertex position where the surface intersect the case
|
||||
if(EdgeArray[l_Index] == 0) // the case is out the surface
|
||||
return 0;
|
||||
if(EdgeArray[l_Index] & 1)
|
||||
interpolateVect(l_GridPositions[0], l_GridPositions[1], l_GridValues[0], l_GridValues[1], l_ListVertex[0]);
|
||||
if(EdgeArray[l_Index] & 2)
|
||||
interpolateVect(l_GridPositions[1], l_GridPositions[2], l_GridValues[1], l_GridValues[2], l_ListVertex[1]);
|
||||
if(EdgeArray[l_Index] & 4)
|
||||
interpolateVect(l_GridPositions[2], l_GridPositions[3], l_GridValues[2], l_GridValues[3], l_ListVertex[2]);
|
||||
if(EdgeArray[l_Index] & 8)
|
||||
interpolateVect(l_GridPositions[3], l_GridPositions[0], l_GridValues[3], l_GridValues[0], l_ListVertex[3]);
|
||||
if(EdgeArray[l_Index] & 16)
|
||||
interpolateVect(l_GridPositions[4], l_GridPositions[5], l_GridValues[4], l_GridValues[5], l_ListVertex[4]);
|
||||
if(EdgeArray[l_Index] & 32)
|
||||
interpolateVect(l_GridPositions[5], l_GridPositions[6], l_GridValues[5], l_GridValues[6], l_ListVertex[5]);
|
||||
if(EdgeArray[l_Index] & 64)
|
||||
interpolateVect(l_GridPositions[6], l_GridPositions[7], l_GridValues[6], l_GridValues[7], l_ListVertex[6]);
|
||||
if(EdgeArray[l_Index] & 128)
|
||||
interpolateVect(l_GridPositions[7], l_GridPositions[4], l_GridValues[7], l_GridValues[4], l_ListVertex[7]);
|
||||
if(EdgeArray[l_Index] & 256)
|
||||
interpolateVect(l_GridPositions[0], l_GridPositions[4], l_GridValues[0], l_GridValues[4], l_ListVertex[8]);
|
||||
if(EdgeArray[l_Index] & 512)
|
||||
interpolateVect(l_GridPositions[1], l_GridPositions[5], l_GridValues[1], l_GridValues[5], l_ListVertex[9]);
|
||||
if(EdgeArray[l_Index] & 1024)
|
||||
interpolateVect(l_GridPositions[2], l_GridPositions[6], l_GridValues[2], l_GridValues[6], l_ListVertex[10]);
|
||||
if(EdgeArray[l_Index] & 2048)
|
||||
interpolateVect(l_GridPositions[3], l_GridPositions[7], l_GridValues[3], l_GridValues[7], l_ListVertex[11]);
|
||||
|
||||
// Calcul the triangles
|
||||
int l_NbTriangles = 0;
|
||||
for(int i=0; TriTable[l_Index][i]!=-1; i+=3)
|
||||
{
|
||||
_TriangleList[l_NbTriangles].m_Vertex[0] = l_ListVertex[TriTable[l_Index][i]];
|
||||
_TriangleList[l_NbTriangles].m_Num[0] = TriTable[l_Index][i];
|
||||
|
||||
_TriangleList[l_NbTriangles].m_Vertex[1] = l_ListVertex[TriTable[l_Index][i+1]];
|
||||
_TriangleList[l_NbTriangles].m_Num[1] = TriTable[l_Index][i+1];
|
||||
|
||||
_TriangleList[l_NbTriangles].m_Vertex[2] = l_ListVertex[TriTable[l_Index][i+2]];
|
||||
_TriangleList[l_NbTriangles].m_Num[2] = TriTable[l_Index][i+2];
|
||||
|
||||
++l_NbTriangles;
|
||||
}
|
||||
|
||||
return l_NbTriangles;
|
||||
}
|
||||
|
||||
//***************************************************
|
||||
|
||||
// draw the triangle in the cellule x,y, z
|
||||
void Isosurface::RenderCell(uint &vtx_count, float &_x, float &_y, float &_z)
|
||||
{
|
||||
// nVector l_U, l_V; // 2 vector permit to define 1 face
|
||||
// float l_Color[3]; // color of the current vertex
|
||||
|
||||
Vector4 *l_Pos; // vertex pos
|
||||
|
||||
// Polygon triangle
|
||||
STriangle l_TriangleList[12];
|
||||
|
||||
int l_NbFace= CalculPolygon(_x, _y, _z, l_TriangleList); // nombre de faces in the current polygone
|
||||
|
||||
for( int Face=0; Face< l_NbFace; ++Face)
|
||||
{
|
||||
m_Mutex.Lock();
|
||||
|
||||
// compute the normal vector
|
||||
for(int i=0; i< 3; ++i)
|
||||
{
|
||||
// calcul of the normale for each vertex for each new triangle
|
||||
// nVector l_Normal; // current Normale
|
||||
|
||||
l_Pos = &l_TriangleList[Face].m_Vertex[i];
|
||||
|
||||
/* if(_x>0 && _y>0 && _z>0)
|
||||
{
|
||||
float l_Xplus1 = _x+1;
|
||||
float l_Yplus1 = _y+1;
|
||||
float l_Zplus1 = _z+1;
|
||||
|
||||
switch(l_TriangleList[Face].m_Num[i])
|
||||
{
|
||||
case 0:
|
||||
CalcNormalX(_x, _y, _z, l_Normal);
|
||||
break;
|
||||
case 1:
|
||||
CalcNormalZ(l_Xplus1, _y, _z, l_Normal);
|
||||
break;
|
||||
case 2:
|
||||
CalcNormalX(_x, _y, l_Zplus1, l_Normal);
|
||||
break;
|
||||
case 3:
|
||||
CalcNormalZ(_x, _y, _z, l_Normal);
|
||||
break;
|
||||
case 4:
|
||||
CalcNormalX(_x, l_Yplus1, _z, l_Normal);
|
||||
break;
|
||||
case 5:
|
||||
CalcNormalZ(l_Xplus1, l_Yplus1, _z, l_Normal);
|
||||
break;
|
||||
case 6:
|
||||
CalcNormalX(_x, l_Yplus1, l_Zplus1, l_Normal);
|
||||
break;
|
||||
case 7:
|
||||
CalcNormalZ(_x, l_Yplus1, _z, l_Normal);
|
||||
break;
|
||||
case 8:
|
||||
CalcNormalY(_x, _y, _z, l_Normal);
|
||||
break;
|
||||
case 9:
|
||||
CalcNormalY(l_Xplus1, _y, _z, l_Normal);
|
||||
break;
|
||||
case 10:
|
||||
CalcNormalY(l_Xplus1, _y, l_Zplus1, l_Normal);
|
||||
break;
|
||||
case 11:
|
||||
CalcNormalY(_x, _y, l_Zplus1, l_Normal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
// if don't use the cube mapping
|
||||
// if(texture == NULL)
|
||||
{
|
||||
// compute vertex color
|
||||
/* l_Color[0] = (l_Pos->x/ m_GridSize.x +1) *0.5f;
|
||||
l_Color[1] = (l_Pos->y/ m_GridSize.y +1) *0.5f;
|
||||
l_Color[2] = (l_Pos->z/ m_GridSize.z +1) *0.5f;
|
||||
|
||||
glMaterialfv(GL_FRONT, GL_SPECULAR, l_Color);
|
||||
glMaterialf(GL_FRONT, GL_SHININESS, 50.0f);
|
||||
glMaterialfv(GL_FRONT, GL_AMBIENT, l_Color);
|
||||
glMaterialfv(GL_FRONT, GL_DIFFUSE, l_Color);*/
|
||||
}
|
||||
|
||||
// re- Normalise normal
|
||||
// l_Normal = l_Normal.Normalize();
|
||||
|
||||
// glNormal3f(-l_Normal.x, -l_Normal.y, -l_Normal.z);
|
||||
// glVertex3f(l_Pos->x, l_Pos->y, l_Pos->z);
|
||||
|
||||
if(vtx_count >= m_MaxNbVertex)
|
||||
{ // not enough vertice increase the temp array
|
||||
TVertex* l_NewTempVertexBuffer = new TVertex[m_MaxNbVertex+3000];
|
||||
memcpy(l_NewTempVertexBuffer, m_TempVertexBuffer, sizeof(TVertex)*m_MaxNbVertex);
|
||||
delete []m_TempVertexBuffer;
|
||||
m_MaxNbVertex += 3000;
|
||||
m_TempVertexBuffer = l_NewTempVertexBuffer;
|
||||
}
|
||||
|
||||
if(m_CountIdxBuffer >= m_MaxIdx)
|
||||
{ // not enough vertice increase the temp array
|
||||
int* l_NewTempVertexBuffer = new int[m_MaxIdx+3000];
|
||||
memcpy(l_NewTempVertexBuffer, m_TempIdxBuffer, sizeof(int)*m_MaxIdx);
|
||||
delete []m_TempIdxBuffer;
|
||||
m_MaxIdx += 3000;
|
||||
m_TempIdxBuffer = l_NewTempVertexBuffer;
|
||||
}
|
||||
/*
|
||||
m_TempVertexBuffer[vtx.GetCount()].nx = -l_Normal.x;
|
||||
m_TempVertexBuffer[vtx.GetCount()].ny = -l_Normal.y;
|
||||
m_TempVertexBuffer[vtx.GetCount()].nz = -l_Normal.z;
|
||||
*/
|
||||
|
||||
// ugly stuff to find id
|
||||
int l_IdFind = -1;
|
||||
for(uint id = 0; id<vtx_count; ++id)
|
||||
if( m_TempVertexBuffer[id].x == l_Pos->x
|
||||
&& m_TempVertexBuffer[id].y == l_Pos->y
|
||||
&& m_TempVertexBuffer[id].z == l_Pos->z)
|
||||
{
|
||||
l_IdFind = id;
|
||||
break;
|
||||
}
|
||||
|
||||
if(l_IdFind == -1)
|
||||
{
|
||||
m_TempVertexBuffer[vtx_count].x = l_Pos->x;
|
||||
m_TempVertexBuffer[vtx_count].y = l_Pos->y;
|
||||
m_TempVertexBuffer[vtx_count].z = l_Pos->z;
|
||||
|
||||
l_IdFind = vtx_count;
|
||||
++vtx_count;
|
||||
}
|
||||
|
||||
m_TempIdxBuffer[m_CountIdxBuffer++] = l_IdFind;
|
||||
|
||||
}
|
||||
m_Mutex.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
//*********************************************
|
||||
|
||||
void ClipVector(Vector4 &_VectorIndexClip, Vector4 &_MaxIndex)
|
||||
{
|
||||
if(_VectorIndexClip.x < 0)
|
||||
_VectorIndexClip.x = 0;
|
||||
if(_VectorIndexClip.x >= _MaxIndex.x)
|
||||
_VectorIndexClip.x = _MaxIndex.x-1;
|
||||
if(_VectorIndexClip.y < 0)
|
||||
_VectorIndexClip.y = 0;
|
||||
if(_VectorIndexClip.y >= _MaxIndex.y)
|
||||
_VectorIndexClip.y = _MaxIndex.y-1;
|
||||
if(_VectorIndexClip.z < 0)
|
||||
_VectorIndexClip.z = 0;
|
||||
if(_VectorIndexClip.z >= _MaxIndex.z)
|
||||
_VectorIndexClip.z = _MaxIndex.z-1;
|
||||
}
|
||||
|
||||
//*********************************************
|
||||
void Isosurface::ComputeMetaball(int nb_metaball, Vector4* pos_metaball, float* value_metaball)
|
||||
{
|
||||
Vector4 l_Vect; // vector between the ball center and the case of the grid
|
||||
Vector4 l_Pos, l_PosMetaBall; // case center position
|
||||
float l_Dist; // distance between ball center and the case
|
||||
float l_TempRayon, l_TempRayon2, l_TempDistDivRayon; // rayon of the metaball and distance divide by the rayon
|
||||
int l_Index, l_IndexMax = (int)(m_NbGridCase.x*m_NbGridCase.y*m_NbGridCase.z);
|
||||
|
||||
float x, y, z;
|
||||
int i; // count paramater
|
||||
Vector4 l_PosMetInGridDeb, l_PosMetInGridFin;
|
||||
|
||||
Vector4 l_TempSize(m_GridSize.x/(m_NbGridCase.x-2), m_GridSize.y/(m_NbGridCase.y-2), m_GridSize.z/(m_NbGridCase.z-2));
|
||||
Vector4 l_TempSizeInverse((m_NbGridCase.x-2)/m_GridSize.x, (m_NbGridCase.y-2)/m_GridSize.y, (m_NbGridCase.z-2)/m_GridSize.z);
|
||||
|
||||
// clean all the grid
|
||||
int l_NbCase = (int)(m_NbGridCase.x*m_NbGridCase.y*m_NbGridCase.z);
|
||||
memset(m_Grid, 0, sizeof(float)*l_NbCase);
|
||||
|
||||
// memset(m_GridMetaBall->m_TabIndexValid, 0, sizeof(nVector)*m_NbGridCase.x*m_NbGridCase.y*m_NbGridCase.z);
|
||||
int l_NbIndexValid = 0;
|
||||
|
||||
float l_MetaballIntensity = 1.0f;
|
||||
|
||||
for(i=0; i< nb_metaball; ++i)
|
||||
{
|
||||
l_PosMetaBall = pos_metaball[i];
|
||||
l_TempRayon = value_metaball[i];
|
||||
l_TempRayon2 = l_TempRayon*l_TempRayon;
|
||||
|
||||
if((l_PosMetaBall.x - l_TempRayon < m_Pos.x + m_GridSize.x && l_PosMetaBall.x + l_TempRayon > m_Pos.x)
|
||||
&& (l_PosMetaBall.y - l_TempRayon < m_Pos.y + m_GridSize.y && l_PosMetaBall.y + l_TempRayon > m_Pos.y)
|
||||
&& (l_PosMetaBall.z - l_TempRayon < m_Pos.z + m_GridSize.z && l_PosMetaBall.z + l_TempRayon > m_Pos.z))
|
||||
{
|
||||
l_PosMetInGridDeb.x = (((l_PosMetaBall.x - l_TempRayon)- m_Pos.x)*l_TempSizeInverse.x);
|
||||
l_PosMetInGridDeb.y = (((l_PosMetaBall.y - l_TempRayon)- m_Pos.y)*l_TempSizeInverse.y);
|
||||
l_PosMetInGridDeb.z = (((l_PosMetaBall.z - l_TempRayon)- m_Pos.z)*l_TempSizeInverse.z);
|
||||
|
||||
l_PosMetInGridFin.x = (((l_PosMetaBall.x + l_TempRayon)- m_Pos.x)*l_TempSizeInverse.x);
|
||||
l_PosMetInGridFin.y = (((l_PosMetaBall.y + l_TempRayon)- m_Pos.y)*l_TempSizeInverse.y);
|
||||
l_PosMetInGridFin.z = (((l_PosMetaBall.z + l_TempRayon)- m_Pos.z)*l_TempSizeInverse.z);
|
||||
|
||||
ClipVector(l_PosMetInGridDeb, m_NbGridCase);
|
||||
ClipVector(l_PosMetInGridFin, m_NbGridCase);
|
||||
|
||||
// find only the voxel touch by the metaball
|
||||
|
||||
for(z=l_PosMetInGridDeb.z; z< l_PosMetInGridFin.z; ++z)
|
||||
for(y=l_PosMetInGridDeb.y; y< l_PosMetInGridFin.y; ++y)
|
||||
for(x=l_PosMetInGridDeb.x; x< l_PosMetInGridFin.x; ++x)
|
||||
{
|
||||
// calcul distance between the case and the center of the metal ball
|
||||
l_Vect.x = (x*l_TempSize.x) + m_Pos.x - l_PosMetaBall.x;
|
||||
l_Vect.y = (y*l_TempSize.y) + m_Pos.y - l_PosMetaBall.y;
|
||||
l_Vect.z = (z*l_TempSize.z) + m_Pos.z - l_PosMetaBall.z;
|
||||
|
||||
l_Dist = l_Vect.Len2();
|
||||
// take place for the metaball in the grid
|
||||
if(l_Dist <= l_TempRayon2)
|
||||
//if(l_Dist <= l_TempRayon2 && (y*l_TempSize.y) + m_Pos.y - l_PosMetaBall.y <= l_TempRayon2)
|
||||
{
|
||||
l_Index = (int)(x+ y*m_NbGridCase.x + z*m_NbGridCase.x*m_NbGridCase.y);
|
||||
|
||||
if(l_Index >=0 && l_Index <l_IndexMax)
|
||||
{
|
||||
l_TempDistDivRayon = 1 - l_Dist / l_TempRayon2;
|
||||
m_Grid[l_Index] += l_MetaballIntensity * l_TempDistDivRayon* l_TempDistDivRayon;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// to go faster after, just stock the used voxel
|
||||
// add the index valid , to don't look the other empty voxels
|
||||
for(z=0; z< m_NbGridCase.z-1; ++z)
|
||||
for(y=0; y< m_NbGridCase.y-1; ++y)
|
||||
for(x=0; x< m_NbGridCase.x-1; ++x)
|
||||
{
|
||||
if(m_Grid[(int)(x+ y*m_NbGridCase.x + z*m_NbGridCase.x*m_NbGridCase.y)] > 0.0f
|
||||
|| m_Grid[(int)((x+1)+ y*m_NbGridCase.x + z*m_NbGridCase.x*m_NbGridCase.y)] > 0.0f
|
||||
|| m_Grid[(int)((x+1)+ (y+1)*m_NbGridCase.x + z*m_NbGridCase.x*m_NbGridCase.y)] > 0.0f
|
||||
|| m_Grid[(int)((x+1)+ (y+1)*m_NbGridCase.x + (z+1)*m_NbGridCase.x*m_NbGridCase.y)] > 0.0f
|
||||
|| m_Grid[(int)((x+1)+ y*m_NbGridCase.x + (z+1)*m_NbGridCase.x*m_NbGridCase.y)] > 0.0f
|
||||
|| m_Grid[(int)(x+ (y+1)*m_NbGridCase.x + z*m_NbGridCase.x*m_NbGridCase.y)] > 0.0f
|
||||
|| m_Grid[(int)(x+ (y+1)*m_NbGridCase.x + (z+1)*m_NbGridCase.x*m_NbGridCase.y)] > 0.0f
|
||||
|| m_Grid[(int)(x+ y*m_NbGridCase.x + (z+1)*m_NbGridCase.x*m_NbGridCase.y)] > 0.0f)
|
||||
{
|
||||
m_TabIndexValid[l_NbIndexValid].x = x;
|
||||
m_TabIndexValid[l_NbIndexValid].y = y;
|
||||
m_TabIndexValid[l_NbIndexValid].z = z;
|
||||
|
||||
++l_NbIndexValid;
|
||||
}
|
||||
}
|
||||
|
||||
m_NbIndexValid = l_NbIndexValid;
|
||||
}
|
||||
|
||||
/// Triangularize the iso-surface
|
||||
void Isosurface::Triangularize(Geometry* geometry, int nb_metaball, Vector4* pos_metaball, float* value_metaball)
|
||||
{
|
||||
// point[0].x += 0.1f;
|
||||
// if(point[0].x > 25)
|
||||
// point[0].x = 0;
|
||||
// point[1].z -= 0.1f;
|
||||
// point[2].y -= 0.1f;
|
||||
// if(point[1].z < -5)
|
||||
// point[1].z = 12;
|
||||
// if(point[2].y < -15)
|
||||
// point[2].y = 15;
|
||||
|
||||
ComputeMetaball(nb_metaball, pos_metaball, value_metaball);
|
||||
|
||||
m_CountIdxBuffer = 0;
|
||||
|
||||
uint vtx_count = 0;
|
||||
|
||||
#pragma omp parallel for
|
||||
for(int i=0; i< m_NbIndexValid; ++i)
|
||||
{
|
||||
RenderCell(vtx_count, m_TabIndexValid[i].x, m_TabIndexValid[i].y, m_TabIndexValid[i].z);
|
||||
}
|
||||
|
||||
if(vtx_count >0 && vtx_count <m_MaxNbVertex)
|
||||
{
|
||||
// initialize Cube vertex
|
||||
// nombre de faces et de sommets
|
||||
|
||||
geometry->AllocateVertex(vtx_count);
|
||||
geometry->AllocatePolygon(m_CountIdxBuffer/3);
|
||||
// geometry->vtx_normal = new nVector[vtx.GetCount()];
|
||||
|
||||
// fill the table
|
||||
#pragma omp parallel for
|
||||
for(int i=0; i< static_cast<int> (vtx_count); ++i)
|
||||
{
|
||||
geometry->vtx[i] = Vector4(m_TempVertexBuffer[i].x, m_TempVertexBuffer[i].y, m_TempVertexBuffer[i].z);
|
||||
// geometry->vtx_normal[i*3 + 2-n] = nVector(m_TempVertexBuffer[i*3 + n].nx, m_TempVertexBuffer[i*3 + n].ny, m_TempVertexBuffer[i*3 + n].nz);
|
||||
}
|
||||
|
||||
#pragma omp parallel for
|
||||
for(int i=0; i< static_cast<int> (geometry->pol.GetCount()); ++i)
|
||||
{
|
||||
geometry->pol[i].vtx_count = 3;
|
||||
geometry->pol[i].material = 0;
|
||||
}
|
||||
|
||||
geometry->AllocatePolygonBinding();
|
||||
|
||||
#pragma omp parallel for
|
||||
for(int i=0; i< static_cast<int> (geometry->pol.GetCount()); ++i)
|
||||
{
|
||||
geometry->pol[i].vtx_count = 3;
|
||||
for (int n = 0; n < 3; ++n)
|
||||
geometry->pol[i].binding[2-n] = m_TempIdxBuffer[i * 3 + n];
|
||||
geometry->pol[i].material = 0;
|
||||
}
|
||||
|
||||
geometry->ComputeVertexNormal(true);
|
||||
}
|
||||
}
|
||||
|
||||
//********************************************************************
|
||||
|
||||
bool Isosurface::Init(const Vector4 &_pos, const Vector4 &size, const Vector4 &step)
|
||||
{
|
||||
m_Pos = _pos;
|
||||
|
||||
m_NbGridCase = step;
|
||||
m_GridSize = size;
|
||||
|
||||
delete []m_Grid;
|
||||
|
||||
m_Grid = new float[int(ceil(m_NbGridCase.x)*ceil(m_NbGridCase.y)*ceil(m_NbGridCase.z))];
|
||||
|
||||
// clean all the grid
|
||||
int l_NbCase = (int)(m_NbGridCase.x*m_NbGridCase.y*m_NbGridCase.z);
|
||||
memset(m_Grid, 0, sizeof(float)*l_NbCase);
|
||||
|
||||
delete []m_TabIndexValid;
|
||||
|
||||
m_TabIndexValid = new Vector4[int(ceil(m_NbGridCase.x)*ceil(m_NbGridCase.y)*ceil(m_NbGridCase.z))];
|
||||
|
||||
m_NbIndexValid = 0;
|
||||
|
||||
for(float z=0; z< m_NbGridCase.z-1; ++z)
|
||||
for(float y=0; y< m_NbGridCase.y-1; ++y)
|
||||
for(float x=0; x< m_NbGridCase.x-1; ++x)
|
||||
{
|
||||
//if(m_Grid[x+ y*m_NbGridCase.x + z*m_NbGridCase.x*m_NbGridCase.y])
|
||||
{
|
||||
m_TabIndexValid[m_NbIndexValid].x = x;
|
||||
m_TabIndexValid[m_NbIndexValid].y = y;
|
||||
m_TabIndexValid[m_NbIndexValid].z = z;
|
||||
|
||||
++m_NbIndexValid;
|
||||
}
|
||||
}
|
||||
|
||||
m_CaseSizeDivNbCase = Vector4(m_GridSize.x/m_NbGridCase.x, m_GridSize.y/m_NbGridCase.y, m_GridSize.z/m_NbGridCase.z);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Get the iso-surface field size
|
||||
void Isosurface::GetFieldSize(int &x, int &y, int &z)
|
||||
{
|
||||
x = (int)(m_GridSize.x);
|
||||
y = (int)(m_GridSize.y);
|
||||
z = (int)(m_GridSize.z);
|
||||
}
|
||||
|
||||
/// Get the iso-surface field
|
||||
void Isosurface::GetField(float *_Grid)
|
||||
{
|
||||
_Grid = m_Grid;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------
|
||||
Isosurface::Isosurface()
|
||||
{
|
||||
m_Pos = Vector4(0,0,0);
|
||||
m_Grid = NULL;
|
||||
m_TabIndexValid = NULL;
|
||||
|
||||
m_IsoValue =0.99f;
|
||||
|
||||
m_CountIdxBuffer = 0;
|
||||
m_TempIdxBuffer = new int[m_MaxIdx];
|
||||
|
||||
m_TempVertexBuffer = new TVertex[m_MaxNbVertex];
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
Isosurface::~Isosurface()
|
||||
{
|
||||
delete []m_Grid;
|
||||
delete []m_TempVertexBuffer;
|
||||
}
|
||||
|
||||
341
include/engine/core/item.cpp
Normal file
341
include/engine/core/item.cpp
Normal file
@ -0,0 +1,341 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/item.h"
|
||||
#include "geometry/bounding_box.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Matrix4 Item::GetMatrixNoPivot()
|
||||
{
|
||||
Matrix4 p = GetPivot();
|
||||
SetPivot(Matrix4::IdentityMatrix());
|
||||
Matrix4 m = GetMatrix();
|
||||
SetPivot(p);
|
||||
return m;
|
||||
}
|
||||
void Item::ComputeLocalMatrix()
|
||||
{
|
||||
if (!item_flags.IsSet(ItemFlagLocalMatrixDirty))
|
||||
return;
|
||||
|
||||
if (!item_flags.IsSet(ItemFlagHasTarget) && item_flags.IsSet(ItemFlagRotationMatrixDirty))
|
||||
{
|
||||
rotation_matrix = Matrix3::FromEuler(rotation.x, rotation.y, rotation.z, rorder);
|
||||
item_flags.Remove(ItemFlagRotationMatrixDirty);
|
||||
}
|
||||
|
||||
local_matrix =
|
||||
(
|
||||
Matrix4::TranslationMatrix(position) *
|
||||
Matrix4::FromMatrix3(rotation_matrix) *
|
||||
Matrix4::ScaleMatrix(scale)
|
||||
)
|
||||
*
|
||||
pivot_matrix;
|
||||
|
||||
item_flags.Remove(ItemFlagLocalMatrixDirty);
|
||||
}
|
||||
void Item::ComputeMatrix()
|
||||
{
|
||||
bool update_local = item_flags.IsSet(ItemFlagLocalMatrixDirty),
|
||||
update_world = item_flags.IsSet(ItemFlagWorldMatrixDirty);
|
||||
|
||||
if (update_local)
|
||||
ComputeLocalMatrix();
|
||||
|
||||
if (!parent)
|
||||
{
|
||||
if (update_world)
|
||||
matrix = local_matrix;
|
||||
}
|
||||
else if (update_world)
|
||||
{
|
||||
matrix = parent->GetMatrix() * local_matrix;
|
||||
|
||||
if (item_flags.IsSet(ItemFlagInheritPositionOnly))
|
||||
{
|
||||
Matrix4 m(Matrix4::FromMatrix3(rotation_matrix));
|
||||
m.SetRow(3, matrix.GetRow(3));
|
||||
matrix = m;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle item target and scale.
|
||||
if (update_local || update_world)
|
||||
if (item_flags.IsSet(ItemFlagHasTarget))
|
||||
{
|
||||
matrix.Decompose(NULL, NULL, &rotation_matrix);
|
||||
Matrix3 tgtm = Matrix3::FromOrthonormalBasis(target - matrix.GetRow(3));
|
||||
matrix = matrix * Matrix4::FromMatrix3(rotation_matrix.Transposed() * tgtm);
|
||||
rotation_matrix = tgtm;
|
||||
}
|
||||
|
||||
item_flags.Remove(ItemFlagWorldMatrixDirty);
|
||||
}
|
||||
void Item::ComputeInverseMatrix()
|
||||
{
|
||||
if (item_flags.IsSet(ItemFlagInverseWorldMatrixDirty))
|
||||
{
|
||||
imatrix = GetMatrix().InversedFast();
|
||||
item_flags.Remove(ItemFlagInverseWorldMatrixDirty);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Item::MarkWorldTransformationDirty()
|
||||
{
|
||||
item_flags.Set(ItemFlagWorldMatrixDirty | ItemFlagInverseWorldMatrixDirty);
|
||||
ListForeachPtr(Item *, i, children)
|
||||
i->MarkWorldTransformationDirty();
|
||||
}
|
||||
void Item::MarkTransformationDirty(bool child_only)
|
||||
{
|
||||
if (!child_only)
|
||||
item_flags.Set(ItemFlagLocalMatrixDirty | ItemFlagWorldMatrixDirty | ItemFlagInverseWorldMatrixDirty);
|
||||
|
||||
ListForeachPtr(Item *, i, children)
|
||||
i->MarkWorldTransformationDirty();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Item::SetPosition(const Vector4 &p)
|
||||
{
|
||||
position = p;
|
||||
MarkTransformationDirty();
|
||||
}
|
||||
const Vector4 &Item::GetPosition() const
|
||||
{ return position; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Item::SetRotation(const Vector4 &r)
|
||||
{
|
||||
rotation = r;
|
||||
item_flags.Set(ItemFlagRotationMatrixDirty);
|
||||
MarkTransformationDirty();
|
||||
}
|
||||
void Item::SetRotation(const Quaternion &q)
|
||||
{
|
||||
rotation_matrix = q.AsMatrix3();
|
||||
item_flags.Remove(ItemFlagRotationMatrixDirty);
|
||||
rotation = rotation_matrix.AsEuler(GetRotationOrder());
|
||||
MarkTransformationDirty();
|
||||
}
|
||||
void Item::SetRotation(const Matrix3 &m)
|
||||
{
|
||||
rotation_matrix = m;
|
||||
item_flags.Remove(ItemFlagRotationMatrixDirty);
|
||||
rotation = rotation_matrix.AsEuler(GetRotationOrder());
|
||||
MarkTransformationDirty();
|
||||
}
|
||||
const Vector4 &Item::GetRotation() const
|
||||
{ return rotation; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Item::SetScale(const Vector4 &s)
|
||||
{
|
||||
scale = s;
|
||||
MarkTransformationDirty();
|
||||
}
|
||||
const Vector4 &Item::GetScale() const
|
||||
{ return scale; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Item::SetPivot(const Matrix4 &m)
|
||||
{
|
||||
pivot_matrix = m;
|
||||
MarkTransformationDirty();
|
||||
}
|
||||
const Matrix4 &Item::GetPivot() const
|
||||
{ return pivot_matrix; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const Matrix4 &Item::GetPreviousMatrix() const
|
||||
{ return prv_matrix; }
|
||||
void Item::SetPreviousMatrix(const Matrix4 &mtx)
|
||||
{ prv_matrix = mtx; }
|
||||
const Matrix4 &Item::GetLocalMatrix()
|
||||
{
|
||||
ComputeMatrix();
|
||||
return local_matrix;
|
||||
}
|
||||
const Matrix4 &Item::GetMatrix()
|
||||
{
|
||||
ComputeMatrix();
|
||||
return matrix;
|
||||
}
|
||||
const Matrix4 &Item::GetInverseMatrix()
|
||||
{
|
||||
ComputeInverseMatrix();
|
||||
return imatrix;
|
||||
}
|
||||
const Matrix3 &Item::GetRotationMatrix()
|
||||
{
|
||||
ComputeMatrix();
|
||||
return rotation_matrix;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
void Item::OffsetWorldPosition(const Vector4 &offset)
|
||||
//-------------------------------------------------------------------
|
||||
{
|
||||
if (parent)
|
||||
position += (matrix.GetRow(3) + offset) * parent->imatrix - matrix.GetRow(3) * parent->imatrix;
|
||||
else position += offset;
|
||||
|
||||
MarkTransformationDirty();
|
||||
}
|
||||
|
||||
//------------------------------------------------
|
||||
bool Item::IsLinkedTo(Item *item)
|
||||
//------------------------------------------------
|
||||
{
|
||||
for (Item *c = this; c; )
|
||||
{
|
||||
if (c == item)
|
||||
return true;
|
||||
c = c->GetParent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
void Item::ComputeLocalMinMax(MinMax &minmax) const
|
||||
//------------------------------------------------------------------
|
||||
{
|
||||
Vector4 w_position = matrix.GetRow(3);
|
||||
minmax.Set(w_position, w_position);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
void Item::SnapshotTransformation(const Matrix4 &m, bool local, bool only_matrix)
|
||||
//------------------------------------------------------------------------------------------------
|
||||
{
|
||||
local_matrix = (parent && !local) ? parent->GetInverseMatrix() * m : m;
|
||||
item_flags.Remove(ItemFlagLocalMatrixDirty);
|
||||
|
||||
if (!only_matrix)
|
||||
{
|
||||
// Remove pivot before extracting transformation components.
|
||||
(local_matrix * pivot_matrix.InversedFast()).Decompose(&position, &scale, &rotation_matrix);
|
||||
rotation = rotation_matrix.AsEuler(rorder);
|
||||
item_flags.Remove(ItemFlagRotationMatrixDirty);
|
||||
}
|
||||
|
||||
MarkWorldTransformationDirty();
|
||||
}
|
||||
|
||||
//--------------------------------------------
|
||||
void Item::SetParent(Item *p)
|
||||
//--------------------------------------------
|
||||
{
|
||||
if (p == this)
|
||||
__ERRRAW__(__LOG_E__ << "Cannot parent item to itself.\n")
|
||||
|
||||
// Already linked.
|
||||
if (p && p->children.Find(this)) // FIXME why not if (parent == i)???
|
||||
return;
|
||||
|
||||
// Catch and correct deadlock.
|
||||
for (Item *c = p; c; c = c->GetParent())
|
||||
if (c->GetParent() && (c->GetParent() == this))
|
||||
{
|
||||
c->SetParent(GetParent()); // Extract 'this' from the parent chain.
|
||||
__LOG_W__ << "Deadlock detected, link chain has been corrected.\n";
|
||||
break;
|
||||
}
|
||||
|
||||
// Remove from current parent children list.
|
||||
if (parent)
|
||||
parent->children.Remove(this);
|
||||
|
||||
// Set parent.
|
||||
parent = p;
|
||||
|
||||
// Insert as a new parent child.
|
||||
if (parent)
|
||||
parent->children.Add(this);
|
||||
|
||||
MarkTransformationDirty();
|
||||
}
|
||||
|
||||
//----------------------------------------------------
|
||||
void Item::SetTarget(const Vector4 *p)
|
||||
//----------------------------------------------------
|
||||
{
|
||||
if (p)
|
||||
{
|
||||
item_flags.Set(ItemFlagHasTarget);
|
||||
target = *p;
|
||||
}
|
||||
else
|
||||
item_flags.Remove(ItemFlagHasTarget);
|
||||
|
||||
MarkTransformationDirty();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
void Item::SetMatrix(const Matrix4 &m)
|
||||
//-----------------------------------------------------
|
||||
{
|
||||
SnapshotTransformation(m, false);
|
||||
|
||||
matrix = m;
|
||||
imatrix = m.InversedFast();
|
||||
item_flags.Remove(ItemFlagWorldMatrixDirty | ItemFlagInverseWorldMatrixDirty);
|
||||
|
||||
MarkTransformationDirty(true);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
void Item::SetRotationOrder(Math::rOrder order)
|
||||
//--------------------------------------------------------------
|
||||
{
|
||||
rorder = order;
|
||||
MarkTransformationDirty();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Item::Item() : position(0, 0, 0), rotation(0, 0, 0), scale(1, 1, 1)
|
||||
{
|
||||
local_matrix = Matrix4::IdentityMatrix();
|
||||
prv_matrix = Matrix4::IdentityMatrix();
|
||||
matrix = Matrix4::IdentityMatrix();
|
||||
imatrix = Matrix4::IdentityMatrix();
|
||||
pivot_matrix = Matrix4::IdentityMatrix();
|
||||
|
||||
parent = NULL;
|
||||
|
||||
rorder = Math::rOrder_Default;
|
||||
|
||||
scale.Set(1,1,1,1);
|
||||
|
||||
mitem = NULL;
|
||||
target.Set(0, 0, 1);
|
||||
|
||||
opacity = 1;
|
||||
|
||||
MarkTransformationDirty();
|
||||
item_flags.Set(ItemFlagRotationMatrixDirty);
|
||||
}
|
||||
Item::~Item()
|
||||
{
|
||||
ListForeachPtr(Item *, child, children)
|
||||
child->SetParent(NULL);
|
||||
SetParent(NULL);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
156
include/engine/core/item_nml.cpp
Normal file
156
include/engine/core/item_nml.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/item.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Item::FromMetaTag(Tag &tag)
|
||||
{
|
||||
if (tag.name != "Item")
|
||||
__ERR__(__LOG_E__ << "Could not parse item, incorrect root tag (" << tag.name << ").\n", false)
|
||||
|
||||
item_flags = 0;
|
||||
|
||||
position.Set(0, 0, 0);
|
||||
rotation.Set(0, 0, 0);
|
||||
scale.Set(1, 1, 1);
|
||||
|
||||
opacity = 1;
|
||||
pivot_matrix = Matrix4::IdentityMatrix();
|
||||
rorder = Math::rOrder_Default;
|
||||
|
||||
registry.Clear();
|
||||
|
||||
// Parse root tags.
|
||||
NMLTagForeach(pt, tag)
|
||||
{
|
||||
// Legacy support.
|
||||
if (pt->name == "OffsetPosition")
|
||||
{
|
||||
Vector4 position;
|
||||
position.FromMetaTag(*pt);
|
||||
pivot_matrix.SetRow(3, position);
|
||||
}
|
||||
else if (pt->name == "OffsetMatrix")
|
||||
pivot_matrix.FromMetaTag(*pt);
|
||||
|
||||
else if (pt->name == "Position")
|
||||
position.FromMetaTag(*pt);
|
||||
else if (pt->name == "Rotation")
|
||||
rotation.FromMetaTag(*pt);
|
||||
else if (pt->name == "Scale")
|
||||
scale.FromMetaTag(*pt);
|
||||
|
||||
else if (pt->name == "Target")
|
||||
target.FromMetaTag(*pt);
|
||||
else if (pt->name == "TargetOffsetRotation")
|
||||
;
|
||||
|
||||
else if (pt->name == "ItemFlag")
|
||||
{
|
||||
NMLTagForeach(ft, *pt)
|
||||
{
|
||||
if (ft->name == "HasTarget")
|
||||
item_flags.Set(ItemFlagHasTarget);
|
||||
if (ft->name == "Invisible")
|
||||
item_flags.Set(ItemFlagInvisible);
|
||||
if (ft->name == "LinkOnlyPosition")
|
||||
item_flags.Set(ItemFlagInheritPositionOnly);
|
||||
}
|
||||
}
|
||||
|
||||
else if (pt->name == "RotationOrder")
|
||||
{
|
||||
String ro(pt->GetString());
|
||||
|
||||
if (ro == "ZYX") rorder = Math::rOrder_ZYX;
|
||||
else if (ro == "YZX") rorder = Math::rOrder_YZX;
|
||||
else if (ro == "ZXY") rorder = Math::rOrder_ZXY;
|
||||
else if (ro == "XZY") rorder = Math::rOrder_XZY;
|
||||
else if (ro == "YXZ") rorder = Math::rOrder_YXZ;
|
||||
else if (ro == "XYZ") rorder = Math::rOrder_XYZ;
|
||||
else if (ro == "XY") rorder = Math::rOrder_XY;
|
||||
|
||||
else __LOG_W__ << "Unknown rotation order '" << ro << "'.\n";
|
||||
}
|
||||
else if (
|
||||
(pt->name == "OrientationQuaternion") ||
|
||||
(pt->name == "OrientationMatrix")
|
||||
)
|
||||
;
|
||||
else if (pt->name == "Registry")
|
||||
{
|
||||
NMLTagForeach(child, *pt)
|
||||
registry.AddRoot(child->Clone());
|
||||
}
|
||||
else __LOG_W__ << "Unknown tag '" << pt->name << "' in <Item>.\n";
|
||||
}
|
||||
|
||||
MarkTransformationDirty();
|
||||
item_flags.Set(ItemFlagRotationMatrixDirty);
|
||||
|
||||
ComputeMatrix();
|
||||
ComputeInverseMatrix();
|
||||
return true;
|
||||
}
|
||||
Tag *Item::AsMetaTag() const
|
||||
{
|
||||
Tag *root = new Tag("Item");
|
||||
if (!root)
|
||||
__ERR__(__LOG_E__ << "Could not serialize item. Failed to create root tag.\n", NULL)
|
||||
|
||||
Vector4 NULL_vec(0, 0, 0), NULL_scale(1, 1, 1);
|
||||
|
||||
// Save transformation.
|
||||
if (pivot_matrix != Matrix4::IdentityMatrix())
|
||||
root->AddChild(pivot_matrix.AsMetaTag("OffsetMatrix"));
|
||||
if (position != NULL_vec)
|
||||
root->AddChild(position.AsMetaTag("Position"));
|
||||
if (rotation != NULL_vec)
|
||||
root->AddChild(rotation.AsMetaTag("Rotation"));
|
||||
if (scale != NULL_scale)
|
||||
root->AddChild(scale.AsMetaTag("Scale"));
|
||||
|
||||
if (item_flags.Get() != 0)
|
||||
if (Tag *ft = root->AddChild("ItemFlag"))
|
||||
{
|
||||
if (item_flags.IsSet(ItemFlagHasTarget))
|
||||
ft->AddChild("HasTarget");
|
||||
if (item_flags.IsSet(ItemFlagInvisible))
|
||||
ft->AddChild("Invisible");
|
||||
if (item_flags.IsSet(ItemFlagInheritPositionOnly))
|
||||
ft->AddChild("LinkOnlyPosition");
|
||||
}
|
||||
|
||||
root->AddChild(target.AsMetaTag("Target"));
|
||||
|
||||
switch (rorder)
|
||||
{
|
||||
case Math::rOrder_ZYX: root->AddChild("RotationOrder", "ZYX"); break;
|
||||
case Math::rOrder_YZX: root->AddChild("RotationOrder", "YZX"); break;
|
||||
case Math::rOrder_ZXY: root->AddChild("RotationOrder", "ZXY"); break;
|
||||
case Math::rOrder_XZY: root->AddChild("RotationOrder", "XZY"); break;
|
||||
case Math::rOrder_YXZ: root->AddChild("RotationOrder", "YXZ"); break;
|
||||
case Math::rOrder_XYZ: root->AddChild("RotationOrder", "XYZ"); break;
|
||||
case Math::rOrder_XY: root->AddChild("RotationOrder", "XY"); break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Registry.
|
||||
Tag *tag_registry = root->AddChild("Registry");
|
||||
NMLFileForeach(child, registry)
|
||||
tag_registry->AddChild(child->Clone());
|
||||
|
||||
return root;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
227
include/engine/core/light.cpp
Normal file
227
include/engine/core/light.cpp
Normal file
@ -0,0 +1,227 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/light.h"
|
||||
#include "core/resource_factories.h"
|
||||
#include "core/render_resource_factory.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Light::ComputeProjectionMatrix(Matrix4 &m) const
|
||||
{
|
||||
switch (model)
|
||||
{
|
||||
case Model_Spot:
|
||||
{
|
||||
const float q = volume_range / (volume_range - z_near),
|
||||
fov = (cone_angle + edge_angle),
|
||||
zoom_factor = float(Math::Cos(fov) / Math::Sin(fov));
|
||||
|
||||
// Default aspect ration: PC(1:1).
|
||||
m.Set
|
||||
(
|
||||
zoom_factor, 0, 0, 0,
|
||||
0, zoom_factor, 0, 0,
|
||||
0, 0, q, 1,
|
||||
0, 0, -q * z_near, 0
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
m = Matrix4::IdentityMatrix();
|
||||
break;
|
||||
}
|
||||
}
|
||||
void Light::ComputeFrustrum(Frustum &f, float zn, float zf) const
|
||||
{
|
||||
if (model == Model_Spot)
|
||||
f.SetPerspective((cone_angle + edge_angle) * 2, zn != -1 ? zn : z_near, zf != -1 ? zf : volume_range, &GetMatrix(), 1, 1);
|
||||
}
|
||||
void Light::ComputeMatrix()
|
||||
{
|
||||
bool update_world = item_flags.IsSet(ItemFlagWorldMatrixDirty);
|
||||
|
||||
Item::ComputeMatrix();
|
||||
|
||||
// Remove scale from world matrix.
|
||||
if (update_world)
|
||||
{
|
||||
Vector4 u = matrix.GetRow(0).Normalized();
|
||||
matrix.m[0][0] = u.x; matrix.m[1][0] = u.y; matrix.m[2][0] = u.z;
|
||||
Vector4 v = matrix.GetRow(1).Normalized();
|
||||
matrix.m[0][1] = v.x; matrix.m[1][1] = v.y; matrix.m[2][1] = v.z;
|
||||
Vector4 w = matrix.GetRow(2).Normalized();
|
||||
matrix.m[0][2] = w.x; matrix.m[1][2] = w.y; matrix.m[2][2] = w.z;
|
||||
}
|
||||
ComputeFrustrum(frustum);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Light::SampleColor(const Vector4 &location, const Vector4 &normal, Color &diff, Color &spec, Vector4 *view, float gloss)
|
||||
{
|
||||
float idiff, ispec;
|
||||
|
||||
if (!SampleEnergy(location, normal, &idiff, &ispec, view, gloss, true, false))
|
||||
{
|
||||
diff.Set();
|
||||
spec.Set();
|
||||
return false;
|
||||
}
|
||||
|
||||
diff = diffuse_color * diffuse_intensity * idiff;
|
||||
spec = specular_color * specular_intensity * ispec;
|
||||
return true;
|
||||
}
|
||||
bool Light::SampleEnergy(const Vector4 &loc, const Vector4 &normal, float *diff, float *spec, Vector4 *view, float gloss, bool halfway, bool cooktorrance)
|
||||
{
|
||||
if (!diff && !spec)
|
||||
return false;
|
||||
|
||||
Vector4 dt;
|
||||
float a = 1;
|
||||
|
||||
switch (model)
|
||||
{
|
||||
case Model_Linear:
|
||||
dt = GetMatrix().GetRow(2).Reversed().Normalized();
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
dt = GetMatrix().GetRow(3) - loc;
|
||||
float dt_length = dt.Len();
|
||||
dt /= dt_length;
|
||||
|
||||
// Compute light attenuation.
|
||||
if (range)
|
||||
{
|
||||
float distance = dt_length / range;
|
||||
|
||||
switch (falloff)
|
||||
{
|
||||
/*
|
||||
case Falloff_InvDist:
|
||||
if (distance)
|
||||
a = 1 / distance;
|
||||
break;
|
||||
|
||||
case Falloff_InvDist2:
|
||||
if (distance)
|
||||
a = 1 / (distance * distance);
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
case Falloff_Linear:
|
||||
a = 1 - distance;
|
||||
break;
|
||||
}
|
||||
if (a <= 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (model == Model_Spot)
|
||||
{
|
||||
float c = Math::ACos(dt.Reversed().Dot(GetMatrix().GetRow(2).Normalized()));
|
||||
if ((c < 0) || (c > (cone_angle + edge_angle)))
|
||||
return false;
|
||||
if (c > cone_angle)
|
||||
a *= 1 - (c - cone_angle) / edge_angle;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Sample diffuse.
|
||||
float e = normal.Dot(dt);
|
||||
if (e < 0)
|
||||
return false;
|
||||
|
||||
if (diff)
|
||||
*diff = e * a * diffuse_intensity;
|
||||
|
||||
/*
|
||||
Sample specular.
|
||||
Use the halfway vector method if you'd like to avoid a vector reflection.
|
||||
*/
|
||||
if (spec)
|
||||
{
|
||||
*spec = 0;
|
||||
|
||||
if (view)
|
||||
{
|
||||
Vector4 local_view = view->Normalized();
|
||||
float e = 0;
|
||||
|
||||
if (cooktorrance)
|
||||
__LOG_W__ << "Cook-Torrance specular model not implemented.\n";
|
||||
|
||||
else
|
||||
{
|
||||
if (halfway)
|
||||
e = (dt - local_view).Normalized().Dot(normal);
|
||||
else e = local_view.Reflected(normal).Dot(dt);
|
||||
|
||||
e = (e > 0) ? Math::Pow(e, gloss * 96.f) : 0;
|
||||
}
|
||||
*spec = e * a * specular_intensity;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Light::RenderSetup(ResourceFactories *f)
|
||||
{
|
||||
render_data = new RenderData;
|
||||
if (f && f->render)
|
||||
if (!projection_texture.IsEmpty())
|
||||
render_data->projection_texture = f->render->LoadTexture(projection_texture);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Light::SetDefaults()
|
||||
{
|
||||
model = Light::Model_Point;
|
||||
shadow = Light::Shadow_None;
|
||||
|
||||
range = Units::Mtr(0.f);
|
||||
volume_range = Units::Mtr(500.f);
|
||||
clip_distance = Units::Mtr(300.f);
|
||||
cone_angle = Units::Deg(30.f);
|
||||
edge_angle = Units::Deg(30.f);
|
||||
|
||||
shadow_cast_all = false;
|
||||
shadow_bias = 0.01f;
|
||||
shadow_distribution = 0.9f;
|
||||
shadow_range = Units::Mtr(100.f);
|
||||
shadow_color.Set(0, 0, 0);
|
||||
|
||||
volumetric = false;
|
||||
volumetric_sample_step = 0.5f;
|
||||
volumetric_range = 8.0f;
|
||||
volumetric_thickness = 1.0f;
|
||||
|
||||
z_near = Units::Cm(2.f);
|
||||
falloff = Falloff_Linear;
|
||||
|
||||
diffuse_color = Color::White;
|
||||
diffuse_intensity = 1;
|
||||
specular_color = Color::White;
|
||||
specular_intensity = 0;
|
||||
}
|
||||
Light::Light()
|
||||
{
|
||||
SetDefaults();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
93
include/engine/core/light_nml.cpp
Normal file
93
include/engine/core/light_nml.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/light.h"
|
||||
#include "metafile/nml_object.h"
|
||||
#include "reflection/c_refl.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using GS::Core::Light;
|
||||
using GS::NML::Tag;
|
||||
using namespace GS::Reflection;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Enum::Dict Light::model_dict[] =
|
||||
{
|
||||
{ Model_None, "None" },
|
||||
{ Model_Point, "Point" },
|
||||
{ Model_Linear, "Parallel" },
|
||||
{ Model_Spot, "Spot" }
|
||||
};
|
||||
Enum::Dict Light::shadow_dict[] =
|
||||
{
|
||||
{ Shadow_None, "None" },
|
||||
{ Shadow_ProjectionMap, "PMap" },
|
||||
{ Shadow_Map, "Map" }
|
||||
};
|
||||
Enum::Dict Light::falloff_dict[] =
|
||||
{
|
||||
{ Falloff_Linear, "Linear" },
|
||||
{ Falloff_InvDist, "InvDist" },
|
||||
{ Falloff_InvDist2, "InvDist2" }
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Property Light::serializable[] =
|
||||
{
|
||||
{ Property::EnumProp, "Type", offsetof(Light, model), model_dict },
|
||||
{ Property::EnumProp, "Falloff", offsetof(Light, falloff), falloff_dict },
|
||||
{ Property::FloatProp, "Range", offsetof(Light, range), 0 },
|
||||
{ Property::FloatProp, "VolumeRange", offsetof(Light, volume_range), 0 },
|
||||
{ Property::FloatProp, "ClipDistance", offsetof(Light, clip_distance), 0 },
|
||||
|
||||
{ Property::StringProp, "ProjectionMap", offsetof(Light, projection_texture), 0 },
|
||||
|
||||
{ Property::FloatProp, "DiffuseIntensity", offsetof(Light, diffuse_intensity), 0 },
|
||||
{ Property::FloatProp, "SpecularIntensity", offsetof(Light, specular_intensity), 0 },
|
||||
|
||||
{ Property::FloatProp, "ConeAngle", offsetof(Light, cone_angle), 0 },
|
||||
{ Property::FloatProp, "EdgeAngle", offsetof(Light, edge_angle), 0 },
|
||||
{ Property::FloatProp, "ZNear", offsetof(Light, z_near), 0 },
|
||||
|
||||
{ Property::EnumProp, "Shadow", offsetof(Light, shadow), shadow_dict },
|
||||
{ Property::FloatProp, "ShadowBias", offsetof(Light, shadow_bias), 0 },
|
||||
{ Property::FloatProp, "ShadowDistribution", offsetof(Light, shadow_distribution), 0 },
|
||||
{ Property::FloatProp, "ShadowRange", offsetof(Light, shadow_range), 0 },
|
||||
{ Property::BoolProp, "ShadowCastAll", offsetof(Light, shadow_cast_all), 0 },
|
||||
|
||||
{ Property::BoolProp, "Volumetric", offsetof(Light, volumetric), 0 },
|
||||
{ Property::FloatProp, "VolumetricSampleStep", offsetof(Light, volumetric_sample_step), 0 },
|
||||
{ Property::FloatProp, "VolumetricThick", offsetof(Light, volumetric_thickness), 0 },
|
||||
{ Property::FloatProp, "VolumetricRange", offsetof(Light, volumetric_range), 0 },
|
||||
|
||||
{ Property::InvalidProp, 0, 0 }
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Light::FromMetaTag(Tag &t)
|
||||
{
|
||||
SetDefaults();
|
||||
if ((t.name != "Light") || !GenericObjectFromMetaTag(t, this, serializable))
|
||||
return false;
|
||||
if (Tag *c = t.GetTag("Item")) Item::FromMetaTag(*c);
|
||||
if (Tag *c = t.GetTag("Diffuse")) diffuse_color.FromMetaTag(*c);
|
||||
if (Tag *c = t.GetTag("Specular")) specular_color.FromMetaTag(*c);
|
||||
if (Tag *c = t.GetTag("ShadowColor")) shadow_color.FromMetaTag(*c);
|
||||
return true;
|
||||
}
|
||||
Tag *Light::AsMetaTag()
|
||||
{
|
||||
Tag *t = new Tag("Light");
|
||||
t->AddChild(Item::AsMetaTag());
|
||||
t->AddChild(diffuse_color.AsMetaTag("Diffuse"));
|
||||
t->AddChild(specular_color.AsMetaTag("Specular"));
|
||||
t->AddChild(shadow_color.AsMetaTag("ShadowColor"));
|
||||
return GenericObjectToMetaTag(t, this, serializable);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
93
include/engine/core/material.cpp
Normal file
93
include/engine/core/material.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/material.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Material::TextureStage *Material::GetChannelStage(MaterialChannel channel) const
|
||||
{
|
||||
for (int n = 0; n < max_texture_stage; ++n)
|
||||
if (texstage[n].channel == channel)
|
||||
return &texstage[n];
|
||||
return NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Material::TextureStage *Material::NewStage(MaterialChannel channel, const char *uri, UVMode uv_mode, uchar uv_index)
|
||||
{
|
||||
TextureStage *s = GetChannelStage(channel);
|
||||
if (s)
|
||||
return s;
|
||||
|
||||
// Find a free stage.
|
||||
for (int n = 0; n < max_texture_stage; ++n)
|
||||
if (texstage[n].channel == Channel_None)
|
||||
{
|
||||
s = &texstage[n];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!s)
|
||||
__ERR__(__LOG_W__ << "No more free texture stage, cannot create new stage.\n", NULL)
|
||||
|
||||
// Initialize stage.
|
||||
s->channel = channel;
|
||||
s->t = uri;
|
||||
s->uv_mode = uv_mode;
|
||||
s->uv_index = uv_index;
|
||||
return s;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Material::Reset()
|
||||
{
|
||||
renderword = Render_None;
|
||||
blendop = Blend_None;
|
||||
|
||||
diffuse.Set(1, 1, 1);
|
||||
specular.Set(1, 1, 1);
|
||||
self.Set(0, 0, 0);
|
||||
ambient.Set(1, 1, 1);
|
||||
|
||||
glossiness = 0.4f;
|
||||
opacity = 1;
|
||||
reflection = 0;
|
||||
irefraction = 1;
|
||||
athreshold = 0.1f;
|
||||
depth_bias = 0;
|
||||
|
||||
shader.Clear();
|
||||
|
||||
for (int n = 0; n < max_texture_stage; n++)
|
||||
texstage[n].Reset();
|
||||
texstage_count = 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Material::TextureStage::Reset()
|
||||
{
|
||||
channel = Channel_None;
|
||||
op = Operator_Default;
|
||||
t.Clear();
|
||||
|
||||
uv_mode = UV_UV;
|
||||
uv_matrix = Matrix4::IdentityMatrix();
|
||||
uv_index = 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Material::Material()
|
||||
{
|
||||
texstage.Allocate(max_texture_stage);
|
||||
Reset();
|
||||
}
|
||||
54
include/engine/core/material_channel.cpp
Normal file
54
include/engine/core/material_channel.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/material_channel.h"
|
||||
#include "nstring/nstring.h"
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char *GS::Core::MaterialChannelName(MaterialChannel channel)
|
||||
{
|
||||
switch (channel)
|
||||
{
|
||||
case Channel_Diffuse: return "Diffuse";
|
||||
case Channel_Decal: return "Decal";
|
||||
case Channel_Opacity: return "Opacity";
|
||||
case Channel_Specular: return "Specular";
|
||||
case Channel_Glossiness: return "Glossiness";
|
||||
case Channel_Normal: return "Normal";
|
||||
case Channel_Reflection: return "Reflection";
|
||||
case Channel_SelfIllum: return "SelfIllum";
|
||||
case Channel_Light: return "Light";
|
||||
case Channel_BlendRGB: return "BlendRGB";
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
GS::Core::MaterialChannel GS::Core::MaterialChannelFromName(const char *channel)
|
||||
{
|
||||
GS::String c(channel);
|
||||
|
||||
if (c == "Diffuse") return Channel_Diffuse;
|
||||
else if (c == "Decal") return Channel_Decal;
|
||||
else if (c == "Opacity") return Channel_Opacity;
|
||||
else if (c == "Specular") return Channel_Specular;
|
||||
else if (c == "Glossiness") return Channel_Glossiness;
|
||||
else if (c == "Normal") return Channel_Normal;
|
||||
else if (c == "Reflection") return Channel_Reflection;
|
||||
else if (c == "SelfIllum") return Channel_SelfIllum;
|
||||
else if (c == "BlendRGB") return Channel_BlendRGB;
|
||||
else if (c == "Light") return Channel_Light;
|
||||
#if 1
|
||||
else if (c == "Detail") return Channel_Decal;
|
||||
else if (c == "VertexColor") return Channel_Light;
|
||||
else if (c == "Color") return Channel_Light;
|
||||
#endif
|
||||
|
||||
return Channel_None;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
310
include/engine/core/material_nml.cpp
Normal file
310
include/engine/core/material_nml.cpp
Normal file
@ -0,0 +1,310 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/material.h"
|
||||
#include "core/embedded_resource_handler_interface.h"
|
||||
#include "metafile/nml.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Material::FromMetaTag(Tag &tag)
|
||||
{
|
||||
if (tag.name != "Material")
|
||||
__ERR__(__LOG_E__ << "Could not parse material, incorrect root tag (" << tag.name << ").\n", false);
|
||||
|
||||
Reset();
|
||||
|
||||
/*
|
||||
Implementation note.
|
||||
These strings are declared here once for the whole function
|
||||
to benefit from the hash value-based early exit that the String
|
||||
comparison function provide.
|
||||
*/
|
||||
static String _diffuse("Diffuse"), _specular("Specular"), _self("Self"), _ambient("Ambient"),
|
||||
_glossiness("Glossiness"), _reflection("Reflection"), _ior("IOR"), _ath("AlphaThreshold"),
|
||||
_msa("MaxSmoothingAngle"), _opac("Opacity"), _rmask("RenderMask"), _blendop("BlendOp"), _shadermap("ShaderMap"),
|
||||
_shd("Shader"), _stage("TextureStage"), _physic("PhysicMaterial"), _depthbias("DepthBias");
|
||||
|
||||
NMLTagForeach(pt, tag)
|
||||
{
|
||||
if (pt->name == _diffuse) diffuse.FromMetaTag(*pt);
|
||||
else if (pt->name == _specular) specular.FromMetaTag(*pt);
|
||||
else if (pt->name == _self) self.FromMetaTag(*pt);
|
||||
else if (pt->name == _ambient) ambient.FromMetaTag(*pt);
|
||||
|
||||
else if (pt->name == _glossiness) glossiness = Types::Clamp(pt->GetReal(), 0.01f, 16.f);
|
||||
else if (pt->name == _reflection) reflection = pt->GetReal();
|
||||
else if (pt->name == _ior) irefraction = pt->GetReal();
|
||||
else if (pt->name == _msa); // Legacy
|
||||
else if (pt->name == _opac) opacity = pt->GetReal();
|
||||
else if (pt->name == _ath) athreshold = pt->GetReal();
|
||||
else if (pt->name == _depthbias) depth_bias = pt->GetReal();
|
||||
|
||||
else if (pt->name == _shd) shader = pt->GetString();
|
||||
|
||||
// Physic material.
|
||||
else if (pt->name == _physic)
|
||||
PhysicMaterial::FromMetaTag(*pt);
|
||||
|
||||
// Render word.
|
||||
else if (pt->name == _rmask)
|
||||
{
|
||||
static String _shdless("Shadeless"), _smooth("Smoothing"), _add("Additive"), _dbs("DoubleSided"), _usefbuffer("UseFramebuffer"), _skn("Skinned"),
|
||||
_wire("Wireframe"), _unlit("Unlit"), _vcolor("VertexColor"), _soft("SoftBody"), _sub("Subtractive"), _subs("Substractive"),
|
||||
_vnrm("ForceVertexNormal"), _nrmtgt("NormalMapTangent"), _toon("Toon"), _alpha("Alpha"), _atest("AlphaTest"),
|
||||
_parr("ParralaxDisp"), _nozw("NoZWrite"), _nozt("NoZTest"), _nofog("NoFog"), _fixdfunc("FixedFunction"), _asoftz("AlphaSoftZ"), _asmap("AlphaInShadow");
|
||||
|
||||
NMLTagForeach(mskt, *pt)
|
||||
{
|
||||
if (mskt->name == _unlit) renderword |= Render_Unlit;
|
||||
else if (mskt->name == _smooth) renderword |= Render_Smooth;
|
||||
else if (mskt->name == _nrmtgt) renderword |= Render_NormalTangent;
|
||||
else if (mskt->name == _nofog) renderword |= Render_NoFog;
|
||||
|
||||
else if (mskt->name == _dbs) renderword |= Render_DoubleSided;
|
||||
else if (mskt->name == _wire) renderword |= Render_Wire;
|
||||
else if (mskt->name == _vcolor) renderword |= Render_VertexColor;
|
||||
else if (mskt->name == _parr) renderword |= Render_ParralaxDisp;
|
||||
else if (mskt->name == _toon) renderword |= Render_Toon;
|
||||
|
||||
else if (mskt->name == _nozw) renderword |= Render_NoZWrite;
|
||||
else if (mskt->name == _nozt) renderword |= Render_NoZTest;
|
||||
|
||||
else if (mskt->name == _asoftz) renderword |= Render_AlphaSoftZ;
|
||||
else if (mskt->name == _asmap) renderword |= Render_AlphaInShadow;
|
||||
else if (mskt->name == _atest) renderword |= Render_AlphaTest;
|
||||
|
||||
else if (mskt->name == _usefbuffer) renderword |= Render_UseFramebuffer;
|
||||
else if (mskt->name == _skn) renderword |= Render_Skinned;
|
||||
#if 1 // Legacy
|
||||
else if (mskt->name == _add) blendop = Blend_Add;
|
||||
else if (mskt->name == _alpha) blendop = Blend_Alpha;
|
||||
else if (mskt->name == _fixdfunc) blendop = Blend_Alpha;
|
||||
|
||||
else if (mskt->name == "Lit")
|
||||
;
|
||||
#endif
|
||||
|
||||
else __LOG_W__ << "Unknown tag '" << mskt->name << "' in material '" << name << "'.\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Blend operator.
|
||||
else if (pt->name == _blendop)
|
||||
{
|
||||
String op(pt->GetString());
|
||||
|
||||
if (op == "Add")
|
||||
blendop = Blend_Add;
|
||||
else if (op == "Alpha")
|
||||
blendop = Blend_Alpha;
|
||||
}
|
||||
|
||||
#if (__PLATFORM_NINTENDO_WII__ == 0)
|
||||
// Shader map.
|
||||
else if (pt->name == _shadermap)
|
||||
IEmbeddedResourceHandler::Get()->ExtractEmbeddedShaderTree(shader, *pt, name);
|
||||
#endif
|
||||
|
||||
// Texture stage.
|
||||
else if (pt->name == _stage)
|
||||
{
|
||||
Tag *idx = pt->GetTag("Index");
|
||||
if (idx && (idx->GetType() == Variant::VariantInteger))
|
||||
{
|
||||
int n = idx->GetInteger();
|
||||
|
||||
if ((n >= 0) && (n < 8))
|
||||
{
|
||||
TextureStage *lvl = &texstage[n];
|
||||
|
||||
static String _texture("Texture"), _iuv("UV");
|
||||
static String _operator("Operator"), _uvsrc("UVSource");
|
||||
|
||||
// Reset level.
|
||||
lvl->channel = Channel_None;
|
||||
lvl->op = Operator_Default;
|
||||
lvl->uv_index = 0;
|
||||
|
||||
// Fetch channel first.
|
||||
Tag *tst = pt->GetTag("Channel");
|
||||
lvl->channel = tst ? MaterialChannelFromName(tst->GetString()) : Channel_Diffuse;
|
||||
|
||||
// Pool remaining tags.
|
||||
NMLTagForeach(tst, *pt)
|
||||
{
|
||||
if (tst->name == _texture)
|
||||
lvl->t = tst->GetString();
|
||||
|
||||
else if (tst->name == _iuv)
|
||||
lvl->uv_index = (uchar)tst->GetInteger();
|
||||
|
||||
else if (tst->name == _operator)
|
||||
{
|
||||
String op(tst->GetString());
|
||||
|
||||
if (op == "Multiply")
|
||||
lvl->op = Operator_Multiply;
|
||||
else if (op == "Add")
|
||||
lvl->op = Operator_Add;
|
||||
}
|
||||
|
||||
// Texture level UV source.
|
||||
else if (tst->name == _uvsrc)
|
||||
{
|
||||
String fnv(tst->GetString());
|
||||
|
||||
if (fnv == "UVMap") lvl->uv_mode = UV_UV;
|
||||
else if (fnv == "LSNormal") lvl->uv_mode = UV_LSN;
|
||||
else if (fnv == "FrontMap") lvl->uv_mode = UV_FrontMap;
|
||||
else if (fnv == "SphericalEnvironment") lvl->uv_mode = UV_SphericalEnvironment;
|
||||
|
||||
else __LOG_W__ << "Unknown UV source for texture stage " << n << " of material '" << name << "'.\n";
|
||||
}
|
||||
}
|
||||
texstage_count++;
|
||||
}
|
||||
else __LOG_W__ << "Illegal texture stage index " << n << " in material '" << name << "'.\n";
|
||||
}
|
||||
else __LOG_W__ << "Illegal index tag type in material '" << name << "'.\n";
|
||||
}
|
||||
#if 1 // Legacy
|
||||
else if (pt->name == "Id")
|
||||
;
|
||||
#endif
|
||||
else __LOG_W__ << "Unknown tag '" << pt->name << "' in <Material>.\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tag *Material::AsMetaTag() const
|
||||
{
|
||||
Tag *material = new Tag("Material");
|
||||
if (!material)
|
||||
__ERR__(__LOG_E__ << "Could not serialize material '" << name << "'. Failed to create root tag.\n", NULL)
|
||||
|
||||
// Base properties.
|
||||
if (diffuse != Color::White)
|
||||
material->AddChild(diffuse.AsMetaTag("Diffuse", true));
|
||||
if (specular != Color::White)
|
||||
material->AddChild(specular.AsMetaTag("Specular"));
|
||||
if (self != Color::Black)
|
||||
material->AddChild(self.AsMetaTag("Self"));
|
||||
if (ambient != Color::White)
|
||||
material->AddChild(ambient.AsMetaTag("Ambient"));
|
||||
|
||||
if (glossiness != 0.4f)
|
||||
material->AddChild("Glossiness", glossiness);
|
||||
if (opacity != 1)
|
||||
material->AddChild("Opacity", opacity);
|
||||
if (reflection != 0)
|
||||
material->AddChild("Reflection", reflection);
|
||||
if (irefraction != 1)
|
||||
material->AddChild("IOR", irefraction);
|
||||
if (athreshold != 0.1)
|
||||
material->AddChild("AlphaThreshold", athreshold);
|
||||
if (depth_bias != 0)
|
||||
material->AddChild("DepthBias", depth_bias);
|
||||
|
||||
// Physics.
|
||||
material->AddChild(PhysicMaterial::AsMetaTag());
|
||||
|
||||
// Render word.
|
||||
if (renderword != Render_None)
|
||||
{
|
||||
if (Tag *rword = material->AddChild("RenderMask"))
|
||||
{
|
||||
if (renderword & Render_Unlit) rword->AddChild("Unlit");
|
||||
if (renderword & Render_Smooth) rword->AddChild("Smoothing");
|
||||
if (renderword & Render_NormalTangent) rword->AddChild("NormalMapTangent");
|
||||
if (renderword & Render_NoFog) rword->AddChild("NoFog");
|
||||
|
||||
if (renderword & Render_DoubleSided) rword->AddChild("DoubleSided");
|
||||
if (renderword & Render_Wire) rword->AddChild("Wireframe");
|
||||
if (renderword & Render_VertexColor) rword->AddChild("VertexColor");
|
||||
if (renderword & Render_ParralaxDisp) rword->AddChild("ParralaxDisp");
|
||||
if (renderword & Render_Toon) rword->AddChild("Toon");
|
||||
|
||||
if (renderword & Render_NoZWrite) rword->AddChild("NoZWrite");
|
||||
if (renderword & Render_NoZTest) rword->AddChild("NoZTest");
|
||||
|
||||
if (renderword & Render_AlphaSoftZ) rword->AddChild("AlphaSoftZ");
|
||||
if (renderword & Render_AlphaInShadow) rword->AddChild("AlphaInShadow");
|
||||
if (renderword & Render_AlphaTest) rword->AddChild("AlphaTest");
|
||||
|
||||
if (renderword & Render_UseFramebuffer) rword->AddChild("UseFramebuffer");
|
||||
if (renderword & Render_Skinned) rword->AddChild("Skinned");
|
||||
}
|
||||
else __LOG_W__ << "Could not serialize material '" << name << "' render word.\n";
|
||||
}
|
||||
|
||||
// Blend operator.
|
||||
if (blendop != Blend_None)
|
||||
switch (blendop)
|
||||
{
|
||||
case Blend_Add: material->AddChild("BlendOp", "Add"); break;
|
||||
case Blend_Alpha: material->AddChild("BlendOp", "Alpha"); break;
|
||||
}
|
||||
|
||||
// Shader id.
|
||||
if (!shader.IsEmpty())
|
||||
material->AddChild("Shader", shader.c_str());
|
||||
|
||||
// Texture stages.
|
||||
for (int n = 0; n < max_texture_stage; n++)
|
||||
{
|
||||
const TextureStage *lvl = &texstage[n];
|
||||
|
||||
if (!lvl->t.IsEmpty())
|
||||
{
|
||||
if (Tag *ts = new Tag("TextureStage"))
|
||||
{
|
||||
material->AddChild(ts);
|
||||
|
||||
ts->AddChild("Index", n);
|
||||
ts->AddChild("Texture", lvl->t.c_str());
|
||||
ts->AddChild("UV", lvl->uv_index);
|
||||
|
||||
if (lvl->op != Operator_Default)
|
||||
switch (lvl->op)
|
||||
{
|
||||
case Operator_Add: ts->AddChild("Operator", "Add"); break;
|
||||
case Operator_Multiply: ts->AddChild("Operator", "Multiply"); break;
|
||||
}
|
||||
|
||||
if (lvl->channel != Channel_Diffuse)
|
||||
{
|
||||
if (const char *c = MaterialChannelName(lvl->channel))
|
||||
ts->AddChild("Channel", c);
|
||||
else
|
||||
__LOG_W__ << "Bogus channel in texture stage " << n << " material '" << name << "'.\n";
|
||||
}
|
||||
|
||||
if (lvl->uv_mode != UV_UV)
|
||||
switch (lvl->uv_mode)
|
||||
{
|
||||
case UV_UV: ts->AddChild("UVSource", "UVMap"); break;
|
||||
case UV_LSN: ts->AddChild("UVSource", "LSNormal"); break;
|
||||
case UV_FrontMap: ts->AddChild("UVSource", "FrontMap"); break;
|
||||
case UV_SphericalEnvironment: ts->AddChild("UVSource", "SphericalEnvironment"); break;
|
||||
|
||||
default:
|
||||
__LOG_W__ << "Bogus channel in texture stage " << n << " material '" << name << "'.\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else __LOG_W__ << "Could not serialize a texture stage in material '" << name << "'.\n";
|
||||
}
|
||||
}
|
||||
return material;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
261
include/engine/core/material_to_shader_tree.cpp
Normal file
261
include/engine/core/material_to_shader_tree.cpp
Normal file
@ -0,0 +1,261 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/material_to_shader_tree.h"
|
||||
#include "core/shader_block.h"
|
||||
#include "core/shader_tree.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ShaderBlock *MaterialToShaderTree::MaterialTextureStageToShaderBlock(const Material &m, const Material::TextureStage *ts, ShaderBlock *nb)
|
||||
{
|
||||
if (!ts)
|
||||
__ERR__(__LOG_W__ << "Cannot convert material null texture stage.\n", NULL)
|
||||
|
||||
// Get texture stage index.
|
||||
uint n = 0;
|
||||
for (; n < Core::Material::max_texture_stage; ++n)
|
||||
if (ts == &m.texstage[n])
|
||||
break;
|
||||
if (n == Material::max_texture_stage)
|
||||
__ERR__(__LOG_W__ << "Texture stage does not belong to this material.\n", NULL)
|
||||
|
||||
//
|
||||
MaterialTextureShaderBlock *texture_block = new MaterialTextureShaderBlock(n);
|
||||
TextureSamplerShaderBlock *sampler_block = new TextureSamplerShaderBlock;
|
||||
ShaderBlock *uv_block = NULL, *output_block = NULL;
|
||||
|
||||
switch (ts->uv_mode)
|
||||
{
|
||||
case Material::UV_UV:
|
||||
uv_block = new GeometryUVShaderBlock(ts->uv_index);
|
||||
break;
|
||||
|
||||
case Material::UV_FrontMap:
|
||||
{
|
||||
uv_block = new DivOperatorShaderBlock(new ScreenUVShaderBlock, new SwizzleShaderBlock(new ViewportShaderBlock, SwizzleShaderBlock::SwizzleZ, SwizzleShaderBlock::SwizzleW));
|
||||
uv_block = new MulOperatorShaderBlock(new ConstantShaderBlock(1, -1), uv_block);
|
||||
if ((ts->channel == Channel_Reflection) && nb)
|
||||
{
|
||||
ShaderBlock *offset_block = new MulOperatorShaderBlock(new SwizzleShaderBlock(nb, SwizzleShaderBlock::SwizzleX, SwizzleShaderBlock::SwizzleY), new ConstantShaderBlock(0.01f, 0.01f));
|
||||
uv_block = new AddOperatorShaderBlock(uv_block, offset_block);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Material::UV_LSN:
|
||||
{
|
||||
ShaderBlock *tmp = new MulOperatorShaderBlock(new NormalViewMatrixShaderBlock, nb ? nb : new GeometryNormalShaderBlock);
|
||||
tmp = new PackVectorToColorShaderBlock(tmp);
|
||||
tmp = new SwizzleShaderBlock(tmp, SwizzleShaderBlock::SwizzleX, SwizzleShaderBlock::SwizzleY);
|
||||
uv_block = tmp;
|
||||
}
|
||||
break;
|
||||
|
||||
case Material::UV_SphericalEnvironment:
|
||||
{
|
||||
// find the reflective vector
|
||||
ConstantShaderBlock* negative_vector = new ConstantShaderBlock(-1.0f, -1.0f, -1.0f);
|
||||
|
||||
GeometryNormalShaderBlock* normal_stream = nb ? (GeometryNormalShaderBlock*)nb : new GeometryNormalShaderBlock;
|
||||
NormalMatrixShaderBlock* normal_matrix = new NormalMatrixShaderBlock();
|
||||
MulOperatorShaderBlock* mul_operator_normal = new MulOperatorShaderBlock(normal_matrix, normal_stream);
|
||||
|
||||
MulOperatorShaderBlock* mul_operator1 = new MulOperatorShaderBlock(negative_vector, mul_operator_normal);
|
||||
|
||||
// dot the negative normal and the view vector
|
||||
ViewVectorShaderBlock* view_vector = new ViewVectorShaderBlock();
|
||||
DotOperatorShaderBlock* dot_vector1 =new DotOperatorShaderBlock(mul_operator1, view_vector);
|
||||
|
||||
// take the abs of the dot
|
||||
AbsShaderBlock* abs_operator2 = new AbsShaderBlock(dot_vector1);
|
||||
|
||||
// normal * 2 * abs(dot(-n, view))
|
||||
ConstantShaderBlock* constant_vector2 = new ConstantShaderBlock(2.0f, 2.0f, 2.0f);
|
||||
MulOperatorShaderBlock* mul_operator3 = new MulOperatorShaderBlock(mul_operator_normal, constant_vector2);
|
||||
|
||||
BuildShaderBlock* build_vector1 = new BuildShaderBlock(abs_operator2, BuildShaderBlock::BuildX, abs_operator2, BuildShaderBlock::BuildX, abs_operator2, BuildShaderBlock::BuildX);
|
||||
|
||||
MulOperatorShaderBlock* mul_operator4 = new MulOperatorShaderBlock(build_vector1, mul_operator3);
|
||||
|
||||
// view + (normal * 2 * abs(dot(-n, view)))
|
||||
AddOperatorShaderBlock* add_vector1 = new AddOperatorShaderBlock(view_vector, mul_operator4);
|
||||
|
||||
NormalizeOperatorShaderBlock* normalize1 = new NormalizeOperatorShaderBlock(add_vector1);
|
||||
|
||||
//compute the euler angle from the vector , to transform into uv coordinate
|
||||
|
||||
// change the Y into {0,1}
|
||||
BuildShaderBlock* build_vector2 = new BuildShaderBlock(normalize1, BuildShaderBlock::BuildY );
|
||||
PackVectorToColorShaderBlock* pack_vector1 = new PackVectorToColorShaderBlock(build_vector2);
|
||||
|
||||
// change the XZ into {0, 1}
|
||||
BuildShaderBlock* build_vector3 = new BuildShaderBlock(normalize1, BuildShaderBlock::BuildX, normalize1, BuildShaderBlock::BuildZ, NULL, BuildShaderBlock::BuildZero);
|
||||
NormalizeOperatorShaderBlock* normalize2 = new NormalizeOperatorShaderBlock(build_vector3 );
|
||||
|
||||
// do the dot in x
|
||||
ConstantShaderBlock* constant_vector5 = new ConstantShaderBlock(1.0f, 0.0f, 0.0f);
|
||||
DotOperatorShaderBlock* dot_vector2 =new DotOperatorShaderBlock(constant_vector5, normalize2);
|
||||
|
||||
// set this dot from {-1, 1} to {0, 1}
|
||||
PackVectorToColorShaderBlock* pack_vector2 = new PackVectorToColorShaderBlock(dot_vector2);
|
||||
|
||||
// do the dot in y
|
||||
ConstantShaderBlock* constant_vector6 = new ConstantShaderBlock(0.0f, 1.0f, 0.0f);
|
||||
DotOperatorShaderBlock* dot_vector3 =new DotOperatorShaderBlock(constant_vector6, normalize2);
|
||||
|
||||
// abs the dot and make it -1 or 1
|
||||
AbsShaderBlock* abs_operator3 = new AbsShaderBlock(dot_vector3);
|
||||
DivOperatorShaderBlock* div_vector3 = new DivOperatorShaderBlock(dot_vector3 , abs_operator3);
|
||||
|
||||
// multiply the 2 transform dot to obtain the good angle between {-1, 1}
|
||||
MulOperatorShaderBlock* mul_operator6 = new MulOperatorShaderBlock(pack_vector2, div_vector3);
|
||||
|
||||
// transform result from {-1, 1} to {0, 1}
|
||||
PackVectorToColorShaderBlock* pack_vector3 = new PackVectorToColorShaderBlock(mul_operator6);
|
||||
|
||||
// create the vector for the sampler 2D
|
||||
BuildShaderBlock* build_vector4 = new BuildShaderBlock(pack_vector1, BuildShaderBlock::BuildX, pack_vector3, BuildShaderBlock::BuildX);
|
||||
|
||||
uv_block = build_vector4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
sampler_block->input[0] = texture_block;
|
||||
sampler_block->input[1] = uv_block;
|
||||
|
||||
output_block = sampler_block;
|
||||
switch (ts->channel)
|
||||
{
|
||||
case Channel_Normal:
|
||||
if (m.renderword & Material::Render_NormalTangent)
|
||||
{
|
||||
GeometryTangentFrameShaderBlock *tangent_frame_block = new GeometryTangentFrameShaderBlock;
|
||||
SwizzleShaderBlock *swizzle_block = new SwizzleShaderBlock(sampler_block, SwizzleShaderBlock::SwizzleX, SwizzleShaderBlock::SwizzleY, SwizzleShaderBlock::SwizzleZ);
|
||||
output_block = new MulOperatorShaderBlock(tangent_frame_block, new UnpackColorToVectorShaderBlock(swizzle_block));
|
||||
}
|
||||
else
|
||||
output_block = new UnpackColorToVectorShaderBlock(new SwizzleShaderBlock(sampler_block, SwizzleShaderBlock::SwizzleX, SwizzleShaderBlock::SwizzleY, SwizzleShaderBlock::SwizzleZ));
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
return output_block;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool MaterialToShaderTree::Convert(const Material &m, ShaderTree &tree)
|
||||
{
|
||||
// Normal.
|
||||
ShaderBlock *normal_block = NULL;
|
||||
if (m.GetChannelStage(Channel_Normal))
|
||||
normal_block = new NormalizeOperatorShaderBlock(MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_Normal)));
|
||||
else normal_block = new GeometryNormalShaderBlock;
|
||||
|
||||
// Diffuse.
|
||||
ShaderBlock *diffuse_block = NULL;
|
||||
|
||||
if (m.GetChannelStage(Channel_Diffuse))
|
||||
{
|
||||
ShaderBlock *texa_block = MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_Diffuse), normal_block);
|
||||
|
||||
if (m.GetChannelStage(Channel_BlendRGB))
|
||||
{
|
||||
ShaderBlock *texb_block = MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_BlendRGB), normal_block);
|
||||
diffuse_block = new MixOperatorShaderBlock(texb_block, texa_block, new SwizzleShaderBlock(new GeometryVertexColorShaderBlock, SwizzleShaderBlock::SwizzleX));
|
||||
}
|
||||
else
|
||||
diffuse_block = texa_block;
|
||||
}
|
||||
else diffuse_block = new MaterialParamShaderBlock(MaterialParamShaderBlock::MaterialDiffuse);
|
||||
|
||||
// RGB stream (only if no blend rgb stage active).
|
||||
GeometryVertexColorShaderBlock *vertex_color_block = NULL;
|
||||
|
||||
if (!m.GetChannelStage(Channel_BlendRGB))
|
||||
if (m.renderword & Material::Render_VertexColor)
|
||||
{
|
||||
vertex_color_block = new GeometryVertexColorShaderBlock;
|
||||
diffuse_block = new MulOperatorShaderBlock(diffuse_block, new BuildShaderBlock(vertex_color_block, BuildShaderBlock::BuildX, vertex_color_block, BuildShaderBlock::BuildY, vertex_color_block, BuildShaderBlock::BuildZ, 0, BuildShaderBlock::BuildOne));
|
||||
}
|
||||
|
||||
// Light map & decal map.
|
||||
if (m.GetChannelStage(Channel_Decal))
|
||||
{
|
||||
ShaderBlock *decal_block = MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_Decal), normal_block);
|
||||
diffuse_block = new MixOperatorShaderBlock(decal_block, diffuse_block, new SwizzleShaderBlock(decal_block, SwizzleShaderBlock::SwizzleW));
|
||||
}
|
||||
if (m.GetChannelStage(Channel_Light))
|
||||
diffuse_block = new MulOperatorShaderBlock(diffuse_block, MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_Light), normal_block));
|
||||
|
||||
// Self.
|
||||
ShaderBlock *constant_block = NULL;
|
||||
if (m.GetChannelStage(Channel_SelfIllum))
|
||||
constant_block = MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_SelfIllum), normal_block);
|
||||
else constant_block = new MaterialParamShaderBlock(MaterialParamShaderBlock::MaterialSelf);
|
||||
|
||||
// Specular.
|
||||
ShaderBlock *specular_block = NULL;
|
||||
if (m.GetChannelStage(Channel_Specular))
|
||||
specular_block = MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_Specular), normal_block);
|
||||
else specular_block = new MaterialParamShaderBlock(MaterialParamShaderBlock::MaterialSpecular);
|
||||
|
||||
// Reflection.
|
||||
if (m.GetChannelStage(Channel_Reflection))
|
||||
{
|
||||
Material::TextureStage *reflection_stage = m.GetChannelStage(Channel_Reflection);
|
||||
ShaderBlock *reflection_block = MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, reflection_stage, normal_block);
|
||||
|
||||
switch (reflection_stage->op)
|
||||
{
|
||||
case Material::Operator_Default:
|
||||
case Material::Operator_Add:
|
||||
constant_block = new AddOperatorShaderBlock(constant_block, reflection_block);
|
||||
break;
|
||||
|
||||
case Material::Operator_Multiply:
|
||||
diffuse_block = new MulOperatorShaderBlock(diffuse_block, reflection_block);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Glossiness.
|
||||
ShaderBlock *glossiness_block = NULL;
|
||||
if (m.GetChannelStage(Channel_Glossiness))
|
||||
glossiness_block = new SwizzleShaderBlock(MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_Glossiness), normal_block), SwizzleShaderBlock::SwizzleX);
|
||||
else glossiness_block = new MaterialParamShaderBlock(MaterialParamShaderBlock::MaterialGlossiness);
|
||||
|
||||
// Opacity.
|
||||
ShaderBlock *opacity_block = NULL;
|
||||
if (m.GetChannelStage(Channel_Opacity))
|
||||
opacity_block = new SwizzleShaderBlock(MaterialToShaderTree::MaterialTextureStageToShaderBlock(m, m.GetChannelStage(Channel_Opacity), normal_block), SwizzleShaderBlock::SwizzleW);
|
||||
else opacity_block = new MaterialParamShaderBlock(MaterialParamShaderBlock::MaterialOpacity);
|
||||
|
||||
if (vertex_color_block)
|
||||
opacity_block = new MulOperatorShaderBlock(opacity_block, new SwizzleShaderBlock(vertex_color_block, SwizzleShaderBlock::SwizzleW));
|
||||
|
||||
// Reflection (raytracer).
|
||||
ShaderBlock *reflection_raytracer_block = NULL;
|
||||
reflection_raytracer_block = new MaterialParamShaderBlock(MaterialParamShaderBlock::MaterialReflection);
|
||||
|
||||
// Assign to sinks.
|
||||
tree.sink[ShaderTree::SinkDiffuse] = diffuse_block;
|
||||
tree.sink[ShaderTree::SinkSpecular] = specular_block;
|
||||
tree.sink[ShaderTree::SinkConstant] = constant_block;
|
||||
tree.sink[ShaderTree::SinkNormal] = normal_block;
|
||||
tree.sink[ShaderTree::SinkOpacity] = opacity_block;
|
||||
tree.sink[ShaderTree::SinkGlossiness] = glossiness_block;
|
||||
tree.sink[ShaderTree::SinkReflection] = reflection_raytracer_block;
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
213
include/engine/core/object.cpp
Normal file
213
include/engine/core/object.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/object.h"
|
||||
#include "core/camera.h"
|
||||
#include "core/geometry.h"
|
||||
#include "core/resource_factories.h"
|
||||
#include "core/render_resource_factory.h"
|
||||
#include "timing/benchmark.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
float Object::lod_bias = 0.f;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Object::RenderSetup(ResourceFactories *f)
|
||||
{
|
||||
render_data = new RenderData;
|
||||
|
||||
if (f && f->render)
|
||||
{
|
||||
render_data->geometry = f->render->LoadGeometry(geometry, !cache_geometry);
|
||||
|
||||
if (render_data->geometry.IsValid())
|
||||
{
|
||||
// Assert skin correctness.
|
||||
uint bone_count = render_data->geometry->bone_bind_matrix.GetCount();
|
||||
|
||||
if (bone_count > 0)
|
||||
{
|
||||
if (skin.IsNull() || (skin->bones_mtx.GetCount() != bone_count))
|
||||
{
|
||||
AllocateSkin(render_data->geometry->bone_bind_matrix.GetCount());
|
||||
|
||||
// Initialize skin to the binding position.
|
||||
for (uint n = 0; n < render_data->geometry->bone_bind_matrix.GetCount(); ++n)
|
||||
skin->previous_bones_mtx[n] = skin->bones_mtx[n] = render_data->geometry->bone_bind_matrix[n];
|
||||
}
|
||||
}
|
||||
else
|
||||
FreeSkin();
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static float GetGeometrySortValue(const Matrix4 &vm, Render::Geometry *g, const Matrix4 &gm)
|
||||
{ return vm.GetRow(2).Dot((g->hotspot * gm) - vm.GetRow(3)); }
|
||||
|
||||
void Object::ComputeRenderableMinMax(MinMax &mm)
|
||||
{
|
||||
ComputeLocalMinMax(mm);
|
||||
OBB obb = OBB::FromMinMax(mm);
|
||||
obb.Transform(GetMatrix());
|
||||
obb.ComputeMinMax(mm);
|
||||
}
|
||||
uint Object::GetRenderablePrimitiveList(const Camera &view, const Camera &default_view, Stack <Render::Primitive *> &list, Renderable::Context context, bool cull)
|
||||
{
|
||||
Render::Geometry *g = render_data.IsValid() ? render_data->geometry.c_ptr() : NULL;
|
||||
if (!g)
|
||||
return 1;
|
||||
|
||||
// Recursive LOD selection.
|
||||
{
|
||||
float d2 = Vector4::Dist2(default_view.GetMatrix().GetRow(3), GetMatrix().GetRow(3)) + lod_bias * lod_bias;
|
||||
|
||||
d2 /= GetScale().Len2(); // [EJ] scale affects lod selection
|
||||
|
||||
for ( ; g; g = g->lod_proxy)
|
||||
{
|
||||
// If closer than lod, stop there.
|
||||
if (d2 < (g->lod_distance * g->lod_distance))
|
||||
break;
|
||||
|
||||
// Null lod.
|
||||
if (g->flag.IsSet(Geometry::FlagNullLodProxy))
|
||||
return 1;
|
||||
|
||||
// No lod to follow, stop there.
|
||||
if (g->lod_proxy.IsNull())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check context proxy on the selected lod.
|
||||
if (context == Renderable::Context_Shadow)
|
||||
{
|
||||
// Null proxy.
|
||||
if (g->flag.IsSet(Geometry::FlagNullShadowProxy))
|
||||
return 1;
|
||||
|
||||
if (g->shadow_proxy.IsValid())
|
||||
g = g->shadow_proxy;
|
||||
}
|
||||
|
||||
// Test geometry hidden flag.
|
||||
if (g->flag.IsSet(Geometry::FlagHidden))
|
||||
return 1;
|
||||
|
||||
// Culling.
|
||||
if (cull)
|
||||
{
|
||||
MinMax minmax;
|
||||
ComputeLocalMinMax(minmax);
|
||||
|
||||
if (view.frustum.ClassifyMinMax(minmax, &GetMatrix()) == Frustum::Outside)
|
||||
return 1;
|
||||
}
|
||||
|
||||
list.Push(new Render::Primitive(g, this, GetGeometrySortValue(view.GetMatrix(), g, GetMatrix())));
|
||||
|
||||
return 1;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Object::GetBindMatrix(uint n, Matrix4 &m) const
|
||||
{
|
||||
if (render_data.IsNull() || render_data->geometry.IsNull())
|
||||
return false;
|
||||
if (n >= render_data->geometry->bone_bind_matrix.GetCount())
|
||||
return false;
|
||||
|
||||
m = render_data->geometry->bone_bind_matrix[n];
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Object::ComputeLocalMinMax(MinMax &minmax) const
|
||||
{
|
||||
if (render_data.IsNull() || render_data->geometry.IsNull())
|
||||
Item::ComputeLocalMinMax(minmax);
|
||||
|
||||
else
|
||||
{
|
||||
if (HasSkin())
|
||||
{
|
||||
// Compute skin minmax.
|
||||
OBB obb;
|
||||
for (uint n = 0; n < GetBoneCount(); ++n)
|
||||
if (skin->ComputeBoneBoundingVolume(n, obb))
|
||||
{
|
||||
MinMax bmm;
|
||||
obb.ComputeMinMax(bmm);
|
||||
if (n)
|
||||
minmax.Grow(bmm);
|
||||
else minmax = bmm;
|
||||
}
|
||||
}
|
||||
else
|
||||
minmax = render_data->geometry->minmax;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Object::UpdateSkin()
|
||||
{
|
||||
if (render_data.IsValid() && render_data->geometry.IsValid())
|
||||
for (uint n = 0; n < GetBoneCount(); ++n)
|
||||
if (Item *bone = GetBone(n))
|
||||
{
|
||||
skin->previous_bones_mtx[n] = GetInverseMatrix() * (bone->GetPreviousMatrix() * render_data->geometry->bone_bind_matrix[n]);
|
||||
skin->bones_mtx[n] = GetInverseMatrix() * (bone->GetMatrix() * render_data->geometry->bone_bind_matrix[n]);
|
||||
}
|
||||
else
|
||||
{
|
||||
skin->previous_bones_mtx[n] = Matrix4::IdentityMatrix();
|
||||
skin->bones_mtx[n] = Matrix4::IdentityMatrix();
|
||||
}
|
||||
}
|
||||
bool Object::BindBone(uint n, Item *bone)
|
||||
{
|
||||
if (skin.IsNull() || (n >= skin->bones.GetCount()))
|
||||
return false;
|
||||
|
||||
skin->bones[n] = bone;
|
||||
if (bone)
|
||||
bone->item_flags.Set(ItemFlagBoneHint);
|
||||
return true;
|
||||
}
|
||||
bool Object::AllocateSkin(uint bone_count)
|
||||
{
|
||||
skin = new Skin;
|
||||
|
||||
if (!skin->bones.Allocate(bone_count) || !skin->bones_mtx.Allocate(bone_count) || !skin->previous_bones_mtx.Allocate(bone_count) || !skin->bones_minmax.Allocate(bone_count))
|
||||
{
|
||||
FreeSkin();
|
||||
__ERR__(__LOG_E__ << "Failed to allocate skinning structure.\n", false);
|
||||
}
|
||||
for (uint n = 0; n < bone_count; ++n)
|
||||
{
|
||||
skin->bones[n] = NULL;
|
||||
skin->bones_mtx[n] = Matrix4::IdentityMatrix();
|
||||
skin->previous_bones_mtx[n] = Matrix4::IdentityMatrix();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void Object::FreeSkin()
|
||||
{
|
||||
skin = NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Object::Object() : cache_geometry(true) {}
|
||||
39
include/engine/core/object_nml.cpp
Normal file
39
include/engine/core/object_nml.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/object.h"
|
||||
#include "reflection/c_refl.h"
|
||||
#include "metafile/nml_object.h"
|
||||
|
||||
using GS::Core::Object;
|
||||
using GS::Reflection::Property;
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Property serializable[] =
|
||||
{
|
||||
{ Property::StringProp, "Geometry", offsetof(Object, geometry), 0 },
|
||||
{ Property::BoolProp, "CacheGeometry", offsetof(Object, cache_geometry), 0 },
|
||||
{ Property::InvalidProp, 0, 0, 0 }
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Object::FromMetaTag(Tag &t)
|
||||
{
|
||||
if ((t.name != "Object") || !GenericObjectFromMetaTag(t, this, serializable))
|
||||
return false;
|
||||
if (Tag *c = t.GetTag("Item")) Item::FromMetaTag(*c);
|
||||
return true;
|
||||
}
|
||||
Tag *Object::AsMetaTag() const
|
||||
{
|
||||
Tag *t = new Tag("Object");
|
||||
t->AddChild(Item::AsMetaTag());
|
||||
return GenericObjectToMetaTag(t, this, serializable);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
211
include/engine/core/octree_renderable.cpp
Normal file
211
include/engine/core/octree_renderable.cpp
Normal file
@ -0,0 +1,211 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/octree_renderable.h"
|
||||
#include "core/camera.h"
|
||||
#include "timing/benchmark.h"
|
||||
#include "sort/sort.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
OctreeCullingSystem::Node *OctreeCullingSystem::InsertList(List <CachedNode *> &list)
|
||||
{
|
||||
AutoPtr <Node> node(new Node);
|
||||
if (node.IsNull())
|
||||
return NULL;
|
||||
|
||||
bool to_child = false;
|
||||
if (list.GetCount() <= 4)
|
||||
to_child = true;
|
||||
|
||||
else
|
||||
{
|
||||
// Build list AABB.
|
||||
bool first = true;
|
||||
|
||||
MinMax mm;
|
||||
ListForeachPtr(CachedNode *, cnode, list)
|
||||
{
|
||||
if (first)
|
||||
mm = cnode->minmax;
|
||||
else mm.Grow(cnode->minmax);
|
||||
first = false;
|
||||
}
|
||||
|
||||
// Select split axis.
|
||||
Vector4 size = mm.mx - mm.mn;
|
||||
|
||||
uint axis = 2;
|
||||
if ((size.x > size.y) && (size.x > size.z))
|
||||
axis = 0;
|
||||
else if ((size.y > size.x) && (size.y > size.z))
|
||||
axis = 1;
|
||||
|
||||
List <CachedNode *> split_list[2];
|
||||
|
||||
// Check list for renderable occupying more than 50% of the split axis.
|
||||
bool exclusion_split = false;
|
||||
float size_threshold = size[axis] * 0.5f;
|
||||
|
||||
ListForeachPtr(CachedNode *, cnode, list)
|
||||
if ((cnode->minmax.mx[axis] - cnode->minmax.mn[axis]) > size_threshold)
|
||||
{
|
||||
exclusion_split = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (exclusion_split)
|
||||
ListForeachPtr(CachedNode *, cnode, list)
|
||||
split_list[(cnode->minmax.mx[axis] - cnode->minmax.mn[axis]) > size_threshold ? 0 : 1].Add(cnode);
|
||||
else
|
||||
{
|
||||
float split_coord = (mm.mn[axis] + mm.mx[axis]) * 0.5f;
|
||||
ListForeachPtr(CachedNode *, cnode, list)
|
||||
split_list[cnode->minmax.GetCenter()[axis] < split_coord ? 0 : 1].Add(cnode);
|
||||
}
|
||||
|
||||
// Recurse split lists.
|
||||
if (!split_list[0].GetCount() || !split_list[1].GetCount())
|
||||
to_child = true;
|
||||
|
||||
else
|
||||
{
|
||||
node->child[0] = InsertList(split_list[0]);
|
||||
node->child[1] = InsertList(split_list[1]);
|
||||
node->minmax = node->child[0]->minmax;
|
||||
node->minmax.Grow(node->child[1]->minmax);
|
||||
}
|
||||
}
|
||||
|
||||
if (to_child)
|
||||
{
|
||||
if (node->cached_node.Allocate(list.GetCount()))
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
uint n = 0;
|
||||
ListForeachPtr(CachedNode *, cnode, list)
|
||||
{
|
||||
node->cached_node[n++] = cnode;
|
||||
if (first)
|
||||
node->minmax = cnode->minmax;
|
||||
else node->minmax.Grow(cnode->minmax);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
__ERR__(__LOG_E__ << "Failed to allocatel leaf container.\n", NULL)
|
||||
}
|
||||
return node.Detach();
|
||||
}
|
||||
bool OctreeCullingSystem::Update()
|
||||
{
|
||||
root = NULL;
|
||||
|
||||
if (renderable_list.GetCount())
|
||||
{
|
||||
if (!nodes.Allocate(renderable_list.GetCount()))
|
||||
return false;
|
||||
|
||||
uint n = 0;
|
||||
ArrayListForeachPtr(Renderable *, renderable, renderable_list)
|
||||
{
|
||||
nodes[n].renderable = renderable;
|
||||
renderable->ComputeRenderableMinMax(nodes[n].minmax);
|
||||
++n;
|
||||
}
|
||||
|
||||
// Build root list.
|
||||
List <CachedNode *> root_list;
|
||||
for (uint n = 0; n < nodes.GetCount(); ++n)
|
||||
root_list.Add(&nodes[n]);
|
||||
|
||||
// Perform insertion.
|
||||
root = InsertList(root_list);
|
||||
}
|
||||
|
||||
dirty = false;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void OctreeCullingSystem::ComputeRenderableMinMax(MinMax &mm)
|
||||
{
|
||||
if (root)
|
||||
mm = root->minmax;
|
||||
}
|
||||
void OctreeCullingSystem::GetNodeRenderablePrimitive(Node *node, const Camera &view, const Camera &default_view, Stack <Render::Primitive *> &list, Context ctx)
|
||||
{
|
||||
// Grab leaf content.
|
||||
if (uint count = node->cached_node.GetCount())
|
||||
{
|
||||
for (uint n = 0; n < count; ++n)
|
||||
{
|
||||
Renderable *renderable = node->cached_node[n]->renderable;
|
||||
if (renderable->IsRenderable())
|
||||
renderable->GetRenderablePrimitiveList(view, default_view, list, ctx, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Gather branches.
|
||||
GetNodeRenderablePrimitive(node->child[0], view, default_view, list, ctx);
|
||||
GetNodeRenderablePrimitive(node->child[1], view, default_view, list, ctx);
|
||||
}
|
||||
}
|
||||
void OctreeCullingSystem::CullNodeRenderablePrimitive(Node *node, const Camera &view, const Camera &default_view, Stack <Render::Primitive *> &list, Context ctx)
|
||||
{
|
||||
if (node->cached_node.GetCount())
|
||||
GetNodeRenderablePrimitive(node, view, default_view, list, ctx);
|
||||
|
||||
else
|
||||
switch (view.frustum.ClassifyMinMax(node->minmax))
|
||||
{
|
||||
case Frustum::Outside:
|
||||
break;
|
||||
case Frustum::Clipped:
|
||||
CullNodeRenderablePrimitive(node->child[0], view, default_view, list, ctx);
|
||||
CullNodeRenderablePrimitive(node->child[1], view, default_view, list, ctx);
|
||||
break;
|
||||
case Frustum::Inside:
|
||||
GetNodeRenderablePrimitive(node->child[0], view, default_view, list, ctx);
|
||||
GetNodeRenderablePrimitive(node->child[1], view, default_view, list, ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint OctreeCullingSystem::GetRenderablePrimitiveList(const Camera &view, const Camera &default_view, Stack <Render::Primitive *> &list, Context ctx, bool cull)
|
||||
{
|
||||
// Rebuild tree if octree is dirty.
|
||||
if (dirty)
|
||||
Update();
|
||||
|
||||
if (root)
|
||||
{
|
||||
if (cull)
|
||||
CullNodeRenderablePrimitive(root, view, default_view, list, ctx);
|
||||
else GetNodeRenderablePrimitive(root, view, default_view, list, ctx);
|
||||
}
|
||||
return renderable_list.GetCount();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void OctreeCullingSystem::AddRenderable(Renderable *r)
|
||||
{
|
||||
renderable_list.Add(r);
|
||||
dirty = true;
|
||||
}
|
||||
void OctreeCullingSystem::DeleteRenderable(Renderable *r)
|
||||
{
|
||||
renderable_list.Remove(r);
|
||||
dirty = true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
446
include/engine/core/path_kdtree.cpp
Normal file
446
include/engine/core/path_kdtree.cpp
Normal file
@ -0,0 +1,446 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
#include <cstring>
|
||||
#include "core/path_kdtree.h"
|
||||
#include "core/geometry.h"
|
||||
#include "log/log.h"
|
||||
#include "core/renderer_toolbox.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
using GS::Render::Renderer;
|
||||
|
||||
|
||||
PathKdtree::KDTreeNode::KDTreeNode():m_KDTREE_NODE_ID_SEGMENT(NULL){ memset(m_KDTREE_NODE_ID_ROPE, -1, sizeof(int)*6);};
|
||||
|
||||
#define KDTREE_MAX_DEPTH 20
|
||||
#define KDTREE_MAX_POLY_PER_NODE 5
|
||||
|
||||
void PathKdtree::DrawKdtreeNode(Renderer &render, int _CurrentNode, Matrix4& m)
|
||||
{
|
||||
MinMax min_max;
|
||||
|
||||
min_max.mn.x = m_NodeTree[_CurrentNode].m_KDTREE_NODE_AABB[KDTREE_SIDE_LEFT];
|
||||
min_max.mn.y = m_NodeTree[_CurrentNode].m_KDTREE_NODE_AABB[KDTREE_SIDE_BOTTOM];
|
||||
min_max.mn.z = m_NodeTree[_CurrentNode].m_KDTREE_NODE_AABB[KDTREE_SIDE_BACK];
|
||||
min_max.mx.x = m_NodeTree[_CurrentNode].m_KDTREE_NODE_AABB[KDTREE_SIDE_RIGHT];
|
||||
min_max.mx.y = m_NodeTree[_CurrentNode].m_KDTREE_NODE_AABB[KDTREE_SIDE_TOP];
|
||||
min_max.mx.z = m_NodeTree[_CurrentNode].m_KDTREE_NODE_AABB[KDTREE_SIDE_FRONT];
|
||||
|
||||
min_max.mn = min_max.mn*m;
|
||||
min_max.mx = min_max.mx*m;
|
||||
|
||||
RendererToolbox::DrawAABB(render, min_max);
|
||||
|
||||
if(!m_NodeTree[_CurrentNode].m_KDTREE_NODE_IS_LEAF)
|
||||
{
|
||||
DrawKdtreeNode(render, m_NodeTree[_CurrentNode].m_KDTREE_NODE_ID_CHILD_RIGHT, m);
|
||||
DrawKdtreeNode(render, m_NodeTree[_CurrentNode].m_KDTREE_NODE_ID_CHILD_LEFT, m);
|
||||
}
|
||||
}
|
||||
|
||||
void PathKdtree::draw_scene_debug(Renderer &render, Matrix4& m)
|
||||
{
|
||||
DrawKdtreeNode(render, 0, m);
|
||||
}
|
||||
|
||||
void PathKdtree::NearestQuadtreeTreeNode(Vector4 p, SharedArrayList<nMSegment*> &list_segment)
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
// go inside the quadtree
|
||||
ArrayList<int> list_id_segment;
|
||||
|
||||
if(segment_list.GetCount() <= 0)
|
||||
return;
|
||||
|
||||
int l_CurrentNode = 0;
|
||||
|
||||
while(!m_NodeTree[l_CurrentNode].m_KDTREE_NODE_IS_LEAF)
|
||||
{
|
||||
switch (m_NodeTree[l_CurrentNode].m_KDTREE_NODE_TYPE_SPLIT)
|
||||
{
|
||||
case KDTREE_X_AXIS:
|
||||
{
|
||||
float l_X = p.x ;
|
||||
if(l_X == m_NodeTree[l_CurrentNode].m_KDTREE_NODE_VALUE_SPLIT)
|
||||
l_X += 0.001f;
|
||||
|
||||
if(l_X > m_NodeTree[l_CurrentNode].m_KDTREE_NODE_VALUE_SPLIT)
|
||||
l_CurrentNode = m_NodeTree[l_CurrentNode].m_KDTREE_NODE_ID_CHILD_RIGHT;
|
||||
else
|
||||
l_CurrentNode = m_NodeTree[l_CurrentNode].m_KDTREE_NODE_ID_CHILD_LEFT;
|
||||
}
|
||||
break;
|
||||
case KDTREE_Y_AXIS:
|
||||
{
|
||||
float l_Y = p.y ;
|
||||
if(l_Y == m_NodeTree[l_CurrentNode].m_KDTREE_NODE_VALUE_SPLIT)
|
||||
l_Y += 0.001f;
|
||||
|
||||
if(l_Y > m_NodeTree[l_CurrentNode].m_KDTREE_NODE_VALUE_SPLIT)
|
||||
l_CurrentNode = m_NodeTree[l_CurrentNode].m_KDTREE_NODE_ID_CHILD_RIGHT;
|
||||
else
|
||||
l_CurrentNode = m_NodeTree[l_CurrentNode].m_KDTREE_NODE_ID_CHILD_LEFT;
|
||||
}
|
||||
break;
|
||||
case KDTREE_Z_AXIS:
|
||||
{
|
||||
float l_Z = p.z;
|
||||
if(l_Z == m_NodeTree[l_CurrentNode].m_KDTREE_NODE_VALUE_SPLIT)
|
||||
l_Z += 0.001f;
|
||||
|
||||
if(l_Z > m_NodeTree[l_CurrentNode].m_KDTREE_NODE_VALUE_SPLIT)
|
||||
l_CurrentNode = m_NodeTree[l_CurrentNode].m_KDTREE_NODE_ID_CHILD_RIGHT;
|
||||
else
|
||||
l_CurrentNode = m_NodeTree[l_CurrentNode].m_KDTREE_NODE_ID_CHILD_LEFT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int* l_TempPntIdSegment = m_NodeTree[l_CurrentNode].m_KDTREE_NODE_ID_SEGMENT;
|
||||
int l_CountSegment = m_NodeTree[l_CurrentNode].m_KDTREE_NODE_COUNT_SEGMENT;
|
||||
|
||||
for(int i=0; i< l_CountSegment; ++i)
|
||||
{
|
||||
list_segment.Add(segment_list[*l_TempPntIdSegment]);
|
||||
++l_TempPntIdSegment;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
void PathKdtree::IncreaseSizeNodeKdtreeBuffer(int _IncreaseSize)
|
||||
//-------------------------------------------------------------------
|
||||
{
|
||||
KDTreeNode* l_tempCopy = new KDTreeNode[m_SizeTree + _IncreaseSize];
|
||||
memcpy(l_tempCopy, m_NodeTree, sizeof(KDTreeNode)*m_SizeTree);
|
||||
|
||||
for(int i=0; i<m_SizeTree; ++i)
|
||||
{
|
||||
if(m_NodeTree[i].m_KDTREE_NODE_ID_SEGMENT)
|
||||
{
|
||||
l_tempCopy[i].m_KDTREE_NODE_ID_SEGMENT = new int[m_NodeTree[i].m_KDTREE_NODE_COUNT_SEGMENT];
|
||||
memcpy( l_tempCopy[i].m_KDTREE_NODE_ID_SEGMENT, m_NodeTree[i].m_KDTREE_NODE_ID_SEGMENT, sizeof(int)*m_NodeTree[i].m_KDTREE_NODE_COUNT_SEGMENT);
|
||||
}
|
||||
}
|
||||
|
||||
m_SizeTree += _IncreaseSize;
|
||||
|
||||
delete []m_NodeTree;
|
||||
m_NodeTree = l_tempCopy;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
void PathKdtree::CreateNodeKdtree(int &_CurrentNode, int *_IdSegment, int _CountSegment, int _CurrentDepth, bool _ForceCreateLeaf )
|
||||
//----------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
// check if there is a minimum of place for 2 child
|
||||
if(m_SizeTree < _CurrentNode + 3)
|
||||
{
|
||||
IncreaseSizeNodeKdtreeBuffer(1000);
|
||||
}
|
||||
|
||||
// set the id of the node
|
||||
m_NodeTree[_CurrentNode].m_KDTREE_NODE_ID = _CurrentNode;
|
||||
|
||||
//get the aabb
|
||||
float * l_TempAABB = m_NodeTree[_CurrentNode].m_KDTREE_NODE_AABB;
|
||||
|
||||
// check if it's the moment to create the leaf
|
||||
if(_ForceCreateLeaf || _CountSegment < KDTREE_MAX_POLY_PER_NODE || _CurrentDepth >= KDTREE_MAX_DEPTH
|
||||
/*|| fabs(l_TempAABB[KDTREE_SIDE_LEFT] - l_TempAABB[KDTREE_SIDE_RIGHT]) < 0.1f
|
||||
|| fabs(l_TempAABB[KDTREE_SIDE_BOTTOM] - l_TempAABB[KDTREE_SIDE_TOP]) < 0.1f
|
||||
|| fabs(l_TempAABB[KDTREE_SIDE_BACK] - l_TempAABB[KDTREE_SIDE_FRONT]) < 0.1f*/)
|
||||
{
|
||||
// check if there is a minimum of place for all the poly
|
||||
if(m_SizeTree < (_CurrentNode + _CountSegment))
|
||||
{
|
||||
IncreaseSizeNodeKdtreeBuffer(10000 + _CountSegment);
|
||||
}
|
||||
|
||||
m_NodeTree[_CurrentNode].m_KDTREE_NODE_IS_LEAF = true;
|
||||
|
||||
m_NodeTree[_CurrentNode].m_KDTREE_NODE_ID_CHILD_LEFT = -1;
|
||||
m_NodeTree[_CurrentNode].m_KDTREE_NODE_ID_CHILD_RIGHT = -1;
|
||||
|
||||
m_NodeTree[_CurrentNode].m_KDTREE_NODE_COUNT_SEGMENT = _CountSegment;
|
||||
|
||||
m_NodeTree[_CurrentNode].m_KDTREE_NODE_ID_SEGMENT = new int[_CountSegment];
|
||||
memcpy( m_NodeTree[_CurrentNode].m_KDTREE_NODE_ID_SEGMENT, _IdSegment, sizeof(int)*_CountSegment);
|
||||
|
||||
// set the new id to set back
|
||||
++_CurrentNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NodeTree[_CurrentNode].m_KDTREE_NODE_IS_LEAF = false;
|
||||
|
||||
//find the correct split axe X or Z
|
||||
float l_TempValueSplit = 0.0f;
|
||||
|
||||
int l_CountSegmentOnX = 0;
|
||||
int l_CountSegmentOnZ = 0;
|
||||
|
||||
// get the middle of the aabb
|
||||
float l_X_MiddleAABB = (l_TempAABB[KDTREE_X_AXIS*2] + l_TempAABB[KDTREE_X_AXIS*2+1])*0.5f;
|
||||
float l_Z_MiddleAABB = (l_TempAABB[KDTREE_Z_AXIS*2] + l_TempAABB[KDTREE_Z_AXIS*2+1])*0.5f;
|
||||
|
||||
for(int i=0; i<_CountSegment; ++i)
|
||||
{
|
||||
if(segment_list[_IdSegment[i]]->bounding_box.GetCenter().x > l_X_MiddleAABB)
|
||||
++l_CountSegmentOnX;
|
||||
else
|
||||
--l_CountSegmentOnX;
|
||||
|
||||
if(segment_list[_IdSegment[i]]->bounding_box.GetCenter().z > l_Z_MiddleAABB)
|
||||
++l_CountSegmentOnZ;
|
||||
else
|
||||
--l_CountSegmentOnZ;
|
||||
}
|
||||
|
||||
//set new axis
|
||||
int l_NewAxis;
|
||||
if(Types::Abs(l_CountSegmentOnX) < Types::Abs(l_CountSegmentOnZ))
|
||||
l_NewAxis = KDTREE_X_AXIS;
|
||||
else
|
||||
l_NewAxis = KDTREE_Z_AXIS;
|
||||
|
||||
// problem , we need absolutly leaf with some path inside, bad split function, so patch it
|
||||
if((l_NewAxis == KDTREE_X_AXIS && _CountSegment == Types::Abs(l_CountSegmentOnX)) || (l_NewAxis == KDTREE_Z_AXIS && _CountSegment == Types::Abs(l_CountSegmentOnZ)))
|
||||
{
|
||||
CreateNodeKdtree(_CurrentNode, _IdSegment, _CountSegment, _CurrentDepth, true );
|
||||
return;
|
||||
}
|
||||
|
||||
// axis check with the length and width
|
||||
float diff_axis_aabb = (l_TempAABB[KDTREE_X_AXIS*2+1] - l_TempAABB[KDTREE_X_AXIS*2]) / (l_TempAABB[KDTREE_Z_AXIS*2+1] - l_TempAABB[KDTREE_Z_AXIS*2]);
|
||||
if(diff_axis_aabb > 1.5)
|
||||
l_NewAxis = KDTREE_X_AXIS;
|
||||
if(diff_axis_aabb < 0.66)
|
||||
l_NewAxis = KDTREE_Z_AXIS;
|
||||
|
||||
m_NodeTree[_CurrentNode].m_KDTREE_NODE_TYPE_SPLIT = l_NewAxis;
|
||||
|
||||
// get the middle of the aabb
|
||||
float l_MiddleAABB = (l_TempAABB[l_NewAxis*2] + l_TempAABB[l_NewAxis*2+1])*0.5f;
|
||||
l_TempValueSplit = l_MiddleAABB;
|
||||
|
||||
m_NodeTree[_CurrentNode].m_KDTREE_NODE_VALUE_SPLIT = l_TempValueSplit;
|
||||
|
||||
// create the 2 childs
|
||||
|
||||
// create the 2 child list
|
||||
int l_IdInBigArray;
|
||||
|
||||
// left node
|
||||
{
|
||||
int l_NewIdChildLeft = _CurrentNode + 1;
|
||||
m_NodeTree[_CurrentNode].m_KDTREE_NODE_ID_CHILD_LEFT = l_NewIdChildLeft;
|
||||
|
||||
// set the new aabb
|
||||
float * l_TempAABBLeftChild = m_NodeTree[l_NewIdChildLeft].m_KDTREE_NODE_AABB;
|
||||
|
||||
l_TempAABB = m_NodeTree[_CurrentNode].m_KDTREE_NODE_AABB;
|
||||
memcpy(l_TempAABBLeftChild, l_TempAABB, sizeof(float)*6);
|
||||
l_TempAABBLeftChild[l_NewAxis*2+1] = l_TempValueSplit;
|
||||
|
||||
int *l_IdLeftSegmentList = new int [_CountSegment];
|
||||
int l_IdLeftCount = 0;
|
||||
|
||||
for(int i=0; i<_CountSegment; ++i)
|
||||
{
|
||||
bool l_Include = false;
|
||||
switch(l_NewAxis)
|
||||
{
|
||||
case KDTREE_X_AXIS:
|
||||
if(segment_list[_IdSegment[i]]->a.x <= l_TempValueSplit ||
|
||||
segment_list[_IdSegment[i]]->b.x <= l_TempValueSplit)
|
||||
l_Include = true;
|
||||
break;
|
||||
case KDTREE_Z_AXIS:
|
||||
if(segment_list[_IdSegment[i]]->a.z <= l_TempValueSplit ||
|
||||
segment_list[_IdSegment[i]]->b.z <= l_TempValueSplit)
|
||||
l_Include = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(l_Include)
|
||||
{
|
||||
l_IdLeftSegmentList[l_IdLeftCount] = _IdSegment[i];
|
||||
|
||||
++l_IdLeftCount;
|
||||
}
|
||||
}
|
||||
|
||||
// copy the strict minimum, not good, because it's fragment memory, but it's just for the creation
|
||||
{
|
||||
int* l_tempCopy = new int[l_IdLeftCount];
|
||||
memcpy(l_tempCopy, l_IdLeftSegmentList, sizeof(int)*l_IdLeftCount);
|
||||
|
||||
delete []l_IdLeftSegmentList;
|
||||
l_IdLeftSegmentList = l_tempCopy;
|
||||
}
|
||||
|
||||
CreateNodeKdtree(l_NewIdChildLeft, l_IdLeftSegmentList, l_IdLeftCount, _CurrentDepth+1);
|
||||
|
||||
l_IdInBigArray = l_NewIdChildLeft;
|
||||
|
||||
delete []l_IdLeftSegmentList;
|
||||
}
|
||||
|
||||
// right node
|
||||
{
|
||||
if(m_SizeTree < l_IdInBigArray + 3)
|
||||
IncreaseSizeNodeKdtreeBuffer(10000);
|
||||
|
||||
int l_NewIdChildRight = l_IdInBigArray;
|
||||
m_NodeTree[_CurrentNode].m_KDTREE_NODE_ID_CHILD_RIGHT = l_NewIdChildRight;
|
||||
|
||||
// set the new aabb
|
||||
float * l_TempAABBRightChild = m_NodeTree[l_NewIdChildRight].m_KDTREE_NODE_AABB;
|
||||
|
||||
l_TempAABB = m_NodeTree[_CurrentNode].m_KDTREE_NODE_AABB;
|
||||
memcpy(l_TempAABBRightChild, l_TempAABB, sizeof(float)*6);
|
||||
l_TempAABBRightChild[l_NewAxis*2] = l_TempValueSplit;
|
||||
|
||||
int *l_IdRightSegmentList = new int [_CountSegment];
|
||||
int l_IdRightCount = 0;
|
||||
|
||||
for(int i=0; i<_CountSegment; ++i)
|
||||
{
|
||||
bool l_Include = false;
|
||||
switch(l_NewAxis)
|
||||
{
|
||||
case KDTREE_X_AXIS:
|
||||
if(segment_list[_IdSegment[i]]->a.x >= l_TempValueSplit ||
|
||||
segment_list[_IdSegment[i]]->b.x >= l_TempValueSplit)
|
||||
l_Include = true;
|
||||
break;
|
||||
case KDTREE_Z_AXIS:
|
||||
if(segment_list[_IdSegment[i]]->a.z >= l_TempValueSplit ||
|
||||
segment_list[_IdSegment[i]]->b.z >= l_TempValueSplit)
|
||||
l_Include = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(l_Include)
|
||||
{
|
||||
l_IdRightSegmentList[l_IdRightCount] = _IdSegment[i];
|
||||
|
||||
++l_IdRightCount;
|
||||
}
|
||||
}
|
||||
|
||||
// copy the strict minimum, not good, because it's fragment memory, but it's just for the creation
|
||||
{
|
||||
int* l_tempCopy = new int[l_IdRightCount];
|
||||
memcpy(l_tempCopy, l_IdRightSegmentList, sizeof(int)*l_IdRightCount);
|
||||
|
||||
delete []l_IdRightSegmentList;
|
||||
l_IdRightSegmentList = l_tempCopy;
|
||||
}
|
||||
|
||||
CreateNodeKdtree(l_NewIdChildRight, l_IdRightSegmentList, l_IdRightCount, _CurrentDepth+1);
|
||||
|
||||
//set the new id for the next node in the stack
|
||||
_CurrentNode = l_NewIdChildRight;
|
||||
|
||||
delete []l_IdRightSegmentList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
void PathKdtree::BuildQuadtree()
|
||||
//--------------------------------------------------------------------------------
|
||||
{
|
||||
if(segment_list.GetCount() <= 0)
|
||||
return;
|
||||
|
||||
//very not powerful kdtree construction
|
||||
|
||||
m_SizeTree = segment_list.GetCount()*4;
|
||||
m_NodeTree = new KDTreeNode[m_SizeTree];
|
||||
|
||||
int m_CurrentNode = 0;
|
||||
|
||||
m_NodeTree[m_CurrentNode].m_KDTREE_NODE_TYPE_SPLIT = KDTREE_X_AXIS;
|
||||
|
||||
// find the big bounding box
|
||||
MinMax max_min_max = segment_list[0]->GetBoundingBox();
|
||||
ArrayListForeachPtr(nMSegment*, segment, segment_list)
|
||||
{
|
||||
max_min_max.Grow(segment->GetBoundingBox());
|
||||
}
|
||||
max_min_max.mn.y -= 100.0f;
|
||||
max_min_max.mx.y += 100.0f;
|
||||
|
||||
float * l_TempAABB = m_NodeTree[m_CurrentNode].m_KDTREE_NODE_AABB;
|
||||
|
||||
l_TempAABB[KDTREE_SIDE_LEFT] = max_min_max.mn.x;
|
||||
l_TempAABB[KDTREE_SIDE_BOTTOM] = max_min_max.mn.y;
|
||||
l_TempAABB[KDTREE_SIDE_BACK] = max_min_max.mn.z;
|
||||
l_TempAABB[KDTREE_SIDE_RIGHT] = max_min_max.mx.x;
|
||||
l_TempAABB[KDTREE_SIDE_TOP] = max_min_max.mx.y;
|
||||
l_TempAABB[KDTREE_SIDE_FRONT] = max_min_max.mx.z;
|
||||
|
||||
|
||||
// to build the kdtree: id of the poly
|
||||
int* l_IdSegment = new int[segment_list.GetCount()];
|
||||
for(uint i=0; i<segment_list.GetCount(); ++i)
|
||||
l_IdSegment[i] = i;
|
||||
|
||||
CreateNodeKdtree(m_CurrentNode, l_IdSegment, segment_list.GetCount(), 0);
|
||||
|
||||
_safe_delete_array(l_IdSegment);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
bool PathKdtree::AddSegment(nMSegment* segment)
|
||||
//-------------------------------------------------------------------
|
||||
{
|
||||
segment_list.Add(segment);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
bool PathKdtree::AddSegment(SharedArrayList<nMSegment*> _segment_list)
|
||||
//------------------------------------------------------------------------------------------
|
||||
{
|
||||
ArrayListForeachPtr(nMSegment*, segment, _segment_list)
|
||||
segment_list.Add(segment);
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------
|
||||
bool PathKdtree::InsideKdTree(const Vector4 &s)
|
||||
//---------------------------------------------------
|
||||
{
|
||||
if(m_NodeTree[0].m_KDTREE_NODE_AABB[0] <= s.x && s.x <= m_NodeTree[0].m_KDTREE_NODE_AABB[1] &&
|
||||
m_NodeTree[0].m_KDTREE_NODE_AABB[2] <= s.y && s.y <= m_NodeTree[0].m_KDTREE_NODE_AABB[3] &&
|
||||
m_NodeTree[0].m_KDTREE_NODE_AABB[4] <= s.z && s.z <= m_NodeTree[0].m_KDTREE_NODE_AABB[5] )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------
|
||||
void PathKdtree::Free()
|
||||
//-----------------------------------------
|
||||
{
|
||||
_safe_delete_array(m_NodeTree);
|
||||
m_count_bih = 0;
|
||||
}
|
||||
|
||||
PathKdtree::PathKdtree()
|
||||
{
|
||||
m_NodeTree = NULL;
|
||||
m_count_bih = 0;
|
||||
}
|
||||
24
include/engine/core/physic_material.cpp
Normal file
24
include/engine/core/physic_material.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "core/physic_material.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
PhysicMaterial::PhysicMaterial()
|
||||
{
|
||||
mass = 1.f;
|
||||
|
||||
SetFriction();
|
||||
restitution = 0.1f;
|
||||
|
||||
sb_damping = 0.99f;
|
||||
sb_stiffness = 1;
|
||||
sb_torsion = 1;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
62
include/engine/core/physic_material_nml.cpp
Normal file
62
include/engine/core/physic_material_nml.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/physic_material.h"
|
||||
#include "metafile/nml.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
using GS::NML::Tag;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool PhysicMaterial::FromMetaTag(Tag &tag)
|
||||
{
|
||||
if (tag.name != "PhysicMaterial")
|
||||
__ERR__(__LOG_E__ << "Could not parse physic material, incorrect root tag (" << tag.name << ").\n", false);
|
||||
|
||||
static String _mss("Mass"), _damp("Damping"), _stiff("Stiffness"), _trs("Torsion"),
|
||||
_sfr("StaticFriction"), _dfr("DynamicFriction"), _res("Restitution");
|
||||
|
||||
NMLTagForeach(pt, tag)
|
||||
{
|
||||
if (pt->name == _mss)
|
||||
mass = pt->GetReal();
|
||||
else if (pt->name == _sfr)
|
||||
static_friction = pt->GetReal();
|
||||
else if (pt->name == _dfr)
|
||||
dynamic_friction = pt->GetReal();
|
||||
else if (pt->name == _res)
|
||||
restitution = pt->GetReal();
|
||||
else if (pt->name == _damp)
|
||||
sb_damping = pt->GetReal();
|
||||
else if (pt->name == _stiff)
|
||||
sb_stiffness = pt->GetReal();
|
||||
else if (pt->name == _trs)
|
||||
sb_torsion = pt->GetBool();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Tag *PhysicMaterial::AsMetaTag() const
|
||||
{
|
||||
Tag *m = new Tag("PhysicMaterial");
|
||||
if (!m)
|
||||
__ERR__(__LOG_E__ << "Could not serialize physic material. Failed to create root tag.\n", NULL);
|
||||
|
||||
if (mass != 1.f)
|
||||
m->AddChild("Mass", mass);
|
||||
m->AddChild("StaticFriction", static_friction);
|
||||
m->AddChild("DynamicFriction", dynamic_friction);
|
||||
m->AddChild("Restitution", restitution);
|
||||
if (sb_damping != 0.99f)
|
||||
m->AddChild("Damping", sb_damping);
|
||||
if (sb_stiffness != 1)
|
||||
m->AddChild("Stiffness", sb_stiffness);
|
||||
if (sb_torsion != 1)
|
||||
m->AddChild("Torsion", sb_torsion);
|
||||
return m;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
193
include/engine/core/raster_font.cpp
Normal file
193
include/engine/core/raster_font.cpp
Normal file
@ -0,0 +1,193 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include "core/raster_font.h"
|
||||
#include "core/render_resource_factory.h"
|
||||
#include "metafile/nml.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Render;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
float RasterFont::GetHeight(bool normalized) const
|
||||
{
|
||||
if (normalized)
|
||||
return height;
|
||||
return GetPage(0) ? height * GetPage(0)->GetWidth() : -1;
|
||||
}
|
||||
float RasterFont::GetBaseline(bool normalized) const
|
||||
{
|
||||
if (normalized)
|
||||
return baseline;
|
||||
return GetPage(0) ? baseline * GetPage(0)->GetWidth() : -1;
|
||||
}
|
||||
Vector2 RasterFont::ComputeLineRect(const char *s, bool normalized) const
|
||||
{
|
||||
Vector2 r(0, height);
|
||||
for ( ; s[0] && s[0] != '\n'; ++s)
|
||||
if (const Glyph *c = GetGlyphInfos(s[0]))
|
||||
r.x += c->step;
|
||||
|
||||
if (!normalized)
|
||||
{
|
||||
if (GetPage(0))
|
||||
{
|
||||
r.x *= GetPage(0)->GetWidth();
|
||||
r.y *= GetPage(0)->GetHeight();
|
||||
}
|
||||
else
|
||||
r.Set(-1, -1);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
Vector2 RasterFont::ComputeStringRect(const char *s, bool normalized) const
|
||||
{
|
||||
Vector2 r(0, 0);
|
||||
|
||||
forever
|
||||
{
|
||||
float w = 0; // Line width.
|
||||
for ( ; s[0] && s[0] != '\n'; ++s)
|
||||
if (const Glyph *c = GetGlyphInfos(s[0]))
|
||||
w += c->step;
|
||||
|
||||
if (w > r.x) // Largest width.
|
||||
r.x = w;
|
||||
r.y += height; // Line height.
|
||||
|
||||
if (!s[0])
|
||||
break;
|
||||
++s; // Jump over the line feed.
|
||||
}
|
||||
|
||||
if (!normalized)
|
||||
{
|
||||
if (GetPage(0))
|
||||
{
|
||||
r.x *= GetPage(0)->GetWidth();
|
||||
r.y *= GetPage(0)->GetHeight();
|
||||
}
|
||||
else
|
||||
r.Set(-1, -1);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Texture *RasterFont::GetPage(uint page) const
|
||||
{ return (page < pages.GetCount()) ? pages.ObjectAt(page) : NULL; }
|
||||
const RasterFont::Glyph *RasterFont::GetGlyphInfos(uchar index) const
|
||||
{ return glyph[index].available ? &glyph[index] : NULL; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool RasterFont::Load(ResourceFactory &rf, const char *nml, const char *base)
|
||||
{
|
||||
Unload();
|
||||
|
||||
//-----------------------------------------------------------
|
||||
#define RFL_ERROR(l) { (l); Unload(); return false; }
|
||||
//-----------------------------------------------------------
|
||||
|
||||
using namespace GS::NML;
|
||||
|
||||
File file;
|
||||
if (!Parser::Load(nml, file))
|
||||
RFL_ERROR(__LOG_E__ << "Failed to load raster font description file '" << nml << "'.\n")
|
||||
|
||||
// Load font settings.
|
||||
Tag *root = file.GetTag("Font:Common;");
|
||||
if (!root)
|
||||
RFL_ERROR(__LOG_E__ << "Missing base tag in font description ('" << nml << "').\n")
|
||||
|
||||
uint page_count = 1;
|
||||
|
||||
NMLTagForeach(tag, *root)
|
||||
{
|
||||
if (tag->name == "Height")
|
||||
height = tag->GetReal();
|
||||
else if (tag->name == "BaseLine")
|
||||
baseline = tag->GetReal();
|
||||
else if (tag->name == "PageCount")
|
||||
page_count = tag->GetInteger();
|
||||
else __LOG_W__ << "Unexpected tag in font description header ('" << tag->name << "').\n";
|
||||
}
|
||||
|
||||
// Parse glyphs...
|
||||
root = file.GetTag("Font;");
|
||||
|
||||
NMLTagForeach(tag, *root)
|
||||
if (tag->name == "Char")
|
||||
{
|
||||
Tag *wrk = tag->GetTag("Id;");
|
||||
if (!wrk || (wrk->GetType() != Variant::VariantInteger))
|
||||
{
|
||||
__LOG_W__ << "Glyph with no or invalid ID in font '" << nml << "', skipping.\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
int glyphidx = wrk->GetInteger();
|
||||
if (glyphidx > 255)
|
||||
continue;
|
||||
|
||||
Glyph *pglyph = &glyph[glyphidx];
|
||||
pglyph->page = 0;
|
||||
pglyph->available = true;
|
||||
|
||||
NMLTagForeach(wrk, *tag)
|
||||
{
|
||||
if (wrk->name == "U")
|
||||
pglyph->u = wrk->GetReal();
|
||||
else if (wrk->name == "V")
|
||||
pglyph->v = wrk->GetReal();
|
||||
else if (wrk->name == "W")
|
||||
pglyph->w = wrk->GetReal();
|
||||
else if (wrk->name == "H")
|
||||
pglyph->h = wrk->GetReal();
|
||||
else if (wrk->name == "OffsetU")
|
||||
pglyph->offx = wrk->GetReal();
|
||||
else if (wrk->name == "OffsetV")
|
||||
pglyph->offy = wrk->GetReal();
|
||||
else if (wrk->name == "Step")
|
||||
pglyph->step = wrk->GetReal();
|
||||
else if (wrk->name == "Page")
|
||||
{
|
||||
if ((wrk->GetType() == Variant::VariantInteger) && ((uint)wrk->GetInteger() < page_count))
|
||||
pglyph->page = wrk->GetInteger();
|
||||
|
||||
else
|
||||
{
|
||||
__LOG_W__ << "Invalid glyph page in font '" << nml << "' (Glyph #" << glyphidx << "), skipping.\n";
|
||||
pglyph->available = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check...
|
||||
if (!glyph[' '].available)
|
||||
__LOG_W__ << "No space character in font '" << nml << "'.\n";
|
||||
|
||||
// Load font pages.
|
||||
for (uint n = 0; n < page_count; n++)
|
||||
if (Texture *t = rf.LoadTexture(String::Format("%s_%02d.tga", base, n)))
|
||||
pages.Add(t);
|
||||
|
||||
name = nml;
|
||||
return true;
|
||||
}
|
||||
void RasterFont::Unload()
|
||||
{
|
||||
pages.Clear();
|
||||
for (uint n = 0; n < 256; n++)
|
||||
glyph[n].Reset();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
22
include/engine/core/render_data.cpp
Normal file
22
include/engine/core/render_data.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/render_data.h"
|
||||
#include "picture/pict.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Render;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Texture::Create(const Picture &p, Usage usage)
|
||||
{
|
||||
__LOG_V__ << "Creating texture '" << p.name << "'.\n";
|
||||
name = p.name;
|
||||
return Create((const char *)p.GetData(), p.GetWidth(), p.GetHeight(), FormatRGBA8, NoAA, usage);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
102
include/engine/core/renderer.cpp
Normal file
102
include/engine/core/renderer.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/renderer.h"
|
||||
#include "core/camera.h"
|
||||
#include "core/raster_font.h"
|
||||
#include "sort/sort.h"
|
||||
#include "platform_config.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
using namespace GS::Render;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Renderer::BuildRenderablePrimitiveList(const Camera &view, const Camera &lod_view, GS::Stack <Primitive *> &list, Renderable::Context context) const
|
||||
{
|
||||
uint tested_primitive = 0;
|
||||
|
||||
list.Clear();
|
||||
ListForeachPtr(Renderable *, renderable, renderable_list)
|
||||
tested_primitive += renderable->GetRenderablePrimitiveList(view, lod_view, list, context, true);
|
||||
|
||||
return tested_primitive;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Camera *Renderer::GetCamera() const
|
||||
{ return view_item; }
|
||||
void Renderer::SetCamera(Camera *item)
|
||||
{
|
||||
view_item = item;
|
||||
view_registry = item ? &item->registry : NULL;
|
||||
}
|
||||
void Renderer::ApplyCamera()
|
||||
{
|
||||
if (view_item)
|
||||
{
|
||||
SetViewMatrix(view_item->GetMatrix(), &view_item->GetInverseMatrix());
|
||||
|
||||
Matrix4 pm;
|
||||
view_item->ComputeProjectionMatrix(GetViewport(), pm);
|
||||
SetProjectionMatrix(pm);
|
||||
|
||||
view_item->ComputeFrustum(view_item->frustum, GetViewport());
|
||||
frustum = view_item->frustum;
|
||||
}
|
||||
}
|
||||
void Renderer::SetViewRegistry(GS::Registry *registry)
|
||||
{ view_registry = registry; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
GS::tVector2 <uint> Renderer::GetOutputDimensions() const
|
||||
{
|
||||
if (output_texture)
|
||||
return tVector2 <uint> (output_texture->GetWidth(), output_texture->GetHeight());
|
||||
return dimensions;
|
||||
}
|
||||
float Renderer::GetOutputAspectRatio() const
|
||||
{
|
||||
if (output_texture)
|
||||
return float(output_texture->GetHeight()) / float(output_texture->GetWidth());
|
||||
return output_aspect_ratio > 0 ? output_aspect_ratio : (float)dimensions.y / (float)dimensions.x;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::PushRenderable(Renderable *renderable)
|
||||
{ renderable_list.Add(renderable); }
|
||||
void Renderer::DeleteRenderableList()
|
||||
{ renderable_list.Clear(); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Renderer::Renderer()
|
||||
{
|
||||
output_aspect_ratio = 0;
|
||||
global_aspect_ratio = AR_Square;
|
||||
|
||||
default_window = NULL;
|
||||
output_window = NULL;
|
||||
|
||||
environment_interface = NULL;
|
||||
|
||||
view_item = NULL;
|
||||
view_registry = NULL;
|
||||
dimensions.Set(1, 1);
|
||||
|
||||
ipd = 0.0f;
|
||||
|
||||
registry.RegisterMessageListener(this);
|
||||
}
|
||||
Renderer::~Renderer()
|
||||
{
|
||||
registry.UnregisterMessageListener(this);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
46
include/engine/core/renderer_nml.cpp
Normal file
46
include/engine/core/renderer_nml.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/renderer.h"
|
||||
|
||||
using namespace GS::Render;
|
||||
using GS::NML::Tag;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::FromMetaTag(Tag &tag)
|
||||
{
|
||||
if (tag.name != GetName())
|
||||
return false; // Incorrect tag.
|
||||
|
||||
registry.Clear();
|
||||
|
||||
NMLTagForeach(pt, tag)
|
||||
{
|
||||
if (pt->name == "Version")
|
||||
;
|
||||
|
||||
else if (pt->name == "Registry")
|
||||
{
|
||||
NMLTagForeach(child, *pt)
|
||||
registry.AddRoot(child->Clone());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Tag *Renderer::AsMetaTag()
|
||||
{
|
||||
Tag *root = new Tag(GetName());
|
||||
|
||||
root->AddChild("Version", GetVersion());
|
||||
|
||||
Tag *tag_registry = root->AddChild("Registry");
|
||||
NMLFileForeach(child, registry)
|
||||
tag_registry->AddChild(child->Clone());
|
||||
|
||||
return root;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
40
include/engine/core/renderer_profiler.cpp
Normal file
40
include/engine/core/renderer_profiler.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/renderer.h"
|
||||
#include "core/core_profiler.h"
|
||||
|
||||
using namespace GS::Render;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::DrawProfilerText(RasterFont *font[2], float &x, float &y)
|
||||
{
|
||||
Color title_color(1.f, 0.9f, 0);
|
||||
WriterConfig config(false);
|
||||
|
||||
// Renderer profiling.
|
||||
Write(*font[1], "Profiler\n\n", x, y, config, 1, &title_color);
|
||||
Write(*font[0], String::Format("Prepare light: slice = %0.02f ms, tasks = %0.02f ms (// x%0.01f)\n", stats.bench_prepare_light.slice_duration.toMs(), stats.bench_prepare_light.tasks_duration.toMs(), stats.bench_prepare_light.tasks_duration.toMs() / stats.bench_prepare_light.slice_duration.toMs()), x, y, config);
|
||||
Write(*font[0], String::Format("Render queue: %0.02f ms\n", stats.bench_render.GetMs()), x, y, config, 1, &Core::GetColorCode <float> (stats.bench_render.GetMs(), 16, 32));
|
||||
Write(*font[0], String::Format("Post-process: %0.02f ms\n", stats.bench_post_process.GetMs()), x, y, config, 1, &Core::GetColorCode <float> (stats.bench_post_process.GetMs(), 2, 3));
|
||||
y += 8;
|
||||
|
||||
Write(*font[1], "Statistics\n\n", x, y, config, 1, &title_color);
|
||||
Write(*font[0], String::Format("Queue pass = %d\n", stats.queue_pass), x, y, config);
|
||||
Write(*font[0], String::Format("Light processed = %d\n", stats.light_processed), x, y, config);
|
||||
y += 8;
|
||||
|
||||
Write(*font[0], String::Format("Renderable processed = %d (pass avg. = %d)\n", stats.renderable_processed, stats.queue_pass ? stats.renderable_processed / stats.queue_pass : 0), x, y, config);
|
||||
Write(*font[0], String::Format("Renderable drawn = %d (pass avg. = %d)\n", stats.renderable_drawn, stats.queue_pass ? stats.renderable_drawn / stats.queue_pass : 0), x, y, config);
|
||||
Write(*font[0], String::Format("Passed culling: %.02f%% (draw/submit ratio)\n", stats.renderable_processed ? stats.renderable_drawn * 100.f / stats.renderable_processed : 0), x, y, config);
|
||||
y += 8;
|
||||
|
||||
Write(*font[0], String::Format("List drawn = %d (avg. tri/list = %s, efficiency = %.02f)\n", stats.list_drawn, Core::FormatNumber(stats.list_drawn ? (float)stats.triangle_drawn / (float)stats.list_drawn : 0).c_str(), stats.list_drawn ? 100.f / ((float)stats.list_drawn / stats.queue_pass) : 1), x, y, config, 1, &Core::GetColorCode <uint> (stats.list_drawn, 600, 1000));
|
||||
Write(*font[0], String::Format("Triangle drawn = %s (pass avg. = %s)\n", Core::FormatNumber(stats.triangle_drawn).c_str(), Core::FormatNumber(stats.queue_pass ? (float)stats.triangle_drawn / (float)stats.queue_pass : 0).c_str()), x, y, config);
|
||||
y += 16;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
158
include/engine/core/renderer_resource_factory.cpp
Normal file
158
include/engine/core/renderer_resource_factory.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/renderer_resource_factory.h"
|
||||
#include "core/renderer.h"
|
||||
#include "core/geometry.h"
|
||||
#include "core/shader.h"
|
||||
#include "core/shader_tree.h"
|
||||
#include "core/shader_tree_to_shader.h"
|
||||
#include "core/resource_geometry_generator.h"
|
||||
#include "picture/pict.h"
|
||||
#include "picture/pict_io.h"
|
||||
#include "metafile/nml_object.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::NML;
|
||||
using namespace GS::Render;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Geometry *RendererResourceFactory::NewGeometry() { return renderer.NewGeometry(); }
|
||||
Material *RendererResourceFactory::NewMaterial() { return renderer.NewMaterial(); }
|
||||
Texture *RendererResourceFactory::NewTexture() { return renderer.NewTexture(); }
|
||||
Shader *RendererResourceFactory::NewShader() { return renderer.NewShader(); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static bool DoLoadResource(ResourceFactory &rf, Geometry *r, const char *uri)
|
||||
{
|
||||
Core::Geometry g;
|
||||
g.name = uri;
|
||||
|
||||
if (Core::GeometryGenerator::IsGenerated(uri))
|
||||
{
|
||||
if (!Core::GeometryGenerator::Generate(uri, g))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
if (!LoadFromFile(g, uri))
|
||||
return false;
|
||||
|
||||
return r->Create(rf, g);
|
||||
}
|
||||
static bool DoLoadResource(ResourceFactory &rf, Material *r, const char *uri)
|
||||
{
|
||||
Core::Material m;
|
||||
m.name = uri;
|
||||
|
||||
if (!LoadFromFile(m, uri))
|
||||
return false;
|
||||
|
||||
return r->Create(rf, m);
|
||||
}
|
||||
static bool DoLoadResource(ResourceFactory &rf, Shader *r, const char *uri)
|
||||
{
|
||||
Core::Shader s;
|
||||
s.name = uri;
|
||||
|
||||
File file;
|
||||
if (!Parser::Load(uri, file))
|
||||
return false;
|
||||
|
||||
if (Tag *tag = file.GetTag("Shader"))
|
||||
{
|
||||
if (!s.FromMetaTag(*tag))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Core::ShaderTree shader_tree;
|
||||
if (!shader_tree.FromMetaTag(*tag) || !Core::ConvertShaderTreeToShader(shader_tree, s))
|
||||
return false;
|
||||
}
|
||||
|
||||
return r->Create(rf, s);
|
||||
}
|
||||
static bool DoLoadResource(ResourceFactory &rf, Texture *r, const char *uri)
|
||||
{
|
||||
// Read optional texture parameters.
|
||||
LoadFromFile(r->parm, TextureParm::GetParmFileName(uri), false);
|
||||
|
||||
// Read data.
|
||||
if (!r->LoadCooked(uri))
|
||||
{
|
||||
Picture picture;
|
||||
if (!PictureIO::Get().Load(picture, uri))
|
||||
return false;
|
||||
|
||||
picture.Convert(PixelFormat::RGBA8);
|
||||
r->parm.Apply(picture);
|
||||
|
||||
if (!r->Create(picture))
|
||||
return false;
|
||||
}
|
||||
|
||||
r->parm.Apply(*r);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static Data *GeometryFactory(ResourceFactory &rf) { return rf.NewGeometry(); }
|
||||
static Data *MaterialFactory(ResourceFactory &rf) { return rf.NewMaterial(); }
|
||||
static Data *TextureFactory(ResourceFactory &rf) { return rf.NewTexture(); }
|
||||
static Data *ShaderFactory(ResourceFactory &rf) { return rf.NewShader(); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T> T *LoadResourceCommonSeq(const char *uri, ResourceFactory &rf, Data *(factory)(ResourceFactory &), T *r, const char *errored_uri)
|
||||
{
|
||||
if (!uri)
|
||||
return NULL;
|
||||
|
||||
String name(uri);
|
||||
name.FileCleanName();
|
||||
|
||||
// Load sequence.
|
||||
if (rf.event_handler)
|
||||
{
|
||||
rf.event_handler->OpenLoad();
|
||||
rf.event_handler->LoadProgress(String::Format("Loading resource '%s'...", name.c_str()));
|
||||
}
|
||||
|
||||
T *res = r ? r : (T *)factory(rf);
|
||||
if (!res)
|
||||
return NULL;
|
||||
|
||||
if (!DoLoadResource(rf, res, name))
|
||||
{
|
||||
if (errored_uri && !DoLoadResource(rf, res, errored_uri))
|
||||
if (!r) // note: do not erase res if it was externally provided (through r)!
|
||||
_safe_delete(res);
|
||||
}
|
||||
|
||||
if (res)
|
||||
res->name = name;
|
||||
|
||||
if (rf.event_handler)
|
||||
rf.event_handler->EndLoad();
|
||||
|
||||
return res;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Geometry *RendererResourceFactory::LoadGeometry(const char *uri, bool, Geometry *g)
|
||||
{ return LoadResourceCommonSeq <Geometry> (uri, *this, GeometryFactory, g, NULL); }
|
||||
Material *RendererResourceFactory::LoadMaterial(const char *uri, bool, Material *m)
|
||||
{ return LoadResourceCommonSeq <Material> (uri, *this, MaterialFactory, m, "@core/builtin/material/missing.nmm"); }
|
||||
Texture *RendererResourceFactory::LoadTexture(const char *uri, bool, Texture *t)
|
||||
{ return LoadResourceCommonSeq <Texture> (uri, *this, TextureFactory, t, "@core/builtin/maps/missing_texture.png"); }
|
||||
Shader *RendererResourceFactory::LoadShader(const char *uri, bool, Shader *s)
|
||||
{ return LoadResourceCommonSeq <Shader> (uri, *this, ShaderFactory, s, "@core/builtin/shader/missing_shader.nsa"); }
|
||||
//------------------------------------------------------------------------------
|
||||
353
include/engine/core/renderer_toolbox.cpp
Normal file
353
include/engine/core/renderer_toolbox.cpp
Normal file
@ -0,0 +1,353 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include "core/renderer_toolbox.h"
|
||||
#include "core/core_profiler.h"
|
||||
#include "core/item.h"
|
||||
#include "core/raster_font.h"
|
||||
#include "log/log.h"
|
||||
#if __PLATFORM_NINTENDO_WII__
|
||||
#include "system/wii_memory.h"
|
||||
#include "wii_platform.h"
|
||||
#endif
|
||||
|
||||
using namespace GS::Units;
|
||||
using namespace GS::Math;
|
||||
|
||||
|
||||
namespace GS {
|
||||
namespace RendererToolbox {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Triangle3D(Renderer &render, const Vector4 vtx[3], const Color color[3], const Vector2 uv[3], const Render::Texture *t, Material::BlendOperator blendop, Material::RenderWord rword)
|
||||
{ render.DrawTriangle(1, vtx, NULL, color, uv, t, blendop, rword); }
|
||||
void Line3D(Renderer &render, const Vector4 &a, const Vector4 &b, const Color *c, Material::BlendOperator bo, Material::RenderWord rw)
|
||||
{
|
||||
Vector4 v[2] = { a, b };
|
||||
Color u[2] = { c ? *c : Color(1, 1, 1), c ? *c : Color(1, 1, 1) };
|
||||
render.DrawLine(1, v, u, bo, rw);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void DrawCircle(Renderer &render, const Vector4 &position, float radius, const Matrix3 *m, const Color *color, Material::BlendOperator bo)
|
||||
{
|
||||
render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
|
||||
Matrix3 m3 = m ? *m : Matrix3::IdentityMatrix();
|
||||
for (float a = Deg(0.f); a < Deg(360.f); a += Deg(10.f))
|
||||
Line3D(render, Vector4(Cos(a), Sin(a), 0) * m3 * radius + position, Vector4(Cos(a + Deg(10.f)), Sin(a + Deg(10.f)), 0) * m3 * radius + position, color, bo);
|
||||
}
|
||||
void DrawCylinder(Renderer &render, const Vector4 &position, float radius, float length, const Matrix3 *m, const Color *color, Material::BlendOperator bo)
|
||||
{
|
||||
render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
|
||||
Matrix3 m3 = m ? *m : Matrix3::IdentityMatrix();
|
||||
DrawCircle(render, position + Vector4(0, 0, length * 0.5f) * m3, radius, &m3, color);
|
||||
DrawCircle(render, position + Vector4(0, 0, length * -0.5f) * m3, radius, &m3, color);
|
||||
Line3D(render, position + Vector4(radius, 0, length * 0.5f) * m3, position + Vector4(radius, 0, length * -0.5f) * m3, color, bo);
|
||||
Line3D(render, position + Vector4(-radius, 0, length * 0.5f) * m3, position + Vector4(-radius, 0, length * -0.5f) * m3, color, bo);
|
||||
Line3D(render, position + Vector4(0, radius, length * 0.5f) * m3, position + Vector4(0, radius, length * -0.5f) * m3, color, bo);
|
||||
Line3D(render, position + Vector4(0, -radius, length * 0.5f) * m3, position + Vector4(0, -radius, length * -0.5f) * m3, color, bo);
|
||||
}
|
||||
void DrawCapsule(Renderer &render, const Vector4 &position, float radius, float length, const Matrix3 *m, const Color *color, Material::BlendOperator bo)
|
||||
{
|
||||
render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
|
||||
Matrix3 m3 = m ? *m : Matrix3::IdentityMatrix();
|
||||
DrawSphere(render, position + Vector4(0, 0, length * 0.5f) * m3, radius, color);
|
||||
DrawSphere(render, position - Vector4(0, 0, length * 0.5f) * m3, radius, color);
|
||||
Line3D(render, position + Vector4(radius, 0, length * 0.5f) * m3, position + Vector4(radius, 0, length * -0.5f) * m3, color, bo);
|
||||
Line3D(render, position + Vector4(-radius, 0, length * 0.5f) * m3, position + Vector4(-radius, 0, length * -0.5f) * m3, color, bo);
|
||||
}
|
||||
void DrawCone(Renderer &render, const Vector4 &position, float radius, float length, const Matrix3 *m, const Color *color, Material::BlendOperator bo)
|
||||
{
|
||||
render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
|
||||
Matrix3 m3 = m ? *m : Matrix3::IdentityMatrix();
|
||||
DrawCircle(render, position + Vector4(0, 0, length * -0.5f) * m3, radius, &m3, color);
|
||||
Line3D(render, position + Vector4(0, 0, length * 0.5f) * m3, position + Vector4(radius, 0, length * -0.5f) * m3, color, bo);
|
||||
Line3D(render, position + Vector4(0, 0, length * 0.5f) * m3, position + Vector4(-radius, 0, length * -0.5f) * m3, color, bo);
|
||||
Line3D(render, position + Vector4(0, 0, length * 0.5f) * m3, position + Vector4(0, radius, length * -0.5f) * m3, color, bo);
|
||||
Line3D(render, position + Vector4(0, 0, length * 0.5f) * m3, position + Vector4(0, -radius, length * -0.5f) * m3, color, bo);
|
||||
}
|
||||
void DrawSphere(Renderer &render, const Vector4 &p, float r, const Color *color, Material::BlendOperator bo)
|
||||
{
|
||||
render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
|
||||
#define SPHERE_NSEG 36
|
||||
|
||||
uint n;
|
||||
float sn, cs;
|
||||
Vector4 a[2], b[2], c[2];
|
||||
float angle, dt;
|
||||
Color _r(1, 1, 0), _g(1, 1, 0), _b(1, 1, 0);
|
||||
|
||||
angle = 0;
|
||||
dt = Deg(360.f) / SPHERE_NSEG;
|
||||
|
||||
sn = Sin(angle) * r;
|
||||
cs = Cos(angle) * r;
|
||||
|
||||
a[1].Set(p.x, p.y + cs, p.z + sn);
|
||||
b[1].Set(p.x + sn, p.y + cs, p.z);
|
||||
c[1].Set(p.x + cs, p.y, p.z + sn);
|
||||
|
||||
for (n = 0; n < SPHERE_NSEG; n++)
|
||||
{
|
||||
a[0] = a[1]; b[0] = b[1]; c[0] = c[1];
|
||||
|
||||
angle += dt;
|
||||
sn = Sin(angle) * r;
|
||||
cs = Cos(angle) * r;
|
||||
|
||||
a[1].Set(p.x, p.y + cs, p.z + sn);
|
||||
b[1].Set(p.x + sn, p.y + cs, p.z);
|
||||
c[1].Set(p.x + cs, p.y, p.z + sn);
|
||||
|
||||
Line3D(render, a[0], a[1], color ? color : &_r);
|
||||
Line3D(render, b[0], b[1], color ? color : &_g);
|
||||
Line3D(render, c[0], c[1], color ? color : &_b);
|
||||
}
|
||||
}
|
||||
void DrawBall(Renderer &render, const Matrix4 &m, float r, Color *uc, Material::BlendOperator bo)
|
||||
{
|
||||
render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
|
||||
#define BALL_NSEG 24
|
||||
|
||||
uint n;
|
||||
float sn, cs;
|
||||
Vector4 a[2], b[2], c[2];
|
||||
float angle, dt;
|
||||
|
||||
angle = 0;
|
||||
dt = Deg(360.f) / BALL_NSEG;
|
||||
|
||||
sn = Sin(angle) * r;
|
||||
cs = Cos(angle) * r;
|
||||
|
||||
a[1].Set(0, cs, sn);
|
||||
b[1].Set(sn, cs, 0);
|
||||
c[1].Set(cs, 0, sn);
|
||||
a[1] *= m;
|
||||
b[1] *= m;
|
||||
c[1] *= m;
|
||||
Color color(1, 1, 1);
|
||||
if (!uc)
|
||||
uc = &color;
|
||||
|
||||
for (n = 0; n < BALL_NSEG; n++)
|
||||
{
|
||||
a[0] = a[1]; b[0] = b[1]; c[0] = c[1];
|
||||
|
||||
angle += dt;
|
||||
sn = Sin(angle) * r;
|
||||
cs = Cos(angle) * r;
|
||||
|
||||
a[1].Set(0, cs, sn);
|
||||
b[1].Set(sn, cs, 0);
|
||||
c[1].Set(cs, 0, sn);
|
||||
a[1] *= m;
|
||||
b[1] *= m;
|
||||
c[1] *= m;
|
||||
|
||||
Line3D(render, a[0], a[1], uc, bo);
|
||||
Line3D(render, b[0], b[1], uc, bo);
|
||||
Line3D(render, c[0], c[1], uc, bo);
|
||||
}
|
||||
}
|
||||
void DrawCross(Renderer &render, const Vector4 &c, float size, const Color *color, Material::BlendOperator bo)
|
||||
{
|
||||
render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
|
||||
const Color dft(1, 1, 1), *cl;
|
||||
|
||||
if (color)
|
||||
cl = color;
|
||||
else cl = &dft;
|
||||
|
||||
Vector4 o(c);
|
||||
Vector4 a[2];
|
||||
|
||||
a[0] = o; a[1] = o;
|
||||
a[0].x -= size;
|
||||
a[1].x += size;
|
||||
Line3D(render, a[0], a[1], cl, bo);
|
||||
a[0] = o; a[1] = o;
|
||||
a[0].y -= size;
|
||||
a[1].y += size;
|
||||
Line3D(render, a[0], a[1], cl, bo);
|
||||
a[0] = o; a[1] = o;
|
||||
a[0].z -= size;
|
||||
a[1].z += size;
|
||||
Line3D(render, a[0], a[1], cl, bo);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void DrawAABB(Renderer &render, const MinMax &minmax, const Color *c, Material::BlendOperator bo)
|
||||
{
|
||||
render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
|
||||
const Color color(0.5, 1.0, 0);
|
||||
if (!c)
|
||||
c = &color;
|
||||
const Vector4 *min = &minmax.mn, *max = &minmax.mx;
|
||||
Vector4 v[2];
|
||||
|
||||
v[0].Set(min->x, min->y, min->z); v[1].Set(max->x, min->y, min->z);
|
||||
Line3D(render, v[0], v[1], c, bo);
|
||||
v[0].Set(min->x, min->y, min->z); v[1].Set(min->x, min->y, max->z);
|
||||
Line3D(render, v[0], v[1], c, bo);
|
||||
v[0].Set(max->x, min->y, min->z); v[1].Set(max->x, min->y, max->z);
|
||||
Line3D(render, v[0], v[1], c, bo);
|
||||
v[0].Set(min->x, min->y, max->z); v[1].Set(max->x, min->y, max->z);
|
||||
Line3D(render, v[0], v[1], c, bo);
|
||||
|
||||
v[0].Set(min->x, max->y, min->z); v[1].Set(max->x, max->y, min->z);
|
||||
Line3D(render, v[0], v[1], c, bo);
|
||||
v[0].Set(min->x, max->y, min->z); v[1].Set(min->x, max->y, max->z);
|
||||
Line3D(render, v[0], v[1], c, bo);
|
||||
v[0].Set(max->x, max->y, min->z); v[1].Set(max->x, max->y, max->z);
|
||||
Line3D(render, v[0], v[1], c, bo);
|
||||
v[0].Set(min->x, max->y, max->z); v[1].Set(max->x, max->y, max->z);
|
||||
Line3D(render, v[0], v[1], c, bo);
|
||||
|
||||
v[0].Set(min->x, min->y, min->z); v[1].Set(min->x, max->y, min->z);
|
||||
Line3D(render, v[0], v[1], c, bo);
|
||||
v[0].Set(max->x, min->y, min->z); v[1].Set(max->x, max->y, min->z);
|
||||
Line3D(render, v[0], v[1], c, bo);
|
||||
v[0].Set(max->x, min->y, max->z); v[1].Set(max->x, max->y, max->z);
|
||||
Line3D(render, v[0], v[1], c, bo);
|
||||
v[0].Set(min->x, min->y, max->z); v[1].Set(min->x, max->y, max->z);
|
||||
Line3D(render, v[0], v[1], c, bo);
|
||||
}
|
||||
void DrawOBB(Renderer &render, const OBB &obb, const Color *color, Material::BlendOperator bo)
|
||||
{
|
||||
Vector4 vtx[8], _vtx[8];
|
||||
|
||||
_vtx[0].Set(-0.5, 0.5, 0.5);
|
||||
_vtx[1].Set( 0.5, 0.5, 0.5);
|
||||
_vtx[2].Set( 0.5, -0.5, 0.5);
|
||||
_vtx[3].Set(-0.5, -0.5, 0.5);
|
||||
_vtx[4].Set(-0.5, 0.5, -0.5);
|
||||
_vtx[5].Set( 0.5, 0.5, -0.5);
|
||||
_vtx[6].Set( 0.5, -0.5, -0.5);
|
||||
_vtx[7].Set(-0.5, -0.5, -0.5);
|
||||
|
||||
Matrix4 mtx = Matrix4::FromMatrix3(obb.bb_rotation * Matrix3::ScaleMatrix(obb.bb_scale));
|
||||
mtx.SetRow(3, obb.bb_position);
|
||||
mtx.Apply(vtx, _vtx, 8);
|
||||
|
||||
const Color _color(1, 0.5f, 0);
|
||||
if (!color)
|
||||
color = &_color;
|
||||
|
||||
render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
|
||||
Line3D(render, vtx[0], vtx[1], color, bo);
|
||||
Line3D(render, vtx[1], vtx[2], color, bo);
|
||||
Line3D(render, vtx[2], vtx[3], color, bo);
|
||||
Line3D(render, vtx[3], vtx[0], color, bo);
|
||||
|
||||
Line3D(render, vtx[4], vtx[5], color, bo);
|
||||
Line3D(render, vtx[5], vtx[6], color, bo);
|
||||
Line3D(render, vtx[6], vtx[7], color, bo);
|
||||
Line3D(render, vtx[7], vtx[4], color, bo);
|
||||
|
||||
Line3D(render, vtx[0], vtx[4], color, bo);
|
||||
Line3D(render, vtx[1], vtx[5], color, bo);
|
||||
Line3D(render, vtx[2], vtx[6], color, bo);
|
||||
Line3D(render, vtx[3], vtx[7], color, bo);
|
||||
}
|
||||
void DrawFilledOBB(Renderer &render, const OBB &obb, const Color *_color, Material::BlendOperator bo, Material::RenderWord rw)
|
||||
{
|
||||
Vector4 vtx[8], v[4];
|
||||
vtx[0].Set(-0.5, 0.5, 0.5);
|
||||
vtx[1].Set( 0.5, 0.5, 0.5);
|
||||
vtx[2].Set( 0.5, -0.5, 0.5);
|
||||
vtx[3].Set(-0.5, -0.5, 0.5);
|
||||
vtx[4].Set(-0.5, 0.5, -0.5);
|
||||
vtx[5].Set( 0.5, 0.5, -0.5);
|
||||
vtx[6].Set( 0.5, -0.5, -0.5);
|
||||
vtx[7].Set(-0.5, -0.5, -0.5);
|
||||
|
||||
Matrix4 mtx = Matrix4::FromMatrix3(obb.bb_rotation * Matrix3::ScaleMatrix(obb.bb_scale));
|
||||
mtx.SetRow(3, obb.bb_position);
|
||||
render.SetWorldMatrix(mtx);
|
||||
|
||||
Color color[3];
|
||||
for (int n = 0; n < 3; ++n)
|
||||
color[n] = _color ? *_color : Color(1, 0.5f, 0);
|
||||
|
||||
v[0] = vtx[0]; v[1] = vtx[1]; v[2] = vtx[2];
|
||||
Triangle3D(render, v, color, NULL, NULL, bo, rw);
|
||||
v[0] = vtx[0]; v[1] = vtx[2]; v[2] = vtx[3];
|
||||
Triangle3D(render, v, color, NULL, NULL, bo, rw);
|
||||
|
||||
v[0] = vtx[4]; v[1] = vtx[5]; v[2] = vtx[1];
|
||||
Triangle3D(render, v, color, NULL, NULL, bo, rw);
|
||||
v[0] = vtx[4]; v[1] = vtx[1]; v[2] = vtx[0];
|
||||
Triangle3D(render, v, color, NULL, NULL, bo, rw);
|
||||
|
||||
v[0] = vtx[7]; v[1] = vtx[6]; v[2] = vtx[5];
|
||||
Triangle3D(render, v, color, NULL, NULL, bo, rw);
|
||||
v[0] = vtx[7]; v[1] = vtx[5]; v[2] = vtx[4];
|
||||
Triangle3D(render, v, color, NULL, NULL, bo, rw);
|
||||
|
||||
v[0] = vtx[3]; v[1] = vtx[2]; v[2] = vtx[6];
|
||||
Triangle3D(render, v, color, NULL, NULL, bo, rw);
|
||||
v[0] = vtx[3]; v[1] = vtx[6]; v[2] = vtx[7];
|
||||
Triangle3D(render, v, color, NULL, NULL, bo, rw);
|
||||
|
||||
v[0] = vtx[2]; v[1] = vtx[1]; v[2] = vtx[5];
|
||||
Triangle3D(render, v, color, NULL, NULL, bo, rw);
|
||||
v[0] = vtx[2]; v[1] = vtx[5]; v[2] = vtx[6];
|
||||
Triangle3D(render, v, color, NULL, NULL, bo, rw);
|
||||
|
||||
v[0] = vtx[0]; v[1] = vtx[3]; v[2] = vtx[7];
|
||||
Triangle3D(render, v, color, NULL, NULL, bo, rw);
|
||||
v[0] = vtx[0]; v[1] = vtx[7]; v[2] = vtx[4];
|
||||
Triangle3D(render, v, color, NULL, NULL, bo, rw);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void DrawCube(Renderer &render, const Vector4 &p, float size, const Color *color, Material::BlendOperator bo)
|
||||
{
|
||||
const Color _color(0, 0.5, 1.f);
|
||||
if (!color)
|
||||
color = &_color;
|
||||
|
||||
Vector4 dt(size, size, size);
|
||||
Vector4 t = p - dt;
|
||||
Vector4 t2 = p + dt;
|
||||
|
||||
MinMax mnmx(t, t2);
|
||||
DrawAABB(render, mnmx, color, bo);
|
||||
}
|
||||
void DrawSquare(Renderer &render, const Vector4 &p, float size, Material::BlendOperator bo)
|
||||
{
|
||||
render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
|
||||
Vector4 a(p), b(p), c(p), d(p);
|
||||
a.x -= size; a.y -= size;
|
||||
b.x += size; b.y -= size;
|
||||
c.x += size; c.y += size;
|
||||
d.x -= size; d.y += size;
|
||||
|
||||
Color cs(1, 1, 1);
|
||||
Line3D(render, a, b, &cs, bo);
|
||||
Line3D(render, b, c, &cs, bo);
|
||||
Line3D(render, c, d, &cs, bo);
|
||||
Line3D(render, d, a, &cs, bo);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // RendererToolbox
|
||||
} // GS
|
||||
213
include/engine/core/resource_geometry_generator.cpp
Normal file
213
include/engine/core/resource_geometry_generator.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/resource_geometry_generator.h"
|
||||
#include "core/geometry.h"
|
||||
#include "core/material_to_shader_tree.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool GeometryGenerator::IsGenerated(const char *name)
|
||||
{ return String(name).StartsWith("@sys/gen/"); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static bool GenerateSphere(const char *name, Geometry &g)
|
||||
{
|
||||
g.Free();
|
||||
|
||||
// Extract dimensions.
|
||||
float r = 0.5f;
|
||||
|
||||
// Generate vertices.
|
||||
const int s_count = 6, c_count = 16;
|
||||
|
||||
if (!g.AllocateVertex((s_count + 1) * c_count + 2))
|
||||
return false;
|
||||
|
||||
Vector4 *v = g.vtx;
|
||||
|
||||
v->Set(0, r, 0);
|
||||
v++;
|
||||
|
||||
for (int s = 0; s < (s_count + 1); ++s)
|
||||
{
|
||||
float t = float(s + 1) / (s_count + 2);
|
||||
float a = t * Units::Deg(180.f);
|
||||
|
||||
float y = Math::Cos(a) * r;
|
||||
float s_r = Math::Sin(a) * r;
|
||||
|
||||
for (int c = 0; c < c_count; ++c)
|
||||
{
|
||||
const float c_a = c * Units::Deg(360.f) / c_count;
|
||||
|
||||
v->Set(Math::Cos(c_a) * s_r, y, Math::Sin(c_a) * s_r);
|
||||
v++;
|
||||
}
|
||||
}
|
||||
|
||||
v->Set(0, -r, 0);
|
||||
|
||||
// Build polygons.
|
||||
if (!g.AllocatePolygon((s_count + 2) * c_count))
|
||||
return false;
|
||||
|
||||
Polygon *p = g.pol;
|
||||
|
||||
for (int c = 0; c < c_count; ++c)
|
||||
{
|
||||
p->vtx_count = 3;
|
||||
p->material = 0;
|
||||
p++;
|
||||
}
|
||||
for (int s = 0; s < s_count; ++s)
|
||||
for (int c = 0; c < c_count; ++c)
|
||||
{
|
||||
p->vtx_count = 4;
|
||||
p->material = 0;
|
||||
p++;
|
||||
}
|
||||
for (int c = 0; c < c_count; ++c)
|
||||
{
|
||||
p->vtx_count = 3;
|
||||
p->material = 0;
|
||||
p++;
|
||||
}
|
||||
|
||||
if (!g.AllocatePolygonBinding())
|
||||
return false;
|
||||
|
||||
p = g.pol;
|
||||
|
||||
for (int c = 0; c < c_count; ++c)
|
||||
{
|
||||
p->binding[0] = 0; p->binding[2] = c + 1; p->binding[1] = Types::Wrap(c + 2, 1, c_count);
|
||||
p++;
|
||||
}
|
||||
for (int s = 0; s < s_count; ++s)
|
||||
{
|
||||
int i = 1 + c_count * s;
|
||||
for (int c = 0; c < c_count; ++c)
|
||||
{
|
||||
p->binding[0] = i + c; p->binding[1] = Types::Wrap(i + c + 1, i, i + c_count - 1);
|
||||
p->binding[3] = i + c + c_count; p->binding[2] = Types::Wrap(i + c + c_count + 1, i + c_count, i + c_count * 2 - 1);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
int i = 1 + c_count * s_count;
|
||||
for (int c = 0; c < c_count; ++c)
|
||||
{
|
||||
p->binding[0] = i + c; p->binding[1] = Types::Wrap(i + c + 1, i, i + c_count - 1);
|
||||
p->binding[2] = i + c_count;
|
||||
p++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static bool GenerateCube(const char *name, Geometry &g)
|
||||
{
|
||||
g.Free();
|
||||
|
||||
// Extract dimensions.
|
||||
Vector4 d(1, 1, 1);
|
||||
d *= 0.5f;
|
||||
|
||||
// Generate vertices.
|
||||
if (!g.AllocateVertex(8))
|
||||
return false;
|
||||
|
||||
g.vtx[0].Set(-d.x, d.y, d.z);
|
||||
g.vtx[1].Set( d.x, d.y, d.z);
|
||||
g.vtx[2].Set( d.x, d.y, -d.z);
|
||||
g.vtx[3].Set(-d.x, d.y, -d.z);
|
||||
g.vtx[4].Set(-d.x, -d.y, d.z);
|
||||
g.vtx[5].Set( d.x, -d.y, d.z);
|
||||
g.vtx[6].Set( d.x, -d.y, -d.z);
|
||||
g.vtx[7].Set(-d.x, -d.y, -d.z);
|
||||
|
||||
// Build polygons.
|
||||
if (!g.AllocatePolygon(6))
|
||||
return false;
|
||||
|
||||
for (uint n = 0; n < 6; ++n)
|
||||
{
|
||||
g.pol[n].vtx_count = 4;
|
||||
g.pol[n].material = 0;
|
||||
}
|
||||
|
||||
if (!g.AllocatePolygonBinding())
|
||||
return false;
|
||||
|
||||
g.pol[0].binding[0] = 0; g.pol[0].binding[1] = 1; g.pol[0].binding[2] = 2; g.pol[0].binding[3] = 3;
|
||||
g.pol[1].binding[0] = 3; g.pol[1].binding[1] = 2; g.pol[1].binding[2] = 6; g.pol[1].binding[3] = 7;
|
||||
g.pol[2].binding[0] = 7; g.pol[2].binding[1] = 6; g.pol[2].binding[2] = 5; g.pol[2].binding[3] = 4;
|
||||
g.pol[3].binding[0] = 4; g.pol[3].binding[1] = 5; g.pol[3].binding[2] = 1; g.pol[3].binding[3] = 0;
|
||||
g.pol[4].binding[0] = 2; g.pol[4].binding[1] = 1; g.pol[4].binding[2] = 5; g.pol[4].binding[3] = 6;
|
||||
g.pol[5].binding[0] = 0; g.pol[5].binding[1] = 3; g.pol[5].binding[2] = 7; g.pol[5].binding[3] = 4;
|
||||
|
||||
return true;
|
||||
}
|
||||
static bool GeneratePlane(const char *name, Geometry &g)
|
||||
{
|
||||
g.Free();
|
||||
|
||||
// Extract dimensions.
|
||||
Vector4 d(1, 1, 1);
|
||||
|
||||
// Generate vertices.
|
||||
if (!g.AllocateVertex(4))
|
||||
return false;
|
||||
|
||||
g.vtx[0].Set(-d.x, 0, d.z);
|
||||
g.vtx[1].Set( d.x, 0, d.z);
|
||||
g.vtx[2].Set( d.x, 0, -d.z);
|
||||
g.vtx[3].Set(-d.x, 0, -d.z);
|
||||
|
||||
// Build polygons.
|
||||
if (!g.AllocatePolygon(1))
|
||||
return false;
|
||||
|
||||
g.pol[0].vtx_count = 4;
|
||||
g.pol[0].material = 0;
|
||||
|
||||
if (!g.AllocatePolygonBinding())
|
||||
return false;
|
||||
|
||||
g.pol[0].binding[0] = 0; g.pol[0].binding[1] = 1; g.pol[0].binding[2] = 2; g.pol[0].binding[3] = 3;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool GeometryGenerator::Generate(const char *name, Geometry &g)
|
||||
{
|
||||
String _name(name);
|
||||
|
||||
bool r = false;
|
||||
|
||||
if (_name.StartsWith("@sys/gen/plane"))
|
||||
r = GeneratePlane(name, g);
|
||||
else if (_name.StartsWith("@sys/gen/cube"))
|
||||
r = GenerateCube(name, g);
|
||||
else if (_name.StartsWith("@sys/gen/sphere"))
|
||||
r = GenerateSphere(name, g);
|
||||
|
||||
if (r)
|
||||
{
|
||||
// Compute extra data.
|
||||
g.ComputeVertexNormal(Units::Deg(45.f));
|
||||
|
||||
// Load materials.
|
||||
if (g.material_table.Allocate(1))
|
||||
g.material_table[0].name = "@core/builtin/material/default.nmm";
|
||||
}
|
||||
return r;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
171
include/engine/core/shader.cpp
Normal file
171
include/engine/core/shader.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/shader.h"
|
||||
#include "metafile/nml.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "platform.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Shader::AddISLSection(const char *uri)
|
||||
{
|
||||
File isl_file;
|
||||
if (!Parser::Load(uri, isl_file))
|
||||
return false;
|
||||
|
||||
// Declare inputs.
|
||||
ParseInputTag(isl_file.GetTag("Shader:Input;"));
|
||||
ParseVaryingTag(isl_file.GetTag("Shader:Varying;"));
|
||||
|
||||
// Append vertex & fragment programs.
|
||||
bool r = true;
|
||||
if (Tag *t = isl_file.GetTypedTag("Shader:Vertex;", Variant::VariantString))
|
||||
{
|
||||
Array <char> data;
|
||||
if ((r &= Platform::Get().io->FileLoad(t->GetString(), data)) != false)
|
||||
vertex += String(data.c_ptr(), data.GetSize());
|
||||
}
|
||||
if (Tag *t = isl_file.GetTypedTag("Shader:Fragment;", Variant::VariantString))
|
||||
{
|
||||
Array <char> data;
|
||||
if ((r &= Platform::Get().io->FileLoad(t->GetString(), data)) != false)
|
||||
pixel += String(data.c_ptr(), data.GetSize());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Shader::Clone(Shader &clone) const
|
||||
{
|
||||
// TODO make something faster... please.
|
||||
AutoPtr <Tag> tag(AsMetaTag());
|
||||
clone.name = name;
|
||||
return clone.FromMetaTag(*tag);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Shader::Define(const char *def, ShaderInput::Scope scope)
|
||||
{
|
||||
String directive = String::Format("#define %s\n", def);
|
||||
|
||||
if (scope & ShaderInput::Vertex)
|
||||
vertex_decl = directive + vertex_decl;
|
||||
if (scope & ShaderInput::Pixel)
|
||||
pixel_decl = directive + pixel_decl;
|
||||
}
|
||||
ShaderVarying *Shader::DeclareVarying(const char *name, const char *type)
|
||||
{
|
||||
ShaderVarying *varying = new ShaderVarying;
|
||||
if (!varying)
|
||||
return NULL;
|
||||
|
||||
varying_list.Add(varying);
|
||||
|
||||
varying->name = name;
|
||||
varying->type = type;
|
||||
return varying;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ShaderInput *Shader::GetInput(ShaderInput::Semantic semantic) const
|
||||
{
|
||||
ListForeachPtr(ShaderInput *, input, input_list)
|
||||
if (input->semantic == semantic)
|
||||
return input;
|
||||
return NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ShaderInput *Shader::DeclareInput(const char *name, ShaderInput::DataType data_type, ShaderInput::Semantic semantic, ShaderInput::Type parm_type, ShaderInput::Scope parm_scope, uint array_size)
|
||||
{
|
||||
// Catch duplicates.
|
||||
ShaderInput *input = NULL;
|
||||
|
||||
switch (semantic)
|
||||
{
|
||||
case ShaderInput::Position:
|
||||
case ShaderInput::Normal:
|
||||
case ShaderInput::UV0:
|
||||
case ShaderInput::UV1:
|
||||
case ShaderInput::UV2:
|
||||
case ShaderInput::Tangent:
|
||||
case ShaderInput::Bitangent:
|
||||
case ShaderInput::BoneIndex:
|
||||
case ShaderInput::BoneWeight:
|
||||
ListForeachPtr(ShaderInput *, p, input_list)
|
||||
if (p->semantic == semantic)
|
||||
{
|
||||
input = p;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ListForeachPtr(ShaderInput *, p, input_list)
|
||||
if ((p->name == name) && (p->semantic == semantic))
|
||||
{
|
||||
input = p;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Equivalent input found.
|
||||
if (input)
|
||||
{
|
||||
input->scope |= parm_scope; // Merge scopes.
|
||||
return input;
|
||||
}
|
||||
|
||||
// Create a new input.
|
||||
if ((input = new ShaderInput) == NULL)
|
||||
return NULL;
|
||||
input_list.Add(input);
|
||||
|
||||
input->name = name;
|
||||
input->semantic = semantic;
|
||||
input->type = parm_type;
|
||||
input->data_type = data_type;
|
||||
input->scope = parm_scope;
|
||||
input->array_size = array_size;
|
||||
|
||||
input->parm_v.Set(0, 0, 0);
|
||||
|
||||
return input;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Shader::Clear()
|
||||
{
|
||||
input_list.Clear();
|
||||
varying_list.Clear();
|
||||
|
||||
geometry_decl.Clear();
|
||||
vertex_decl.Clear();
|
||||
pixel_decl.Clear();
|
||||
geometry.Clear();
|
||||
vertex.Clear();
|
||||
pixel.Clear();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Shader::Shader(const char *n, const char *v, const char *f, const char *g)
|
||||
{
|
||||
name = n;
|
||||
vertex = v;
|
||||
pixel = f;
|
||||
geometry = g;
|
||||
}
|
||||
446
include/engine/core/shader_block.cpp
Normal file
446
include/engine/core/shader_block.cpp
Normal file
@ -0,0 +1,446 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/shader_block.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
using GS::String;
|
||||
|
||||
|
||||
// Tools.
|
||||
ShaderBlockPin SwizzleShaderBlock::input_pin[] =
|
||||
{ { "Input", "Source input to extract components from.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 } };
|
||||
ShaderBlockPin SwizzleShaderBlock::output_pin =
|
||||
{ "Swizzled", "Swizzled input.", ShaderInput::NoData };
|
||||
|
||||
ShaderBlockPin BuildShaderBlock::input_pin[] =
|
||||
{ { "Input X", "Source component of the built vector.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 },
|
||||
{ "Input Y", "Source component of the built vector.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 },
|
||||
{ "Input Z", "Source component of the built vector.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 },
|
||||
{ "Input W", "Source component of the built vector.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 } };
|
||||
ShaderBlockPin BuildShaderBlock::output_pin =
|
||||
{ "Built", "Built vector.", ShaderInput::NoData };
|
||||
|
||||
ShaderBlockPin ClampShaderBlock::input_pin[] =
|
||||
{ { "Input", "Source to clamp.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 },
|
||||
{ "Min", "Minimum value.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 },
|
||||
{ "Max", "Maximum value.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 } };
|
||||
ShaderBlockPin ClampShaderBlock::output_pin =
|
||||
{ "Clamped", "Clamped input.", ShaderInput::NoData };
|
||||
|
||||
ShaderBlockPin UnpackColorToVectorShaderBlock::input_pin[] =
|
||||
{ { "Input", "Vector in the [0;1] range.", ShaderInput::Float | ShaderInput::Vector3 | ShaderInput::Vector4 } };
|
||||
ShaderBlockPin UnpackColorToVectorShaderBlock::output_pin =
|
||||
{ "Unpacked [-1;1]", "Vector in the [-1;1] range.", ShaderInput::NoData };
|
||||
ShaderBlockPin PackVectorToColorShaderBlock::input_pin[] =
|
||||
{ { "Input", "Vector in the [-1;1] range.", ShaderInput::Float | ShaderInput::Vector3 | ShaderInput::Vector4 } };
|
||||
ShaderBlockPin PackVectorToColorShaderBlock::output_pin =
|
||||
{ "Packed [0;1]", "Vector in the [0;1] range.", ShaderInput::NoData };
|
||||
|
||||
// Operator.
|
||||
ShaderBlockPin MixOperatorShaderBlock::input_pin[] =
|
||||
{ { "Input A", "Source input A.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 },
|
||||
{ "Input B", "Source input B.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 },
|
||||
{ "Input K", "Source input K.", ShaderInput::Float } };
|
||||
ShaderBlockPin MixOperatorShaderBlock::output_pin =
|
||||
{ "A*K+B*(1-K)", "Input A * Input K + Input B * (1.0 - Input K).", ShaderInput::NoData };
|
||||
|
||||
ShaderBlockPin AddOperatorShaderBlock::input_pin[] =
|
||||
{ { "Input A", "Source input A.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 },
|
||||
{ "Input B", "Source input B.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 } };
|
||||
ShaderBlockPin AddOperatorShaderBlock::output_pin =
|
||||
{ "A+B", "Input A + Input B.", ShaderInput::NoData };
|
||||
ShaderBlockPin SubOperatorShaderBlock::input_pin[] =
|
||||
{ { "Input A", "Source input A.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 },
|
||||
{ "Input B", "Source input B.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 } };
|
||||
ShaderBlockPin SubOperatorShaderBlock::output_pin =
|
||||
{ "A-B", "Input A - Input B.", ShaderInput::NoData };
|
||||
ShaderBlockPin MulOperatorShaderBlock::input_pin[] =
|
||||
{ { "Input A", "Source input A.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 | ShaderInput::Matrix3 | ShaderInput::Matrix4 },
|
||||
{ "Input B", "Source input B.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 | ShaderInput::Matrix3 | ShaderInput::Matrix4 } };
|
||||
ShaderBlockPin MulOperatorShaderBlock::output_pin =
|
||||
{ "A*B", "Input A * Input B.", ShaderInput::NoData };
|
||||
ShaderBlockPin DivOperatorShaderBlock::input_pin[] =
|
||||
{ { "Input A", "Source input A.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 },
|
||||
{ "Input B", "Source input B.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 } };
|
||||
ShaderBlockPin DivOperatorShaderBlock::output_pin =
|
||||
{ "A/B", "Input A / Input B.", ShaderInput::NoData };
|
||||
|
||||
ShaderBlockPin DotOperatorShaderBlock::input_pin[] =
|
||||
{ { "Input A", "Source input A.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 },
|
||||
{ "Input B", "Source input B.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 } };
|
||||
ShaderBlockPin DotOperatorShaderBlock::output_pin =
|
||||
{ "A.Dot(B)", "The cosinus of the angle between A and B.", ShaderInput::NoData };
|
||||
ShaderBlockPin CrossOperatorShaderBlock::input_pin[] =
|
||||
{ { "Input A", "Source input A.", ShaderInput::Vector3 },
|
||||
{ "Input B", "Source input B.", ShaderInput::Vector3 } };
|
||||
ShaderBlockPin CrossOperatorShaderBlock::output_pin =
|
||||
{ "A.Cross(B)", "A vector perpendicular to both A and B.", ShaderInput::NoData };
|
||||
|
||||
ShaderBlockPin NormalizeOperatorShaderBlock::input_pin[] =
|
||||
{ { "Input", "Source input.", ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 } };
|
||||
ShaderBlockPin NormalizeOperatorShaderBlock::output_pin =
|
||||
{ "Normalized", "Normalized input.", ShaderInput::NoData };
|
||||
|
||||
ShaderBlockPin CosinusShaderBlock::input_pin[] =
|
||||
{ { "Input", "Input value in radian.", ShaderInput::Float } };
|
||||
ShaderBlockPin CosinusShaderBlock::output_pin =
|
||||
{ "Cosinus", "Cosinus of input.", ShaderInput::NoData };
|
||||
ShaderBlockPin SinusShaderBlock::input_pin[] =
|
||||
{ { "Input", "Input value in radian.", ShaderInput::Float } };
|
||||
ShaderBlockPin SinusShaderBlock::output_pin =
|
||||
{ "Sinus", "Sinus of input.", ShaderInput::NoData };
|
||||
|
||||
ShaderBlockPin PowShaderBlock::input_pin[] =
|
||||
{ { "Value", "Input value.", ShaderInput::Float },
|
||||
{ "Power", "Power to raise value to.", ShaderInput::Float } };
|
||||
ShaderBlockPin PowShaderBlock::output_pin =
|
||||
{ "Pow", "Value raised to power.", ShaderInput::NoData };
|
||||
|
||||
ShaderBlockPin AbsShaderBlock::input_pin[] =
|
||||
{ { "Value", "Input value.", ShaderInput::Float | ShaderInput::Vector2 | ShaderInput::Vector3 | ShaderInput::Vector4 } };
|
||||
ShaderBlockPin AbsShaderBlock::output_pin =
|
||||
{ "Abs", "Absolute value of input.", ShaderInput::NoData };
|
||||
|
||||
// Texture
|
||||
ShaderBlockPin TextureSamplerShaderBlock::input_pin[] =
|
||||
{ { "Texture", "Texture input.", ShaderInput::Texture2D },
|
||||
{ "UV Stream", "UV stream input.", ShaderInput::Vector2 | ShaderInput::Vector3 } };
|
||||
ShaderBlockPin TextureSamplerShaderBlock::output_pin =
|
||||
{ "Texel", "Texture sample.", ShaderInput::NoData };
|
||||
|
||||
// No input blocks.
|
||||
ShaderBlockPin GeometryVertexShaderBlock::output_pin =
|
||||
{ "Vertex", "Vertex stream.", ShaderInput::NoData };
|
||||
ShaderBlockPin GeometrySkinningShaderBlock::output_pin =
|
||||
{ "Skinned", "Skinned vector.", ShaderInput::NoData };
|
||||
ShaderBlockPin GeometryNormalShaderBlock::output_pin =
|
||||
{ "Normal", "Normal stream.", ShaderInput::NoData };
|
||||
ShaderBlockPin GeometryUVShaderBlock::output_pin =
|
||||
{ "UV", "UV stream.", ShaderInput::NoData };
|
||||
ShaderBlockPin GeometryVertexColorShaderBlock::output_pin =
|
||||
{ "Color", "Vertex color stream.", ShaderInput::NoData };
|
||||
ShaderBlockPin GeometryTangentFrameShaderBlock::output_pin =
|
||||
{ "Tangent", "Tangent frame.", ShaderInput::NoData };
|
||||
ShaderBlockPin RenderBufferShaderBlock::output_pin =
|
||||
{ "Buffer", "Texture object.", ShaderInput::NoData };
|
||||
ShaderBlockPin TextureShaderBlock::output_pin =
|
||||
{ "Texture", "Texture object.", ShaderInput::NoData };
|
||||
ShaderBlockPin ConstantShaderBlock::output_pin =
|
||||
{ "Constant", "Constant value.", ShaderInput::NoData };
|
||||
ShaderBlockPin ColorShaderBlock::output_pin =
|
||||
{ "Color", "RGBA Color.", ShaderInput::NoData };
|
||||
|
||||
ShaderBlockPin MaterialParamShaderBlock::output_pin =
|
||||
{ "Parameter", "Material parameter.", ShaderInput::NoData };
|
||||
ShaderBlockPin MaterialTextureShaderBlock::output_pin =
|
||||
{ "Slot", "Texture slot.", ShaderInput::NoData };
|
||||
|
||||
ShaderBlockPin ClockShaderBlock::output_pin =
|
||||
{ "Clock (s)", "System clock in second.", ShaderInput::NoData };
|
||||
|
||||
ShaderBlockPin ScreenUVShaderBlock::output_pin =
|
||||
{ "Screen UV", "Screen position.", ShaderInput::NoData };
|
||||
ShaderBlockPin ViewVectorShaderBlock::output_pin =
|
||||
{ "World View", "View vector in world space.", ShaderInput::NoData };
|
||||
ShaderBlockPin ViewportShaderBlock::output_pin =
|
||||
{ "Viewport", "Viewport origin and dimension in 2d.", ShaderInput::NoData };
|
||||
|
||||
ShaderBlockPin NormalViewMatrixShaderBlock::output_pin =
|
||||
{ "Normal View Matrix", "Normal view matrix.", ShaderInput::NoData };
|
||||
ShaderBlockPin NormalMatrixShaderBlock::output_pin =
|
||||
{ "Normal Matrix", "Normal matrix.", ShaderInput::NoData };
|
||||
ShaderBlockPin ModelViewMatrixShaderBlock::output_pin =
|
||||
{ "Model View Matrix", "Model view matrix.", ShaderInput::NoData };
|
||||
ShaderBlockPin ModelMatrixShaderBlock::output_pin =
|
||||
{ "Model Matrix", "Model matrix.", ShaderInput::NoData };
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
String TextureShaderBlock::GetId() const
|
||||
{ return String("Texture") + texture; }
|
||||
|
||||
String ConstantShaderBlock::GetId() const
|
||||
{
|
||||
switch (constant_type)
|
||||
{
|
||||
case ShaderInput::NoData:
|
||||
return String("ConstNone");
|
||||
case ShaderInput::Float:
|
||||
return String("ConstFloat");
|
||||
case ShaderInput::Vector2:
|
||||
return String("ConstVec2");
|
||||
case ShaderInput::Vector3:
|
||||
return String("ConstVec3");
|
||||
case ShaderInput::Vector4:
|
||||
return String("ConstVec4");
|
||||
}
|
||||
__ERR__(__LOG_E__ << "Unknown constant.\n", String("ConstUnk"))
|
||||
}
|
||||
|
||||
String MaterialParamShaderBlock::GetId() const
|
||||
{ return String::Format("MatParm%d", param); }
|
||||
|
||||
String MaterialTextureShaderBlock::GetId() const
|
||||
{ return String::Format("MatTexture%d", slot); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool ShaderBlock::IsEquivalent(const ShaderBlock *input_block) const
|
||||
{
|
||||
if (this == input_block)
|
||||
return true;
|
||||
|
||||
if (type == input_block->type)
|
||||
switch (type)
|
||||
{
|
||||
case ShaderBlock::TypeRenderBuffer:
|
||||
{
|
||||
RenderBufferShaderBlock *a = (RenderBufferShaderBlock *)this, *b = (RenderBufferShaderBlock *)input_block;
|
||||
return a->buffer == b->buffer;
|
||||
}
|
||||
|
||||
case ShaderBlock::TypeTextureSampler:
|
||||
{
|
||||
TextureSamplerShaderBlock *a = (TextureSamplerShaderBlock *)this, *b = (TextureSamplerShaderBlock *)input_block;
|
||||
return (a->sampler_type == b->sampler_type) && a->input[0] && a->input[1] && b->input[0] && b->input[1] ? a->input[0]->IsEquivalent(b->input[0]) && a->input[1]->IsEquivalent(b->input[1]) : false;
|
||||
}
|
||||
|
||||
case ShaderBlock::TypeTexture:
|
||||
{
|
||||
TextureShaderBlock *a = (TextureShaderBlock *)this, *b = (TextureShaderBlock *)input_block;
|
||||
return a->texture == b->texture;
|
||||
}
|
||||
|
||||
case ShaderBlock::TypeGeometryUV:
|
||||
{
|
||||
GeometryUVShaderBlock *a = (GeometryUVShaderBlock *)this, *b = (GeometryUVShaderBlock *)input_block;
|
||||
return a->channel == b->channel;
|
||||
}
|
||||
|
||||
case ShaderBlock::TypeMaterialParam:
|
||||
{
|
||||
MaterialParamShaderBlock *a = (MaterialParamShaderBlock *)this, *b = (MaterialParamShaderBlock *)input_block;
|
||||
return a->param == b->param;
|
||||
}
|
||||
|
||||
case ShaderBlock::TypeClock:
|
||||
case ShaderBlock::TypeScreenUV:
|
||||
case ShaderBlock::TypeViewVector:
|
||||
case ShaderBlock::TypeViewport:
|
||||
case ShaderBlock::TypeNormalViewMatrix:
|
||||
case ShaderBlock::TypeNormalMatrix:
|
||||
case ShaderBlock::TypeModelViewMatrix:
|
||||
case ShaderBlock::TypeModelMatrix:
|
||||
case ShaderBlock::TypeGeometryVertex:
|
||||
case ShaderBlock::TypeGeometrySkinning:
|
||||
case ShaderBlock::TypeGeometryNormal:
|
||||
case ShaderBlock::TypeGeometryVertexColor:
|
||||
case ShaderBlock::TypeGeometryTangentFrame:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int UnpackColorToVectorShaderBlock::GetOutputType() const
|
||||
{ return !input[0] ? ShaderInput::NoData : input[0]->GetOutputType(); }
|
||||
int PackVectorToColorShaderBlock::GetOutputType() const
|
||||
{ return !input[0] ? ShaderInput::NoData : input[0]->GetOutputType(); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int MixOperatorShaderBlock::GetOutputType() const
|
||||
{ return (!input[0] || !input[1] || !input[2] || (input[0]->GetOutputType() != input[1]->GetOutputType()) || (input[2]->GetOutputType() != ShaderInput::Float)) ? ShaderInput::NoData : input[0]->GetOutputType(); }
|
||||
|
||||
int DotOperatorShaderBlock::GetOutputType() const
|
||||
{ return (!input[0] || !input[1] || (input[0]->GetOutputType() != input[1]->GetOutputType())) ? ShaderInput::NoData : ShaderInput::Float; }
|
||||
int CrossOperatorShaderBlock::GetOutputType() const
|
||||
{ return (!input[0] || !input[1] || ((input[0]->GetOutputType() != input[1]->GetOutputType()) && (input[0]->GetOutputType() != ShaderInput::Vector3))) ? ShaderInput::NoData : ShaderInput::Vector3; }
|
||||
|
||||
int AddOperatorShaderBlock::GetOutputType() const
|
||||
{ return (!input[0] || !input[1] || (input[0]->GetOutputType() != input[1]->GetOutputType())) ? ShaderInput::NoData : input[0]->GetOutputType(); }
|
||||
int SubOperatorShaderBlock::GetOutputType() const
|
||||
{ return (!input[0] || !input[1] || (input[0]->GetOutputType() != input[1]->GetOutputType())) ? ShaderInput::NoData : input[0]->GetOutputType(); }
|
||||
int DivOperatorShaderBlock::GetOutputType() const
|
||||
{ return (!input[0] || !input[1] || (input[0]->GetOutputType() != input[1]->GetOutputType())) ? ShaderInput::NoData : input[0]->GetOutputType(); }
|
||||
int MulOperatorShaderBlock::GetOutputType() const
|
||||
{
|
||||
if (!input[0] || !input[1])
|
||||
return ShaderInput::NoData;
|
||||
|
||||
int type_0 = input[0]->GetOutputType(),
|
||||
type_1 = input[1]->GetOutputType();
|
||||
|
||||
if (type_0 > type_1)
|
||||
{ int tmp = type_0; type_0 = type_1; type_1 = tmp; }
|
||||
|
||||
// Switch on largest type.
|
||||
switch (type_1)
|
||||
{
|
||||
case ShaderInput::Matrix3:
|
||||
return (type_0 != ShaderInput::Vector3) ? ShaderInput::NoData : ShaderInput::Vector3;
|
||||
case ShaderInput::Matrix4:
|
||||
return (type_0 != ShaderInput::Vector4) ? ShaderInput::NoData : ShaderInput::Vector4;
|
||||
|
||||
case ShaderInput::Float:
|
||||
case ShaderInput::Vector2:
|
||||
case ShaderInput::Vector3:
|
||||
case ShaderInput::Vector4:
|
||||
return (type_0 != type_1) ? ShaderInput::NoData : type_0;
|
||||
}
|
||||
return ShaderInput::NoData;
|
||||
}
|
||||
|
||||
int ClampShaderBlock::GetOutputType() const
|
||||
{
|
||||
if (!input[0] || !input[1] || !input[2])
|
||||
return ShaderInput::NoData;
|
||||
|
||||
int type[3] =
|
||||
{
|
||||
input[0]->GetOutputType(),
|
||||
input[1]->GetOutputType(),
|
||||
input[2]->GetOutputType()
|
||||
};
|
||||
|
||||
if (type[1] != type[2]) // min/max must match.
|
||||
return ShaderInput::NoData;
|
||||
if (type[0] != type[1])
|
||||
return ShaderInput::NoData; // input and min/max types must match.
|
||||
|
||||
return type[0];
|
||||
}
|
||||
|
||||
int NormalizeOperatorShaderBlock::GetOutputType() const
|
||||
{ return !input[0] ? ShaderInput::NoData : input[0]->GetOutputType(); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------
|
||||
int SwizzleShaderBlock::GetOutputType() const
|
||||
//-----------------------------------------------------------
|
||||
{
|
||||
if (!input[0] || (input[0]->GetOutputType() == ShaderInput::NoData))
|
||||
return ShaderInput::NoData;
|
||||
|
||||
int output_count = 0;
|
||||
for (int n = 0; n < 4; ++n)
|
||||
if (swizzle[n] != SwizzleNone)
|
||||
output_count++;
|
||||
|
||||
switch (output_count)
|
||||
{
|
||||
case 1: return ShaderInput::Float;
|
||||
case 2: return ShaderInput::Vector2;
|
||||
case 3: return ShaderInput::Vector3;
|
||||
case 4: return ShaderInput::Vector4;
|
||||
}
|
||||
return ShaderInput::NoData;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
int BuildShaderBlock::GetOutputType() const
|
||||
//---------------------------------------------------------
|
||||
{
|
||||
int output_count = 0;
|
||||
for (int n = 0; n < 4; ++n)
|
||||
switch (build[n])
|
||||
{
|
||||
case BuildZero:
|
||||
case BuildOne:
|
||||
output_count++;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!input[n])
|
||||
n = 4;
|
||||
else
|
||||
{
|
||||
if (input[n]->GetOutputType() == ShaderInput::NoData)
|
||||
return ShaderInput::NoData;
|
||||
output_count++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (output_count)
|
||||
{
|
||||
case 1: return ShaderInput::Float;
|
||||
case 2: return ShaderInput::Vector2;
|
||||
case 3: return ShaderInput::Vector3;
|
||||
case 4: return ShaderInput::Vector4;
|
||||
}
|
||||
return ShaderInput::NoData;
|
||||
}
|
||||
|
||||
int CosinusShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Float; }
|
||||
int SinusShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Float; }
|
||||
int PowShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Float; }
|
||||
|
||||
int AbsShaderBlock::GetOutputType() const
|
||||
{ return !input[0] ? ShaderInput::NoData : input[0]->GetOutputType(); }
|
||||
|
||||
int GeometryVertexShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Vector4; }
|
||||
int GeometryUVShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Vector2; }
|
||||
int GeometryNormalShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Vector3; }
|
||||
int GeometrySkinningShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Matrix4; }
|
||||
int GeometryVertexColorShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Vector3; }
|
||||
int GeometryTangentFrameShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Matrix3; }
|
||||
int RenderBufferShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Texture2D; }
|
||||
int TextureShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Texture2D; }
|
||||
int TextureSamplerShaderBlock::GetOutputType() const
|
||||
{ return (input[0] && input[1]) ? ShaderInput::Vector4 : ShaderInput::NoData; }
|
||||
int ConstantShaderBlock::GetOutputType() const
|
||||
{ return constant_type; }
|
||||
|
||||
int ColorShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Vector4; }
|
||||
int MaterialParamShaderBlock::GetOutputType() const
|
||||
{
|
||||
switch (param)
|
||||
{
|
||||
case MaterialGlossiness:
|
||||
case MaterialOpacity:
|
||||
case MaterialReflection:
|
||||
return ShaderInput::Float;
|
||||
}
|
||||
return ShaderInput::Vector4;
|
||||
}
|
||||
int MaterialTextureShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Texture2D; }
|
||||
|
||||
int ScreenUVShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Vector2; }
|
||||
int ViewVectorShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Vector3; }
|
||||
int ViewportShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Vector4; }
|
||||
|
||||
int NormalViewMatrixShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Matrix3; }
|
||||
int NormalMatrixShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Matrix3; }
|
||||
int ModelViewMatrixShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Matrix4; }
|
||||
int ModelMatrixShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Matrix4; }
|
||||
|
||||
int ClockShaderBlock::GetOutputType() const
|
||||
{ return ShaderInput::Float; }
|
||||
175
include/engine/core/shader_input.cpp
Normal file
175
include/engine/core/shader_input.cpp
Normal file
@ -0,0 +1,175 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/shader_input.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ShaderInput::SemanticDesc ShaderInput::semantic_desc[LastSemantic + 1] =
|
||||
{
|
||||
{ "Position", CategoryVertexStream, Vector3, MediumP },
|
||||
{ "Normal", CategoryVertexStream, Vector3, LowP },
|
||||
{ "UV0", CategoryVertexStream, Vector2, MediumP },
|
||||
{ "UV1", CategoryVertexStream, Vector2, MediumP },
|
||||
{ "UV2", CategoryVertexStream, Vector2, MediumP },
|
||||
{ "VertexColor", CategoryVertexStream, Vector4, LowP },
|
||||
{ "Tangent", CategoryVertexStream, Vector3, LowP },
|
||||
{ "Bitangent", CategoryVertexStream, Vector3, LowP },
|
||||
{ "BoneIndex", CategoryVertexStream, Vector4, LowP },
|
||||
{ "BoneWeight", CategoryVertexStream, Vector4, LowP },
|
||||
|
||||
// Data uniform.
|
||||
{ "Constant", CategoryConstant, Vector4, MediumP },
|
||||
|
||||
{ "Texture2D", CategoryTexture, DataTexture2D, NoP },
|
||||
{ "Texture3D", CategoryTexture, DataTexture2D, NoP },
|
||||
{ "TextureCube", CategoryTexture, DataTextureCube, NoP },
|
||||
|
||||
{ "Clock", CategoryRenderer, Float, MediumP },
|
||||
{ "TimeOfDay", CategoryRenderer, Float, MediumP },
|
||||
{ "ViewVector", CategoryRenderer, Vector3, MediumP },
|
||||
{ "ViewPosition", CategoryRenderer, Vector4, MediumP },
|
||||
{ "Viewport", CategoryRenderer, Vector4, MediumP },
|
||||
{ "ZNear", CategoryRenderer, Float, MediumP },
|
||||
{ "ZFar", CategoryRenderer, Float, MediumP },
|
||||
{ "ZoomFactor", CategoryRenderer, Float, MediumP },
|
||||
{ "FxScale", CategoryRenderer, Float, MediumP },
|
||||
{ "InverseBufferSize", CategoryRenderer, Vector2, LowP },
|
||||
{ "InverseViewportSize", CategoryRenderer, Vector2, LowP },
|
||||
{ "DisplayBufferRatio", CategoryRenderer, Vector2, MediumP },
|
||||
{ "ViewportRatio", CategoryRenderer, Vector2, MediumP },
|
||||
{ "ViewDepthOffset", CategoryRenderer, Float, MediumP },
|
||||
{ "AmbientColor", CategoryRenderer, Vector3, LowP },
|
||||
{ "FogColor", CategoryRenderer, Vector3, LowP },
|
||||
{ "FogNear", CategoryRenderer, Float, MediumP },
|
||||
{ "FogFar", CategoryRenderer, Float, MediumP },
|
||||
{ "FogInverseRange", CategoryRenderer, Float, LowP },
|
||||
{ "DepthBuffer", CategoryRenderer, DataTexture2D, NoP },
|
||||
{ "FrameBuffer", CategoryRenderer, DataTexture2D, NoP },
|
||||
{ "GBuffer0", CategoryRenderer, DataTexture2D, NoP },
|
||||
{ "GBuffer1", CategoryRenderer, DataTexture2D, NoP },
|
||||
{ "GBuffer2", CategoryRenderer, DataTexture2D, NoP },
|
||||
{ "GBuffer3", CategoryRenderer, DataTexture2D, NoP },
|
||||
{ "NoiseMap", CategoryRenderer, DataTexture2D, NoP },
|
||||
|
||||
{ "NormalMatrix", CategoryTransform, Matrix3, LowP },
|
||||
{ "NormalViewMatrix", CategoryTransform, Matrix3, HighP },
|
||||
{ "ModelMatrix", CategoryTransform, Matrix4, HighP },
|
||||
{ "ViewMatrix", CategoryTransform, Matrix4, HighP },
|
||||
{ "ProjectionMatrix", CategoryTransform, Matrix4, HighP },
|
||||
{ "ModelViewMatrix", CategoryTransform, Matrix4, HighP },
|
||||
{ "ModelViewProjectionMatrix", CategoryTransform, Matrix4, HighP },
|
||||
{ "InverseViewProjectionMatrix", CategoryTransform, Matrix4, HighP },
|
||||
{ "InverseViewProjectionMatrixAtOrigin", CategoryTransform, Matrix4, HighP },
|
||||
|
||||
{ "PreviousModelViewMatrix", CategoryPreviousTransform, Matrix4, HighP },
|
||||
{ "PreviousModelViewProjectionMatrix", CategoryPreviousTransform, Matrix4, HighP },
|
||||
|
||||
{ "MaterialOpacity", CategoryMaterialOpacity, Float, LowP },
|
||||
|
||||
{ "MaterialDiffuse", CategoryMaterial, Vector4, LowP },
|
||||
{ "MaterialSpecular", CategoryMaterial, Vector4, LowP },
|
||||
{ "MaterialSelf", CategoryMaterial, Vector4, LowP },
|
||||
{ "MaterialAmbient", CategoryMaterial, Vector4, LowP },
|
||||
{ "MaterialGlossiness", CategoryMaterial, Float, LowP },
|
||||
{ "MaterialReflection", CategoryMaterial, Float, LowP },
|
||||
{ "MaterialAlphaThreshold", CategoryMaterial, Float, LowP },
|
||||
{ "MaterialDepthBias", CategoryMaterial, Float, LowP },
|
||||
{ "MaterialTexture0", CategoryMaterial, DataTexture2D, NoP },
|
||||
{ "MaterialTexture1", CategoryMaterial, DataTexture2D, NoP },
|
||||
{ "MaterialTexture2", CategoryMaterial, DataTexture2D, NoP },
|
||||
{ "MaterialTexture3", CategoryMaterial, DataTexture2D, NoP },
|
||||
{ "MaterialTexture4", CategoryMaterial, DataTexture2D, NoP },
|
||||
{ "MaterialTexture5", CategoryMaterial, DataTexture2D, NoP },
|
||||
{ "MaterialTexture6", CategoryMaterial, DataTexture2D, NoP },
|
||||
{ "MaterialTexture7", CategoryMaterial, DataTexture2D, NoP },
|
||||
|
||||
{ "LightRange", CategoryLight, Float, MediumP },
|
||||
{ "LightSpotEdge", CategoryLight, Float, MediumP },
|
||||
{ "LightSpotCone", CategoryLight, Float, MediumP },
|
||||
{ "LightShadowBias", CategoryLight, Float, MediumP },
|
||||
{ "LightDiffuseColor", CategoryLight, Vector3, LowP },
|
||||
{ "LightSpecularColor", CategoryLight, Vector3, LowP },
|
||||
{ "LightShadowColor", CategoryLight, Vector3, LowP },
|
||||
{ "LightViewPosition", CategoryLight, Vector3, MediumP },
|
||||
{ "LightViewDirection", CategoryLight, Vector3, LowP },
|
||||
{ "LightShadowMatrix0", CategoryLight, Matrix4, MediumP },
|
||||
{ "LightShadowMatrix1", CategoryLight, Matrix4, MediumP },
|
||||
{ "LightShadowMatrix2", CategoryLight, Matrix4, MediumP },
|
||||
{ "LightShadowMatrix3", CategoryLight, Matrix4, MediumP },
|
||||
{ "LightShadowMatrix4", CategoryLight, Matrix4, MediumP },
|
||||
{ "LightShadowMatrix5", CategoryLight, Matrix4, MediumP },
|
||||
{ "InverseShadowMapSize", CategoryLight, Float, LowP },
|
||||
{ "LightShadowMap0", CategoryLight, DataTextureShadow, NoP },
|
||||
{ "LightShadowMap1", CategoryLight, DataTextureShadow, NoP },
|
||||
{ "LightShadowMap2", CategoryLight, DataTextureShadow, NoP },
|
||||
{ "LightShadowMap3", CategoryLight, DataTextureShadow, NoP },
|
||||
{ "LightShadowMap4", CategoryLight, DataTextureShadow, NoP },
|
||||
{ "LightShadowMap5", CategoryLight, DataTextureShadow, NoP },
|
||||
{ "LightPSSMSliceDistance0", CategoryLight, Float, MediumP },
|
||||
{ "LightPSSMSliceDistance1", CategoryLight, Float, MediumP },
|
||||
{ "LightPSSMSliceDistance2", CategoryLight, Float, MediumP },
|
||||
{ "LightPSSMSliceDistance3", CategoryLight, Float, MediumP },
|
||||
{ "ViewToLightMatrix", CategoryLight, Matrix4, MediumP },
|
||||
{ "LightProjectionMap", CategoryLight, DataTexture2D, NoP },
|
||||
|
||||
{ "BoneMatrix", CategorySkin, Matrix4, MediumP },
|
||||
{ "PreviousBoneMatrix", CategorySkin, Matrix4, MediumP },
|
||||
|
||||
{ "SemanticLast", CategoryLast, NoData, NoP }
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char *ShaderInput::GetCategoryName(Category category)
|
||||
{
|
||||
static const char *cat[CategoryLast] =
|
||||
{
|
||||
"VertexStream",
|
||||
"Constant",
|
||||
"Texture",
|
||||
"Skin",
|
||||
"Renderer",
|
||||
"MaterialOpacity",
|
||||
"Material",
|
||||
"Transform",
|
||||
"PreviousTransform",
|
||||
"Light"
|
||||
};
|
||||
return cat[category];
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool ShaderInput::ConsumesTextureUnit() const
|
||||
{
|
||||
switch (data_type)
|
||||
{
|
||||
case DataTexture2D:
|
||||
case DataTexture3D:
|
||||
case DataTextureCube:
|
||||
case DataTextureShadow:
|
||||
return true;
|
||||
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ShaderInput::ShaderInput()
|
||||
{
|
||||
type = None;
|
||||
scope = Vertex | Pixel;
|
||||
semantic = LastSemantic;
|
||||
array_size = 1;
|
||||
|
||||
parm_v.Set(0, 0, 0);
|
||||
data_type = NoData;
|
||||
}
|
||||
165
include/engine/core/shader_isl_to_glsl.cpp
Normal file
165
include/engine/core/shader_isl_to_glsl.cpp
Normal file
@ -0,0 +1,165 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/shader_isl_to_glsl.h"
|
||||
#include "core/shader.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool ISLtoGLSL::GetType(ShaderInput::DataType data_type, String &decl)
|
||||
{
|
||||
switch (data_type)
|
||||
{
|
||||
case ShaderInput::Float: decl = "float"; return true;
|
||||
case ShaderInput::Vector2: decl = "vec2"; return true;
|
||||
case ShaderInput::Vector3: decl = "vec3"; return true;
|
||||
case ShaderInput::Vector4: decl = "vec4"; return true;
|
||||
case ShaderInput::Matrix3: decl = "mat3"; return true;
|
||||
case ShaderInput::Matrix4: decl = "mat4"; return true;
|
||||
case ShaderInput::DataTexture2D: decl = "sampler2D"; return true;
|
||||
case ShaderInput::DataTexture3D: decl = "sampler3D"; return true;
|
||||
case ShaderInput::DataTextureCube: decl = "samplerCube"; return true;
|
||||
case ShaderInput::DataTextureShadow: decl = "sampler2DShadow"; return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool ISLtoGLSL::Translate(const Shader &shader, String &glsl_vertex, String &glsl_pixel, GLSLVariant variant)
|
||||
{
|
||||
glsl_vertex = shader.vertex;
|
||||
glsl_pixel = shader.pixel;
|
||||
|
||||
String vertex_decl, pixel_decl, type_decl;
|
||||
|
||||
// Variant defaults.
|
||||
switch (variant)
|
||||
{
|
||||
case EGL20:
|
||||
{
|
||||
vertex_decl += "#version 100\n\n"; // EGL 100 ~= GL 120
|
||||
pixel_decl += "#version 100\n\n";
|
||||
|
||||
static String gles_precision("precision mediump float;\n");
|
||||
vertex_decl += gles_precision;
|
||||
pixel_decl += gles_precision;
|
||||
}
|
||||
break;
|
||||
|
||||
case OGL32:
|
||||
vertex_decl += "#version 130\n\n";
|
||||
pixel_decl += "#version 130\n\n";
|
||||
break;
|
||||
|
||||
default:
|
||||
// Allow implicit conversions.
|
||||
// vertex_decl += "#version 120\n\n";
|
||||
// pixel_decl += "#version 120\n\n";
|
||||
break;
|
||||
}
|
||||
|
||||
// Helper macros.
|
||||
String mtx_mul = "#define n_mtx_mul(A, B) ((A)*(B))\n";
|
||||
vertex_decl += mtx_mul;
|
||||
pixel_decl += mtx_mul;
|
||||
|
||||
String mtx_conv = "mat3 _mat4_to_mat3(mat4 m) { return mat3(m[0].xyz, m[1].xyz, m[2].xyz); }\n";
|
||||
vertex_decl += mtx_conv;
|
||||
pixel_decl += mtx_conv;
|
||||
|
||||
String buildm3 = "\nmat3 _build_mat3(vec3 a, vec3 b, vec3 c) { return mat3(a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z); }\n";
|
||||
vertex_decl += buildm3;
|
||||
pixel_decl += buildm3;
|
||||
|
||||
// Convert symbols.
|
||||
{
|
||||
static const char *isl_symbol[] = { "%out.position%", NULL };
|
||||
static const char *egl_symbol[] = { "gl_Position", NULL };
|
||||
glsl_vertex.ReplaceAll(isl_symbol, egl_symbol, true);
|
||||
}
|
||||
{
|
||||
static const char *isl_symbol[] = { "%in.fragcoord%", "%out.color%", "%out.color0%", "%out.color1%", "%out.color2%", "%out.color3%", "%out.depth%", NULL };
|
||||
static const char *egl_symbol[] = { "gl_FragCoord", "gl_FragColor", "gl_FragData[0]", "gl_FragData[1]", "gl_FragData[2]", "gl_FragData[3]", "gl_FragDepth", NULL };
|
||||
glsl_pixel.ReplaceAll(isl_symbol, egl_symbol, true);
|
||||
}
|
||||
|
||||
//
|
||||
{
|
||||
static const char *outputs[] = { "%position%", "%normal%", "%diffuse%", "%specular%", "%glossiness%", "%constant%", "%opacity%", NULL };
|
||||
static const char *out_vars[] = { "_o_vertex", "_o_normal", "_o_diffuse", "_o_specular", "_o_glossiness", "_o_constant", "_o_opacity", NULL };
|
||||
glsl_vertex.ReplaceAll(outputs, out_vars, true);
|
||||
glsl_pixel.ReplaceAll(outputs, out_vars, true);
|
||||
}
|
||||
|
||||
// Create function declaration.
|
||||
if (!glsl_vertex.Replace("%main%", "void main()"))
|
||||
glsl_vertex = String("void main()\n{\n") + glsl_vertex + "}";
|
||||
if (!glsl_pixel.Replace("%main%", "void main()"))
|
||||
glsl_pixel = String("void main()\n{\n") + glsl_pixel + "}";
|
||||
|
||||
// Declare inputs.
|
||||
ListForeachPtr(ShaderInput *, input, shader.input_list)
|
||||
if (GetType(input->data_type, type_decl))
|
||||
{
|
||||
if (variant == EGL20)
|
||||
{
|
||||
static String lowp("lowp "), mediump("mediump "), highp("highp ");
|
||||
|
||||
switch (ShaderInput::semantic_desc[input->semantic].precision)
|
||||
{
|
||||
case ShaderInput::LowP: type_decl = lowp + type_decl; break;
|
||||
case ShaderInput::MediumP: type_decl = mediump + type_decl; break;
|
||||
case ShaderInput::HighP: type_decl = highp + type_decl; break;
|
||||
}
|
||||
}
|
||||
|
||||
String input_decl = input->array_size > 1 ? String::Format("%s[%d]", input->name.c_str(), input->array_size) : input->name,
|
||||
local_decl;
|
||||
|
||||
switch (input->type)
|
||||
{
|
||||
case ShaderInput::Attribute:
|
||||
local_decl = String::Format("attribute %s %s;\n", type_decl.c_str(), input_decl.c_str());
|
||||
break;
|
||||
|
||||
case ShaderInput::Uniform:
|
||||
local_decl = String::Format("uniform %s %s;\n", type_decl.c_str(), input_decl.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
if (input->scope & ShaderInput::Vertex)
|
||||
vertex_decl += local_decl;
|
||||
if (input->scope & ShaderInput::Pixel)
|
||||
pixel_decl += local_decl;
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "Unsupported input '" << input->name << "' type (" << input->type << ").\n";
|
||||
|
||||
// Declare varyings.
|
||||
ListForeachPtr(ShaderVarying *, varying, shader.varying_list)
|
||||
{
|
||||
String decl = String::Format("varying %s %s;\n", varying->type.c_str(), varying->name.c_str());
|
||||
vertex_decl += decl;
|
||||
pixel_decl += decl;
|
||||
}
|
||||
|
||||
// Assemble final source.
|
||||
glsl_vertex = vertex_decl + shader.vertex_decl + glsl_vertex;
|
||||
glsl_pixel = pixel_decl + shader.pixel_decl + glsl_pixel;
|
||||
|
||||
//
|
||||
if (variant == EGL20)
|
||||
{
|
||||
static const char *isl_symbol[] = { "sampler2DShadow", "shadow2D", NULL };
|
||||
static const char *egl_symbol[] = { "sampler2D", "texture2D", NULL };
|
||||
glsl_vertex.ReplaceAll(isl_symbol, egl_symbol, true);
|
||||
glsl_pixel.ReplaceAll(isl_symbol, egl_symbol, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
265
include/engine/core/shader_isl_to_hlsl.cpp
Normal file
265
include/engine/core/shader_isl_to_hlsl.cpp
Normal file
@ -0,0 +1,265 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "core/shader_isl_to_hlsl.h"
|
||||
#include "core/shader.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
String RewriteTextureSampling(const char *source, StringList &arg)
|
||||
{
|
||||
// ISL arg0: texture object variable.
|
||||
// ISL arg1: UV variable.
|
||||
return String::Format("%s_res.Sample(%s, %s)", arg[0].c_str(), arg[0].c_str(), arg[1].c_str());
|
||||
}
|
||||
String RewriteShadowSampling(const char *source, StringList &arg)
|
||||
{
|
||||
// ISL arg0: texture object variable.
|
||||
// ISL arg1: UV variable.
|
||||
return String::Format("%s_res.SampleCmpLevelZero(%s, (%s).xy, (%s).z * 2.0 - 1.0)", arg[0].c_str(), arg[0].c_str(), arg[1].c_str(), arg[1].c_str());
|
||||
}
|
||||
String RewritePCFCall(const char *source, StringList &arg)
|
||||
{
|
||||
// Make sure a call to ComputePCF is submitted and not the function definition (which already has 5 parameters by now).
|
||||
if (arg[3].IndexOf(',') != -1)
|
||||
return source;
|
||||
return String::Format("ComputePCF(%s, %s, %s, %s_res, %s)", arg[0].c_str(), arg[1].c_str(), arg[2].c_str(), arg[2].c_str(), arg[3].c_str());
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
String ISLtoHLSL::GetCategoryCBufferName(ShaderInput::Category c)
|
||||
{
|
||||
return String::Format("CBuffer%s", ShaderInput::GetCategoryName(c));
|
||||
}
|
||||
bool ISLtoHLSL::GetType(ShaderInput::DataType type, String &decl)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ShaderInput::Float: decl = "float"; return true;
|
||||
case ShaderInput::Vector2: decl = "vec2"; return true;
|
||||
case ShaderInput::Vector3: decl = "vec3"; return true;
|
||||
case ShaderInput::Vector4: decl = "vec4"; return true;
|
||||
case ShaderInput::Matrix3: decl = "mat3"; return true;
|
||||
case ShaderInput::Matrix4: decl = "mat4"; return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static void BuildInputLocalDecl(const ShaderInput * input, String &local_decl)
|
||||
{
|
||||
String type_decl;
|
||||
switch (input->data_type)
|
||||
{
|
||||
case ShaderInput::DataTexture2D:
|
||||
case ShaderInput::DataTextureCube:
|
||||
local_decl = String::Format("SamplerState %s;\nTexture2D %s_res;\n", input->name.c_str(), input->name.c_str());
|
||||
break;
|
||||
|
||||
case ShaderInput::DataTextureShadow:
|
||||
local_decl = String::Format("SamplerComparisonState %s;\nTexture2D %s_res;\n", input->name.c_str(), input->name.c_str());
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ISLtoHLSL::GetType(input->data_type, type_decl))
|
||||
{
|
||||
String input_decl = input->array_size > 1 ? String::Format("%s[%d]", input->name.c_str(), input->array_size) : input->name;
|
||||
local_decl = String::Format("%s %s;\n", type_decl.c_str(), input_decl.c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
static void DeclareInputs(const Shader &shader, String &vertex_decl, String &pixel_decl)
|
||||
{
|
||||
// Declare constants with one cbuffer per category.
|
||||
String local_decl;
|
||||
|
||||
for (uint c = 0; c < ShaderInput::CategoryLast; ++c)
|
||||
{
|
||||
bool vertex_constant_scope_open = false,
|
||||
pixel_constant_scope_open = false;
|
||||
|
||||
ListForeachPtr(ShaderInput *, input, shader.input_list)
|
||||
{
|
||||
if (input->type != ShaderInput::Uniform)
|
||||
continue;
|
||||
if (ShaderInput::semantic_desc[input->semantic].category != (ShaderInput::Category)c)
|
||||
continue; // filter on category
|
||||
|
||||
BuildInputLocalDecl(input, local_decl);
|
||||
|
||||
if (input->scope & ShaderInput::Vertex)
|
||||
{
|
||||
if (!vertex_constant_scope_open && !input->ConsumesTextureUnit())
|
||||
{
|
||||
vertex_decl += String::Format("\ncbuffer CBuffer%s\n{\n", ShaderInput::GetCategoryName((ShaderInput::Category)c));
|
||||
vertex_constant_scope_open = true;
|
||||
}
|
||||
vertex_decl += local_decl;
|
||||
}
|
||||
if (input->scope & ShaderInput::Pixel)
|
||||
{
|
||||
if (!pixel_constant_scope_open && !input->ConsumesTextureUnit())
|
||||
{
|
||||
pixel_decl += String::Format("\ncbuffer CBuffer%s\n{\n", ShaderInput::GetCategoryName((ShaderInput::Category)c));
|
||||
pixel_constant_scope_open = true;
|
||||
}
|
||||
pixel_decl += local_decl;
|
||||
}
|
||||
}
|
||||
|
||||
// Close cbuffer scope.
|
||||
if (vertex_constant_scope_open)
|
||||
vertex_decl += "};\n";
|
||||
if (pixel_constant_scope_open)
|
||||
pixel_decl += "};\n";
|
||||
}
|
||||
}
|
||||
bool ISLtoHLSL::Translate(const Shader &shader, String &hlsl_vertex, String &hlsl_pixel)
|
||||
{
|
||||
hlsl_vertex = shader.vertex;
|
||||
hlsl_pixel = shader.pixel;
|
||||
|
||||
// Vertex/pixel structures.
|
||||
String v_in_struct = "struct VertexInput\n{\n",
|
||||
p_in_struct = "struct PixelInput\n{\n",
|
||||
type_decl;
|
||||
|
||||
p_in_struct += " vec4 position : SV_Position;\n";
|
||||
|
||||
ListForeachPtr(ShaderInput *, input, shader.input_list)
|
||||
if ((input->type == ShaderInput::Attribute) && GetType(input->data_type, type_decl))
|
||||
{
|
||||
String input_decl = input->array_size > 1 ? String::Format("%s[%d]", input->name.c_str(), input->array_size) : input->name;
|
||||
String local_decl = String::Format(" %s %s : %s;\n", type_decl.c_str(), input_decl.c_str(), ShaderInput::semantic_desc[input->semantic].name);
|
||||
|
||||
if (input->scope & ShaderInput::Vertex)
|
||||
v_in_struct += local_decl;
|
||||
if (input->scope & ShaderInput::Pixel)
|
||||
p_in_struct += local_decl;
|
||||
|
||||
hlsl_vertex.ReplaceAll(input->name, String::Format("IN.%s", input->name.c_str()), true);
|
||||
}
|
||||
|
||||
int i_interpolator = 0;
|
||||
ListForeachPtr(ShaderVarying *, varying, shader.varying_list)
|
||||
{
|
||||
p_in_struct += String::Format(" %s %s : Varying%d;\n", varying->type.c_str(), varying->name.c_str(), i_interpolator);
|
||||
|
||||
// Reserve enough registers for the varying type.
|
||||
if (varying->type == "mat4")
|
||||
i_interpolator += 4;
|
||||
else if (varying->type == "mat3")
|
||||
i_interpolator += 3;
|
||||
else
|
||||
i_interpolator++;
|
||||
|
||||
hlsl_vertex.ReplaceAll(varying->name, String::Format("OUT.%s", varying->name.c_str()), true);
|
||||
hlsl_pixel.ReplaceAll(varying->name, String::Format("IN.%s", varying->name.c_str()), true);
|
||||
}
|
||||
|
||||
v_in_struct += "};\n\n";
|
||||
p_in_struct += "};\n\n";
|
||||
|
||||
// Pixel output.
|
||||
String p_out_struct = "struct PixelOutput\n{\n";
|
||||
if (hlsl_pixel.Contains("%out.color%")) p_out_struct += " vec4 color : SV_Target;\n";
|
||||
if (hlsl_pixel.Contains("%out.color0%")) p_out_struct += " vec4 color0 : SV_Target1;\n";
|
||||
if (hlsl_pixel.Contains("%out.color1%")) p_out_struct += " vec4 color1 : SV_Target2;\n";
|
||||
if (hlsl_pixel.Contains("%out.color2%")) p_out_struct += " vec4 color2 : SV_Target3;\n";
|
||||
if (hlsl_pixel.Contains("%out.color3%")) p_out_struct += " vec4 color3 : SV_Target4;\n";
|
||||
if (hlsl_pixel.Contains("%out.depth%")) p_out_struct += " float depth : SV_Depth;\n";
|
||||
p_out_struct += "};\n\n";
|
||||
|
||||
//
|
||||
String vertex_decl, pixel_decl;
|
||||
|
||||
String header = String::Format("// GSFramework ISL to HLSL converter.\n// File: '%s'\n\n", shader.name.c_str());
|
||||
vertex_decl += header;
|
||||
pixel_decl += header;
|
||||
|
||||
vertex_decl += v_in_struct;
|
||||
vertex_decl += p_in_struct;
|
||||
pixel_decl += p_in_struct;
|
||||
pixel_decl += p_out_struct;
|
||||
|
||||
// Translation helper.
|
||||
vertex_decl += "\n#define mix lerp\n";
|
||||
pixel_decl += "\n#define mix lerp\n";
|
||||
|
||||
vertex_decl += "\n#define n_mtx_mul mul\n";
|
||||
pixel_decl += "\n#define n_mtx_mul mul\n";
|
||||
|
||||
String m4tom3 = "\nfloat3x3 _mat4_to_mat3(const float4x4 m) { return (float3x3)m; }\n";
|
||||
vertex_decl += m4tom3;
|
||||
pixel_decl += m4tom3;
|
||||
|
||||
String buildm3 = "\nfloat3x3 _build_mat3(const float3 a, const float3 b, const float3 c) { return float3x3(a.x, b.x, c.x, a.y, b.y, c.y, a.z, b.z, c.z); }\n";
|
||||
vertex_decl += buildm3;
|
||||
pixel_decl += buildm3;
|
||||
|
||||
// Declare uniforms.
|
||||
DeclareInputs(shader, vertex_decl, pixel_decl);
|
||||
|
||||
// Create function declaration.
|
||||
if (!hlsl_vertex.Replace("%main%", "void main(in VertexInput IN, out PixelInput OUT)"))
|
||||
hlsl_vertex = String("void main(in VertexInput IN, out PixelInput OUT)\n{\n") + hlsl_vertex + "}";
|
||||
if (!hlsl_pixel.Replace("%main%", "void main(in PixelInput IN, out PixelOutput OUT)"))
|
||||
hlsl_pixel = String("void main(in PixelInput IN, out PixelOutput OUT)\n{\n") + hlsl_pixel + "}";
|
||||
|
||||
// Assemble final source.
|
||||
hlsl_vertex = vertex_decl + shader.vertex_decl + hlsl_vertex;
|
||||
hlsl_pixel = pixel_decl + shader.pixel_decl + hlsl_pixel;
|
||||
|
||||
// Convert shadow sampling.
|
||||
hlsl_pixel.Replace("ComputePCF(vec3 fvp, mat4 pjm, sampler2DShadow tsampler, float k)", "ComputePCF(float3 fvp, float4x4 pjm, SamplerComparisonState tsampler, Texture2D tsampler_res, float k)", String::CaseSensitive);
|
||||
hlsl_pixel = String::RewritePatternAll(hlsl_pixel, "ComputePCF(%,%,%,%)", RewritePCFCall);
|
||||
|
||||
// Convert texture sampling.
|
||||
hlsl_vertex = String::RewritePatternAll(hlsl_vertex, "texture2D(%,%)", RewriteTextureSampling);
|
||||
hlsl_pixel = String::RewritePatternAll(hlsl_pixel, "texture2D(%,%)", RewriteTextureSampling);
|
||||
hlsl_vertex = String::RewritePatternAll(hlsl_vertex, "shadow2D(%,%)", RewriteShadowSampling);
|
||||
hlsl_pixel = String::RewritePatternAll(hlsl_pixel, "shadow2D(%,%)", RewriteShadowSampling);
|
||||
hlsl_vertex = String::RewritePatternAll(hlsl_vertex, "textureCube(%,%)", RewriteTextureSampling);
|
||||
hlsl_pixel = String::RewritePatternAll(hlsl_pixel, "textureCube(%,%)", RewriteTextureSampling);
|
||||
|
||||
// Convert symbols.
|
||||
{
|
||||
static const char *isl_symbol[] = { "%out.position%", NULL };
|
||||
static const char *hsl_symbol[] = { "OUT.position", NULL };
|
||||
hlsl_vertex.ReplaceAll(isl_symbol, hsl_symbol, true);
|
||||
}
|
||||
|
||||
{
|
||||
static const char *isl_symbol[] = { "%in.fragcoord%", "%out.color%", "%out.color0%", "%out.color1%", "%out.color2%", "%out.color3%", "%out.depth%", NULL };
|
||||
static const char *hsl_symbol[] = { "IN.position", "OUT.color", "OUT.color0", "OUT.color1", "OUT.color2", "OUT.color3", "OUT.depth", NULL };
|
||||
hlsl_pixel.ReplaceAll(isl_symbol, hsl_symbol, true);
|
||||
}
|
||||
|
||||
//
|
||||
{
|
||||
static const char *outputs[] = { "%position%", "%normal%", "%diffuse%", "%specular%", "%glossiness%", "%constant%", "%opacity%", NULL };
|
||||
static const char *out_vars[] = { "_o_vertex", "_o_normal", "_o_diffuse", "_o_specular", "_o_glossiness", "_o_constant", "_o_opacity", NULL };
|
||||
hlsl_vertex.ReplaceAll(outputs, out_vars, true);
|
||||
hlsl_pixel.ReplaceAll(outputs, out_vars, true);
|
||||
}
|
||||
|
||||
{
|
||||
static const char *isl_symbol[] = { "vec2", "vec3", "vec4", "mat3", "mat4", "sampler2DShadow", NULL };
|
||||
static const char *hsl_symbol[] = { "float2", "float3", "float4", "float3x3", "float4x4", "SamplerComparisonState", NULL };
|
||||
hlsl_vertex.ReplaceAll(isl_symbol, hsl_symbol, true);
|
||||
hlsl_pixel.ReplaceAll(isl_symbol, hsl_symbol, true);
|
||||
}
|
||||
|
||||
// [EJ] PIX won't display sources correctly unless they use Windows eol markers.
|
||||
hlsl_vertex.NormalizeEOL(String::EOLWindows);
|
||||
hlsl_pixel.NormalizeEOL(String::EOLWindows);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
272
include/engine/core/shader_nml.cpp
Normal file
272
include/engine/core/shader_nml.cpp
Normal file
@ -0,0 +1,272 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/shader.h"
|
||||
#include "metafile/nml.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "platform.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
using GS::NML::Tag;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Shader::ParseVaryingTag(Tag *tag)
|
||||
{
|
||||
if (!tag || tag->name != "Varying")
|
||||
return;
|
||||
|
||||
NMLTagForeach(it, *tag)
|
||||
if (it->name == "Variable")
|
||||
{
|
||||
Tag *t_name = it->GetTypedTag("Name", Variant::VariantString),
|
||||
*t_type = it->GetTypedTag("Type", Variant::VariantString);
|
||||
if (t_name && t_type)
|
||||
DeclareVarying(t_name->GetString(), t_type->GetString());
|
||||
}
|
||||
}
|
||||
void Shader::ParseInputTag(Tag *tag)
|
||||
{
|
||||
if (!tag || tag->name != "Input")
|
||||
return;
|
||||
|
||||
NMLTagForeach(it, *tag)
|
||||
{
|
||||
ShaderInput::Type type = ShaderInput::None;
|
||||
uint scope = 0;
|
||||
|
||||
// Parse shader input.
|
||||
if (it->name == "Attribute")
|
||||
{
|
||||
type = ShaderInput::Attribute;
|
||||
scope = ShaderInput::Vertex;
|
||||
}
|
||||
else if (it->name == "Uniform")
|
||||
{
|
||||
type = ShaderInput::Uniform;
|
||||
scope = ShaderInput::Pixel;
|
||||
}
|
||||
|
||||
if (type != ShaderInput::None)
|
||||
{
|
||||
String input_name;
|
||||
ShaderInput::DataType data_type = ShaderInput::NoData;
|
||||
ShaderInput::Semantic semantic = ShaderInput::Constant;
|
||||
|
||||
if (Tag *t = it->GetTypedTag("Name", Variant::VariantString))
|
||||
input_name = t->GetString();
|
||||
|
||||
if (Tag *t = it->GetTypedTag("Semantic", Variant::VariantString))
|
||||
{
|
||||
String cs(t->GetString());
|
||||
|
||||
#if 1 // Legacy semantic support (changed with 1.3.0).
|
||||
if (cs == "Texture")
|
||||
cs = "Texture2D";
|
||||
else if (cs == "NativeTexture")
|
||||
cs = "Texture2D";
|
||||
else if (cs == "CubeTexture")
|
||||
cs = "TextureCube";
|
||||
else if (cs == "User")
|
||||
cs = "Constant";
|
||||
#endif
|
||||
|
||||
uint n = 0;
|
||||
for (; n < ShaderInput::LastSemantic; ++n)
|
||||
if (cs == ShaderInput::semantic_desc[n].name)
|
||||
{
|
||||
semantic = (ShaderInput::Semantic)n;
|
||||
data_type = ShaderInput::semantic_desc[semantic].data_type;
|
||||
break;
|
||||
}
|
||||
|
||||
if (n == ShaderInput::LastSemantic)
|
||||
__LOG_E__ << "Unknown shader input semantic '" << cs << "'.\n";
|
||||
}
|
||||
|
||||
if (Tag *t = it->GetTag("Type"))
|
||||
{
|
||||
String type(t->GetString());
|
||||
|
||||
if (type == "float") data_type = ShaderInput::Float;
|
||||
else if (type == "vec2") data_type = ShaderInput::Vector2;
|
||||
else if (type == "vec3") data_type = ShaderInput::Vector3;
|
||||
else if (type == "vec4") data_type = ShaderInput::Vector4;
|
||||
else if (type == "mat3") data_type = ShaderInput::Matrix3;
|
||||
else if (type == "mat4") data_type = ShaderInput::Matrix4;
|
||||
else
|
||||
__LOG_E__ << "Unknown type '" << type << "' for input '" << input_name << "'.\n";
|
||||
}
|
||||
|
||||
if (Tag *t = it->GetTag("Scope"))
|
||||
{
|
||||
scope = 0;
|
||||
if (t->GetTag("Vertex"))
|
||||
scope |= ShaderInput::Vertex;
|
||||
if (t->GetTag("Pixel") || t->GetTag("Fragment"))
|
||||
scope |= ShaderInput::Pixel;
|
||||
}
|
||||
|
||||
ShaderInput *input = NULL;
|
||||
|
||||
if (semantic != ShaderInput::LastSemantic)
|
||||
input = DeclareInput(input_name, data_type, semantic, type, (ShaderInput::Scope)scope);
|
||||
else
|
||||
__LOG_W__ << "Unknown shader input semantic in shader '" << name << "'.\n";
|
||||
|
||||
if (input)
|
||||
switch (input->semantic)
|
||||
{
|
||||
case ShaderInput::Texture2D:
|
||||
case ShaderInput::TextureCube:
|
||||
if (Tag *t = it->GetTypedTag("Texture", Variant::VariantString))
|
||||
input->parm_t = t->GetString();
|
||||
break;
|
||||
|
||||
case ShaderInput::Constant:
|
||||
if (Tag *t = it->GetTag("Vector"))
|
||||
input->parm_v.FromMetaTag(*t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool Shader::FromMetaTag(Tag &tag)
|
||||
{
|
||||
if (tag.name != "Shader")
|
||||
return false;
|
||||
|
||||
Clear();
|
||||
|
||||
NMLTagForeach(pt, tag)
|
||||
{
|
||||
if (pt->name == "Name")
|
||||
name = pt->GetString();
|
||||
|
||||
else if (pt->name == "Varying")
|
||||
ParseVaryingTag(pt);
|
||||
else if (pt->name == "Input")
|
||||
ParseInputTag(pt);
|
||||
|
||||
else if (pt->name == "VertexDeclaration")
|
||||
vertex_decl = pt->GetString();
|
||||
else if (pt->name == "PixelDeclaration")
|
||||
pixel_decl = pt->GetString();
|
||||
else if (pt->name == "GeometryDeclaration")
|
||||
geometry_decl = pt->GetString();
|
||||
|
||||
else if (pt->name == "VertexSource")
|
||||
vertex = pt->GetString();
|
||||
else if ((pt->name == "PixelSource") || (pt->name == "FragmentSource"))
|
||||
pixel = pt->GetString();
|
||||
else if (pt->name == "GeometrySource")
|
||||
geometry = pt->GetString();
|
||||
|
||||
else if (pt->name == "Vertex")
|
||||
{
|
||||
Array <char> buffer;
|
||||
if (Platform::Get().io->FileLoad(pt->GetString(), buffer))
|
||||
vertex.Set(buffer, &buffer[buffer.GetCount()]);
|
||||
}
|
||||
else if ((pt->name == "Pixel") || (pt->name == "Fragment"))
|
||||
{
|
||||
Array <char> buffer;
|
||||
if (Platform::Get().io->FileLoad(pt->GetString(), buffer))
|
||||
pixel.Set(buffer, &buffer[buffer.GetCount()]);
|
||||
}
|
||||
else if (pt->name == "Geometry")
|
||||
{
|
||||
Array <char> buffer;
|
||||
if (Platform::Get().io->FileLoad(pt->GetString(), buffer))
|
||||
geometry.Set(buffer, &buffer[buffer.GetCount()]);
|
||||
}
|
||||
else
|
||||
__LOG_W__ << "Unknown tag '" << pt->name << "' in <Shader>.\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Tag *Shader::AsMetaTag() const
|
||||
{
|
||||
Tag *root = new Tag("Shader");
|
||||
if (!root)
|
||||
__ERR__(__LOG_E__ << "Could not serialize shader. Failed to create root tag.\n", NULL)
|
||||
|
||||
if (input_list.GetCount())
|
||||
if (Tag *it = root->AddChild("Input"))
|
||||
ListForeachPtr(ShaderInput *, input, input_list)
|
||||
{
|
||||
Tag *t = NULL;
|
||||
switch (input->type)
|
||||
{
|
||||
case ShaderInput::Attribute: t = new Tag("Attribute"); break;
|
||||
case ShaderInput::Uniform: t = new Tag("Uniform"); break;
|
||||
}
|
||||
if (!t)
|
||||
continue;
|
||||
|
||||
t->AddChild("Name", input->name);
|
||||
t->AddChild("Semantic", ShaderInput::semantic_desc[input->semantic].name);
|
||||
|
||||
if (Tag *st = t->AddChild("Scope"))
|
||||
{
|
||||
if (input->scope & ShaderInput::Vertex)
|
||||
st->AddChild("Vertex");
|
||||
if (input->scope & ShaderInput::Pixel)
|
||||
st->AddChild("Pixel");
|
||||
if (input->scope & ShaderInput::Geometry)
|
||||
st->AddChild("Geometry");
|
||||
}
|
||||
|
||||
switch (input->semantic)
|
||||
{
|
||||
case ShaderInput::Constant:
|
||||
switch (input->data_type)
|
||||
{
|
||||
case ShaderInput::Float: t->AddChild("Type", "float"); break;
|
||||
case ShaderInput::Vector2: t->AddChild("Type", "vec2"); break;
|
||||
case ShaderInput::Vector3: t->AddChild("Type", "vec3"); break;
|
||||
case ShaderInput::Vector4: t->AddChild("Type", "vec4"); break;
|
||||
case ShaderInput::Matrix3: t->AddChild("Type", "mat3"); break;
|
||||
case ShaderInput::Matrix4: t->AddChild("Type", "mat4"); break;
|
||||
}
|
||||
|
||||
t->AddChild(input->parm_v.AsMetaTag("Vector"));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!input->parm_t.IsEmpty())
|
||||
t->AddChild("Texture", input->parm_t.c_str());
|
||||
|
||||
it->AddChild(t);
|
||||
}
|
||||
|
||||
if (varying_list.GetCount())
|
||||
if (Tag *it = root->AddChild("Varying"))
|
||||
ListForeachPtr(ShaderVarying *, varying, varying_list)
|
||||
if (Tag *v = it->AddChild("Variable"))
|
||||
{
|
||||
v->AddChild("Name", varying->name);
|
||||
v->AddChild("Type", varying->type);
|
||||
}
|
||||
|
||||
if (!geometry_decl.IsEmpty())
|
||||
root->AddChild("GeometryDeclaration", geometry_decl);
|
||||
if (!vertex_decl.IsEmpty())
|
||||
root->AddChild("VertexDeclaration", vertex_decl);
|
||||
if (!pixel_decl.IsEmpty())
|
||||
root->AddChild("PixelDeclaration", pixel_decl);
|
||||
|
||||
if (!geometry.IsEmpty())
|
||||
root->AddChild("GeometrySource", geometry);
|
||||
if (!vertex.IsEmpty())
|
||||
root->AddChild("VertexSource", vertex);
|
||||
if (!pixel.IsEmpty())
|
||||
root->AddChild("PixelSource", pixel);
|
||||
|
||||
return root;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
69
include/engine/core/shader_tree.cpp
Normal file
69
include/engine/core/shader_tree.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
#include "core/shader_tree.h"
|
||||
#include "core/shader_block.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int ShaderTree::GetSinkCompatibility(ShaderSinkType sink) const
|
||||
{
|
||||
switch (sink)
|
||||
{
|
||||
case SinkNormal: return ShaderInput::Vector3;
|
||||
case SinkDiffuse: return ShaderInput::Vector4;
|
||||
case SinkModulate: return ShaderInput::Float;
|
||||
case SinkSpecular: return ShaderInput::Vector4;
|
||||
case SinkGlossiness: return ShaderInput::Float;
|
||||
case SinkConstant: return ShaderInput::Vector4;
|
||||
case SinkOpacity: return ShaderInput::Float;
|
||||
case SinkReflection: return ShaderInput::Float;
|
||||
}
|
||||
return ShaderInput::NoData;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void ShaderTree::GatherBlockList(GS::List <ShaderBlock *> &block_list, ShaderBlock *root)
|
||||
{
|
||||
if (!root)
|
||||
return;
|
||||
|
||||
// Gather all children.
|
||||
for (uint n = 0; n < root->GetInputCount(); ++n)
|
||||
GatherBlockList(block_list, root->GetInput(n));
|
||||
|
||||
// Add self.
|
||||
if (!block_list.Find(root))
|
||||
block_list.Append(root);
|
||||
}
|
||||
|
||||
void ShaderTree::Free()
|
||||
{
|
||||
// Gather all blocks in tree.
|
||||
List <ShaderBlock *> block_list;
|
||||
for (int n = 0; n < SinkInvalid; ++n)
|
||||
GatherBlockList(block_list, sink[n]);
|
||||
|
||||
// Disconnect all sinks.
|
||||
for (int n = 0; n < SinkInvalid; ++n)
|
||||
sink[n] = NULL;
|
||||
|
||||
// Delete all blocks.
|
||||
ListDeleteAllPtr(ShaderBlock *, block_list)
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ShaderTree::ShaderTree()
|
||||
{
|
||||
for (int n = 0; n < SinkInvalid; ++n)
|
||||
sink[n] = 0;
|
||||
}
|
||||
ShaderTree::~ShaderTree()
|
||||
{ Free(); }
|
||||
//------------------------------------------------------------------------------
|
||||
188
include/engine/core/shader_tree_compiler.cpp
Normal file
188
include/engine/core/shader_tree_compiler.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/shader_tree_compiler.h"
|
||||
#include "core/geometry.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool ShaderTreeCompiler::GetNewVariable(String &variable, const char *prefix)
|
||||
{
|
||||
variable = String::Format("n_%s%d", prefix ? prefix : "n_var", variable_count++);
|
||||
return true;
|
||||
}
|
||||
CShaderBlock *ShaderTreeCompiler::GetNewCompiledBlock(const ShaderBlock *block)
|
||||
{
|
||||
List <CShaderBlock *> ::Item *item = compiled_tree_block.Add(new CShaderBlock(block));
|
||||
return item ? item->Object() : NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
void ShaderTreeCompiler::AddPrefix(const char *prefix)
|
||||
//-------------------------------------------------------------------
|
||||
{
|
||||
if (prefix)
|
||||
id += prefix;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------
|
||||
CShaderBlock *ShaderTreeCompiler::CompileTemporary(ShaderBlock *block, const char *prefix, ShaderInput::Scope scope)
|
||||
//-------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
temporary_block_list.Add(block);
|
||||
return CompileShaderBlock(block, prefix, scope);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------------------
|
||||
CShaderBlock *ShaderTreeCompiler::CompileShaderBlock(const ShaderBlock *block, const char *prefix, ShaderInput::Scope scope)
|
||||
//---------------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
if (!block)
|
||||
return NULL;
|
||||
|
||||
// Id should describe the full tree structure.
|
||||
AddPrefix(prefix);
|
||||
id += block->GetId();
|
||||
|
||||
/*
|
||||
Avoid compiling twice a block referenced multiple times.
|
||||
Also geometry input blocks only need to be evaluated once.
|
||||
*/
|
||||
ListForeachPtr(CShaderBlock *, compiled_block, compiled_tree_block)
|
||||
if (compiled_block->block->IsEquivalent(block))
|
||||
return compiled_block;
|
||||
|
||||
// Compile as new block.
|
||||
switch (block->type)
|
||||
{
|
||||
case ShaderBlock::TypeConstant:
|
||||
return CompileConstantShaderBlock((const ConstantShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeColor:
|
||||
return CompileColorShaderBlock((const ColorShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeMaterialParam:
|
||||
return CompileMaterialParamShaderBlock((const MaterialParamShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeMaterialTexture:
|
||||
return CompileMaterialTextureShaderBlock((const MaterialTextureShaderBlock *)block, scope);
|
||||
|
||||
case ShaderBlock::TypeGeometryVertex:
|
||||
return CompileGeometryVertexShaderBlock((const GeometryVertexShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeGeometryNormal:
|
||||
return CompileGeometryNormalShaderBlock((const GeometryNormalShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeGeometrySkinning:
|
||||
return CompileGeometrySkinningShaderBlock((const GeometrySkinningShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeGeometryVertexColor:
|
||||
return CompileGeometryVertexColorShaderBlock((const GeometryVertexColorShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeGeometryTangentFrame:
|
||||
return CompileGeometryTangentFrameShaderBlock((const GeometryTangentFrameShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeGeometryUV:
|
||||
return CompileGeometryUVShaderBlock((const GeometryUVShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeRenderBuffer:
|
||||
return CompileRenderBufferShaderBlock((const RenderBufferShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeTexture:
|
||||
return CompileTextureShaderBlock((const TextureShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeTextureSampler:
|
||||
return CompileTextureSamplerShaderBlock((const TextureSamplerShaderBlock *)block, scope);
|
||||
|
||||
case ShaderBlock::TypeMix:
|
||||
return CompileMixShaderBlock((const MixOperatorShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeAdd:
|
||||
return CompileAddShaderBlock((const AddOperatorShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeSub:
|
||||
return CompileSubShaderBlock((const SubOperatorShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeMul:
|
||||
return CompileMulShaderBlock((const MulOperatorShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeDiv:
|
||||
return CompileDivShaderBlock((const DivOperatorShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeSwizzle:
|
||||
return CompileSwizzleShaderBlock((const SwizzleShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeBuild:
|
||||
return CompileBuildShaderBlock((const BuildShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeCos:
|
||||
return CompileCosinusShaderBlock((const CosinusShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeSin:
|
||||
return CompileSinusShaderBlock((const SinusShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypePow:
|
||||
return CompilePowShaderBlock((const PowShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeAbs:
|
||||
return CompileAbsShaderBlock((const AbsShaderBlock *)block, scope);
|
||||
|
||||
case ShaderBlock::TypeNormalize:
|
||||
return CompileNormalizeBlock((const NormalizeOperatorShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeDot:
|
||||
return CompileDotShaderBlock((const DotOperatorShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeCross:
|
||||
return CompileCrossShaderBlock((const CrossOperatorShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeClamp:
|
||||
return CompileClampShaderBlock((const ClampShaderBlock *)block, scope);
|
||||
|
||||
case ShaderBlock::TypePackVectorToColor:
|
||||
return CompilePackVectorToColor((const PackVectorToColorShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeUnpackColorToVector:
|
||||
return CompileUnpackColorToVector((const UnpackColorToVectorShaderBlock *)block, scope);
|
||||
|
||||
case ShaderBlock::TypeClock:
|
||||
return CompileClockShaderBlock((const ClockShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeScreenUV:
|
||||
return CompileScreenUVShaderBlock((const ScreenUVShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeViewVector:
|
||||
return CompileViewVectorShaderBlock((const ViewVectorShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeViewport:
|
||||
return CompileViewportShaderBlock((const ViewportShaderBlock *)block, scope);
|
||||
|
||||
case ShaderBlock::TypeNormalViewMatrix:
|
||||
return CompileNormalViewMatrixShaderBlock((const NormalViewMatrixShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeNormalMatrix:
|
||||
return CompileNormalMatrixShaderBlock((const NormalMatrixShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeModelViewMatrix:
|
||||
return CompileModelViewMatrixShaderBlock((const ModelViewMatrixShaderBlock *)block, scope);
|
||||
case ShaderBlock::TypeModelMatrix:
|
||||
return CompileModelMatrixShaderBlock((const ModelMatrixShaderBlock *)block, scope);
|
||||
|
||||
default:
|
||||
__LOG_E__ << "Unsupported render block type (" << block->type << "), does not known how to compile.\n";
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void ShaderTreeCompiler::Free()
|
||||
{
|
||||
id.Clear();
|
||||
|
||||
compiled_tree_block.Clear();
|
||||
temporary_block_list.Clear();
|
||||
|
||||
vertex_declaration.Clear();
|
||||
vertex_source.Clear();
|
||||
pixel_declaration.Clear();
|
||||
pixel_source.Clear();
|
||||
|
||||
texture_count = 0;
|
||||
variable_count = 0;
|
||||
shader = NULL;
|
||||
}
|
||||
void ShaderTreeCompiler::RestartCompiler(Shader *s)
|
||||
{
|
||||
Free();
|
||||
shader = s;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ShaderTreeCompiler::ShaderTreeCompiler()
|
||||
{
|
||||
texture_count = 0;
|
||||
variable_count = 0;
|
||||
}
|
||||
ShaderTreeCompiler::~ShaderTreeCompiler()
|
||||
{ Free(); }
|
||||
//------------------------------------------------------------------------------
|
||||
882
include/engine/core/shader_tree_compiler_isl.cpp
Normal file
882
include/engine/core/shader_tree_compiler_isl.cpp
Normal file
@ -0,0 +1,882 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/shader_tree_compiler_isl.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#define __MapCompilerGetNewCompiledBlock(__COMPILED_VAR__)\
|
||||
CShaderBlock *__COMPILED_VAR__ = GetNewCompiledBlock(block);\
|
||||
if (!__COMPILED_VAR__) return NULL;
|
||||
|
||||
#define __MapCompilerCompileInput(__COMPILED_VAR__, __INPUT_INDEX__)\
|
||||
CShaderBlock *__COMPILED_VAR__ = CompileShaderBlock(block->GetInput(__INPUT_INDEX__));\
|
||||
if (!__COMPILED_VAR__) return NULL;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool ISLShaderTreeCompiler::GetTypeDeclaration(ShaderInput::DataType data_type, String &declaration)
|
||||
{
|
||||
switch (data_type)
|
||||
{
|
||||
case ShaderInput::Float: declaration = "float"; return true;
|
||||
case ShaderInput::Vector2: declaration = "vec2"; return true;
|
||||
case ShaderInput::Vector3: declaration = "vec3"; return true;
|
||||
case ShaderInput::Vector4: declaration = "vec4"; return true;
|
||||
case ShaderInput::Matrix3: declaration = "mat3"; return true;
|
||||
case ShaderInput::Matrix4: declaration = "mat4"; return true;
|
||||
|
||||
case ShaderInput::DataTexture2D: declaration = "sampler2D"; return true;
|
||||
case ShaderInput::DataTextureShadow: declaration = "sampler2DShadow"; return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileConstantShaderBlock(const ConstantShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "const");
|
||||
if (ShaderInput *parm = shader->DeclareInput(compiled_block->variable, block->constant_type, ShaderInput::Constant, ShaderInput::Uniform, scope))
|
||||
parm->parm_v.Set(block->constant[0], block->constant[1], block->constant[2], block->constant[3]);
|
||||
|
||||
compiled_block->output = block->constant_type;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileColorShaderBlock(const ColorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "color");
|
||||
if (ShaderInput *parm = shader->DeclareInput(compiled_block->variable, ShaderInput::Vector4, ShaderInput::Constant, ShaderInput::Uniform, scope))
|
||||
parm->parm_v = block->color;
|
||||
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileClockShaderBlock(const ClockShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "clock");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Float, ShaderInput::Clock, ShaderInput::Uniform, scope);
|
||||
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileViewVectorShaderBlock(const ViewVectorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "view_vector");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Vector3, ShaderInput::ViewVector, ShaderInput::Uniform, scope);
|
||||
|
||||
compiled_block->output = ShaderInput::Vector3;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileViewportShaderBlock(const ViewportShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "viewport");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Vector4, ShaderInput::Viewport, ShaderInput::Uniform, scope);
|
||||
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileNormalViewMatrixShaderBlock(const NormalViewMatrixShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "normal_view_matrix");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Matrix3, ShaderInput::NormalViewMatrix, ShaderInput::Uniform, scope);
|
||||
|
||||
compiled_block->output = ShaderInput::Matrix3;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileNormalMatrixShaderBlock(const NormalMatrixShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "normal_matrix");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Matrix3, ShaderInput::NormalMatrix, ShaderInput::Uniform, scope);
|
||||
|
||||
compiled_block->output = ShaderInput::Matrix3;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileModelViewMatrixShaderBlock(const ModelViewMatrixShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "model_view_matrix");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Matrix4, ShaderInput::ModelViewMatrix, ShaderInput::Uniform, scope);
|
||||
|
||||
compiled_block->output = ShaderInput::Matrix4;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileModelMatrixShaderBlock(const ModelMatrixShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "model_matrix");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Matrix4, ShaderInput::ModelMatrix, ShaderInput::Uniform, scope);
|
||||
|
||||
compiled_block->output = ShaderInput::Matrix4;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileMaterialParamShaderBlock(const MaterialParamShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "mat_param");
|
||||
switch (block->param)
|
||||
{
|
||||
case MaterialParamShaderBlock::MaterialDiffuse:
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Vector4, ShaderInput::MaterialDiffuse, ShaderInput::Uniform, scope);
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
break;
|
||||
case MaterialParamShaderBlock::MaterialSpecular:
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Vector4, ShaderInput::MaterialSpecular, ShaderInput::Uniform, scope);
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
break;
|
||||
case MaterialParamShaderBlock::MaterialSelf:
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Vector4, ShaderInput::MaterialSelf, ShaderInput::Uniform, scope);
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
break;
|
||||
case MaterialParamShaderBlock::MaterialAmbient:
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Vector4, ShaderInput::MaterialAmbient, ShaderInput::Uniform, scope);
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
break;
|
||||
case MaterialParamShaderBlock::MaterialGlossiness:
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Float, ShaderInput::MaterialGlossiness, ShaderInput::Uniform, scope);
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
break;
|
||||
case MaterialParamShaderBlock::MaterialOpacity:
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Float, ShaderInput::MaterialOpacity, ShaderInput::Uniform, scope);
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
break;
|
||||
case MaterialParamShaderBlock::MaterialReflection:
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Float, ShaderInput::MaterialReflection, ShaderInput::Uniform, scope);
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
break;
|
||||
}
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileMaterialTextureShaderBlock(const MaterialTextureShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "mat_tex");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Vector4, ShaderInput::Semantic(ShaderInput::MaterialTexture0 + block->slot), ShaderInput::Uniform, scope);
|
||||
|
||||
compiled_block->output = ShaderInput::DataTexture2D;
|
||||
return compiled_block;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileGeometrySkinningShaderBlock(const GeometrySkinningShaderBlock *block, ShaderInput::Scope scope)
|
||||
//----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "skin_mtx");
|
||||
|
||||
shader->DeclareInput("bone_mtx", ShaderInput::Matrix4, ShaderInput::BoneMatrix, ShaderInput::Uniform, ShaderInput::Vertex, __PL_BONE_LIMIT__);
|
||||
shader->DeclareInput("bone_idx", ShaderInput::Vector4, ShaderInput::BoneIndex, ShaderInput::Attribute, ShaderInput::Vertex);
|
||||
shader->DeclareInput("bone_w", ShaderInput::Vector4, ShaderInput::BoneWeight, ShaderInput::Attribute, ShaderInput::Vertex);
|
||||
|
||||
shader->DeclareVarying(compiled_block->variable, "mat4");
|
||||
vertex_source += String::Format("%s = bone_mtx[int(bone_idx.x)] * bone_w.x + bone_mtx[int(bone_idx.y)] * bone_w.y + bone_mtx[int(bone_idx.z)] * bone_w.z + bone_mtx[int(bone_idx.w)] * bone_w.w;\n", compiled_block->variable.c_str());
|
||||
|
||||
compiled_block->output = ShaderInput::Matrix4;
|
||||
return compiled_block;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileGeometryTangentFrameShaderBlock(const GeometryTangentFrameShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
/*
|
||||
Request the geometry normal.
|
||||
Note: If an equivalent block already exists in the map it will be used
|
||||
instead of the newly created one.
|
||||
*/
|
||||
ShaderBlock *normal_block = new GeometryNormalShaderBlock;
|
||||
CShaderBlock *gl_normal_block = CompileShaderBlock(normal_block);
|
||||
_safe_delete(normal_block);
|
||||
|
||||
// Compile the tangent frame generation code.
|
||||
GetNewVariable(compiled_block->variable, "tangent_frame");
|
||||
|
||||
ShaderInput *a_tangent = shader->DeclareInput("a_tangent", ShaderInput::Vector3, ShaderInput::Tangent, ShaderInput::Attribute, ShaderInput::Vertex),
|
||||
*a_bitangent = shader->DeclareInput("a_bitangent", ShaderInput::Vector3, ShaderInput::Bitangent, ShaderInput::Attribute, ShaderInput::Vertex);
|
||||
|
||||
shader->DeclareVarying("_T", "vec3");
|
||||
shader->DeclareVarying("_B", "vec3");
|
||||
vertex_source += String::Format("_T = %s;\n _B = %s;\n", a_tangent->name.c_str(), a_bitangent->name.c_str());
|
||||
|
||||
pixel_source += String::Format("mat3 %s = _build_mat3(normalize(_T), normalize(_B), %s);\n", compiled_block->variable.c_str(), gl_normal_block->variable.c_str());
|
||||
|
||||
compiled_block->output = ShaderInput::Matrix3;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileGeometryVertexShaderBlock(const GeometryVertexShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "vertex");
|
||||
ShaderInput *pos_parm = shader->DeclareInput("a_position", ShaderInput::Vector3, ShaderInput::Position, ShaderInput::Attribute, ShaderInput::Vertex);
|
||||
|
||||
shader->DeclareVarying(compiled_block->variable, "vec4");
|
||||
vertex_source += String::Format("%s = vec4(%s, 1.0);\n", compiled_block->variable.c_str(), pos_parm->name.c_str());
|
||||
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileGeometryNormalShaderBlock(const GeometryNormalShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
String varying;
|
||||
GetNewVariable(varying, "varying_normal");
|
||||
GetNewVariable(compiled_block->variable, "normal");
|
||||
ShaderInput *nrm_parm = shader->DeclareInput("a_normal", ShaderInput::Vector3, ShaderInput::Normal, ShaderInput::Attribute, ShaderInput::Vertex);
|
||||
shader->DeclareVarying(varying, "vec3");
|
||||
vertex_source += String::Format("%s = %s;\n", varying.c_str(), nrm_parm->name.c_str());
|
||||
pixel_source += String::Format("vec3 %s = normalize(%s);\n", compiled_block->variable.c_str(), varying.c_str());
|
||||
|
||||
compiled_block->output = ShaderInput::Vector3;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileGeometryVertexColorShaderBlock(const GeometryVertexColorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "vertex_color");
|
||||
ShaderInput *rgb_parm = shader->DeclareInput("a_rgb", ShaderInput::Vector4, ShaderInput::VertexColor, ShaderInput::Attribute, ShaderInput::Vertex);
|
||||
shader->DeclareVarying(compiled_block->variable, "vec4");
|
||||
vertex_source += String::Format("%s = %s;\n", compiled_block->variable.c_str(), rgb_parm->name.c_str());
|
||||
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileGeometryUVShaderBlock(const GeometryUVShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "uv");
|
||||
ShaderInput *uv_parm = shader->DeclareInput(String::Format("a_uv%d", block->channel), ShaderInput::Vector2, ShaderInput::Semantic(ShaderInput::UV0 + block->channel), ShaderInput::Attribute, ShaderInput::Vertex);
|
||||
shader->DeclareVarying(compiled_block->variable, "vec2");
|
||||
vertex_source += String::Format("%s = %s;\n", compiled_block->variable.c_str(), uv_parm->name.c_str());
|
||||
|
||||
compiled_block->output = ShaderInput::Vector2;
|
||||
return compiled_block;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------------------------
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileRenderBufferShaderBlock(const RenderBufferShaderBlock *block, ShaderInput::Scope scope)
|
||||
//--------------------------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
/*
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "render_buffer");
|
||||
fragment_declaration += String::Format("uniform sampler2D %s;\n", compiled_block->variable.c_str());
|
||||
compiled_block->output = ShaderBlock::RenderPinTexture;
|
||||
return compiled_block;
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileTextureShaderBlock(const TextureShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "texture");
|
||||
|
||||
switch (block->texture_type)
|
||||
{
|
||||
default:
|
||||
case TextureShaderBlock::Texture2D:
|
||||
if (ShaderInput *parm = shader->DeclareInput(compiled_block->variable, ShaderInput::DataTexture2D, ShaderInput::Texture2D, ShaderInput::Uniform, ShaderInput::Pixel))
|
||||
parm->parm_t = block->texture;
|
||||
compiled_block->output = ShaderInput::DataTexture2D;
|
||||
break;
|
||||
|
||||
case TextureShaderBlock::Texture3D:
|
||||
if (ShaderInput *parm = shader->DeclareInput(compiled_block->variable, ShaderInput::DataTexture3D, ShaderInput::Texture3D, ShaderInput::Uniform, ShaderInput::Pixel))
|
||||
parm->parm_t = block->texture;
|
||||
compiled_block->output = ShaderInput::DataTexture3D;
|
||||
break;
|
||||
|
||||
case TextureShaderBlock::TextureCube:
|
||||
if (ShaderInput *parm = shader->DeclareInput(compiled_block->variable, ShaderInput::DataTextureCube, ShaderInput::TextureCube, ShaderInput::Uniform, ShaderInput::Pixel))
|
||||
parm->parm_t = block->texture;
|
||||
compiled_block->output = ShaderInput::DataTextureCube;
|
||||
break;
|
||||
}
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileTextureSamplerShaderBlock(const TextureSamplerShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(texture_block, 0)
|
||||
__MapCompilerCompileInput(uv_block, 1)
|
||||
|
||||
if (
|
||||
!( ((texture_block->output == ShaderInput::DataTexture2D) && (uv_block->output == ShaderInput::Vector2)) ||
|
||||
((texture_block->output == ShaderInput::DataTexture3D) && (uv_block->output == ShaderInput::Vector3)) ||
|
||||
((texture_block->output == ShaderInput::DataTextureCube) && (uv_block->output == ShaderInput::Vector3)) )
|
||||
)
|
||||
__ERR__(__LOG_E__ << "Invalid input to the texture sampler block.\n", NULL)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "texel");
|
||||
|
||||
switch (block->sampler_type)
|
||||
{
|
||||
default:
|
||||
case TextureSamplerShaderBlock::Sampler2D:
|
||||
pixel_source += String::Format("vec4 %s = texture2D(%s, %s);\n", compiled_block->variable.c_str(), texture_block->variable.c_str(), uv_block->variable.c_str());
|
||||
break;
|
||||
case TextureSamplerShaderBlock::Sampler3D:
|
||||
pixel_source += String::Format("vec4 %s = texture3D(%s, %s);\n", compiled_block->variable.c_str(), texture_block->variable.c_str(), uv_block->variable.c_str());
|
||||
break;
|
||||
case TextureSamplerShaderBlock::SamplerCube:
|
||||
pixel_source += String::Format("vec4 %s = textureCube(%s, %s);\n", compiled_block->variable.c_str(), texture_block->variable.c_str(), uv_block->variable.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
return compiled_block;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileMixShaderBlock(const MixOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(left_block, 0)
|
||||
__MapCompilerCompileInput(right_block, 1)
|
||||
__MapCompilerCompileInput(mix_block, 2)
|
||||
|
||||
if (left_block->output != right_block->output)
|
||||
__ERR__(__LOG_E__ << "Cannot mix blocks, output do not match.\n", NULL)
|
||||
|
||||
switch (left_block->output)
|
||||
{
|
||||
case ShaderInput::NoData:
|
||||
__ERR__(__LOG_E__ << "Block has no output.\n", NULL)
|
||||
case ShaderInput::Matrix3:
|
||||
case ShaderInput::Matrix4:
|
||||
case ShaderInput::DataTexture2D:
|
||||
__ERR__(__LOG_E__ << "Cannot mix blocks, incorrect type (" << left_block->output << ").\n", NULL)
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
GetNewVariable(compiled_block->variable, "mix");
|
||||
String type;
|
||||
GetTypeDeclaration(left_block->output, type);
|
||||
pixel_source += String::Format("%s %s = mix(%s, %s, %s);\n", type.c_str(), compiled_block->variable.c_str(), right_block->variable.c_str(), left_block->variable.c_str(), mix_block->variable.c_str());
|
||||
compiled_block->output = left_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileAddShaderBlock(const AddOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(left_block, 0)
|
||||
__MapCompilerCompileInput(right_block, 1)
|
||||
|
||||
if (left_block->output != right_block->output)
|
||||
__ERR__(__LOG_E__ << "Cannot add blocks, output do not match.\n", NULL)
|
||||
|
||||
switch (left_block->output)
|
||||
{
|
||||
case ShaderInput::NoData:
|
||||
__ERR__(__LOG_E__ << "Block has no output.\n", NULL)
|
||||
case ShaderInput::Matrix3:
|
||||
case ShaderInput::Matrix4:
|
||||
case ShaderInput::DataTexture2D:
|
||||
__ERR__(__LOG_E__ << "Cannot add blocks (" << left_block->output << ").\n", NULL)
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
GetNewVariable(compiled_block->variable, "add");
|
||||
String type;
|
||||
GetTypeDeclaration(left_block->output, type);
|
||||
pixel_source += String::Format("%s %s = %s + %s;\n", type.c_str(), compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
compiled_block->output = left_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileSubShaderBlock(const SubOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(left_block, 0)
|
||||
__MapCompilerCompileInput(right_block, 1)
|
||||
|
||||
if (left_block->output != right_block->output)
|
||||
__ERR__(__LOG_E__ << "Cannot subtract blocks, output do not match.\n", NULL)
|
||||
|
||||
switch (left_block->output)
|
||||
{
|
||||
case ShaderInput::NoData:
|
||||
__ERR__(__LOG_E__ << "Block has no output.\n", NULL)
|
||||
case ShaderInput::Matrix3:
|
||||
case ShaderInput::Matrix4:
|
||||
case ShaderInput::DataTexture2D:
|
||||
__ERR__(__LOG_E__ << "Cannot subtract blocks (" << left_block->output << ").\n", NULL)
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
GetNewVariable(compiled_block->variable, "sub");
|
||||
String type;
|
||||
GetTypeDeclaration(left_block->output, type);
|
||||
pixel_source += String::Format("%s %s = %s - %s;\n", type.c_str(), compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
compiled_block->output = left_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileMulShaderBlock(const MulOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(left_block, 0)
|
||||
__MapCompilerCompileInput(right_block, 1)
|
||||
|
||||
// Check type compatibility.
|
||||
ShaderInput::DataType left_type = left_block->output, right_type = right_block->output;
|
||||
|
||||
if (left_type > right_type)
|
||||
{ ShaderInput::DataType tmp; tmp = left_type; left_type = right_type; right_type = tmp; }
|
||||
|
||||
if (
|
||||
!(
|
||||
(left_type == right_type) ||
|
||||
((left_type == ShaderInput::Vector3) && (right_type == ShaderInput::Matrix3)) ||
|
||||
((left_type == ShaderInput::Vector4) && (right_type == ShaderInput::Matrix4))
|
||||
)
|
||||
)
|
||||
__ERR__(__LOG_E__ << "Cannot multiply blocks, output do not match.\n", NULL)
|
||||
|
||||
// Check type validity.
|
||||
if (left_type == right_type)
|
||||
switch (left_block->output)
|
||||
{
|
||||
case ShaderInput::NoData:
|
||||
__ERR__(__LOG_E__ << "Block has no output.\n", NULL)
|
||||
case ShaderInput::DataTexture2D:
|
||||
__ERR__(__LOG_E__ << "Cannot multiply textures.\n", NULL)
|
||||
}
|
||||
|
||||
// Compile operator.
|
||||
GetNewVariable(compiled_block->variable, "mul");
|
||||
String type;
|
||||
GetTypeDeclaration(left_type, type);
|
||||
|
||||
switch (right_type)
|
||||
{
|
||||
// n_mtx_mul
|
||||
case ShaderInput::Matrix3:
|
||||
case ShaderInput::Matrix4:
|
||||
pixel_source += String::Format("%s %s = n_mtx_mul(%s, %s);\n", type.c_str(), compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
break;
|
||||
|
||||
default:
|
||||
pixel_source += String::Format("%s %s = %s * %s;\n", type.c_str(), compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
compiled_block->output = left_type;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileDivShaderBlock(const DivOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(left_block, 0)
|
||||
__MapCompilerCompileInput(right_block, 1)
|
||||
|
||||
if (left_block->output != right_block->output)
|
||||
__ERR__(__LOG_E__ << "Cannot divide blocks, output do not match.\n", NULL)
|
||||
|
||||
switch (left_block->output)
|
||||
{
|
||||
case ShaderInput::NoData:
|
||||
__ERR__(__LOG_E__ << "Block has no output.\n", NULL)
|
||||
case ShaderInput::Matrix3:
|
||||
case ShaderInput::Matrix4:
|
||||
case ShaderInput::DataTexture2D:
|
||||
__ERR__(__LOG_E__ << "Cannot divide blocks (" << left_block->output << ").\n", NULL)
|
||||
}
|
||||
|
||||
GetNewVariable(compiled_block->variable, "div");
|
||||
String type;
|
||||
GetTypeDeclaration(left_block->output, type);
|
||||
pixel_source += String::Format("%s %s = %s / %s;\n", type.c_str(), compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
compiled_block->output = left_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileDotShaderBlock(const DotOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(left_block, 0)
|
||||
__MapCompilerCompileInput(right_block, 1)
|
||||
|
||||
if (left_block->output != right_block->output)
|
||||
__ERR__(__LOG_E__ << "Cannot compute dot operator, output do not match.\n", NULL)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "dot");
|
||||
pixel_source += String::Format("float %s = dot(%s, %s);\n", compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileCrossShaderBlock(const CrossOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(left_block, 0)
|
||||
__MapCompilerCompileInput(right_block, 1)
|
||||
|
||||
if ((left_block->output != right_block->output) && (left_block->output != ShaderInput::Vector3))
|
||||
__ERR__(__LOG_E__ << "Cannot only compute cross product on Vector3.\n", NULL)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "cross");
|
||||
pixel_source += String::Format("vec3 %s = cross(%s, %s);\n", compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
compiled_block->output = ShaderInput::Vector3;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileClampShaderBlock(const ClampShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(value_block, 0)
|
||||
__MapCompilerCompileInput(min_block, 1)
|
||||
__MapCompilerCompileInput(max_block, 2)
|
||||
|
||||
if ((min_block->output != max_block->output) && (value_block->output != min_block->output))
|
||||
__ERR__(__LOG_E__ << ".\n", NULL)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "clamp");
|
||||
String type;
|
||||
GetTypeDeclaration(value_block->output, type);
|
||||
pixel_source += String::Format("%s %s = clamp(%s, %s, %s);\n", type.c_str(), compiled_block->variable.c_str(), value_block->variable.c_str(), min_block->variable.c_str(), max_block->variable.c_str());
|
||||
compiled_block->output = value_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileSwizzleShaderBlock(const SwizzleShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(input_block, 0)
|
||||
|
||||
// Set block output.
|
||||
if ((compiled_block->output = (ShaderInput::DataType)block->GetOutputType()) == ShaderInput::NoData)
|
||||
__ERR__(__LOG_E__ << "Invalid swizzle, no output.\n", NULL)
|
||||
|
||||
String output_type;
|
||||
GetTypeDeclaration(compiled_block->output, output_type);
|
||||
|
||||
// Build fragment shader swizzle.
|
||||
GetNewVariable(compiled_block->variable, "swizzle");
|
||||
|
||||
if (input_block->output == ShaderInput::Float)
|
||||
pixel_source += String::Format("%s %s = %s(", output_type.c_str(), compiled_block->variable.c_str(), output_type.c_str());
|
||||
else pixel_source += String::Format("%s %s = %s.", output_type.c_str(), compiled_block->variable.c_str(), input_block->variable.c_str());
|
||||
|
||||
switch (input_block->output)
|
||||
{
|
||||
case ShaderInput::Float:
|
||||
for (int n = 0; (n < 4) && (block->swizzle[n] != SwizzleShaderBlock::SwizzleNone); ++n)
|
||||
{
|
||||
if (block->swizzle[n] != SwizzleShaderBlock::SwizzleX)
|
||||
__ERR__(__LOG_E__ << "Impossible swizzle.\n", NULL)
|
||||
|
||||
pixel_source += ((n == 3) || (block->swizzle[n + 1] == SwizzleShaderBlock::SwizzleNone)) ?
|
||||
String::Format("%s)", input_block->variable.c_str()) :
|
||||
String::Format("%s,", input_block->variable.c_str());
|
||||
}
|
||||
break;
|
||||
|
||||
case ShaderInput::Vector2:
|
||||
for (int n = 0; n < 4; ++n)
|
||||
switch (block->swizzle[n])
|
||||
{
|
||||
case SwizzleShaderBlock::SwizzleX: pixel_source += "x"; break;
|
||||
case SwizzleShaderBlock::SwizzleY: pixel_source += "y"; break;
|
||||
case SwizzleShaderBlock::SwizzleNone: break;
|
||||
default: __ERR__(__LOG_E__ << "Invalid component to swizzle.\n", NULL)
|
||||
}
|
||||
break;
|
||||
|
||||
case ShaderInput::Vector3:
|
||||
for (int n = 0; n < 4; ++n)
|
||||
switch (block->swizzle[n])
|
||||
{
|
||||
case SwizzleShaderBlock::SwizzleX: pixel_source += "x"; break;
|
||||
case SwizzleShaderBlock::SwizzleY: pixel_source += "y"; break;
|
||||
case SwizzleShaderBlock::SwizzleZ: pixel_source += "z"; break;
|
||||
case SwizzleShaderBlock::SwizzleNone: break;
|
||||
default: __ERR__(__LOG_E__ << "Invalid component to swizzle.\n", NULL)
|
||||
}
|
||||
break;
|
||||
|
||||
case ShaderInput::Vector4:
|
||||
for (int n = 0; n < 4; ++n)
|
||||
switch (block->swizzle[n])
|
||||
{
|
||||
case SwizzleShaderBlock::SwizzleX: pixel_source += "x"; break;
|
||||
case SwizzleShaderBlock::SwizzleY: pixel_source += "y"; break;
|
||||
case SwizzleShaderBlock::SwizzleZ: pixel_source += "z"; break;
|
||||
case SwizzleShaderBlock::SwizzleW: pixel_source += "w"; break;
|
||||
case SwizzleShaderBlock::SwizzleNone: break;
|
||||
default: __ERR__(__LOG_E__ << "Invalid component to swizzle.\n", NULL)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
pixel_source += ";\n";
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileBuildShaderBlock(const BuildShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
String component[4];
|
||||
int count = 0;
|
||||
|
||||
for (int n = 0; n < 4; ++n)
|
||||
{
|
||||
switch (block->build[n])
|
||||
{
|
||||
case BuildShaderBlock::BuildZero:
|
||||
component[count++] = "0.0";
|
||||
break;
|
||||
case BuildShaderBlock::BuildOne:
|
||||
component[count++] = "1.0";
|
||||
break;
|
||||
|
||||
case BuildShaderBlock::BuildX:
|
||||
case BuildShaderBlock::BuildY:
|
||||
case BuildShaderBlock::BuildZ:
|
||||
case BuildShaderBlock::BuildW:
|
||||
if (!block->input[n])
|
||||
n = 4;
|
||||
else
|
||||
{
|
||||
__MapCompilerCompileInput(input, n)
|
||||
|
||||
if (input->output == ShaderInput::Float)
|
||||
component[count++] = input->variable.c_str();
|
||||
else
|
||||
switch (block->build[n])
|
||||
{
|
||||
case BuildShaderBlock::BuildX: component[count++] = String::Format("%s.x", input->variable.c_str()); break;
|
||||
case BuildShaderBlock::BuildY: component[count++] = String::Format("%s.y", input->variable.c_str()); break;
|
||||
case BuildShaderBlock::BuildZ: component[count++] = String::Format("%s.z", input->variable.c_str()); break;
|
||||
case BuildShaderBlock::BuildW: component[count++] = String::Format("%s.w", input->variable.c_str()); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!count)
|
||||
compiled_block->output = ShaderInput::NoData;
|
||||
else
|
||||
{
|
||||
GetNewVariable(compiled_block->variable, "built");
|
||||
|
||||
switch (count)
|
||||
{
|
||||
case 1:
|
||||
pixel_source += String::Format("float %s = %s;\n", compiled_block->variable.c_str(), component[0].c_str());
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
break;
|
||||
case 2:
|
||||
pixel_source += String::Format("vec2 %s = vec2(%s, %s);\n", compiled_block->variable.c_str(), component[0].c_str(), component[1].c_str());
|
||||
compiled_block->output = ShaderInput::Vector2;
|
||||
break;
|
||||
case 3:
|
||||
pixel_source += String::Format("vec3 %s = vec3(%s, %s, %s);\n", compiled_block->variable.c_str(), component[0].c_str(), component[1].c_str(), component[2].c_str());
|
||||
compiled_block->output = ShaderInput::Vector3;
|
||||
break;
|
||||
case 4:
|
||||
pixel_source += String::Format("vec4 %s = vec4(%s, %s, %s, %s);\n", compiled_block->variable.c_str(), component[0].c_str(), component[1].c_str(), component[2].c_str(), component[3].c_str());
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompilePowShaderBlock(const PowShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(value_block, 0)
|
||||
__MapCompilerCompileInput(power_block, 1)
|
||||
|
||||
if (value_block->output != ShaderInput::Float)
|
||||
__ERR__(__LOG_E__ << "Incompatible value type.\n", NULL)
|
||||
if (power_block->output != ShaderInput::Float)
|
||||
__ERR__(__LOG_E__ << "Incompatible power type.\n", NULL)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "pow");
|
||||
pixel_source += String::Format("float %s = pow(%s, %s);\n", compiled_block->variable.c_str(), value_block->variable.c_str(), power_block->variable.c_str());
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileAbsShaderBlock(const AbsShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(value_block, 0)
|
||||
|
||||
switch (value_block->output)
|
||||
{
|
||||
case ShaderInput::Float:
|
||||
case ShaderInput::Vector2:
|
||||
case ShaderInput::Vector3:
|
||||
case ShaderInput::Vector4:
|
||||
break;
|
||||
default:
|
||||
__ERR__(__LOG_E__ << "Incompatible value type.\n", NULL)
|
||||
}
|
||||
|
||||
GetNewVariable(compiled_block->variable, "abs");
|
||||
|
||||
switch (value_block->output)
|
||||
{
|
||||
case ShaderInput::Float: pixel_source += String::Format("float %s = abs(%s);\n", compiled_block->variable.c_str(), value_block->variable.c_str()); break;
|
||||
case ShaderInput::Vector2: pixel_source += String::Format("vec2 %s = abs(%s);\n", compiled_block->variable.c_str(), value_block->variable.c_str()); break;
|
||||
case ShaderInput::Vector3: pixel_source += String::Format("vec3 %s = abs(%s);\n", compiled_block->variable.c_str(), value_block->variable.c_str()); break;
|
||||
case ShaderInput::Vector4: pixel_source += String::Format("vec4 %s = abs(%s.xyz, 1.0);\n", compiled_block->variable.c_str(), value_block->variable.c_str()); break;
|
||||
}
|
||||
compiled_block->output = value_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileCosinusShaderBlock(const CosinusShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(value_block, 0)
|
||||
|
||||
if (value_block->output != ShaderInput::Float)
|
||||
__ERR__(__LOG_E__ << "Incompatible source type.\n", NULL)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "cos");
|
||||
pixel_source += String::Format("float %s = cos(%s);\n", compiled_block->variable.c_str(), value_block->variable.c_str());
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileSinusShaderBlock(const SinusShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(value_block, 0)
|
||||
|
||||
if (value_block->output != ShaderInput::Float)
|
||||
__ERR__(__LOG_E__ << "Incompatible source type.\n", NULL)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "sin");
|
||||
pixel_source += String::Format("float %s = sin(%s);\n", compiled_block->variable.c_str(), value_block->variable.c_str());
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileNormalizeBlock(const NormalizeOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(input_block, 0)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "normalized");
|
||||
String type;
|
||||
GetTypeDeclaration(input_block->output, type);
|
||||
pixel_source += String::Format("%s %s = normalize(%s);\n", type.c_str(), compiled_block->variable.c_str(), input_block->variable.c_str());
|
||||
|
||||
compiled_block->output = input_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------------
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileScreenUVShaderBlock(const ScreenUVShaderBlock *block, ShaderInput::Scope scope)
|
||||
//------------------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "screen_uv");
|
||||
pixel_source += String::Format("vec2 %s = gl_FragCoord.xy;\n", compiled_block->variable.c_str());
|
||||
compiled_block->output = ShaderInput::Vector2;
|
||||
return compiled_block;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompilePackVectorToColor(const PackVectorToColorShaderBlock *block, ShaderInput::Scope scope)
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(input_block, 0)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "packed_vector");
|
||||
|
||||
switch (input_block->output)
|
||||
{
|
||||
case ShaderInput::Float:
|
||||
pixel_source += String::Format("float %s = (%s + 1.0) * 0.5;\n", compiled_block->variable.c_str(), input_block->variable.c_str());
|
||||
break;
|
||||
case ShaderInput::Vector3:
|
||||
pixel_source += String::Format("vec3 %s = (%s + vec3(1.0, 1.0, 1.0)) * vec3(0.5, 0.5, 0.5);\n", compiled_block->variable.c_str(), input_block->variable.c_str());
|
||||
break;
|
||||
case ShaderInput::Vector4:
|
||||
pixel_source += String::Format("vec4 %s = (%s + vec4(1.0, 1.0, 1.0, 0.0)) * vec3(0.5, 0.5, 0.5, 1.0);\n", compiled_block->variable.c_str(), input_block->variable.c_str());
|
||||
break;
|
||||
|
||||
default:
|
||||
__ERR__(__LOG_E__ << "Invalid input to pack to vector.\n", NULL)
|
||||
}
|
||||
compiled_block->output = input_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------------
|
||||
CShaderBlock *ISLShaderTreeCompiler::CompileUnpackColorToVector(const UnpackColorToVectorShaderBlock *block, ShaderInput::Scope scope)
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(input_block, 0)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "unpacked_vector");
|
||||
|
||||
switch (input_block->output)
|
||||
{
|
||||
case ShaderInput::Float:
|
||||
pixel_source += String::Format("float %s = (%s - 0.5) * 2.0;\n", compiled_block->variable.c_str(), input_block->variable.c_str());
|
||||
break;
|
||||
case ShaderInput::Vector3:
|
||||
pixel_source += String::Format("vec3 %s = (%s - vec3(0.5, 0.5, 0.5)) * vec3(2.0, 2.0, 2.0);\n", compiled_block->variable.c_str(), input_block->variable.c_str());
|
||||
break;
|
||||
case ShaderInput::Vector4:
|
||||
pixel_source += String::Format("vec4 %s = (%s - vec4(0.5, 0.5, 0.5, 0.0)) * vec4(2.0, 2.0, 2.0, 1.0);\n", compiled_block->variable.c_str(), input_block->variable.c_str());
|
||||
break;
|
||||
|
||||
default:
|
||||
__ERR__(__LOG_E__ << "Invalid input to unpack to vector.\n", NULL)
|
||||
}
|
||||
compiled_block->output = input_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Shader *ISLShaderTreeCompiler::Finish()
|
||||
{
|
||||
__LOG__ << "Done compiling shader tree '" << id << "'.\n";
|
||||
return shader;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
942
include/engine/core/shader_tree_compiler_tinyc.cpp
Normal file
942
include/engine/core/shader_tree_compiler_tinyc.cpp
Normal file
@ -0,0 +1,942 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/shader_tree_compiler_tinyc.h"
|
||||
#include "core/graphic_resource_factory.h"
|
||||
#include "core/material.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#define __MapCompilerGetNewCompiledBlock(__COMPILED_VAR__)\
|
||||
CShaderBlock *__COMPILED_VAR__ = GetNewCompiledBlock(block);\
|
||||
if (!__COMPILED_VAR__) return NULL;
|
||||
|
||||
#define __MapCompilerCompileInput(__COMPILED_VAR__, __INPUT_INDEX__)\
|
||||
CShaderBlock *__COMPILED_VAR__ = CompileShaderBlock(block->GetInput(__INPUT_INDEX__));\
|
||||
if (!__COMPILED_VAR__) return NULL;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
bool TinyCShaderTreeCompiler::GetTypeDeclaration(ShaderInput::DataType type, String &declaration)
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ShaderInput::Float: declaration = "struct nVector"; return true;
|
||||
case ShaderInput::Vector2: declaration = "struct nVector"; return true;
|
||||
case ShaderInput::Vector3: declaration = "struct nVector"; return true;
|
||||
case ShaderInput::Vector4: declaration = "struct nVector"; return true;
|
||||
case ShaderInput::Matrix3: declaration = "struct nMatrix3"; return true;
|
||||
case ShaderInput::Matrix4: declaration = "struct nMatrix4"; return true;
|
||||
case ShaderInput::DataTexture2D: declaration = "struct nTexture"; return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileConstantShaderBlock(const ConstantShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "const");
|
||||
if (ShaderInput *parm = shader->DeclareInput(compiled_block->variable, block->constant_type, ShaderInput::Constant, ShaderInput::Uniform, ShaderInput::Pixel))
|
||||
parm->parm_v.Set(block->constant[0], block->constant[1], block->constant[2], block->constant[3]);
|
||||
|
||||
compiled_block->output = block->constant_type;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileColorShaderBlock(const ColorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "color");
|
||||
if (ShaderInput *parm = shader->DeclareInput(compiled_block->variable, ShaderInput::Vector4, ShaderInput::Constant, ShaderInput::Uniform, ShaderInput::Pixel))
|
||||
parm->parm_v = block->color;
|
||||
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileClockShaderBlock(const ClockShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "clock");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Float, ShaderInput::Clock, ShaderInput::Uniform, ShaderInput::Pixel);
|
||||
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileViewVectorShaderBlock(const ViewVectorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "view_vector");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Vector3, ShaderInput::ViewVector, ShaderInput::Uniform, ShaderInput::Pixel);
|
||||
|
||||
compiled_block->output = ShaderInput::Vector3;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileViewportShaderBlock(const ViewportShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "viewport");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Vector4, ShaderInput::Viewport, ShaderInput::Uniform, ShaderInput::Pixel);
|
||||
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileNormalViewMatrixShaderBlock(const NormalViewMatrixShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "normal_view_matrix");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Matrix3, ShaderInput::NormalViewMatrix, ShaderInput::Uniform, ShaderInput::Pixel);
|
||||
|
||||
compiled_block->output = ShaderInput::Matrix3;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileNormalMatrixShaderBlock(const NormalMatrixShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "normal_matrix");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Matrix3, ShaderInput::NormalMatrix, ShaderInput::Uniform, ShaderInput::Pixel);
|
||||
|
||||
compiled_block->output = ShaderInput::Matrix3;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileModelViewMatrixShaderBlock(const ModelViewMatrixShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "model_view_matrix");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Matrix4, ShaderInput::ModelViewMatrix, ShaderInput::Uniform, ShaderInput::Pixel);
|
||||
|
||||
compiled_block->output = ShaderInput::Matrix4;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileModelMatrixShaderBlock(const ModelMatrixShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "model_matrix");
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Matrix4, ShaderInput::ModelMatrix, ShaderInput::Uniform, ShaderInput::Pixel);
|
||||
|
||||
compiled_block->output = ShaderInput::Matrix4;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileMaterialParamShaderBlock(const MaterialParamShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "mat_param");
|
||||
switch (block->param)
|
||||
{
|
||||
case MaterialParamShaderBlock::MaterialDiffuse:
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Vector4, ShaderInput::MaterialDiffuse, ShaderInput::Uniform, ShaderInput::Pixel);
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
break;
|
||||
case MaterialParamShaderBlock::MaterialSpecular:
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Vector4, ShaderInput::MaterialSpecular, ShaderInput::Uniform, ShaderInput::Pixel);
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
break;
|
||||
case MaterialParamShaderBlock::MaterialSelf:
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Vector4, ShaderInput::MaterialSelf, ShaderInput::Uniform, ShaderInput::Pixel);
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
break;
|
||||
case MaterialParamShaderBlock::MaterialAmbient:
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Vector4, ShaderInput::MaterialAmbient, ShaderInput::Uniform, ShaderInput::Pixel);
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
break;
|
||||
case MaterialParamShaderBlock::MaterialGlossiness:
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Float, ShaderInput::MaterialGlossiness, ShaderInput::Uniform, ShaderInput::Pixel);
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
break;
|
||||
case MaterialParamShaderBlock::MaterialOpacity:
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Float, ShaderInput::MaterialOpacity, ShaderInput::Uniform, ShaderInput::Pixel);
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
break;
|
||||
case MaterialParamShaderBlock::MaterialReflection:
|
||||
shader->DeclareInput(compiled_block->variable, ShaderInput::Float, ShaderInput::MaterialReflection, ShaderInput::Uniform, ShaderInput::Pixel);
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
break;
|
||||
}
|
||||
return compiled_block;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileMaterialTextureShaderBlock(const MaterialTextureShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "texture");
|
||||
|
||||
pixel_declaration += String::Format("struct nTexture %s;\n", compiled_block->variable.c_str());
|
||||
|
||||
pixel_source += String::Format("%s.TexPnt = (void *)texture_slot_%d;\n", compiled_block->variable.c_str(), block->slot);
|
||||
|
||||
compiled_block->output = ShaderInput::DataTexture2D;
|
||||
return compiled_block;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileGeometrySkinningShaderBlock(const GeometrySkinningShaderBlock *block, ShaderInput::Scope scope)
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
//__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
// GetNewVariable(compiled_block->variable, "skin_mtx");
|
||||
|
||||
//shader->DeclareInput("bone_mtx", ShaderBlock::RenderShaderInput::Matrix4, ShaderInput::BoneMatrix, ShaderInput::Uniform, ShaderInput::Vertex);
|
||||
//shader->DeclareInput("bone_idx", ShaderBlock::RenderShaderInput::Vector4, ShaderInput::BoneIndex, ShaderInput::Attribute, ShaderInput::Vertex);
|
||||
//shader->DeclareInput("bone_w", ShaderBlock::RenderShaderInput::Vector4, ShaderInput::BoneWeight, ShaderInput::Attribute, ShaderInput::Vertex);
|
||||
|
||||
//vertex_declaration += String::Format("varying mat4 %s;\n", compiled_block->variable.c_str());
|
||||
//vertex_shader += String::Format("%s = (bone_mtx[int(bone_id.x)] * bone_w.x + bone_mtx[int(bone_id.y)] * bone_w.y + bone_mtx[int(bone_id.z)] * bone_w.z + bone_mtx[int(bone_id.w)] * bone_w.w) / (bone_w.x + bone_w.y + bone_w.z + bone_w.w);\n", compiled_block->variable.c_str());
|
||||
//fragment_declaration += String::Format("varying mat4 %s;\n", compiled_block->variable.c_str());
|
||||
|
||||
//compiled_block->output = ShaderBlock::RenderShaderInput::Matrix4;
|
||||
//return compiled_block;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileGeometryTangentFrameShaderBlock(const GeometryTangentFrameShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "tangentframe");
|
||||
|
||||
pixel_declaration += String::Format("struct nMatrix3 %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("TinyCGeometryTangentFrame(&%s, trace);\n", compiled_block->variable.c_str());
|
||||
|
||||
compiled_block->output = ShaderInput::Matrix3;
|
||||
return compiled_block;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileGeometryVertexShaderBlock(const GeometryVertexShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "vertex");
|
||||
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("TinyCGeometryVertex(&%s, trace);\n", compiled_block->variable.c_str());
|
||||
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileGeometryNormalShaderBlock(const GeometryNormalShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "normal");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("TinyCGeometryNormal(&%s, trace);\n", compiled_block->variable.c_str());
|
||||
|
||||
compiled_block->output = ShaderInput::Vector3;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileGeometryVertexColorShaderBlock(const GeometryVertexColorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "vertex_color");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("TinyCGeometryVertexColor(&%s, trace);\n", compiled_block->variable.c_str());
|
||||
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileGeometryUVShaderBlock(const GeometryUVShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "uv");
|
||||
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("TinyCGeometryUV(&%s, trace, %d);\n", compiled_block->variable.c_str(), block->channel);
|
||||
|
||||
compiled_block->output = ShaderInput::Vector2;
|
||||
return compiled_block;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
vec3 vector_offset = (%s.xyz - vec3(0.5, 0.5, 0.0)) * vec3(2.0, 2.0, 1.0);\n\
|
||||
|
||||
{
|
||||
// Object space normal map.
|
||||
pixel_program += "vec3 normal = gl_NormalMatrix * normalize(texture2D(normal_texture, normal_uv).xzy - vec3(0.5, 0.5, 0.5));\n";
|
||||
}
|
||||
*/
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileRenderBufferShaderBlock(const RenderBufferShaderBlock *block, ShaderInput::Scope scope)
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
/*
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "render_buffer");
|
||||
fragment_declaration += String::Format("uniform sampler2D %s;\n", compiled_block->variable.c_str());
|
||||
compiled_block->output = ShaderBlock::RenderPinTexture;
|
||||
return compiled_block;
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileTextureShaderBlock(const TextureShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "texture");
|
||||
|
||||
pixel_declaration += String::Format("struct nTexture %s;\n", compiled_block->variable.c_str());
|
||||
|
||||
if(((TextureShaderBlock *)block)->texture)
|
||||
// fragment_shader += String::Format("%s.TexName = \"%s\";\n", compiled_block->variable.c_str(), ((nTextureShaderBlock *)block)->texture->name.CleanFilePath());
|
||||
pixel_source += String::Format("%s.TexPnt = (void *)%d;\n", compiled_block->variable.c_str(), graphic_factory.LoadPicture(((TextureShaderBlock *)block)->texture.CleanFilePath()));
|
||||
else
|
||||
pixel_source += String::Format("%s.TexPnt = 0;\n", compiled_block->variable.c_str());
|
||||
|
||||
compiled_block->output = ShaderInput::DataTexture2D;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileTextureSamplerShaderBlock(const TextureSamplerShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(texture_block, 0)
|
||||
__MapCompilerCompileInput(uv_block, 1)
|
||||
|
||||
if (
|
||||
(texture_block->output != ShaderInput::DataTexture2D) ||
|
||||
(uv_block->output != ShaderInput::Vector2)
|
||||
)
|
||||
__ERR__(__LOG_E__ << "Invalid input to the sampler2D block.\n", NULL)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "texel");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
|
||||
pixel_source += String::Format(" nTexture_LowLevelSampling(&%s, &%s, %s.TexPnt, %d, %d);\n", compiled_block->variable.c_str(), uv_block->variable.c_str(), texture_block->variable.c_str(), /* block->wrap[0]?1:0 */ 0, /* block->wrap[1]?1:0 */ 0);
|
||||
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
return compiled_block;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileMixShaderBlock(const MixOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(left_block, 0)
|
||||
__MapCompilerCompileInput(right_block, 1)
|
||||
__MapCompilerCompileInput(mix_block, 2)
|
||||
|
||||
if (left_block->output != right_block->output)
|
||||
__ERR__(__LOG_E__ << "Cannot mix blocks, output do not match.\n", NULL)
|
||||
|
||||
switch (left_block->output)
|
||||
{
|
||||
case ShaderInput::NoData:
|
||||
__ERR__(__LOG_E__ << "Block has no output.\n", NULL)
|
||||
case ShaderInput::Matrix3:
|
||||
case ShaderInput::Matrix4:
|
||||
case ShaderInput::DataTexture2D:
|
||||
__ERR__(__LOG_E__ << "Cannot mix blocks, incorrect type (" << left_block->output << ").\n", NULL)
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
GetNewVariable(compiled_block->variable, "mix");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
|
||||
pixel_source += String::Format("nReturnMix(%s, %s, %s, %s.x);\n", compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str(), mix_block->variable.c_str());
|
||||
compiled_block->output = left_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileAddShaderBlock(const AddOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(left_block, 0)
|
||||
__MapCompilerCompileInput(right_block, 1)
|
||||
|
||||
if (left_block->output != right_block->output)
|
||||
__ERR__(__LOG_E__ << "Cannot add blocks, output do not match.\n", NULL)
|
||||
|
||||
switch (left_block->output)
|
||||
{
|
||||
case ShaderInput::NoData:
|
||||
__ERR__(__LOG_E__ << "Block has no output.\n", NULL)
|
||||
case ShaderInput::Matrix3:
|
||||
case ShaderInput::Matrix4:
|
||||
case ShaderInput::DataTexture2D:
|
||||
__ERR__(__LOG_E__ << "Cannot add blocks (" << left_block->output << ").\n", NULL)
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
GetNewVariable(compiled_block->variable, "add");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("nReturnVectorAddVector(%s , %s, %s);\n", compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
compiled_block->output = left_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileSubShaderBlock(const SubOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(left_block, 0)
|
||||
__MapCompilerCompileInput(right_block, 1)
|
||||
|
||||
if (left_block->output != right_block->output)
|
||||
__ERR__(__LOG_E__ << "Cannot subtract blocks, output do not match.\n", NULL)
|
||||
|
||||
switch (left_block->output)
|
||||
{
|
||||
case ShaderInput::NoData:
|
||||
__ERR__(__LOG_E__ << "Block has no output.\n", NULL)
|
||||
case ShaderInput::Matrix3:
|
||||
case ShaderInput::Matrix4:
|
||||
case ShaderInput::DataTexture2D:
|
||||
__ERR__(__LOG_E__ << "Cannot subtract blocks (" << left_block->output << ").\n", NULL)
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
GetNewVariable(compiled_block->variable, "sub");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("nReturnVectorMinusnVector(%s, %s, %s);\n", compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
compiled_block->output = left_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileMulShaderBlock(const MulOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(left_block, 0)
|
||||
__MapCompilerCompileInput(right_block, 1)
|
||||
|
||||
// Check type compatibility.
|
||||
ShaderInput::DataType
|
||||
left_type = left_block->output,
|
||||
right_type = right_block->output;
|
||||
if (left_type > right_type)
|
||||
{ ShaderInput::DataType tmp; tmp = left_type; left_type = right_type; right_type = tmp;
|
||||
CShaderBlock* tmp_block; tmp_block = left_block; left_block = right_block; right_block = tmp_block;
|
||||
}
|
||||
|
||||
if (
|
||||
!(
|
||||
(left_type == right_type) ||
|
||||
((left_type == ShaderInput::Vector3) && (right_type == ShaderInput::Matrix3)) ||
|
||||
((left_type == ShaderInput::Vector4) && (right_type == ShaderInput::Matrix4))
|
||||
)
|
||||
)
|
||||
__ERR__(__LOG_E__ << "Cannot multiply blocks, output do not match.\n", NULL)
|
||||
|
||||
// Check type validity.
|
||||
if (left_type == right_type)
|
||||
switch (left_block->output)
|
||||
{
|
||||
case ShaderInput::NoData:
|
||||
__ERR__(__LOG_E__ << "Block has no output.\n", NULL)
|
||||
case ShaderInput::DataTexture2D:
|
||||
__ERR__(__LOG_E__ << "Cannot multiply textures.\n", NULL)
|
||||
}
|
||||
|
||||
// Compile operator.
|
||||
GetNewVariable(compiled_block->variable, "mul");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
|
||||
if (left_type == right_type)
|
||||
{
|
||||
switch (left_type)
|
||||
{
|
||||
case ShaderInput::Float:
|
||||
case ShaderInput::Vector3:
|
||||
case ShaderInput::Vector4:
|
||||
pixel_source += String::Format("nReturnVectorMultiplyVector(%s, %s, %s);\n", compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
break;
|
||||
case ShaderInput::Matrix3:
|
||||
pixel_source += String::Format("nReturnnMatrix3MultiplynMatrix3(&%s, &%s, &%s);\n", compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
break;
|
||||
case ShaderInput::Matrix4:
|
||||
pixel_source += String::Format("nReturnnMatrix4MultiplynMatrix4(&%s, &%s, &%s);\n", compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
break;
|
||||
default:
|
||||
pixel_source += String::Format("nReturnVector(%s,1, 1, 1);\n", compiled_block->variable.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (left_type == ShaderInput::Float ||
|
||||
left_type == ShaderInput::Vector3 ||
|
||||
left_type == ShaderInput::Vector4)
|
||||
{
|
||||
if (right_type == ShaderInput::Matrix3)
|
||||
pixel_source += String::Format("nReturnnVectorMultiplynMatrix3(&%s, &%s, &%s);\n", compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
else if (right_type == ShaderInput::Matrix4)
|
||||
pixel_source += String::Format("nReturnnVectorMultiplynMatrix4(&%s, &%s, &%s);\n", compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
else
|
||||
if (right_type == ShaderInput::Vector3 ||
|
||||
left_type == ShaderInput::Vector4)
|
||||
pixel_source += String::Format("nReturnVectorMultiplyFloat(&%s, &%s, &%s.x);\n", compiled_block->variable.c_str(), right_block->variable.c_str(), left_block->variable.c_str());
|
||||
else
|
||||
pixel_source += String::Format("nReturnVector(%s, 1, 1, 1);\n", compiled_block->variable.c_str());
|
||||
}
|
||||
else
|
||||
pixel_source += String::Format("nReturnVector(%s, 1, 1, 1);\n", compiled_block->variable.c_str());
|
||||
}
|
||||
|
||||
compiled_block->output = left_type;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileDivShaderBlock(const DivOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(left_block, 0)
|
||||
__MapCompilerCompileInput(right_block, 1)
|
||||
|
||||
if (left_block->output != right_block->output)
|
||||
__ERR__(__LOG_E__ << "Cannot divide blocks, output do not match.\n", NULL)
|
||||
|
||||
switch (left_block->output)
|
||||
{
|
||||
case ShaderInput::NoData:
|
||||
__ERR__(__LOG_E__ << "Block has no output.\n", NULL)
|
||||
case ShaderInput::Matrix3:
|
||||
case ShaderInput::Matrix4:
|
||||
case ShaderInput::DataTexture2D:
|
||||
__ERR__(__LOG_E__ << "Cannot divide blocks (" << left_block->output << ").\n", NULL)
|
||||
}
|
||||
|
||||
GetNewVariable(compiled_block->variable, "div");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("nReturnVectorDivnVector(%s, %s, %s);\n", compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
compiled_block->output = left_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileDotShaderBlock(const DotOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(left_block, 0)
|
||||
__MapCompilerCompileInput(right_block, 1)
|
||||
|
||||
if (left_block->output != right_block->output)
|
||||
__ERR__(__LOG_E__ << "Cannot compute dot operator, output do not match.\n", NULL)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "dot");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("nReturnVectorDotnVector(%s, %s, %s);\n", compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileCrossShaderBlock(const CrossOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(left_block, 0)
|
||||
__MapCompilerCompileInput(right_block, 1)
|
||||
|
||||
if ((left_block->output != right_block->output) && (left_block->output != ShaderInput::Vector3))
|
||||
__ERR__(__LOG_E__ << "Cannot only compute cross product on Vector3.\n", NULL)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "cross");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("nReturnVectorCrossnVector(%s, %s, %s);\n", compiled_block->variable.c_str(), left_block->variable.c_str(), right_block->variable.c_str());
|
||||
compiled_block->output = ShaderInput::Vector3;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileClampShaderBlock(const ClampShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(value_block, 0)
|
||||
__MapCompilerCompileInput(min_block, 1)
|
||||
__MapCompilerCompileInput(max_block, 2)
|
||||
|
||||
if ((min_block->output != max_block->output) && (value_block->output != min_block->output))
|
||||
__ERR__(__LOG_E__ << ".\n", NULL)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "clamp");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("nReturnVectorClampMinMax(%s, %s, %s, %s);\n", compiled_block->variable.c_str(), value_block->variable.c_str(), min_block->variable.c_str(), max_block->variable.c_str());
|
||||
compiled_block->output = value_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileSwizzleShaderBlock(const SwizzleShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(input_block, 0)
|
||||
|
||||
// Set block output.
|
||||
if ((compiled_block->output = (ShaderInput::DataType)block->GetOutputType()) == ShaderInput::NoData)
|
||||
__ERR__(__LOG_E__ << "Invalid swizzle, no output.\n", NULL)
|
||||
|
||||
String output_type;
|
||||
GetTypeDeclaration(compiled_block->output, output_type);
|
||||
|
||||
// Build fragment shader swizzle.
|
||||
GetNewVariable(compiled_block->variable, "swizzle");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
|
||||
pixel_source += String::Format(" nReturnVector4(%s, ", compiled_block->variable.c_str());
|
||||
for (int n = 0; n < 4; ++n)
|
||||
{
|
||||
if (block->swizzle[n] != SwizzleShaderBlock::SwizzleNone)
|
||||
switch(block->swizzle[n] - SwizzleShaderBlock::SwizzleX)
|
||||
{
|
||||
case 0: //SwizzleX
|
||||
pixel_source += String::Format("%s.x", input_block->variable.c_str());
|
||||
break;
|
||||
case 1: //SwizzleY
|
||||
pixel_source += String::Format("%s.y", input_block->variable.c_str());
|
||||
break;
|
||||
case 2: //SwizzleZ
|
||||
pixel_source += String::Format("%s.z", input_block->variable.c_str());
|
||||
break;
|
||||
case 3: //SwizzleW
|
||||
pixel_source += String::Format("%s.w", input_block->variable.c_str());
|
||||
break;
|
||||
default: //SwizzleNone
|
||||
pixel_source += String::Format("0");
|
||||
}
|
||||
else //SwizzleNone
|
||||
pixel_source += String::Format("0");
|
||||
|
||||
// check to put the , or the )
|
||||
if(n<3)
|
||||
pixel_source += String::Format(",");
|
||||
else
|
||||
pixel_source += String::Format(");");
|
||||
}
|
||||
pixel_source += "\n";
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileBuildShaderBlock(const BuildShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
String component[4];
|
||||
int count = 0;
|
||||
|
||||
for (int n = 0; n < 4; ++n)
|
||||
{
|
||||
switch (block->build[n])
|
||||
{
|
||||
case BuildShaderBlock::BuildZero:
|
||||
component[count++] = "0.0";
|
||||
break;
|
||||
case BuildShaderBlock::BuildOne:
|
||||
component[count++] = "1.0";
|
||||
break;
|
||||
|
||||
case BuildShaderBlock::BuildX:
|
||||
case BuildShaderBlock::BuildY:
|
||||
case BuildShaderBlock::BuildZ:
|
||||
case BuildShaderBlock::BuildW:
|
||||
if (!block->input[n])
|
||||
n = 4;
|
||||
else
|
||||
{
|
||||
__MapCompilerCompileInput(input, n)
|
||||
|
||||
if (input->output == ShaderInput::Float)
|
||||
component[count++] = String::Format("%s.x", input->variable.c_str());
|
||||
else
|
||||
switch (block->build[n])
|
||||
{
|
||||
case BuildShaderBlock::BuildX: component[count++] = String::Format("%s.x", input->variable.c_str()); break;
|
||||
case BuildShaderBlock::BuildY: component[count++] = String::Format("%s.y", input->variable.c_str()); break;
|
||||
case BuildShaderBlock::BuildZ: component[count++] = String::Format("%s.z", input->variable.c_str()); break;
|
||||
case BuildShaderBlock::BuildW: component[count++] = String::Format("%s.w", input->variable.c_str()); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!count)
|
||||
compiled_block->output = ShaderInput::NoData;
|
||||
else
|
||||
{
|
||||
GetNewVariable(compiled_block->variable, "built");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("nReturnVector4(%s, ", compiled_block->variable.c_str());
|
||||
|
||||
switch (count)
|
||||
{
|
||||
case 1:
|
||||
pixel_source += String::Format("%s, 0, 0, 1);\n", component[0].c_str());
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
break;
|
||||
case 2:
|
||||
pixel_source += String::Format("%s, %s, 0, 1);\n", component[0].c_str(), component[1].c_str());
|
||||
compiled_block->output = ShaderInput::Vector2;
|
||||
break;
|
||||
case 3:
|
||||
pixel_source += String::Format("%s, %s, %s, 1);\n", component[0].c_str(), component[1].c_str(), component[2].c_str());
|
||||
compiled_block->output = ShaderInput::Vector3;
|
||||
break;
|
||||
case 4:
|
||||
pixel_source += String::Format("%s, %s, %s, %s);\n", component[0].c_str(), component[1].c_str(), component[2].c_str(), component[3].c_str());
|
||||
compiled_block->output = ShaderInput::Vector4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompilePowShaderBlock(const PowShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(value_block, 0)
|
||||
__MapCompilerCompileInput(power_block, 1)
|
||||
|
||||
if (value_block->output != ShaderInput::Float)
|
||||
__ERR__(__LOG_E__ << "Incompatible value type.\n", NULL)
|
||||
if (power_block->output != ShaderInput::Float)
|
||||
__ERR__(__LOG_E__ << "Incompatible power type.\n", NULL)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "pow");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("nReturnPow(&%s.x, &%s.x, &%s.x);\n", compiled_block->variable.c_str(), value_block->variable.c_str(), power_block->variable.c_str());
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileAbsShaderBlock(const AbsShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(value_block, 0)
|
||||
|
||||
switch (value_block->output)
|
||||
{
|
||||
case ShaderInput::Float:
|
||||
case ShaderInput::Vector2:
|
||||
case ShaderInput::Vector3:
|
||||
case ShaderInput::Vector4:
|
||||
break;
|
||||
default:
|
||||
__ERR__(__LOG_E__ << "Incompatible value type.\n", NULL)
|
||||
}
|
||||
|
||||
GetNewVariable(compiled_block->variable, "abs");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
|
||||
switch (value_block->output)
|
||||
{
|
||||
case ShaderInput::Float: pixel_source += String::Format("nReturnFabs(&%s.x , &%s.x);\n", compiled_block->variable.c_str(), value_block->variable.c_str()); break;
|
||||
case ShaderInput::Vector2: pixel_source += String::Format("nReturnFabs(&%s.x , &%s.x);nReturnFabs(&%s.y, &%s.y);\n", compiled_block->variable.c_str(), value_block->variable.c_str(), compiled_block->variable.c_str(), value_block->variable.c_str()); break;
|
||||
case ShaderInput::Vector3: pixel_source += String::Format("nReturnFabs(&%s.x , &%s.x);nReturnFabs(&%s.y, &%s.y);nReturnFabs(&%s.z, &%s.z);\n", compiled_block->variable.c_str(), value_block->variable.c_str(), compiled_block->variable.c_str(), value_block->variable.c_str(), compiled_block->variable.c_str(), value_block->variable.c_str()); break;
|
||||
case ShaderInput::Vector4: pixel_source += String::Format("nReturnFabs(&%s.x , &%s.x);nReturnFabs(&%s.y, &%s.y);nReturnFabs(&%s.z, &%s.z);nReturnFabs(&%s.w, &%s.w);\n", compiled_block->variable.c_str(), value_block->variable.c_str(), compiled_block->variable.c_str(), value_block->variable.c_str(), compiled_block->variable.c_str(), value_block->variable.c_str(), compiled_block->variable.c_str(), value_block->variable.c_str()); break;
|
||||
}
|
||||
compiled_block->output = value_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileCosinusShaderBlock(const CosinusShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(value_block, 0)
|
||||
|
||||
if (value_block->output != ShaderInput::Float)
|
||||
__ERR__(__LOG_E__ << "Incompatible source type.\n", NULL)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "cos");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("nReturnCosinus(&%s.x, &%s.x);\n", compiled_block->variable.c_str(), value_block->variable.c_str());
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileSinusShaderBlock(const SinusShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(value_block, 0)
|
||||
|
||||
if (value_block->output != ShaderInput::Float)
|
||||
__ERR__(__LOG_E__ << "Incompatible source type.\n", NULL)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "sin");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("nReturnSinus(&%s.x, &%s.x);\n", compiled_block->variable.c_str(), value_block->variable.c_str());
|
||||
compiled_block->output = ShaderInput::Float;
|
||||
return compiled_block;
|
||||
}
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileNormalizeBlock(const NormalizeOperatorShaderBlock *block, ShaderInput::Scope scope)
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(input_block, 0)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "normalized");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
pixel_source += String::Format("nReturnVectorNormalize(&%s, &%s);\n", compiled_block->variable.c_str(), input_block->variable.c_str());
|
||||
|
||||
compiled_block->output = input_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileScreenUVShaderBlock(const ScreenUVShaderBlock *block, ShaderInput::Scope scope)
|
||||
//-------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "screen_uv");
|
||||
pixel_declaration += String::Format("struct nVector %s; nReturnVector4(%s, 0.5f,, 0.5f, 0.5f, 1.0f);\n", compiled_block->variable.c_str(), compiled_block->variable.c_str());
|
||||
|
||||
compiled_block->output = ShaderInput::Vector2;
|
||||
return compiled_block;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompilePackVectorToColor(const PackVectorToColorShaderBlock *block, ShaderInput::Scope scope)
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(input_block, 0)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "packed_vector");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
|
||||
switch (input_block->output)
|
||||
{
|
||||
case ShaderInput::Float:
|
||||
case ShaderInput::Vector3:
|
||||
case ShaderInput::Vector4:
|
||||
pixel_source += String::Format("TinyCPackVectorToColor(&%s, &%s);\n", compiled_block->variable.c_str(), input_block->variable.c_str());
|
||||
break;
|
||||
|
||||
default:
|
||||
__ERR__(__LOG_E__ << "Invalid input to pack to vector.\n", NULL)
|
||||
}
|
||||
compiled_block->output = input_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
CShaderBlock *TinyCShaderTreeCompiler::CompileUnpackColorToVector(const UnpackColorToVectorShaderBlock *block, ShaderInput::Scope scope)
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
__MapCompilerGetNewCompiledBlock(compiled_block)
|
||||
__MapCompilerCompileInput(input_block, 0)
|
||||
|
||||
GetNewVariable(compiled_block->variable, "unpacked_vector");
|
||||
pixel_declaration += String::Format("struct nVector %s;\n", compiled_block->variable.c_str());
|
||||
|
||||
switch (input_block->output)
|
||||
{
|
||||
case ShaderInput::Float:
|
||||
case ShaderInput::Vector3:
|
||||
case ShaderInput::Vector4:
|
||||
pixel_source += String::Format("TinyCUnpackColorToVector(&%s, &%s);\n", compiled_block->variable.c_str(), input_block->variable.c_str());
|
||||
break;
|
||||
|
||||
default:
|
||||
__ERR__(__LOG_E__ << "Invalid input to unpack to vector.\n", NULL)
|
||||
}
|
||||
compiled_block->output = input_block->output;
|
||||
return compiled_block;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void TinyCShaderTreeCompiler::SetShaderInputs( const Material &m)
|
||||
{
|
||||
// declaration of the input
|
||||
ListForeachPtr(ShaderInput *, input, shader->input_list)
|
||||
{
|
||||
String type_declaration;
|
||||
if (GetTypeDeclaration(input->data_type, type_declaration))
|
||||
{
|
||||
switch (input->type)
|
||||
{
|
||||
case ShaderInput::Attribute:
|
||||
if (input->scope & ShaderInput::Vertex)
|
||||
vertex_declaration += String::Format("%s %s;\n", type_declaration.c_str(), input->name.c_str());
|
||||
if (input->scope & ShaderInput::Pixel)
|
||||
pixel_declaration += String::Format("%s %s;\n", type_declaration.c_str(), input->name.c_str());
|
||||
break;
|
||||
|
||||
case ShaderInput::Uniform:
|
||||
if (input->scope & ShaderInput::Vertex)
|
||||
vertex_declaration += String::Format("%s %s;\n", type_declaration.c_str(), input->name.c_str());
|
||||
if (input->scope & ShaderInput::Pixel)
|
||||
pixel_declaration += String::Format("%s %s;\n", type_declaration.c_str(), input->name.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "Unsupported input '" << input->name << "' type (" << input->type << ").\n";
|
||||
}
|
||||
|
||||
// fill the base input
|
||||
ListForeachPtr(ShaderInput *, input, shader->input_list)
|
||||
{
|
||||
switch (input->semantic)
|
||||
{
|
||||
case ShaderInput::NormalMatrix:
|
||||
pixel_declaration += String::Format("TinyCNormalMatrix(&%s, trace);\n", input->name.c_str());
|
||||
break;
|
||||
|
||||
case ShaderInput::NormalViewMatrix:
|
||||
pixel_declaration += String::Format("TinyCNormalViewMatrix(&%s, trace);\n", input->name.c_str());
|
||||
break;
|
||||
|
||||
case ShaderInput::ModelMatrix:
|
||||
pixel_declaration += String::Format("TinyCModelMatrix(&%s, trace);\n", input->name.c_str());
|
||||
break;
|
||||
|
||||
case ShaderInput::ModelViewMatrix:
|
||||
pixel_declaration += String::Format("TinyCModelViewMatrix(&%s, trace);\n", input->name.c_str());
|
||||
break;
|
||||
|
||||
case ShaderInput::ViewVector:
|
||||
pixel_declaration += String::Format("TinyCViewVector(&%s, trace);\n", input->name.c_str());
|
||||
break;
|
||||
|
||||
case ShaderInput::Clock:
|
||||
pixel_declaration += String::Format("nReturnVector(%s, 0.0f, 0.0f, 0.0f);\n", input->name.c_str());
|
||||
break;
|
||||
|
||||
/* case ShaderInput::ModelViewProjectionMatrix:
|
||||
fragment_shader += String::Format("TinyCModelViewMatrix(&%s, trace);\n", input->name.c_str());
|
||||
break;*/
|
||||
case ShaderInput::Constant: pixel_declaration += String::Format("nReturnVector4(%s, %f, %f, %f, %f)\n", input->name.c_str(), input->parm_v.x, input->parm_v.y, input->parm_v.z, input->parm_v.w); break;
|
||||
|
||||
case ShaderInput::MaterialDiffuse: pixel_declaration += String::Format("nReturnVector4(%s, %f, %f, %f, %f)\n", input->name.c_str(), m.diffuse.x, m.diffuse.y, m.diffuse.z, m.diffuse.w); break;
|
||||
case ShaderInput::MaterialSpecular: pixel_declaration += String::Format("nReturnVector4(%s, %f, %f, %f, %f)\n", input->name.c_str(), m.specular.x, m.specular.y, m.specular.z, m.specular.w); break;
|
||||
case ShaderInput::MaterialAmbient: pixel_declaration += String::Format("nReturnVector4(%s, %f, %f, %f, %f)\n", input->name.c_str(),m.ambient.x, m.ambient.y, m.ambient.z, m.ambient.w); break;
|
||||
case ShaderInput::MaterialSelf: pixel_declaration += String::Format("nReturnVector4(%s, %f, %f, %f, %f)\n", input->name.c_str(), m.self.x, m.self.y, m.self.z, m.self.w); break;
|
||||
|
||||
case ShaderInput::MaterialOpacity: pixel_declaration += String::Format("nReturnVector4(%s, %f, 0, 0, 1)\n", input->name.c_str(), m.opacity); break;
|
||||
case ShaderInput::MaterialGlossiness: pixel_declaration += String::Format("nReturnVector4(%s, %f, 0, 0, 1)\n", input->name.c_str(), m.glossiness); break;
|
||||
case ShaderInput::MaterialReflection: pixel_declaration += String::Format("nReturnVector4(%s, %f, 0, 0, 1)\n", input->name.c_str(), 0.2f/*m.reflection*/); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
Shader *TinyCShaderTreeCompiler::Finish()
|
||||
{
|
||||
// Finish shader programs.
|
||||
shader->pixel.Clear();
|
||||
if (!pixel_declaration.IsEmpty())
|
||||
shader->pixel += pixel_declaration;
|
||||
|
||||
if (!pixel_source.IsEmpty())
|
||||
shader->pixel += String::Format("%s \n}\n", pixel_source.c_str());
|
||||
else shader->pixel += "struct nVector TempValue;\n nReturnVector(TempValue, 1, 1, 1);\n return TempValue;\n }\n";
|
||||
|
||||
return shader;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@ -0,0 +1,51 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/shader_tree_convert_static_texture_block_to_dynamic.h"
|
||||
#include "core/shader_block.h"
|
||||
#include "core/shader_tree.h"
|
||||
#include "core/material.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void RecurseAndConvertInputs(ShaderBlock *block, const Material &m)
|
||||
{
|
||||
for (uint n = 0; n < block->GetInputCount(); ++n)
|
||||
if (ShaderBlock *input = block->GetInput(n))
|
||||
{
|
||||
if (input->type == ShaderBlock::TypeTexture)
|
||||
{
|
||||
// Seek a channel in the material using this texture.
|
||||
TextureShaderBlock *t = (TextureShaderBlock *)input;
|
||||
|
||||
for (uint i = 0; i < Material::max_texture_stage; ++i)
|
||||
if (m.texstage[n].t == t->texture)
|
||||
{
|
||||
// Got a match, replace static texture by a material texture reference to this slot.
|
||||
if (MaterialTextureShaderBlock *new_block = new MaterialTextureShaderBlock(i))
|
||||
{
|
||||
new_block->pos = input->pos;
|
||||
block->SetInput(n, new_block);
|
||||
_safe_delete(input);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Texture block has no input, end here.
|
||||
}
|
||||
else
|
||||
RecurseAndConvertInputs(input, m);
|
||||
}
|
||||
}
|
||||
void GS::Core::ConvertStaticToDynamicTextureBlocks(ShaderTree &tree, const Material &m)
|
||||
{
|
||||
for (uint n = 0; n < ShaderTree::SinkInvalid; ++n)
|
||||
if (tree.sink[n] != NULL)
|
||||
RecurseAndConvertInputs(tree.sink[n], m);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
584
include/engine/core/shader_tree_nml.cpp
Normal file
584
include/engine/core/shader_tree_nml.cpp
Normal file
@ -0,0 +1,584 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/shader_tree.h"
|
||||
#include "core/shader_block.h"
|
||||
#include "math/vector_nml.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
using NML::Tag;
|
||||
|
||||
/// Id of the NOOP shader block.
|
||||
static String __ShaderBlockNoneTagId("None");
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Array <pShaderBlock> *ShaderBlock::BlockMapFromMetaTag(Tag &tag)
|
||||
{
|
||||
if (tag.name != "Map")
|
||||
return NULL;
|
||||
|
||||
// Allocate block map.
|
||||
Array <pShaderBlock> *block_map = new Array <pShaderBlock> (tag.GetChildCount());
|
||||
if (!block_map)
|
||||
__ERR__(__LOG_E__ << "Failed to allocate shader block map.\n", NULL)
|
||||
|
||||
for (uint n = 0; n < block_map->GetCount(); ++n)
|
||||
(*block_map)[n] = NULL;
|
||||
|
||||
// Fill the block map.
|
||||
uint block_count = 0;
|
||||
|
||||
NMLTagForeach(entry_tag, tag)
|
||||
if (entry_tag->name == "Entry")
|
||||
{
|
||||
ShaderBlock *_block = NULL;
|
||||
Tag *type_tag = entry_tag->GetTag("Type"),
|
||||
*parm_tag = entry_tag->GetTag("Param");
|
||||
|
||||
//----------------------------------------------------
|
||||
#define __InstanciateNewShaderBlock(__VAR__, __TYPE__)\
|
||||
__TYPE__ *__VAR__ = new __TYPE__;\
|
||||
_block = __VAR__;
|
||||
|
||||
#define __ValidateShaderBlockParam\
|
||||
if (!parm_tag)\
|
||||
break;
|
||||
//----------------------------------------------------
|
||||
|
||||
if (type_tag)
|
||||
{
|
||||
int type;
|
||||
String type_string(type_tag->GetString());
|
||||
|
||||
// Legacy block type support.
|
||||
if ((type_string == "Sampler 2D") || (type_string == "Sampler Cube"))
|
||||
type_string = "Texture Sampler";
|
||||
if (type_string == "Normal Matrix")
|
||||
type_string = "Normal View Matrix";
|
||||
|
||||
// Locate block.
|
||||
for (type = ShaderBlock::TypeNone; type < ShaderBlock::TypeInvalid; ++type)
|
||||
if (type_string == ShaderBlock::BlockTypeToString((BlockType)type))
|
||||
break;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case TypeGeometryUV:
|
||||
{
|
||||
__InstanciateNewShaderBlock(block, GeometryUVShaderBlock)
|
||||
__ValidateShaderBlockParam
|
||||
|
||||
if (Tag *tag = parm_tag->GetTypedTag("Channel;", Variant::VariantInteger))
|
||||
block->channel = tag->GetInteger();
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeTextureSampler:
|
||||
{
|
||||
__InstanciateNewShaderBlock(block, TextureSamplerShaderBlock)
|
||||
__ValidateShaderBlockParam
|
||||
|
||||
if (Tag *tag = parm_tag->GetTypedTag("Type;", Variant::VariantString))
|
||||
{
|
||||
String sampler_type(tag->GetString());
|
||||
|
||||
if (sampler_type == "3D") block->sampler_type = TextureSamplerShaderBlock::Sampler3D;
|
||||
else if (sampler_type == "Cube") block->sampler_type = TextureSamplerShaderBlock::SamplerCube;
|
||||
else block->sampler_type = TextureSamplerShaderBlock::Sampler2D;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeRenderBuffer:
|
||||
{
|
||||
__InstanciateNewShaderBlock(block, RenderBufferShaderBlock)
|
||||
__ValidateShaderBlockParam
|
||||
|
||||
if (Tag *tag = parm_tag->GetTypedTag("Buffer;", Variant::VariantInteger))
|
||||
block->buffer = (RenderBufferShaderBlock::RenderBuffer)tag->GetInteger();
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeTexture:
|
||||
{
|
||||
__InstanciateNewShaderBlock(block, TextureShaderBlock)
|
||||
__ValidateShaderBlockParam
|
||||
|
||||
if (Tag *tag = parm_tag->GetTypedTag("Texture;", Variant::VariantString))
|
||||
block->texture = tag->GetString();
|
||||
|
||||
if (Tag *tag = parm_tag->GetTypedTag("Type;", Variant::VariantString))
|
||||
{
|
||||
String texture_type(tag->GetString());
|
||||
|
||||
if (texture_type == "3D") block->texture_type = TextureShaderBlock::Texture3D;
|
||||
else if (texture_type == "Cube") block->texture_type = TextureShaderBlock::TextureCube;
|
||||
else block->texture_type = TextureShaderBlock::Texture2D;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeConstant:
|
||||
{
|
||||
__InstanciateNewShaderBlock(block, ConstantShaderBlock)
|
||||
__ValidateShaderBlockParam
|
||||
|
||||
if (Tag *tag = parm_tag->GetTypedTag("Type;", Variant::VariantInteger))
|
||||
block->constant_type = (ShaderInput::DataType)tag->GetInteger();
|
||||
|
||||
Vector4 v(0, 0, 0);
|
||||
if (Tag *tag = parm_tag->GetTag("Value;"))
|
||||
v.FromMetaTag(*tag);
|
||||
|
||||
block->constant[0] = v.x;
|
||||
block->constant[1] = v.y;
|
||||
block->constant[2] = v.z;
|
||||
block->constant[3] = v.w;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeColor:
|
||||
{
|
||||
__InstanciateNewShaderBlock(block, ColorShaderBlock)
|
||||
__ValidateShaderBlockParam
|
||||
|
||||
if (Tag *tag = parm_tag->GetTag("Color;"))
|
||||
block->color.FromMetaTag(*tag);
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeMaterialParam:
|
||||
{
|
||||
__InstanciateNewShaderBlock(block, MaterialParamShaderBlock)
|
||||
__ValidateShaderBlockParam
|
||||
|
||||
if (Tag *tag = parm_tag->GetTypedTag("Param;", Variant::VariantInteger))
|
||||
block->param = (MaterialParamShaderBlock::MaterialParam)tag->GetInteger();
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeMaterialTexture:
|
||||
{
|
||||
__InstanciateNewShaderBlock(block, MaterialTextureShaderBlock)
|
||||
__ValidateShaderBlockParam
|
||||
|
||||
if (Tag *tag = parm_tag->GetTypedTag("Slot;", Variant::VariantInteger))
|
||||
block->slot = tag->GetInteger();
|
||||
|
||||
if (Tag *tag = parm_tag->GetTypedTag("Type;", Variant::VariantString))
|
||||
{
|
||||
String texture_type(tag->GetString());
|
||||
|
||||
if (texture_type == "3D") block->texture_type = MaterialTextureShaderBlock::Texture3D;
|
||||
else if (texture_type == "Cube") block->texture_type = MaterialTextureShaderBlock::TextureCube;
|
||||
else block->texture_type = MaterialTextureShaderBlock::Texture2D;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeSwizzle:
|
||||
{
|
||||
__InstanciateNewShaderBlock(block, SwizzleShaderBlock)
|
||||
__ValidateShaderBlockParam
|
||||
|
||||
const char *swizzle = "nnnn";
|
||||
if (Tag *tag = parm_tag->GetTag("Swizzle;"))
|
||||
swizzle = tag->GetString();
|
||||
|
||||
for (int n = 0; n < 4; ++n)
|
||||
if (swizzle[n] == 'n') block->swizzle[n] = SwizzleShaderBlock::SwizzleNone;
|
||||
else if (swizzle[n] == 'x') block->swizzle[n] = SwizzleShaderBlock::SwizzleX;
|
||||
else if (swizzle[n] == 'y') block->swizzle[n] = SwizzleShaderBlock::SwizzleY;
|
||||
else if (swizzle[n] == 'z') block->swizzle[n] = SwizzleShaderBlock::SwizzleZ;
|
||||
else if (swizzle[n] == 'w') block->swizzle[n] = SwizzleShaderBlock::SwizzleW;
|
||||
else
|
||||
{
|
||||
__LOG_E__ << "Unexpected end of swizzle mask.\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeBuild:
|
||||
{
|
||||
__InstanciateNewShaderBlock(block, BuildShaderBlock)
|
||||
__ValidateShaderBlockParam
|
||||
|
||||
const char *build = "0000";
|
||||
if (Tag *tag = parm_tag->GetTag("Build;"))
|
||||
build = tag->GetString();
|
||||
|
||||
for (int n = 0; n < 4; ++n)
|
||||
if (build[n] == '0') block->build[n] = BuildShaderBlock::BuildZero;
|
||||
else if (build[n] == '1') block->build[n] = BuildShaderBlock::BuildOne;
|
||||
else if (build[n] == 'x') block->build[n] = BuildShaderBlock::BuildX;
|
||||
else if (build[n] == 'y') block->build[n] = BuildShaderBlock::BuildY;
|
||||
else if (build[n] == 'z') block->build[n] = BuildShaderBlock::BuildZ;
|
||||
else if (build[n] == 'w') block->build[n] = BuildShaderBlock::BuildW;
|
||||
else
|
||||
{
|
||||
__LOG_E__ << "Unexpected end of build mask.\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeGeometryVertex: _block = new GeometryVertexShaderBlock; break;
|
||||
case TypeGeometrySkinning: _block = new GeometrySkinningShaderBlock; break;
|
||||
case TypeGeometryNormal: _block = new GeometryNormalShaderBlock; break;
|
||||
case TypeGeometryVertexColor: _block = new GeometryVertexColorShaderBlock; break;
|
||||
case TypeGeometryTangentFrame: _block = new GeometryTangentFrameShaderBlock; break;
|
||||
case TypeScreenUV: _block = new ScreenUVShaderBlock; break;
|
||||
case TypeViewVector: _block = new ViewVectorShaderBlock; break;
|
||||
case TypeViewport: _block = new ViewportShaderBlock; break;
|
||||
case TypeNormalViewMatrix: _block = new NormalViewMatrixShaderBlock; break;
|
||||
case TypeNormalMatrix: _block = new NormalMatrixShaderBlock; break;
|
||||
case TypeModelViewMatrix: _block = new ModelViewMatrixShaderBlock; break;
|
||||
case TypeModelMatrix: _block = new ModelMatrixShaderBlock; break;
|
||||
|
||||
case TypeMix: _block = new MixOperatorShaderBlock; break;
|
||||
case TypeAdd: _block = new AddOperatorShaderBlock; break;
|
||||
case TypeMul: _block = new MulOperatorShaderBlock; break;
|
||||
case TypeSub: _block = new SubOperatorShaderBlock; break;
|
||||
case TypeDiv: _block = new DivOperatorShaderBlock; break;
|
||||
case TypeDot: _block = new DotOperatorShaderBlock; break;
|
||||
case TypeCross: _block = new CrossOperatorShaderBlock; break;
|
||||
case TypeClamp: _block = new ClampShaderBlock; break;
|
||||
case TypeNormalize: _block = new NormalizeOperatorShaderBlock; break;
|
||||
case TypeSin: _block = new SinusShaderBlock; break;
|
||||
case TypeCos: _block = new CosinusShaderBlock; break;
|
||||
case TypePow: _block = new PowShaderBlock; break;
|
||||
case TypeAbs: _block = new AbsShaderBlock; break;
|
||||
case TypeClock: _block = new ClockShaderBlock; break;
|
||||
|
||||
case TypeUnpackColorToVector: _block = new UnpackColorToVectorShaderBlock; break;
|
||||
case TypePackVectorToColor: _block = new PackVectorToColorShaderBlock; break;
|
||||
|
||||
default:
|
||||
__LOG_E__ << "Invalid block type '" << type_string << "'.\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_block)
|
||||
if (Tag *pos_tag = entry_tag->GetTag("Pos"))
|
||||
tVectorFromMetaTag(_block->pos, *pos_tag);
|
||||
|
||||
// Add block to the map.
|
||||
if (block_map->GetCount() > block_count)
|
||||
(*block_map)[block_count++] = _block;
|
||||
else
|
||||
__LOG_E__ << "Block map full, unexpected error.\n";
|
||||
}
|
||||
else __LOG_W__ << "Unexpected tag '" << entry_tag->name << "' in shader map.\n";
|
||||
|
||||
return block_map;
|
||||
}
|
||||
ShaderBlock *ShaderBlock::BranchFromMetaTag(Tag &tag, Array <pShaderBlock> *block_map)
|
||||
{
|
||||
// Load block.
|
||||
Tag *index_tag = tag.GetTypedTag("Index;", Variant::VariantInteger);
|
||||
if (!index_tag)
|
||||
__ERR__(__LOG_E__ << "No block index found.\n", NULL)
|
||||
|
||||
// Load map if none provided.
|
||||
bool drop_map = false;
|
||||
|
||||
if (!block_map)
|
||||
{
|
||||
Tag *map_tag = tag.GetTag("Map;");
|
||||
if (!map_tag)
|
||||
__ERR__(__LOG_E__ << "No map to build shader branch.\n", NULL)
|
||||
|
||||
if ((block_map = BlockMapFromMetaTag(*map_tag)) == NULL)
|
||||
return NULL;
|
||||
|
||||
drop_map = true;
|
||||
}
|
||||
|
||||
// Link block to its inputs.
|
||||
ShaderBlock *block = (*block_map)[index_tag->GetUnsigned()];
|
||||
if (block && block->GetInputCount())
|
||||
if (Tag *inputs_tag = tag.GetTag("Input;"))
|
||||
{
|
||||
int input_count = 0;
|
||||
NMLTagForeach(input_tag, *inputs_tag)
|
||||
if (input_tag && (input_tag->name != __ShaderBlockNoneTagId))
|
||||
block->SetInput(input_count++, ShaderBlock::BranchFromMetaTag(*input_tag, block_map));
|
||||
}
|
||||
|
||||
if (drop_map)
|
||||
_safe_delete(block_map);
|
||||
return block;
|
||||
}
|
||||
bool ShaderTree::FromMetaTag(Tag &tag)
|
||||
{
|
||||
if (tag.name != "ShaderMap")
|
||||
return false;
|
||||
|
||||
// Load import map.
|
||||
Tag *map_tag = tag.GetTag("Map;");
|
||||
Array <pShaderBlock> *block_map = map_tag ? ShaderBlock::BlockMapFromMetaTag(*map_tag) : NULL;
|
||||
|
||||
// Sink block position.
|
||||
if (Tag *pos_tag = tag.GetTag("Pos"))
|
||||
tVectorFromMetaTag(pos, *pos_tag);
|
||||
|
||||
// Read in sinks.
|
||||
Tag *sink_tag;
|
||||
|
||||
if ((sink_tag = tag.GetTag("Vertex:Block;")) != NULL)
|
||||
sink[SinkVertex] = ShaderBlock::BranchFromMetaTag(*sink_tag, block_map);
|
||||
if ((sink_tag = tag.GetTag("Normal:Block;")) != NULL)
|
||||
sink[SinkNormal] = ShaderBlock::BranchFromMetaTag(*sink_tag, block_map);
|
||||
if ((sink_tag = tag.GetTag("Diffuse:Block;")) != NULL)
|
||||
sink[SinkDiffuse] = ShaderBlock::BranchFromMetaTag(*sink_tag, block_map);
|
||||
if ((sink_tag = tag.GetTag("Modulate:Block;")) != NULL)
|
||||
sink[SinkModulate] = ShaderBlock::BranchFromMetaTag(*sink_tag, block_map);
|
||||
if ((sink_tag = tag.GetTag("Specular:Block;")) != NULL)
|
||||
sink[SinkSpecular] = ShaderBlock::BranchFromMetaTag(*sink_tag, block_map);
|
||||
if ((sink_tag = tag.GetTag("Glossiness:Block;")) != NULL)
|
||||
sink[SinkGlossiness] = ShaderBlock::BranchFromMetaTag(*sink_tag, block_map);
|
||||
if ((sink_tag = tag.GetTag("Constant:Block;")) != NULL)
|
||||
sink[SinkConstant] = ShaderBlock::BranchFromMetaTag(*sink_tag, block_map);
|
||||
if ((sink_tag = tag.GetTag("Opacity:Block;")) != NULL)
|
||||
sink[SinkOpacity] = ShaderBlock::BranchFromMetaTag(*sink_tag, block_map);
|
||||
if ((sink_tag = tag.GetTag("Reflection:Block;")) != NULL)
|
||||
sink[SinkReflection] = ShaderBlock::BranchFromMetaTag(*sink_tag, block_map);
|
||||
|
||||
_safe_delete(block_map);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tag *ShaderBlock::ParamAsMetaTag() const
|
||||
{
|
||||
Tag *parm = new Tag("Param");
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case TypeGeometryUV:
|
||||
{
|
||||
GeometryUVShaderBlock *block = (GeometryUVShaderBlock *)this;
|
||||
parm->AddChild("Channel", block->channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeTextureSampler:
|
||||
{
|
||||
TextureSamplerShaderBlock *block = (TextureSamplerShaderBlock *)this;
|
||||
|
||||
switch (block->sampler_type)
|
||||
{
|
||||
default:
|
||||
case TextureSamplerShaderBlock::Sampler2D: parm->AddChild("Type", "2D"); break;
|
||||
case TextureSamplerShaderBlock::Sampler3D: parm->AddChild("Type", "3D"); break;
|
||||
case TextureSamplerShaderBlock::SamplerCube: parm->AddChild("Type", "Cube"); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeRenderBuffer:
|
||||
{
|
||||
RenderBufferShaderBlock *block = (RenderBufferShaderBlock *)this;
|
||||
parm->AddChild("Buffer", (int)block->buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeTexture:
|
||||
{
|
||||
TextureShaderBlock *block = (TextureShaderBlock *)this;
|
||||
if (!block->texture.IsEmpty())
|
||||
parm->AddChild("Texture", block->texture.c_str());
|
||||
|
||||
switch (block->texture_type)
|
||||
{
|
||||
default:
|
||||
case TextureShaderBlock::Texture2D: parm->AddChild("Type", "2D"); break;
|
||||
case TextureShaderBlock::Texture3D: parm->AddChild("Type", "3D"); break;
|
||||
case TextureShaderBlock::TextureCube: parm->AddChild("Type", "Cube"); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeConstant:
|
||||
{
|
||||
ConstantShaderBlock *block = (ConstantShaderBlock *)this;
|
||||
parm->AddChild("Type", (int)block->constant_type);
|
||||
parm->AddChild(Vector4(block->constant[0], block->constant[1], block->constant[2], block->constant[3]).AsMetaTag("Value", true));
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeColor:
|
||||
{
|
||||
ColorShaderBlock *block = (ColorShaderBlock *)this;
|
||||
parm->AddChild(block->color.AsMetaTag("Color", true));
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeMaterialParam:
|
||||
{
|
||||
MaterialParamShaderBlock *block = (MaterialParamShaderBlock *)this;
|
||||
parm->AddChild("Param", block->param);
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeMaterialTexture:
|
||||
{
|
||||
MaterialTextureShaderBlock *block = (MaterialTextureShaderBlock *)this;
|
||||
parm->AddChild(new Tag("Slot", block->slot));
|
||||
|
||||
switch (block->texture_type)
|
||||
{
|
||||
default:
|
||||
case MaterialTextureShaderBlock::Texture2D: parm->AddChild("Type", "2D"); break;
|
||||
case MaterialTextureShaderBlock::Texture3D: parm->AddChild("Type", "3D"); break;
|
||||
case MaterialTextureShaderBlock::TextureCube: parm->AddChild("Type", "Cube"); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeSwizzle:
|
||||
{
|
||||
SwizzleShaderBlock *block = (SwizzleShaderBlock *)this;
|
||||
|
||||
char swizzle[5];
|
||||
for (int n = 0; n < 4; ++n)
|
||||
switch (block->swizzle[n])
|
||||
{
|
||||
case SwizzleShaderBlock::SwizzleNone: swizzle[n] = 'n'; break;
|
||||
case SwizzleShaderBlock::SwizzleX: swizzle[n] = 'x'; break;
|
||||
case SwizzleShaderBlock::SwizzleY: swizzle[n] = 'y'; break;
|
||||
case SwizzleShaderBlock::SwizzleZ: swizzle[n] = 'z'; break;
|
||||
case SwizzleShaderBlock::SwizzleW: swizzle[n] = 'w'; break;
|
||||
}
|
||||
swizzle[4] = 0;
|
||||
|
||||
parm->AddChild("Swizzle", (const char *)swizzle);
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeBuild:
|
||||
{
|
||||
BuildShaderBlock *block = (BuildShaderBlock *)this;
|
||||
|
||||
char build[5];
|
||||
for (int n = 0; n < 4; ++n)
|
||||
switch (block->build[n])
|
||||
{
|
||||
case BuildShaderBlock::BuildZero: build[n] = '0'; break;
|
||||
case BuildShaderBlock::BuildOne: build[n] = '1'; break;
|
||||
case BuildShaderBlock::BuildX: build[n] = 'x'; break;
|
||||
case BuildShaderBlock::BuildY: build[n] = 'y'; break;
|
||||
case BuildShaderBlock::BuildZ: build[n] = 'z'; break;
|
||||
case BuildShaderBlock::BuildW: build[n] = 'w'; break;
|
||||
}
|
||||
build[4] = 0;
|
||||
|
||||
parm->AddChild("Build", (const char *)build);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return parm;
|
||||
}
|
||||
void ShaderBlock::GatherChildren(List <ShaderBlock *> &children)
|
||||
{
|
||||
if (!children.Find(this))
|
||||
children.Add(this);
|
||||
|
||||
for (uint n = 0; n < GetInputCount(); ++n)
|
||||
if (GetInput(n))
|
||||
GetInput(n)->GatherChildren(children);
|
||||
}
|
||||
Tag *ShaderBlock::BranchAsMetaTag(List <ShaderBlock *> &block_map)
|
||||
{
|
||||
// Locate block in map.
|
||||
uint index = 0;
|
||||
ListForeachPtr(ShaderBlock *, block, block_map)
|
||||
{
|
||||
if (block == this)
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
if (index == block_map.GetCount())
|
||||
return NULL;
|
||||
|
||||
// Export block and inputs.
|
||||
Tag *block = new Tag("Block");
|
||||
block->AddChild("Index", index);
|
||||
|
||||
if (GetInputCount())
|
||||
{
|
||||
Tag *input = block->AddChild("Input");
|
||||
for (uint n = 0; n < GetInputCount(); ++n)
|
||||
input->AddChild(GetInput(n) ? GetInput(n)->BranchAsMetaTag(block_map) : new Tag(__ShaderBlockNoneTagId.c_str()));
|
||||
}
|
||||
return block;
|
||||
}
|
||||
Tag *ShaderBlock::BlockMapAsMetaTag(const List <ShaderBlock *> &block_map)
|
||||
{
|
||||
Tag *map = new Tag("Map");
|
||||
if (!map)
|
||||
__ERR__(__LOG_E__ << "Failed to allocate root tag.\n", NULL);
|
||||
|
||||
ListForeachPtr(ShaderBlock *, block, block_map)
|
||||
if (block)
|
||||
if (Tag *entry = map->AddChild("Entry"))
|
||||
{
|
||||
entry->AddChild("Type", ShaderBlock::BlockTypeToString(block->type));
|
||||
entry->AddChild(tVectorAsMetaTag(block->pos, "Pos"));
|
||||
entry->AddChild(block->ParamAsMetaTag());
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
Tag *ShaderTree::AsMetaTag() const
|
||||
{
|
||||
Tag *map = new Tag("ShaderMap");
|
||||
if (!map)
|
||||
__ERR__(__LOG_E__ << "Could not serialize shader map. Failed to create root tag.\n", NULL);
|
||||
|
||||
// Sink block position.
|
||||
map->AddChild(tVectorAsMetaTag(pos, "Pos"));
|
||||
|
||||
// Build block export map.
|
||||
List <ShaderBlock *> block_map;
|
||||
for (int n = 0; n < SinkInvalid; ++n)
|
||||
if (sink[n])
|
||||
sink[n]->GatherChildren(block_map);
|
||||
|
||||
// Serialize map.
|
||||
map->AddChild(ShaderBlock::BlockMapAsMetaTag(block_map));
|
||||
|
||||
// Serialize all sinks.
|
||||
#define __SerializeShaderMapSink(__SINK__, __LABEL__)\
|
||||
if (__SINK__)\
|
||||
{\
|
||||
Tag *sinktag = map->AddChild(__LABEL__);\
|
||||
sinktag->AddChild((__SINK__)->BranchAsMetaTag(block_map));\
|
||||
}
|
||||
|
||||
// Serialize shader map.
|
||||
__SerializeShaderMapSink(sink[SinkVertex], "Vertex")
|
||||
__SerializeShaderMapSink(sink[SinkNormal], "Normal")
|
||||
__SerializeShaderMapSink(sink[SinkDiffuse], "Diffuse")
|
||||
__SerializeShaderMapSink(sink[SinkModulate], "Modulate")
|
||||
__SerializeShaderMapSink(sink[SinkSpecular], "Specular")
|
||||
__SerializeShaderMapSink(sink[SinkGlossiness], "Glossiness")
|
||||
__SerializeShaderMapSink(sink[SinkConstant], "Constant")
|
||||
__SerializeShaderMapSink(sink[SinkOpacity], "Opacity")
|
||||
__SerializeShaderMapSink(sink[SinkReflection], "Reflection")
|
||||
|
||||
return map;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
60
include/engine/core/shader_tree_to_shader.cpp
Normal file
60
include/engine/core/shader_tree_to_shader.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/shader_tree_to_shader.h"
|
||||
#include "core/shader_tree_compiler_isl.h"
|
||||
#include "core/shader_tree.h"
|
||||
#include "core/shader.h"
|
||||
#include "memory/nauto_ptr.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Core::ConvertShaderTreeToShader(const Core::ShaderTree &tree, Core::Shader &shader)
|
||||
{
|
||||
ISLShaderTreeCompiler cp;
|
||||
cp.RestartCompiler(&shader);
|
||||
|
||||
struct ShaderTreeOutput
|
||||
{
|
||||
const char *builtin;
|
||||
ShaderTree::ShaderSinkType sink;
|
||||
const char *default_value;
|
||||
};
|
||||
|
||||
static ShaderTreeOutput vertex_sink[] =
|
||||
{
|
||||
{ "%position%", ShaderTree::SinkVertex, "vec4(a_position, 1.0)" },
|
||||
{ NULL, ShaderTree::SinkInvalid }
|
||||
};
|
||||
|
||||
for (uint n = 0; vertex_sink[n].builtin; ++n)
|
||||
if (CShaderBlock *cblock = cp.CompileShaderBlock(tree.sink[vertex_sink[n].sink]))
|
||||
shader.vertex += String::Format("%s = %s;\n", vertex_sink[n].builtin, cblock->variable.c_str());
|
||||
|
||||
static ShaderTreeOutput pixel_sink[] =
|
||||
{
|
||||
{ "%normal%", ShaderTree::SinkNormal, "vec3(0.0, 0.0, 0.0)" },
|
||||
{ "%diffuse%", ShaderTree::SinkDiffuse, "vec4(0.75, 0.75, 0.75, 1.0)" },
|
||||
{ "%specular%", ShaderTree::SinkSpecular, "vec4(0.5, 0.5, 0.5, 1.0)" },
|
||||
{ "%glossiness%", ShaderTree::SinkGlossiness, "0.25" },
|
||||
{ "%constant%", ShaderTree::SinkConstant, "vec4(0.0, 0.0, 0.0, 1.0)" },
|
||||
{ "%opacity%", ShaderTree::SinkOpacity, "1.0" },
|
||||
{ NULL, ShaderTree::SinkInvalid }
|
||||
};
|
||||
|
||||
for (uint n = 0; pixel_sink[n].builtin; ++n)
|
||||
if (CShaderBlock *cblock = cp.CompileShaderBlock(tree.sink[pixel_sink[n].sink]))
|
||||
shader.pixel += String::Format("%s = %s;\n", pixel_sink[n].builtin, cblock->variable.c_str());
|
||||
|
||||
shader.name = cp.GetId();
|
||||
shader.vertex = cp.GetVertexSource() + shader.vertex;
|
||||
shader.pixel = cp.GetPixelSource() + shader.pixel;
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
45
include/engine/core/simple_list_renderable.cpp
Normal file
45
include/engine/core/simple_list_renderable.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/simple_list_renderable.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void SimpleCullingSystem::AddRenderable(Renderable *r)
|
||||
{ renderable_list.Add(r); }
|
||||
void SimpleCullingSystem::DeleteRenderable(Renderable *r)
|
||||
{ renderable_list.Remove(r); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void SimpleCullingSystem::ComputeRenderableMinMax(MinMax &mm)
|
||||
{
|
||||
if (!renderable_list.GetCount())
|
||||
return;
|
||||
|
||||
renderable_list[0]->ComputeRenderableMinMax(mm);
|
||||
|
||||
MinMax l_mm;
|
||||
for (uint n = 1; n < renderable_list.GetCount(); ++n)
|
||||
{
|
||||
renderable_list[n]->ComputeRenderableMinMax(l_mm);
|
||||
mm.Grow(l_mm);
|
||||
}
|
||||
}
|
||||
uint SimpleCullingSystem::GetRenderablePrimitiveList(const Camera &view, const Camera &default_view, Stack <Render::Primitive *> &list, Context ctx, bool cull)
|
||||
{
|
||||
uint tested_primitive = 0;
|
||||
ArrayListForeachPtr(Renderable *, renderable, renderable_list)
|
||||
if (renderable->IsRenderable())
|
||||
tested_primitive += renderable->GetRenderablePrimitiveList(view, default_view, list, ctx, cull);
|
||||
|
||||
return tested_primitive;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
23
include/engine/core/skin.cpp
Normal file
23
include/engine/core/skin.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "core/skin.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Skin::ComputeBoneBoundingVolume(uint n, OBB &obb) const
|
||||
{
|
||||
if (n >= bones.GetCount())
|
||||
return false;
|
||||
|
||||
obb = OBB::FromMinMax(bones_minmax[n]);
|
||||
obb.Transform(bones_mtx[n]);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
18
include/engine/core/sound.cpp
Normal file
18
include/engine/core/sound.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/sound.h"
|
||||
#include "core/mixer.h"
|
||||
|
||||
using namespace GS::Audio;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Sound::~Sound()
|
||||
{
|
||||
mixer.UnloadSound(mixer_data);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
468
include/engine/core/terrain.cpp
Normal file
468
include/engine/core/terrain.cpp
Normal file
@ -0,0 +1,468 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/camera.h"
|
||||
#include "core/terrain.h"
|
||||
#include "core/geometry.h"
|
||||
#include "core/resource_factories.h"
|
||||
#include "core/render_resource_factory.h"
|
||||
#include "picture/pict.h"
|
||||
#include "picture/pict_io.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Terrain::RenderSetup(ResourceFactories *f)
|
||||
{
|
||||
if (LoadHeightmap(heightmap_path))
|
||||
{
|
||||
ComputeNormals();
|
||||
BuildQuadtree();
|
||||
}
|
||||
|
||||
ComputeNormals();
|
||||
BuildQuadtree();
|
||||
|
||||
if (render_data = new RenderData)
|
||||
if (f && f->render)
|
||||
{
|
||||
render_data->blendmap = f->render->LoadTexture(blendmap_path);
|
||||
render_data->material = f->render->LoadMaterial(material);
|
||||
|
||||
for (uint n = 0; n < 4; ++n)
|
||||
{
|
||||
render_data->layer[n].diffuse = f->render->LoadTexture(layer[n].diffuse);
|
||||
render_data->layer[n].specular = f->render->LoadTexture(layer[n].specular);
|
||||
render_data->layer[n].normal = f->render->LoadTexture(layer[n].normal);
|
||||
render_data->layer[n].self = f->render->LoadTexture(layer[n].self);
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Terrain::LocalToTexture(const Vector4 &p, float w, float h, float &u, float &v) const
|
||||
{
|
||||
float lx = p.x + width * 0.5f,
|
||||
lz = p.z + depth * 0.5f;
|
||||
|
||||
u = (lx / width) * w;
|
||||
v = (lz / depth) * h;
|
||||
|
||||
return asbool((u >= 0) && (u < w) && (v >= 0) && (v < h));
|
||||
}
|
||||
bool Terrain::LocalToHeightmap(const Vector4 &p, float &u, float &v) const
|
||||
{
|
||||
return LocalToTexture(p, (float)heightmap_w, (float)heightmap_h, u, v);
|
||||
}
|
||||
float Terrain::SampleHeight(float u, float v) const
|
||||
{
|
||||
// Fetch samples coordinates.
|
||||
int s_u[4], s_v[4];
|
||||
s_u[0] = Types::Clamp(int(u), 0, heightmap_w); s_v[0] = Types::Clamp(int(v), 0, heightmap_h);
|
||||
s_u[1] = Types::Clamp(s_u[0] + 1, 0, heightmap_w); s_v[1] = Types::Clamp(s_v[0] + 0, 0, heightmap_h);
|
||||
s_u[2] = Types::Clamp(s_u[0] + 0, 0, heightmap_w); s_v[2] = Types::Clamp(s_v[0] + 1, 0, heightmap_h);
|
||||
s_u[3] = Types::Clamp(s_u[0] + 1, 0, heightmap_w); s_v[3] = Types::Clamp(s_v[0] + 1, 0, heightmap_h);
|
||||
|
||||
// Fetch samples.
|
||||
float s[4];
|
||||
for (int n = 0; n < 4; ++n)
|
||||
s[n] = heightmap[s_v[n] * GetHeightmapPitch() + s_u[n]];
|
||||
|
||||
// Bilinear.
|
||||
float k_u = u - int(u), k_v = v - (int)v;
|
||||
return (s[0] * (1 - k_u) + s[1] * k_u) * (1 - k_v) + (s[2] * (1 - k_u) + s[3] * k_u) * k_v;
|
||||
}
|
||||
float Terrain::SampleHeight(const Vector4 &p) const
|
||||
{
|
||||
float u, v;
|
||||
if (!LocalToHeightmap(p, u, v))
|
||||
return 0;
|
||||
return SampleHeight(u, v);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Terrain::ComputeRenderableMinMax(MinMax &mm)
|
||||
{
|
||||
if (root_node)
|
||||
{
|
||||
OBB obb(root_node->minmax);
|
||||
obb.Transform(GetMatrix());
|
||||
obb.ComputeMinMax(mm);
|
||||
}
|
||||
}
|
||||
void Terrain::CullRenderablePrimitiveList(const Camera &view, const Camera &default_view, Stack <Render::Primitive *> &list, Context context, Patch *node)
|
||||
{
|
||||
MinMax &minmax = node->minmax;
|
||||
if (view.frustum.ClassifyMinMax(minmax, &GetMatrix()) == Frustum::Outside)
|
||||
return;
|
||||
|
||||
// Check error for the current level.
|
||||
Vector4 center = minmax.GetCenter() * GetMatrix();
|
||||
|
||||
float lod_d = (minmax.mx - minmax.mn).Len() * 1.5f,
|
||||
/*v_d = nVector::Dist(center, view.GetMatrix().GetRow(3)),*/
|
||||
d_d = Vector4::Dist(center, default_view.GetMatrix().GetRow(3));
|
||||
|
||||
float d = d_d;// Types::Min(v_d, d_d); // Ensure high-resolution close to the viewer and the light source.
|
||||
|
||||
if ((d > lod_d) || !node->children[0])
|
||||
list.Push(new Render::Primitive(node, this, 0));
|
||||
else
|
||||
for (int n = 0; n < 4; ++n)
|
||||
if (node->children[n])
|
||||
CullRenderablePrimitiveList(view, default_view, list, context, node->children[n]);
|
||||
}
|
||||
uint Terrain::GetRenderablePrimitiveList(const Camera &view, const Camera &default_view, Stack <Render::Primitive *> &list, Context context, bool nUnused(cull))
|
||||
{
|
||||
if (root_node)
|
||||
CullRenderablePrimitiveList(view, default_view, list, context, root_node);
|
||||
return node_count;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Geometry *Terrain::CreateNodeGeometry(const Patch *node)
|
||||
{
|
||||
Geometry *geometry = new Geometry;
|
||||
|
||||
int patch_w = node->w / node->decimation,
|
||||
patch_h = node->h / node->decimation;
|
||||
|
||||
// Create vertices.
|
||||
geometry->vtx.Allocate((patch_w + 1) * (patch_h + 1));
|
||||
|
||||
float step_w = width / heightmap_w, step_h = depth / heightmap_h;
|
||||
|
||||
Vector4 p(node->u * step_w * GetUnit(), 0, -node->v * step_h * GetUnit()),
|
||||
*pv = geometry->vtx;
|
||||
|
||||
for (int _v = node->v; _v <= (node->v + node->h); _v += node->decimation)
|
||||
{
|
||||
float *ph = &heightmap[_v * heightmap_w + node->u];
|
||||
|
||||
for (int _u = node->u; _u <= (node->u + node->w); _u += node->decimation)
|
||||
{
|
||||
pv->Set(p.x, ph[0], p.z);
|
||||
pv++;
|
||||
|
||||
ph += node->decimation;
|
||||
p.x += step_w * node->decimation * GetUnit();
|
||||
}
|
||||
p.x = node->u * step_w * GetUnit();
|
||||
p.z += step_h * node->decimation * GetUnit();
|
||||
}
|
||||
|
||||
// Create polygons.
|
||||
geometry->AllocatePolygon(patch_w * patch_h);
|
||||
for (uint n = 0; n < geometry->pol.GetCount(); ++n)
|
||||
{
|
||||
geometry->pol[n].vtx_count = 4;
|
||||
geometry->pol[n].material = 0;
|
||||
}
|
||||
geometry->AllocatePolygonBinding();
|
||||
|
||||
Polygon *polygon = geometry->pol;
|
||||
for (int _v = 0; _v < patch_h; ++_v)
|
||||
for (int _u = 0; _u < patch_w; ++_u)
|
||||
{
|
||||
polygon->vtx_count = 4;
|
||||
polygon->material = 0;
|
||||
|
||||
polygon->binding[0] = _v * (patch_w + 1) + _u;
|
||||
polygon->binding[1] = _v * (patch_w + 1) + _u + 1;
|
||||
polygon->binding[2] = (_v + 1) * (patch_w + 1) + _u + 1;
|
||||
polygon->binding[3] = (_v + 1) * (patch_w + 1) + _u;
|
||||
|
||||
polygon++;
|
||||
}
|
||||
|
||||
geometry->ComputeVertexNormal();
|
||||
return geometry;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Terrain::UpdatePatchMinMax(Patch *node) const
|
||||
{
|
||||
float *ph = &heightmap[node->v * GetHeightmapPitch() + node->u];
|
||||
float min_height, max_height;
|
||||
|
||||
min_height = max_height = ph[0];
|
||||
for (int _v = 0; _v < node->h; _v += node->decimation)
|
||||
{
|
||||
for (int _u = 0; _u < node->w; _u += node->decimation)
|
||||
{
|
||||
if (ph[_u] < min_height)
|
||||
min_height = ph[_u];
|
||||
if (ph[_u] > max_height)
|
||||
max_height = ph[_u];
|
||||
}
|
||||
ph += GetHeightmapPitch() * node->decimation;
|
||||
}
|
||||
|
||||
node->minmax.mn.Set(node->u * GetUnit() - width * 0.5f, min_height, (node->v + node->h) * GetUnit() - depth * 0.5f);
|
||||
node->minmax.mx.Set((node->u + node->w) * GetUnit() - width * 0.5f, max_height, node->v * GetUnit() - depth * 0.5f);
|
||||
}
|
||||
Patch *Terrain::BuildTerrainStaticQuadtree(int u, int v, int w, int h, int decimation, int tree_depth)
|
||||
{
|
||||
if (!decimation || (tree_depth == 8))
|
||||
return NULL;
|
||||
if (!w || !h)
|
||||
return NULL;
|
||||
|
||||
// Create a new node.
|
||||
Patch *node = new Patch;
|
||||
|
||||
node->terrain = this;
|
||||
node->u = u; node->v = v;
|
||||
node->w = w; node->h = h;
|
||||
node->decimation = decimation;
|
||||
|
||||
// Create node geometry (helper function, should normally be done on the fly by the renderer).
|
||||
// CreateNodeGeometry(node);
|
||||
|
||||
// Update patch minmax.
|
||||
UpdatePatchMinMax(node);
|
||||
|
||||
// Split to create children.
|
||||
int hw = w / 2, hh = h / 2;
|
||||
|
||||
node->children[0] = BuildTerrainStaticQuadtree(node->u, node->v, hw, hh, decimation / 2, tree_depth + 1);
|
||||
node->children[1] = BuildTerrainStaticQuadtree(node->u + hw, node->v, node->w - hw, hh, decimation / 2, tree_depth + 1);
|
||||
node->children[2] = BuildTerrainStaticQuadtree(node->u, node->v + hh, hw, node->h - hh, decimation / 2, tree_depth + 1);
|
||||
node->children[3] = BuildTerrainStaticQuadtree(node->u + hw, node->v + hh, node->w - hw, node->h - hh, decimation / 2, tree_depth + 1);
|
||||
|
||||
node_count++;
|
||||
return node;
|
||||
}
|
||||
Patch *Terrain::BuildQuadtree()
|
||||
{
|
||||
node_count = 0;
|
||||
return root_node = BuildTerrainStaticQuadtree(0, 0, heightmap_w, heightmap_h, heightmap_w / 64, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Terrain::UpdateQuadtreePatch(Patch *patch, iRect &update_rect)
|
||||
{
|
||||
iRect patch_rect = patch->GetRect();
|
||||
|
||||
if (!patch_rect.Intersect(update_rect))
|
||||
return;
|
||||
|
||||
UpdatePatchMinMax(patch);
|
||||
for (int n = 0; n < 4; ++n)
|
||||
if (patch->children[n])
|
||||
UpdateQuadtreePatch(patch->children[n], update_rect);
|
||||
}
|
||||
void Terrain::UpdateQuadtree(int u, int v, int w, int h)
|
||||
{
|
||||
iRect update_rect(u, v, u + w, v + h);
|
||||
UpdateQuadtreePatch(root_node, update_rect);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Terrain::ComputeNormals(int u, int v, int w, int h)
|
||||
{
|
||||
if (!attribmap)
|
||||
return;
|
||||
|
||||
if (!u && !v && !w && !h)
|
||||
{
|
||||
w = heightmap_w;
|
||||
h = heightmap_h;
|
||||
}
|
||||
|
||||
u = Types::Clamp(u, 0, heightmap_w);
|
||||
v = Types::Clamp(v, 0, heightmap_h);
|
||||
|
||||
if (u + w > heightmap_w) w = heightmap_w - u;
|
||||
if (v + h > heightmap_h) h = heightmap_h - v;
|
||||
|
||||
float *ph = &heightmap[v * GetHeightmapPitch() + u];
|
||||
Attrib *pv = &attribmap[v * GetHeightmapPitch() + u];
|
||||
|
||||
for (int _v = 0; _v < h; ++_v)
|
||||
{
|
||||
float *sh = ph;
|
||||
Attrib *sv = pv;
|
||||
for (int _u = 0; _u < w; ++_u)
|
||||
{
|
||||
Vector4 vu(unit, sh[1] - sh[0], 0),
|
||||
vv(0, sh[GetHeightmapPitch()] - sh[0], unit),
|
||||
n = vv.Normalized().Cross(vu.Normalized());
|
||||
|
||||
sv[0].nx = (char)(n.x * 127.f);
|
||||
sv[0].ny = (char)(n.y * 127.f);
|
||||
sv[0].nz = (char)(n.z * 127.f);
|
||||
++sh;
|
||||
++sv;
|
||||
}
|
||||
pv += GetHeightmapPitch();
|
||||
ph += GetHeightmapPitch();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Terrain::Allocate(uint w_res, uint h_res, float _unit)
|
||||
{
|
||||
Free();
|
||||
|
||||
// Global heightmap.
|
||||
heightmap_w = w_res;
|
||||
heightmap_h = h_res;
|
||||
|
||||
if (!attribmap.Allocate((heightmap_w + 1) * (heightmap_h + 1)) || !heightmap.Allocate((heightmap_w + 1) * (heightmap_h + 1)))
|
||||
__ERR__(__LOG_E__ << "Failed to allocate terrain heightmap.\n", false)
|
||||
|
||||
Memory::Set(&attribmap[0], 0, (heightmap_w + 1) * (heightmap_h + 1) * sizeof(Attrib));
|
||||
Memory::Set(&heightmap[0], 0, sizeof(float) * (heightmap_w + 1) * (heightmap_h + 1));
|
||||
|
||||
width = w_res * _unit;
|
||||
depth = h_res * _unit;
|
||||
unit = _unit;
|
||||
return true;
|
||||
}
|
||||
void Terrain::Free()
|
||||
{
|
||||
width = 0;
|
||||
depth = 0;
|
||||
attribmap.Free();
|
||||
heightmap.Free();
|
||||
heightmap_w = 0;
|
||||
heightmap_h = 0;
|
||||
node_count = 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Terrain::Raytrace(const Vector4 &w_s, const Vector4 &w_d, TraceResult &trace, float len)
|
||||
{
|
||||
trace.has_hit = false;
|
||||
|
||||
// Move ray to terrain space.
|
||||
Vector4 s = w_s * GetInverseMatrix(),
|
||||
d = w_d * GetRotationMatrix().Transposed();
|
||||
|
||||
// Clip ray against the terrain bounding box.
|
||||
float tmin, tmax;
|
||||
if (!root_node->minmax.IntersectRay(s, d, tmin, tmax))
|
||||
return false;
|
||||
|
||||
tmin -= 0.1f; tmax += 0.1f;
|
||||
Vector4 e = s + d * tmax;
|
||||
if (tmin > 0)
|
||||
s += d * tmin;
|
||||
|
||||
// Walk along the ray, looking for an intersection point.
|
||||
bool side = SampleHeight(s) < s.y;
|
||||
|
||||
Vector4 dt = e - s;
|
||||
float max_dist = dt.Len();
|
||||
|
||||
dt /= max_dist;
|
||||
dt *= 1.f; // minimum step size is 1 meters (so as not to spend too much time stepping)
|
||||
float dt_len = dt.Len();
|
||||
|
||||
float dist = 0.f;
|
||||
for (Vector4 p = s + dt; dist < max_dist; p += dt)
|
||||
{
|
||||
if ((SampleHeight(p) < p.y) != side)
|
||||
{
|
||||
#if 1
|
||||
// [EJ] Extra precision at terrain crossing boundary.
|
||||
s = p - dt;
|
||||
for (int n = 0; n < 24; ++n)
|
||||
{
|
||||
Vector4 m = (s + p) * 0.5f;
|
||||
if ((SampleHeight(m) < m.y) != side)
|
||||
p = m;
|
||||
else s = m;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Final hit.
|
||||
trace.has_hit = true;
|
||||
trace.w_i = p * GetMatrix();
|
||||
LocalToHeightmap(p, trace.uv.x, trace.uv.y);
|
||||
return true;
|
||||
}
|
||||
dist += dt_len;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Terrain::ApplyBlur(int pass_count)
|
||||
{
|
||||
if (heightmap)
|
||||
for (int p = 0; p < pass_count; ++p)
|
||||
{
|
||||
int n = (heightmap_w + 1) + 1;
|
||||
for (int v = 1; v < heightmap_h; ++v)
|
||||
{
|
||||
for (int u = 1; u < heightmap_w; ++u)
|
||||
{
|
||||
heightmap[n] = (heightmap[n] + heightmap[n - 1] + heightmap[n - (heightmap_w + 1)] + heightmap[n + 1] + heightmap[n + (heightmap_w + 1)]) / 5.f;
|
||||
++n;
|
||||
}
|
||||
n += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool Terrain::FromPicture(const char *path, int blur_pass_count)
|
||||
{
|
||||
// Initial values.
|
||||
Picture pic;
|
||||
PictureIO::Get().Load(pic, path);
|
||||
|
||||
int n = 0;
|
||||
for (int v = 0; v < (heightmap_h + 1); ++v)
|
||||
for (int u = 0; u < (heightmap_w + 1); ++u)
|
||||
heightmap[n++] = pic.SampleColor((float)u / heightmap_w, (float)v / heightmap_h).x * 16.f;
|
||||
|
||||
ApplyBlur(blur_pass_count);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Terrain::Attrib *Terrain::GetAttributesMapAt(int u, int v) const
|
||||
{ return &attribmap[(heightmap_w + 1) * v + u]; }
|
||||
Vector4 Terrain::GetNormalAt(int u, int v) const
|
||||
{
|
||||
Attrib *n = GetAttributesMapAt(u, v);
|
||||
return Vector4((float)n->nx / 127.f, (float)n->ny / 127.f, (float)n->nz / 127.f);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Terrain::Terrain()
|
||||
{
|
||||
width = 0;
|
||||
depth = 0;
|
||||
|
||||
heightmap_w = 0;
|
||||
heightmap_h = 0;
|
||||
node_count = 0;
|
||||
|
||||
for (uint n = 1; n < 4; ++n)
|
||||
layer[n].enabled = false;
|
||||
}
|
||||
Terrain::~Terrain()
|
||||
{
|
||||
Free();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
194
include/engine/core/terrain_nml.cpp
Normal file
194
include/engine/core/terrain_nml.cpp
Normal file
@ -0,0 +1,194 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/terrain.h"
|
||||
#include "core/engine.h"
|
||||
#include "core/embedded_resource_handler_interface.h"
|
||||
#include "picture/pict_io.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "platform.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
using GS::NML::Tag;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Terrain::LoadHeightmap(const char *uri)
|
||||
{
|
||||
if (Platform::Get().io->FileSize(uri) != (size_t)(4 * heightmap_w * heightmap_h))
|
||||
__ERR__(__LOG_W__ << "Corrupt heightmap data in " << uri << ", incorrect data size.\n", false)
|
||||
|
||||
AutoPtr <IO::Handle> h(Platform::Get().io->Open(uri));
|
||||
if (h.IsNull())
|
||||
return false;
|
||||
|
||||
float *ph = heightmap;
|
||||
for (int v = 0; v < heightmap_h; ++v)
|
||||
{
|
||||
h->Read(ph, heightmap_w * 4);
|
||||
ph += heightmap_w;
|
||||
ph[0] = ph[-1];
|
||||
ph++;
|
||||
}
|
||||
for (int u = 0; u < heightmap_w; ++u)
|
||||
{
|
||||
ph[0] = ph[-(heightmap_w + 1)];
|
||||
ph++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool Terrain::SaveHeightmap(const char *uri)
|
||||
{
|
||||
AutoPtr <IO::Handle> h(Platform::Get().io->Open(uri, IO::ModeWrite));
|
||||
if (h.IsNull())
|
||||
return false;
|
||||
|
||||
float *ph = heightmap;
|
||||
for (int v = 0; v < heightmap_h; ++v)
|
||||
{
|
||||
h->Write(ph, heightmap_w * 4);
|
||||
ph += heightmap_w + 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Terrain::FromMetaTag(Tag &tag)
|
||||
{
|
||||
if (tag.name != "Terrain")
|
||||
__ERR__(__LOG_E__ << "Could not parse terrain, incorrect root tag (" << tag.name << ").\n", false)
|
||||
|
||||
NMLTagForeach(pt, tag)
|
||||
{
|
||||
if (pt->name == "Item")
|
||||
Item::FromMetaTag(*pt);
|
||||
|
||||
else if (pt->name == "Width")
|
||||
width = pt->GetReal();
|
||||
else if (pt->name == "Height")
|
||||
depth = pt->GetReal();
|
||||
|
||||
else if (pt->name == "Material") // Legacy path, embedded material.
|
||||
IEmbeddedResourceHandler::Get()->ExtractEmbeddedMaterial(material, *pt, "terrain", 0);
|
||||
else if (pt->name == "MaterialName")
|
||||
material = pt->GetString();
|
||||
|
||||
else if (pt->name == "Layers")
|
||||
{
|
||||
NMLTagForeach(layer_tag, *pt)
|
||||
if (layer_tag->name == "Layer")
|
||||
{
|
||||
Tag *index_tag = layer_tag->GetTag("Index");
|
||||
Layer &l = layer[index_tag ? index_tag->GetInteger() : 0];
|
||||
|
||||
if (layer_tag->GetTag("Enabled;"))
|
||||
l.enabled = true;
|
||||
|
||||
if (Tag *map = layer_tag->GetTag("Diffuse;"))
|
||||
l.diffuse = map->GetString();
|
||||
if (Tag *map = layer_tag->GetTag("Normal;"))
|
||||
l.normal = map->GetString();
|
||||
if (Tag *map = layer_tag->GetTag("Specular;"))
|
||||
l.specular = map->GetString();
|
||||
if (Tag *map = layer_tag->GetTag("Self;"))
|
||||
l.self = map->GetString();
|
||||
|
||||
if (Tag *map = layer_tag->GetTag("UVAngle;"))
|
||||
l.angle = map->GetReal();
|
||||
if (Tag *map = layer_tag->GetTag("UVTiling;"))
|
||||
l.tiling = map->GetReal();
|
||||
}
|
||||
}
|
||||
else if (pt->name == "Shader")
|
||||
shader_path = pt->GetString();
|
||||
|
||||
else if (pt->name == "Blendmap")
|
||||
blendmap_path = pt->GetString();
|
||||
|
||||
else if (pt->name == "Heightmap")
|
||||
{
|
||||
Tag *w_tag = pt->GetTypedTag("Width;", Variant::VariantInteger),
|
||||
*h_tag = pt->GetTypedTag("Height;", Variant::VariantInteger),
|
||||
*d_tag = pt->GetTypedTag("Data;", Variant::VariantString),
|
||||
*r_tag = pt->GetTypedTag("Resolution;", Variant::VariantFloat);
|
||||
|
||||
if (w_tag && h_tag && d_tag && r_tag)
|
||||
if (Allocate(w_tag->GetInteger(), h_tag->GetInteger(), r_tag->GetReal()))
|
||||
heightmap_path = d_tag->GetString();
|
||||
}
|
||||
else
|
||||
__LOG_W__ << "Unknown tag '" << pt->name << "' in <Terrain>.\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tag *Terrain::LayerAsMetaTag(int index)
|
||||
{
|
||||
Layer &l = layer[index];
|
||||
|
||||
Tag *layer_tag = new Tag("Layer");
|
||||
if (!layer_tag)
|
||||
__ERR__(__LOG_E__ << "Failed to serialize terrain layer " << index << ".\n", NULL)
|
||||
|
||||
layer_tag->AddChild("Index", index);
|
||||
|
||||
if (l.enabled)
|
||||
layer_tag->AddChild(new Tag("Enabled"));
|
||||
|
||||
if (!l.diffuse.IsEmpty())
|
||||
layer_tag->AddChild("Diffuse", l.diffuse.toUtf8());
|
||||
if (!l.normal.IsEmpty())
|
||||
layer_tag->AddChild("Normal", l.normal.toUtf8());
|
||||
if (!l.specular.IsEmpty())
|
||||
layer_tag->AddChild("Specular", l.specular.toUtf8());
|
||||
if (!l.self.IsEmpty())
|
||||
layer_tag->AddChild("Self", l.self.toUtf8());
|
||||
|
||||
layer_tag->AddChild("UVAngle", l.angle);
|
||||
layer_tag->AddChild("UVTiling", l.tiling);
|
||||
|
||||
return layer_tag;
|
||||
}
|
||||
Tag *Terrain::AsMetaTag()
|
||||
{
|
||||
Tag *root = new Tag("Terrain");
|
||||
if (!root)
|
||||
__ERR__(__LOG_E__ << "Could not serialize terrain. Failed to create root tag.\n", NULL)
|
||||
|
||||
// Store item.
|
||||
root->AddChild(Item::AsMetaTag());
|
||||
|
||||
// Store terrain.
|
||||
root->AddChild("Width", width);
|
||||
root->AddChild("Height", depth);
|
||||
|
||||
if (!material.IsEmpty())
|
||||
root->AddChild("MaterialName", material.c_str());
|
||||
|
||||
// Store layers.
|
||||
if (Tag *layers_tag = root->AddChild("Layers"))
|
||||
for (int n = 0; n < 4; ++n)
|
||||
layers_tag->AddChild(LayerAsMetaTag(n));
|
||||
|
||||
root->AddChild("Blendmap", blendmap_path.c_str());
|
||||
root->AddChild("Shader", shader_path.c_str());
|
||||
|
||||
// Store heightmap.
|
||||
if (Tag *height_tag = root->AddChild("Heightmap"))
|
||||
{
|
||||
height_tag->AddChild("Width", heightmap_w);
|
||||
height_tag->AddChild("Height", heightmap_h);
|
||||
height_tag->AddChild("Resolution", unit);
|
||||
// Note: The terrain heightmap is saved by the editor.
|
||||
height_tag->AddChild("Data", heightmap_path.c_str());
|
||||
}
|
||||
return root;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
166
include/engine/core/terrain_shader_generator.cpp
Normal file
166
include/engine/core/terrain_shader_generator.cpp
Normal file
@ -0,0 +1,166 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/terrain_shader_generator.h"
|
||||
#include "core/shader.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void DeclareStaticTexture(Shader &shader, const char *var, const char *name)
|
||||
{
|
||||
if (name)
|
||||
if (ShaderInput *input = shader.DeclareInput(var, ShaderInput::DataTexture2D, ShaderInput::Texture2D, ShaderInput::Uniform, ShaderInput::Pixel))
|
||||
input->parm_t = name;
|
||||
}
|
||||
bool TerrainShaderGenerator::GenerateShader(Shader &shader, const char *t_blend, const Terrain::Layer layers[4])
|
||||
{
|
||||
shader.Clear();
|
||||
|
||||
// Get the number of layers.
|
||||
uint layer_count = 0;
|
||||
for (uint n = 0; n < 4; ++n)
|
||||
if (layers[n].enabled)
|
||||
++layer_count;
|
||||
|
||||
if (layer_count == 0)
|
||||
return false;
|
||||
|
||||
// Declare default inputs.
|
||||
shader.DeclareInput("a_position", ShaderInput::Vector3, ShaderInput::Position, ShaderInput::Attribute, ShaderInput::Vertex);
|
||||
shader.DeclareInput("a_normal", ShaderInput::Vector3, ShaderInput::Normal, ShaderInput::Attribute, ShaderInput::Vertex);
|
||||
shader.DeclareInput("a_uv", ShaderInput::Vector2, ShaderInput::UV0, ShaderInput::Attribute, ShaderInput::Vertex);
|
||||
|
||||
shader.DeclareVarying("v_normal", "vec3");
|
||||
shader.vertex << "v_normal = a_normal;\n";
|
||||
|
||||
// If using more than one layer, declare the blend map.
|
||||
if (layer_count > 1)
|
||||
{
|
||||
DeclareStaticTexture(shader, "t_blend", t_blend);
|
||||
shader.DeclareVarying("v_uv", "vec2");
|
||||
shader.vertex << "v_uv = a_uv;\n";
|
||||
shader.pixel << "vec4 blend = texture2D(t_blend, v_uv);\n";
|
||||
}
|
||||
|
||||
// Declare texture inputs.
|
||||
bool use_tangent = false;
|
||||
for (uint n = 0; n < 4; ++n)
|
||||
{
|
||||
if (!layers[n].enabled)
|
||||
continue;
|
||||
|
||||
if (!layers[n].normal.IsEmpty())
|
||||
{
|
||||
DeclareStaticTexture(shader, String::Format("t_norm_%d", n), layers[n].normal);
|
||||
use_tangent = true;
|
||||
}
|
||||
|
||||
DeclareStaticTexture(shader, String::Format("t_diff_%d", n), layers[n].diffuse);
|
||||
DeclareStaticTexture(shader, String::Format("t_spec_%d", n), layers[n].specular);
|
||||
DeclareStaticTexture(shader, String::Format("t_self_%d", n), layers[n].self);
|
||||
}
|
||||
|
||||
// Build tangent matrix.
|
||||
if (use_tangent)
|
||||
{
|
||||
shader.DeclareVarying("v_tangent", "vec3");
|
||||
shader.DeclareVarying("v_bitangent", "vec3");
|
||||
|
||||
// Compute tangent & bitangent.
|
||||
shader.vertex <<
|
||||
"\
|
||||
v_tangent = normalize(cross(a_normal, vec3(0, 0, 1)));\n\
|
||||
v_bitangent = normalize(cross(v_tangent, a_normal));\n\
|
||||
";
|
||||
|
||||
// Build tangent frame.
|
||||
shader.pixel << "mat3 tangent_frame = _build_mat3(v_tangent, v_bitangent, v_normal);\n";
|
||||
}
|
||||
|
||||
// Declare layers.
|
||||
for (uint n = 0; n < 4; ++n)
|
||||
if (layers[n].enabled)
|
||||
{
|
||||
// Vertex
|
||||
ShaderVarying *uv_varying = shader.DeclareVarying(String("v_uv_layer_") << n, "vec2");
|
||||
shader.vertex << String::Format("%s = a_uv * %.2f;\n", uv_varying->name.c_str(), layers[n].tiling);
|
||||
|
||||
// Pixel
|
||||
if (layers[n].normal.IsEmpty())
|
||||
shader.pixel << String::Format("vec3 norm_%d = v_normal;\n", n);
|
||||
else
|
||||
{
|
||||
shader.pixel << String::Format("vec3 normal_map_%d = texture2D(t_norm_%d, %s).xyz;\n", n, n, uv_varying->name.c_str());
|
||||
shader.pixel << String::Format("normal_map_%d = normalize(vec3(normal_map_%d.xy * 2.0 - 1.0, normal_map_%d.z));\n", n, n, n);
|
||||
shader.pixel << String::Format("vec3 norm_%d = n_mtx_mul(tangent_frame, normal_map_%d);\n", n, n);
|
||||
}
|
||||
|
||||
if (layers[n].diffuse.IsEmpty())
|
||||
shader.pixel << String::Format("vec4 diff_%d = vec4(1.0, 1.0, 1.0, 1.0);\n", n);
|
||||
else shader.pixel << String::Format("vec4 diff_%d = texture2D(t_diff_%d, %s);\n", n, n, uv_varying->name.c_str());
|
||||
|
||||
if (layers[n].specular.IsEmpty())
|
||||
shader.pixel << String::Format("vec4 spec_%d = vec4(1.0, 1.0, 1.0, 1.0);\n", n);
|
||||
else shader.pixel << String::Format("vec4 spec_%d = texture2D(t_spec_%d, %s);\n", n, n, uv_varying->name.c_str());
|
||||
|
||||
if (layers[n].self.IsEmpty())
|
||||
shader.pixel << String::Format("vec4 self_%d = vec4(0.0, 0.0, 0.0, 1.0);\n", n);
|
||||
else shader.pixel << String::Format("vec4 self_%d = texture2D(t_self_%d, %s);\n", n, n, uv_varying->name.c_str());
|
||||
}
|
||||
|
||||
// Build the layer mixing code.
|
||||
if (layer_count == 1)
|
||||
{
|
||||
uint n = 0;
|
||||
for ( ; n < 4; ++n)
|
||||
if (layers[n].enabled)
|
||||
break;
|
||||
|
||||
shader.pixel << "%normal% = " << String::Format("norm_%d", n) << ";\n";
|
||||
shader.pixel << "%diffuse% = " << String::Format("diff_%d", n) << ";\n";
|
||||
shader.pixel << "%specular% = " << String::Format("diff_%d", n) << ";\n";
|
||||
shader.pixel << "%constant% = " << String::Format("self_%d", n) << ";\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
static const char *layer_comp[4] = { "x", "y", "z", "w" };
|
||||
|
||||
String norm, diff, spec, self;
|
||||
|
||||
bool first_assign = true;
|
||||
for (uint n = 0; n < 4; ++n)
|
||||
{
|
||||
if (!layers[n].enabled)
|
||||
continue;
|
||||
|
||||
if (first_assign)
|
||||
{
|
||||
norm << "%normal% = " << String::Format("norm_%d * blend.%s", n, layer_comp[n]);
|
||||
diff << "%diffuse% = " << String::Format("diff_%d * blend.%s", n, layer_comp[n]);
|
||||
spec << "%specular% = " << String::Format("spec_%d * blend.%s", n, layer_comp[n]);
|
||||
self << "%constant% = " << String::Format("self_%d * blend.%s", n, layer_comp[n]);
|
||||
|
||||
first_assign = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
norm << String::Format(" + norm_%d * blend.%s", n, layer_comp[n]);
|
||||
diff << String::Format(" + diff_%d * blend.%s", n, layer_comp[n]);
|
||||
spec << String::Format(" + spec_%d * blend.%s", n, layer_comp[n]);
|
||||
self << String::Format(" + self_%d * blend.%s", n, layer_comp[n]);
|
||||
}
|
||||
}
|
||||
|
||||
shader.pixel << norm << ";\n";
|
||||
shader.pixel << diff << ";\n";
|
||||
shader.pixel << spec << ";\n";
|
||||
shader.pixel << self << ";\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
141
include/engine/core/texture_parm.cpp
Normal file
141
include/engine/core/texture_parm.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/texture_parm.h"
|
||||
#include "core/render_data.h"
|
||||
#include "picture/pict.h"
|
||||
#include "metafile/nml.h"
|
||||
#include "metafile/nml_object.h"
|
||||
#include "reflection/c_refl.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Render;
|
||||
using namespace GS::Reflection;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char *TextureParm::GetSwizzleFlags(Swizzle swizzle)
|
||||
{
|
||||
static char RGBA[4] = { 0, 1, 2, 3 };
|
||||
static char BGRA[4] = { 2, 1, 0, 3 };
|
||||
static char ARGB[4] = { 3, 0, 1, 2 };
|
||||
static char ABGR[4] = { 3, 2, 1, 0 };
|
||||
static char XYZ[4] = { 0, 1, 2, 3 };
|
||||
static char XZY[4] = { 0, 2, 1, 3 };
|
||||
static char YXZ[4] = { 1, 0, 2, 3 };
|
||||
static char YZX[4] = { 1, 2, 0, 3 };
|
||||
static char ZXY[4] = { 2, 0, 1, 3 };
|
||||
static char ZYX[4] = { 2, 1, 0, 3 };
|
||||
|
||||
switch (swizzle)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
|
||||
case SwizzleRGBA: return RGBA;
|
||||
case SwizzleBGRA: return BGRA;
|
||||
case SwizzleARGB: return ARGB;
|
||||
case SwizzleABGR: return ABGR;
|
||||
case SwizzleXYZ: return XYZ;
|
||||
case SwizzleXZY: return XZY;
|
||||
case SwizzleYXZ: return YXZ;
|
||||
case SwizzleYZX: return YZX;
|
||||
case SwizzleZXY: return ZXY;
|
||||
case SwizzleZYX: return ZYX;
|
||||
}
|
||||
return RGBA;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void TextureParm::Apply(Picture &p) const
|
||||
{
|
||||
const char *f = GetSwizzleFlags(swizzle);
|
||||
p.Swizzle(f[0], f[1], f[2], f[3]);
|
||||
p.Negative(invert[0], invert[1], invert[2], invert[3]);
|
||||
p.Flip(flip[0], flip[1]);
|
||||
}
|
||||
void TextureParm::Apply(Texture &t) const
|
||||
{
|
||||
t.SetFiltering(filtering);
|
||||
t.SetAnisotropy(anisotropy);
|
||||
t.SetWrapping(wrap_u, wrap_v);
|
||||
|
||||
// Note: LOD bias is done per texture stage.
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Enum::Dict TextureParm::filtering_dict[] =
|
||||
{
|
||||
{ TextureParm::FilterDefault, "Default" },
|
||||
{ TextureParm::FilterNearest, "Nearest" },
|
||||
{ TextureParm::FilterBilinear, "Bilinear" },
|
||||
{ TextureParm::FilterTrilinear, "Trilinear" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
Enum::Dict TextureParm::anisotropic_dict[] =
|
||||
{
|
||||
{ TextureParm::AnisotropyDefault, "Default" },
|
||||
{ TextureParm::AnisotropyNone, "None" },
|
||||
{ TextureParm::Anisotropy2x, "2x" },
|
||||
{ TextureParm::Anisotropy4x, "4x" },
|
||||
{ TextureParm::Anisotropy8x, "8x" },
|
||||
{ TextureParm::Anisotropy16x, "16x" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
Enum::Dict TextureParm::wrap_dict[] =
|
||||
{
|
||||
{ TextureParm::WrapDefault, "Default" },
|
||||
{ TextureParm::WrapRepeat, "Repeat" },
|
||||
{ TextureParm::WrapClamp, "Clamp" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
Enum::Dict TextureParm::swizzle_dict[] =
|
||||
{
|
||||
{ TextureParm::SwizzleRGBA, "RGBA" },
|
||||
{ TextureParm::SwizzleBGRA, "BGRA" },
|
||||
{ TextureParm::SwizzleARGB, "ARGB" },
|
||||
{ TextureParm::SwizzleABGR, "ABGR" },
|
||||
{ TextureParm::SwizzleXYZ, "XYZ" },
|
||||
{ TextureParm::SwizzleXZY, "XZY" },
|
||||
{ TextureParm::SwizzleYXZ, "YXZ" },
|
||||
{ TextureParm::SwizzleYZX, "YZX" },
|
||||
{ TextureParm::SwizzleZXY, "ZXY" },
|
||||
{ TextureParm::SwizzleZYX, "ZYX" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
Property TextureParm::serializable[] =
|
||||
{
|
||||
{ Property::EnumProp, "WrapU", offsetof(TextureParm, wrap_u), wrap_dict },
|
||||
{ Property::EnumProp, "WrapV", offsetof(TextureParm, wrap_v), wrap_dict },
|
||||
{ Property::EnumProp, "Filtering", offsetof(TextureParm, filtering), filtering_dict },
|
||||
{ Property::EnumProp, "Anisotropy", offsetof(TextureParm, anisotropy), anisotropic_dict },
|
||||
{ Property::EnumProp, "Swizzle", offsetof(TextureParm, swizzle), swizzle_dict },
|
||||
{ Property::BoolProp, "InvertR", offsetof(TextureParm, invert[0]), NULL },
|
||||
{ Property::BoolProp, "InvertG", offsetof(TextureParm, invert[1]), NULL },
|
||||
{ Property::BoolProp, "InvertB", offsetof(TextureParm, invert[2]), NULL },
|
||||
{ Property::BoolProp, "InvertA", offsetof(TextureParm, invert[3]), NULL },
|
||||
{ Property::BoolProp, "FlipU", offsetof(TextureParm, flip[0]), NULL },
|
||||
{ Property::BoolProp, "FlipV", offsetof(TextureParm, flip[1]), NULL },
|
||||
{ Property::FloatProp, "LODBias", offsetof(TextureParm, lod_bias), NULL },
|
||||
|
||||
{ Property::InvalidProp, 0, 0 }
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
String TextureParm::GetParmFileName(const char *uri)
|
||||
{ return String::Format("%s.parm", uri); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool TextureParm::FromMetaTag(NML::Tag &t)
|
||||
{ return t.name == "TextureParm" ? GenericObjectFromMetaTag(t, this, serializable) : false; }
|
||||
NML::Tag *TextureParm::AsMetaTag() const
|
||||
{ return NML::GenericObjectToMetaTag(new NML::Tag("TextureParm"), this, serializable); }
|
||||
//------------------------------------------------------------------------------
|
||||
212
include/engine/core/triangle_list_optimizer.cpp
Normal file
212
include/engine/core/triangle_list_optimizer.cpp
Normal file
@ -0,0 +1,212 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
Original implementation of the following publication:
|
||||
|
||||
Linear-Speed Vertex Cache Optimization
|
||||
Tom Forsyth, RAD Game Tools: tom.forsyth@eelpi.gotdns.org
|
||||
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include "core/triangle_list.h"
|
||||
#include "container/nlist.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
struct VertexScore
|
||||
{
|
||||
uint triangle_to_draw;
|
||||
float score;
|
||||
int position; ///< Position in cache.
|
||||
};
|
||||
struct TriangleData
|
||||
{
|
||||
bool processed;
|
||||
};
|
||||
|
||||
#define MaxSizeVertexCache 32
|
||||
|
||||
#define FindVertexScore_CacheDecayPower 1.5f
|
||||
#define FindVertexScore_LastTriScore 0.75f
|
||||
#define FindVertexScore_ValenceBoostScale 2.0f
|
||||
#define FindVertexScore_ValenceBoostPower 0.5f
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void ComputeVertexScore(VertexScore *vertex)
|
||||
{
|
||||
// Unneeded vertex.
|
||||
if (!vertex->triangle_to_draw)
|
||||
{
|
||||
vertex->score = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
float score = 0;
|
||||
|
||||
if (vertex->position < 0)
|
||||
; // Not in cache, no score.
|
||||
else
|
||||
{
|
||||
/*
|
||||
This vertex was used in the last triangle,
|
||||
so it has a fixed score, whichever of the three
|
||||
it's in. Otherwise, you can get very different
|
||||
answers depending on whether you add the triangle
|
||||
1,2,3 or 3,1,2 - which is silly.
|
||||
*/
|
||||
if (vertex->position < 3)
|
||||
score = FindVertexScore_LastTriScore;
|
||||
else score = std::pow(1.f - (vertex->position - 3) / (MaxSizeVertexCache - 3), FindVertexScore_CacheDecayPower);
|
||||
}
|
||||
|
||||
/*
|
||||
Bonus points for having a low number of triangles still to
|
||||
use the vertex, so we get rid of lone vertice quickly.
|
||||
*/
|
||||
float boost = std::pow((float)vertex->triangle_to_draw, -FindVertexScore_ValenceBoostPower);
|
||||
vertex->score = score + FindVertexScore_ValenceBoostScale * boost;
|
||||
}
|
||||
static float ComputeTriangleScore(uint n, uint *idx, const VertexScore *vertex)
|
||||
{ return vertex[idx[n * 3]].score + vertex[idx[n * 3 + 1]].score + vertex[idx[n * 3 + 2]].score; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void CacheVertex(uint idx, VertexScore *vertex, Array <int> &LRUCache)
|
||||
{
|
||||
// Seek for vertex in cache.
|
||||
int m;
|
||||
for (m = 0; m < MaxSizeVertexCache; ++m)
|
||||
if (LRUCache[m] == (int)idx)
|
||||
break;
|
||||
|
||||
// Handle corner case where a vertice is pushed out of cache.
|
||||
if (m == MaxSizeVertexCache)
|
||||
{
|
||||
m--;
|
||||
if (LRUCache[m] != -1)
|
||||
{
|
||||
vertex[LRUCache[m]].position = -1;
|
||||
ComputeVertexScore(&vertex[LRUCache[m]]);
|
||||
}
|
||||
}
|
||||
|
||||
// Shift cache, insert vertex to top.
|
||||
for (int t = (m - 1); t >= 0; --t)
|
||||
if (LRUCache[t] != -1)
|
||||
{
|
||||
vertex[LRUCache[t]].position++;
|
||||
LRUCache[t + 1] = LRUCache[t];
|
||||
}
|
||||
|
||||
LRUCache[0] = idx;
|
||||
vertex[idx].position = 0;
|
||||
}
|
||||
void Trilist::Optimize(const List <Trilist *> &list, int /*cache_size*/)
|
||||
{
|
||||
__LOG__ << "Optimizing triangle list...\n";
|
||||
|
||||
uint total_vtx_count = 0, total_tri_count = 0, n;
|
||||
|
||||
ListForeachPtr(Trilist *, t, list)
|
||||
{
|
||||
if (t->vtx.GetCount() > total_vtx_count)
|
||||
total_vtx_count = t->vtx.GetCount();
|
||||
if (t->GetTriangleCount() > total_tri_count)
|
||||
total_tri_count = t->GetTriangleCount();
|
||||
}
|
||||
|
||||
Array <VertexScore> vertex(total_vtx_count);
|
||||
Array <TriangleData> triangle(total_tri_count);
|
||||
|
||||
if (!vertex || !triangle)
|
||||
__ERRRAW__(__LOG_E__ << "Failed to allocate vertex optimization array.\n");
|
||||
|
||||
// Process each list.
|
||||
Array <int> LRUCache(MaxSizeVertexCache);
|
||||
Array <uint> ordered_idx;
|
||||
|
||||
ListForeachPtr(Trilist *, t, list)
|
||||
{
|
||||
// Reset scores.
|
||||
for (n = 0; n < total_vtx_count; ++n)
|
||||
{
|
||||
vertex[n].triangle_to_draw = 0;
|
||||
vertex[n].score = 0;
|
||||
}
|
||||
for (n = 0; n < total_tri_count; ++n)
|
||||
triangle[n].processed = false;
|
||||
for (n = 0; n < MaxSizeVertexCache; ++n)
|
||||
LRUCache[n] = -1;
|
||||
|
||||
// Compute triangle count per vertex index.
|
||||
for (n = 0; n < t->GetTriangleCount(); ++n)
|
||||
for (uint m = 0; m < 3; ++m)
|
||||
vertex[t->idx[n * 3 + m]].triangle_to_draw++;
|
||||
|
||||
// Start optimization cycles.
|
||||
if (!ordered_idx.Allocate(t->idx.GetCount()))
|
||||
{
|
||||
__LOG_W__ << "Failed to allocate triangle list ordered index list, skipping.\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint processed_tri_count = 0; processed_tri_count < t->GetTriangleCount(); ++processed_tri_count)
|
||||
{
|
||||
int best_score_triangle_index = -1, m;
|
||||
float best_score_triangle = -1;
|
||||
|
||||
/// Pick up best triangle.
|
||||
for (n = 0; n < t->GetTriangleCount(); ++n)
|
||||
{
|
||||
if (triangle[n].processed)
|
||||
continue;
|
||||
|
||||
float score = ComputeTriangleScore(n, t->idx, vertex);
|
||||
|
||||
if (score > best_score_triangle)
|
||||
{
|
||||
best_score_triangle_index = n;
|
||||
best_score_triangle = score;
|
||||
}
|
||||
}
|
||||
|
||||
// Failsafe match.
|
||||
if (best_score_triangle_index == -1)
|
||||
{
|
||||
for (n = 0; n < t->GetTriangleCount(); ++n)
|
||||
if (!triangle[n].processed)
|
||||
break;
|
||||
best_score_triangle_index = n;
|
||||
}
|
||||
|
||||
// Add best triangle to the new list.
|
||||
for (m = 0; m < 3; ++m)
|
||||
{
|
||||
uint idx = t->idx[best_score_triangle_index * 3 + m];
|
||||
|
||||
ordered_idx[processed_tri_count * 3 + m] = idx;
|
||||
CacheVertex(idx, vertex, LRUCache);
|
||||
vertex[idx].triangle_to_draw--;
|
||||
}
|
||||
|
||||
// Update cached vertices score.
|
||||
for (m = 0; m < MaxSizeVertexCache; ++m)
|
||||
if (LRUCache[m] != -1)
|
||||
ComputeVertexScore(&vertex[LRUCache[m]]);
|
||||
|
||||
triangle[best_score_triangle_index].processed = true;
|
||||
}
|
||||
|
||||
// Commit optimized index list.
|
||||
t->idx.Transfer(ordered_idx);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
58
include/engine/core/trigger.cpp
Normal file
58
include/engine/core/trigger.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/trigger.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Trigger::DropItem(Item *item) {
|
||||
ListForeachPtr(ItemInTrigger *, ti, items_in_trigger)
|
||||
if (ti->item == item) {
|
||||
items_in_trigger.Remove(ti);
|
||||
delete ti;
|
||||
}
|
||||
}
|
||||
void Trigger::MarkItem(Item *item)
|
||||
{
|
||||
ListForeachPtr(ItemInTrigger *, ti, items_in_trigger)
|
||||
if (ti->item == item)
|
||||
{
|
||||
ti->inside = true;
|
||||
return;
|
||||
}
|
||||
|
||||
items_in_trigger.Add(new ItemInTrigger(item));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Trigger::ReadyItemList()
|
||||
{
|
||||
ListForeachPtr(ItemInTrigger *, ti, items_in_trigger)
|
||||
ti->inside = false;
|
||||
}
|
||||
void Trigger::PurgeItemList()
|
||||
{
|
||||
ListForeachPtr(ItemInTrigger *, ti, items_in_trigger)
|
||||
if (!ti->inside)
|
||||
DropItem(ti->item);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool Trigger::IsInside(const Vector4 &p)
|
||||
{
|
||||
Vector4 local = p * GetInverseMatrix();
|
||||
return (local.x > -0.5) && (local.x < 0.5) && (local.y > -0.5) && (local.y < 0.5) && (local.z > -0.5) && (local.z < 0.5);
|
||||
}
|
||||
|
||||
Trigger::~Trigger()
|
||||
{
|
||||
ListForeachPtr(ItemInTrigger *, ti, items_in_trigger)
|
||||
delete ti;
|
||||
}
|
||||
148
include/engine/gpu/gpu_core_shader.cpp
Normal file
148
include/engine/gpu/gpu_core_shader.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "metafile/nml_object.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::GPU;
|
||||
using GS::NML::Tag;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tag *Renderer::GetPCFShaderTag() const
|
||||
{
|
||||
const char *pcf_tag[] = { "PCF:3x3GaussianDithered;", "PCF:3x3;", "PCF:2x2;", "PCF:1x1;" };
|
||||
int pcf_q = GS::Types::Clamp(registry.GetInteger("ShadowMapping:PCF:Quality", 1), 0, 3);
|
||||
return shader_dict.GetTag(pcf_tag[pcf_q]);
|
||||
}
|
||||
Tag *Renderer::GetPSMShaderTag() const
|
||||
{ return shader_dict.GetTag("PSM;"); }
|
||||
Tag *Renderer::GetPSSMShaderTag() const
|
||||
{
|
||||
const char *pssm_tag[] = { "PSSM:2Split;", "PSSM:3Split;", "PSSM:4Split;" };
|
||||
int split_count = GS::Types::Clamp(registry.GetInteger("ShadowMapping:PSSM:Split", 3), 2, 4);
|
||||
return shader_dict.GetTag(pssm_tag[split_count - 2]);
|
||||
}
|
||||
void Renderer::MarshallCoreShader(GS::String &source)
|
||||
{
|
||||
if (Tag *t = shader_dict.GetTag("UnpackGBuffer:Float;"))
|
||||
source.Replace("#(UnpackNormalDepth)", t->GetString());
|
||||
if (Tag *t = GetPCFShaderTag())
|
||||
source.Replace("#(ComputePCF)", t->GetString());
|
||||
if (Tag *t = GetPSSMShaderTag())
|
||||
source.Replace("#(DispatchPSSM)", t->GetString());
|
||||
}
|
||||
Shader *Renderer::SetupCoreShader(const char *name)
|
||||
{
|
||||
Core::Shader shader;
|
||||
if (!NML::LoadFromFile(shader, name))
|
||||
return NULL;
|
||||
|
||||
shader.name = name;
|
||||
|
||||
// Note: Marshalling is only done on core shaders.
|
||||
MarshallCoreShader(shader.vertex);
|
||||
MarshallCoreShader(shader.pixel);
|
||||
|
||||
AutoPtr <Shader> gpu_shader((Shader *)NewShader());
|
||||
if (gpu_shader.IsNull() || !gpu_shader->Create(*core_resource_factory, shader))
|
||||
return NULL;
|
||||
return gpu_shader.Detach();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::LoadCoreShaders(bool support_3d)
|
||||
{
|
||||
__LOG_FUNC__
|
||||
|
||||
// Load the shader dictionaries.
|
||||
if (!NML::Parser::Load("@core/shaders/gpu/dict.txt", shader_dict))
|
||||
__ERR__(__LOG_E__ << "Shader dictionnary missing.\n", false)
|
||||
if (!NML::Parser::Load("@core/shaders/gpu/dict_material.txt", material_dict))
|
||||
__ERR__(__LOG_E__ << "Material dictionnary missing.\n", false)
|
||||
|
||||
// Load core shaders.
|
||||
single_texture_color_program = SetupCoreShader("@core/shaders/gpu/single_texture_color.nsa");
|
||||
single_texture_program = SetupCoreShader("@core/shaders/gpu/single_texture.nsa");
|
||||
single_color_program = SetupCoreShader("@core/shaders/gpu/single_color.nsa");
|
||||
simple_program = SetupCoreShader("@core/shaders/gpu/simple.nsa");
|
||||
|
||||
if (support_3d)
|
||||
{
|
||||
single_texture_cutoff_program = SetupCoreShader("@core/shaders/gpu/single_texture_cutoff.nsa");
|
||||
single_texture_fx_program = SetupCoreShader("@core/shaders/gpu/single_texture_fx.nsa");
|
||||
tone_mapping_program = SetupCoreShader("@core/shaders/gpu/tone_mapping.nsa");
|
||||
|
||||
ambient_program = SetupCoreShader("@core/shaders/gpu/ambient.nsa");
|
||||
|
||||
spotlight_program = SetupCoreShader("@core/shaders/gpu/deferred/spotlight.nsa");
|
||||
spotlight_shadow_program = SetupCoreShader("@core/shaders/gpu/deferred/spotlight_shadow.nsa");
|
||||
pointlight_program = SetupCoreShader("@core/shaders/gpu/deferred/pointlight.nsa");
|
||||
pointlight_shadow_program = SetupCoreShader("@core/shaders/gpu/deferred/pointlight_shadow.nsa");
|
||||
linearlight_program = SetupCoreShader("@core/shaders/gpu/deferred/linearlight.nsa");
|
||||
linearlight_shadow_program = SetupCoreShader("@core/shaders/gpu/deferred/linearlight_shadow.nsa");
|
||||
ds_fog_program = SetupCoreShader("@core/shaders/gpu/deferred/ds_fog.nsa");
|
||||
|
||||
fx_blur_program = SetupCoreShader("@core/shaders/gpu/fx_blur.nsa");
|
||||
noise_program = SetupCoreShader("@core/shaders/gpu/noise.nsa");
|
||||
sharpen_program = SetupCoreShader("@core/shaders/gpu/sharpen.nsa");
|
||||
hsl_program = SetupCoreShader("@core/shaders/gpu/hsl.nsa");
|
||||
chromatic_dispersion_program = SetupCoreShader("@core/shaders/gpu/chromatic_dispersion.nsa");
|
||||
|
||||
ssaa_program = SetupCoreShader("@core/shaders/gpu/ssaa.nsa");
|
||||
ssao_program = SetupCoreShader("@core/shaders/gpu/ssao.nsa");
|
||||
ssao_blur_program = SetupCoreShader("@core/shaders/gpu/ssao_blur.nsa");
|
||||
|
||||
motion_blur_program = SetupCoreShader("@core/shaders/gpu/motion_blur.nsa");
|
||||
radial_blur_program = SetupCoreShader("@core/shaders/gpu/radial_blur.nsa");
|
||||
|
||||
resolve_msaa_depth_program = SetupCoreShader("@core/shaders/gpu/resolve_msaa_depth.nsa");
|
||||
|
||||
skybox_program = SetupCoreShader("@core/shaders/gpu/skybox.nsa");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void Renderer::UnloadCoreShaders()
|
||||
{
|
||||
single_texture_color_program = NULL;
|
||||
single_texture_program = NULL;
|
||||
single_color_program = NULL;
|
||||
simple_program = NULL;
|
||||
|
||||
single_texture_cutoff_program = NULL;
|
||||
single_texture_fx_program = NULL;
|
||||
tone_mapping_program = NULL;
|
||||
|
||||
ambient_program = NULL;
|
||||
|
||||
spotlight_program = NULL;
|
||||
spotlight_shadow_program = NULL;
|
||||
pointlight_program = NULL;
|
||||
pointlight_shadow_program = NULL;
|
||||
linearlight_program = NULL;
|
||||
linearlight_shadow_program = NULL;
|
||||
ds_fog_program = NULL;
|
||||
|
||||
fx_blur_program = NULL;
|
||||
noise_program = NULL;
|
||||
sharpen_program = NULL;
|
||||
hsl_program = NULL;
|
||||
chromatic_dispersion_program = NULL;
|
||||
|
||||
ssaa_program = NULL;
|
||||
ssao_program = NULL;
|
||||
ssao_blur_program = NULL;
|
||||
|
||||
motion_blur_program = NULL;
|
||||
radial_blur_program = NULL;
|
||||
|
||||
resolve_msaa_depth_program = NULL;
|
||||
|
||||
skybox_program = NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
220
include/engine/gpu/gpu_display_list.cpp
Normal file
220
include/engine/gpu/gpu_display_list.cpp
Normal file
@ -0,0 +1,220 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_display_list.h"
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "core/triangle_list.h"
|
||||
#include "log/log.h"
|
||||
|
||||
#define __USE_VBO__ 1
|
||||
|
||||
using namespace GS::GPU;
|
||||
|
||||
namespace GS {
|
||||
namespace Core {
|
||||
bool ComputeVertexArrayMinMax(const Array <Vector4> &, MinMax &, const Matrix4 * = 0);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool DisplayList::Create(GS::Core::Trilist *trilist, GS::Render::Material *list_material)
|
||||
{
|
||||
// Setup skin data.
|
||||
bone.Clone(trilist->bone);
|
||||
|
||||
// Setup triangle list indices.
|
||||
index_count = trilist->idx.GetCount();
|
||||
|
||||
#if __USE_VBO__
|
||||
|
||||
if (!idx)
|
||||
idx = renderer.NewVBO();
|
||||
if (!idx || !idx->Create(index_count * sizeof(ushort), VBO::Index, VBO::Static))
|
||||
return false;
|
||||
|
||||
#endif
|
||||
|
||||
if (!idx_map.Allocate(index_count))
|
||||
return false;
|
||||
|
||||
for (size_t n = 0; n <index_count; ++n)
|
||||
idx_map[(int)n] = (ushort)trilist->idx[(int)n];
|
||||
|
||||
#if __USE_VBO__
|
||||
|
||||
idx->Update(idx_map, 0, idx_map.GetSize());
|
||||
idx_map.Free();
|
||||
|
||||
#endif
|
||||
|
||||
// Setup triangle list vertex.
|
||||
stride = 0;
|
||||
|
||||
// Vertex offset.
|
||||
vertex_offset = stride;
|
||||
stride += 3 * sizeof(hfloat);
|
||||
|
||||
// Compute normal stream size.
|
||||
if (trilist->nrm)
|
||||
{
|
||||
normal_offset = stride;
|
||||
stride += 4 * sizeof(char);
|
||||
}
|
||||
|
||||
// Compute RGB stream size.
|
||||
if (trilist->rgb)
|
||||
{
|
||||
rgb_offset = stride;
|
||||
stride += 4 * sizeof(char);
|
||||
}
|
||||
|
||||
// Compute UV stream size.
|
||||
for (int s = 0; s < __UV_PER_GEOMETRY__; ++s)
|
||||
if (trilist->uv[s])
|
||||
{
|
||||
uv_offset[s] = stride;
|
||||
stride += 2 * sizeof(hfloat);
|
||||
}
|
||||
|
||||
// Compute tangent stream size.
|
||||
if (trilist->tangent)
|
||||
{
|
||||
tangent_offset = stride;
|
||||
stride += 4 * 2 * sizeof(char);
|
||||
}
|
||||
|
||||
// Compute skinning stream size.
|
||||
if (trilist->skin)
|
||||
{
|
||||
skinning_offset = stride;
|
||||
stride += 4 * 2 * sizeof(uchar);
|
||||
}
|
||||
|
||||
#define __GPU_PADSIZE 4
|
||||
|
||||
// Compute vertex padding.
|
||||
int padding = 0;
|
||||
padding = stride % __GPU_PADSIZE ? __GPU_PADSIZE - (stride % __GPU_PADSIZE) : 0;
|
||||
if (padding)
|
||||
__LOG__ << "Padding GPU vertex to " << __GPU_PADSIZE << "B by " << padding << "B (from " << (uint)stride << "B)\n";
|
||||
stride += padding;
|
||||
|
||||
// Setup attribute streams.
|
||||
size_t vtx_stream_size = stride * size_t(trilist->vtx.GetCount());
|
||||
|
||||
#if __USE_VBO__
|
||||
|
||||
if (!vtx)
|
||||
vtx = renderer.NewVBO();
|
||||
if (!vtx || !vtx->Create(vtx_stream_size, VBO::Vertex, VBO::Static))
|
||||
return false;
|
||||
|
||||
#endif
|
||||
|
||||
if (!vtx_map.Allocate(vtx_stream_size))
|
||||
return false;
|
||||
|
||||
char *p_stream = (char *)vtx_map.c_ptr();
|
||||
for (uint n = 0; n < trilist->vtx.GetCount(); ++n)
|
||||
{
|
||||
// Output vertex stream.
|
||||
hfloat *p_vtx = (hfloat *)p_stream;
|
||||
p_vtx[0] = Types::FloatToHFloat(trilist->vtx[n].x);
|
||||
p_vtx[1] = Types::FloatToHFloat(trilist->vtx[n].y);
|
||||
p_vtx[2] = Types::FloatToHFloat(trilist->vtx[n].z);
|
||||
p_stream += 3 * sizeof(hfloat);
|
||||
|
||||
// Output normal stream.
|
||||
if (trilist->nrm)
|
||||
{
|
||||
schar *p_nrm = (schar *)p_stream;
|
||||
p_nrm[0] = (schar)(trilist->nrm[n].x * 127.f);
|
||||
p_nrm[1] = (schar)(trilist->nrm[n].y * 127.f);
|
||||
p_nrm[2] = (schar)(trilist->nrm[n].z * 127.f);
|
||||
p_stream += 4 * sizeof(schar);
|
||||
}
|
||||
|
||||
// Output RGB stream.
|
||||
if (trilist->rgb)
|
||||
{
|
||||
uchar *p_rgb = (uchar *)p_stream;
|
||||
p_rgb[0] = uchar(trilist->rgb[n].x * 255.f);
|
||||
p_rgb[1] = uchar(trilist->rgb[n].y * 255.f);
|
||||
p_rgb[2] = uchar(trilist->rgb[n].z * 255.f);
|
||||
p_rgb[3] = uchar(trilist->rgb[n].w * 255.f);
|
||||
p_stream += 4 * sizeof(uchar);
|
||||
}
|
||||
|
||||
// Output UV streams.
|
||||
for (uint s = 0; s < __UV_PER_GEOMETRY__; ++s)
|
||||
if (trilist->uv[s])
|
||||
{
|
||||
hfloat *p_uv = (hfloat *)p_stream;
|
||||
p_uv[0] = Types::FloatToHFloat(trilist->uv[s][n].x);
|
||||
p_uv[1] = Types::FloatToHFloat(trilist->uv[s][n].y);
|
||||
p_stream += 2 * sizeof(hfloat);
|
||||
}
|
||||
|
||||
// Output tangent stream.
|
||||
if (trilist->tangent)
|
||||
{
|
||||
schar *p_tng = (schar *)p_stream;
|
||||
p_tng[0] = schar(trilist->tangent[n].T.x * 127.f);
|
||||
p_tng[1] = schar(trilist->tangent[n].T.y * 127.f);
|
||||
p_tng[2] = schar(trilist->tangent[n].T.z * 127.f);
|
||||
// Mind the gap!
|
||||
p_tng[4] = schar(trilist->tangent[n].B.x * 127.f);
|
||||
p_tng[5] = schar(trilist->tangent[n].B.y * 127.f);
|
||||
p_tng[6] = schar(trilist->tangent[n].B.z * 127.f);
|
||||
p_stream += 4 * 2 * sizeof(schar);
|
||||
}
|
||||
|
||||
// Output skinning stream.
|
||||
if (trilist->skin)
|
||||
{
|
||||
uchar *p_skn = (uchar *)p_stream;
|
||||
p_skn[0] = uchar(trilist->skin[n].bone_index[0]);
|
||||
p_skn[1] = uchar(trilist->skin[n].bone_index[1]);
|
||||
p_skn[2] = uchar(trilist->skin[n].bone_index[2]);
|
||||
p_skn[3] = uchar(trilist->skin[n].bone_index[3]);
|
||||
p_skn[4] = uchar(trilist->skin[n].w[0] * 255.f);
|
||||
p_skn[5] = uchar(trilist->skin[n].w[1] * 255.f);
|
||||
p_skn[6] = uchar(trilist->skin[n].w[2] * 255.f);
|
||||
p_skn[7] = uchar(trilist->skin[n].w[3] * 255.f);
|
||||
p_stream += 4 * 2 * sizeof(uchar);
|
||||
}
|
||||
|
||||
p_stream += padding;
|
||||
}
|
||||
|
||||
#if __USE_VBO__
|
||||
|
||||
vtx->Update(vtx_map, 0, vtx_map.GetSize());
|
||||
vtx_map.Free();
|
||||
|
||||
#else
|
||||
|
||||
p_stream = (char *)vtx_map.c_ptr();
|
||||
|
||||
#define OffsetToAdress(_Offset) \
|
||||
if (_Offset != -1) _Offset += (size_t)p_stream;
|
||||
|
||||
OffsetToAdress(vertex_offset)
|
||||
OffsetToAdress(normal_offset)
|
||||
OffsetToAdress(rgb_offset)
|
||||
for (uint n = 0; n < __UV_PER_GEOMETRY__; ++n)
|
||||
OffsetToAdress(uv_offset[n])
|
||||
OffsetToAdress(tangent_offset)
|
||||
OffsetToAdress(skinning_offset)
|
||||
|
||||
#endif
|
||||
|
||||
Core::ComputeVertexArrayMinMax(trilist->vtx, minmax);
|
||||
material = list_material;
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@ -52,6 +52,7 @@ virtual void SetDepthTexture(Render::Texture *t)
|
||||
virtual void Blit(FBO *out, const iRect &src, const iRect &dst, bool color = true, bool depth = true) = 0;
|
||||
/// Transfer color pixels into a CPU based buffer, this buffer is expected to be big enough to hold the required region in RGBA format.
|
||||
virtual void ReadColorPixels(char *out, int x, int y, int w, int h) = 0;
|
||||
virtual void ReadDepthPixels(float *out) = 0;
|
||||
|
||||
/// Create FBO.
|
||||
virtual bool Create() = 0;
|
||||
|
||||
109
include/engine/gpu/gpu_geometry.cpp
Normal file
109
include/engine/gpu/gpu_geometry.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_geometry.h"
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "core/geometry.h"
|
||||
#include "core/geometry_to_triangle_list.h"
|
||||
#include "core/triangle_list.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Geometry::AllocateVBO(uint count)
|
||||
{
|
||||
if (!vbo.Allocate(count))
|
||||
return false;
|
||||
|
||||
for (uint n = 0; n < count; ++n)
|
||||
vbo[n] = renderer.NewVBO();
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Geometry::SetMaterial(uint index, GS::Render::Material *m)
|
||||
{
|
||||
if (index >= material_table.GetCount())
|
||||
return false;
|
||||
|
||||
// Update all display lists using this material index.
|
||||
for (uint n = 0; n < display_list.GetCount(); ++n)
|
||||
if (DisplayList *dlist = display_list[n])
|
||||
if (dlist->material == material_table[index])
|
||||
dlist->material = (GPU::Material *)m;
|
||||
|
||||
// Update the material table.
|
||||
material_table[index] = m;
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Geometry::ShouldReloadOnDependencyChange(const char *n) const
|
||||
{
|
||||
for (uint i = 0; i < material_table.GetCount(); ++i)
|
||||
if (material_table[i] && (material_table[i]->name == n))
|
||||
return true;
|
||||
|
||||
return name == n;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Geometry::Create(GS::Render::ResourceFactory &rf, const GS::Core::Geometry &g)
|
||||
{
|
||||
__LOG_H__ << "Setup geometry '" << g.name << "'.\n";
|
||||
name = g.name;
|
||||
|
||||
using namespace Core;
|
||||
|
||||
// Setup materials.
|
||||
if (material_table.Allocate(g.material_table.GetCount()))
|
||||
for (uint n = 0; n < g.material_table.GetCount(); ++n)
|
||||
material_table[n] = rf.LoadMaterial(g.material_table[n].name, !g.material_table[n].use_cache);
|
||||
|
||||
// Build geometry triangle list.
|
||||
AutoList <Trilist *> tlist;
|
||||
if (!GeometryToTriangleList::Convert(g, tlist))
|
||||
return false;
|
||||
|
||||
// Setup geometry display lists.
|
||||
if (display_list.Allocate(tlist.GetCount()))
|
||||
{
|
||||
uint n = 0;
|
||||
ListForeachPtr(Trilist *, t, tlist)
|
||||
{
|
||||
display_list[n] = renderer.NewDisplayList();
|
||||
display_list[n]->Create(t, material_table[t->mat]);
|
||||
++n;
|
||||
}
|
||||
}
|
||||
flag = g.flag;
|
||||
|
||||
// Skinning.
|
||||
bone_bind_matrix.Clone(g.bone_bind_matrix);
|
||||
g.ComputeBoneBoundingVolumes(bone_minmax);
|
||||
|
||||
// Load proxies.
|
||||
if (!g.lod_proxy.IsEmpty())
|
||||
lod_proxy = rf.LoadGeometry(g.lod_proxy);
|
||||
lod_distance = g.lod_distance;
|
||||
|
||||
if (!g.shadow_proxy.IsEmpty())
|
||||
shadow_proxy = rf.LoadGeometry(g.shadow_proxy);
|
||||
|
||||
minmax = g.ComputeMinMax();
|
||||
// hotspot.Set(0, 0, 0);
|
||||
hotspot = (minmax.mn + minmax.mx) * 0.5f;
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
128
include/engine/gpu/gpu_half_float.cpp
Normal file
128
include/engine/gpu/gpu_half_float.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "gpu/gpu_types.h"
|
||||
#include "gpu/gpu_renderer.h"
|
||||
|
||||
using namespace GS::GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#ifdef EGL_HALF_FLOAT_SUPPORT
|
||||
|
||||
// -15 stored using a single precision bias of 127
|
||||
static const unsigned int HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP = 0x38000000;
|
||||
// max exponent value in single precision that will be converted
|
||||
// to Inf or Nan when stored as a half-float
|
||||
static const unsigned int HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP = 0x47800000;
|
||||
// 255 is the max exponent biased value
|
||||
static const unsigned int FLOAT_MAX_BIASED_EXP = (0xFF << 23);
|
||||
static const unsigned int HALF_FLOAT_MAX_BIASED_EXP = (0x1F << 10);
|
||||
|
||||
hfloat Types::FloatToHFloat(float f)
|
||||
{
|
||||
unsigned int x = *(unsigned int *)&f;
|
||||
unsigned int sign = (unsigned short)(x >> 31);
|
||||
unsigned int mantissa;
|
||||
unsigned int exp;
|
||||
|
||||
hfloat hf;
|
||||
|
||||
// get mantissa
|
||||
mantissa = x & ((1 << 23) - 1);
|
||||
|
||||
// get exponent bits
|
||||
exp = x & FLOAT_MAX_BIASED_EXP;
|
||||
|
||||
if (exp >= HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP)
|
||||
{
|
||||
// check if the original single precision float number is a NaN
|
||||
if (mantissa && (exp == FLOAT_MAX_BIASED_EXP))
|
||||
{
|
||||
// we have a single precision NaN
|
||||
mantissa = (1 << 23) - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 16-bit half-float representation stores number as Inf
|
||||
mantissa = 0;
|
||||
}
|
||||
|
||||
hf = (((hfloat)sign) << 15) | (hfloat)(HALF_FLOAT_MAX_BIASED_EXP) | (hfloat)(mantissa >> 13);
|
||||
}
|
||||
// check if exponent is <= -15
|
||||
else if (exp <= HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP)
|
||||
{
|
||||
// store a denorm half-float value or zero.
|
||||
exp = (HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP - exp) >> 23;
|
||||
mantissa >>= (14 + exp);
|
||||
hf = (((hfloat)sign) << 15) | (hfloat)(mantissa);
|
||||
}
|
||||
else
|
||||
hf = (((hfloat)sign) << 15) | (hfloat)((exp - HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP) >> 13) | (hfloat)(mantissa >> 13);
|
||||
|
||||
return hf;
|
||||
}
|
||||
float Types::HFloatToFloat(hfloat hf)
|
||||
{
|
||||
unsigned int sign = (unsigned int)(hf >> 15);
|
||||
unsigned int mantissa = (unsigned int)(hf & ((1 << 10) - 1));
|
||||
unsigned int exp = (unsigned int)(hf & HALF_FLOAT_MAX_BIASED_EXP);
|
||||
unsigned int f;
|
||||
|
||||
if (exp == HALF_FLOAT_MAX_BIASED_EXP)
|
||||
{
|
||||
// we have a half-float NaN or Inf
|
||||
// half-float NaNs will be converted to a single precision NaN
|
||||
// half-float Infs will be converted to a single precision Inf
|
||||
exp = FLOAT_MAX_BIASED_EXP;
|
||||
|
||||
if (mantissa)
|
||||
mantissa = (1 << 23) - 1; // set all bits to indicate a NaN
|
||||
}
|
||||
else if (exp == 0x0)
|
||||
{
|
||||
// convert half-float zero/denorm to single precision value
|
||||
if (mantissa)
|
||||
{
|
||||
mantissa <<= 1;
|
||||
exp = HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
|
||||
|
||||
// check for leading 1 in denorm mantissa
|
||||
while ((mantissa & (1 << 10)) == 0)
|
||||
{
|
||||
// for every leading 0, decrement single precision exponent by 1
|
||||
// and shift half-float mantissa value to the left
|
||||
mantissa <<= 1;
|
||||
exp -= (1 << 23);
|
||||
}
|
||||
|
||||
// clamp the mantissa to 10-bits
|
||||
mantissa &= ((1 << 10) - 1);
|
||||
// shift left to generate single-precision mantissa of 23-bits
|
||||
mantissa <<= 13;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// shift left to generate single-precision mantissa of 23-bits
|
||||
mantissa <<= 13;
|
||||
|
||||
// generate single precision biased exponent value
|
||||
exp = (exp << 13) + HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
|
||||
}
|
||||
|
||||
f = (sign << 31) | exp | mantissa;
|
||||
return *((float *)&f);
|
||||
}
|
||||
|
||||
#else // No half-float support.
|
||||
|
||||
hfloat Types::FloatToHFloat(float f) { return f; }
|
||||
float Types::HFloatToFloat(hfloat hf) { return hf; }
|
||||
|
||||
#endif
|
||||
//------------------------------------------------------------------------------
|
||||
234
include/engine/gpu/gpu_helper.cpp
Normal file
234
include/engine/gpu/gpu_helper.cpp
Normal file
@ -0,0 +1,234 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_types.h"
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::GPU;
|
||||
|
||||
/*
|
||||
0 - - 3
|
||||
| |
|
||||
| |
|
||||
1 - - 2
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::BuildDirectVertexLayout(uint idx_count, const ushort *idx, const Vector4 *v, const Color *c, const Vector2 *uv, DirectVertexLayout &layout, VBO *idx_vbo, VBO *vtx_vbo)
|
||||
{
|
||||
if (idx_vbo)
|
||||
{
|
||||
size_t size = idx_count * sizeof(ushort);
|
||||
if (size > idx_vbo->GetSize())
|
||||
if (!idx_vbo->Create(size, VBO::Index, VBO::Dynamic))
|
||||
return false;
|
||||
|
||||
if (void *p = idx_vbo->Map())
|
||||
{
|
||||
Memory::Copy(p, idx, size);
|
||||
idx_vbo->Unmap();
|
||||
}
|
||||
}
|
||||
|
||||
// Find vertex count.
|
||||
ushort vtx_count = 0;
|
||||
for (uint n = 0; n < idx_count; ++n)
|
||||
vtx_count = GS::Types::Max(vtx_count, idx[n]);
|
||||
++vtx_count;
|
||||
|
||||
// Build layout.
|
||||
layout.stride = 0;
|
||||
|
||||
if (v)
|
||||
{
|
||||
layout.vtx_offset = layout.stride;
|
||||
layout.stride += 3 * sizeof(float);
|
||||
}
|
||||
if (c)
|
||||
{
|
||||
layout.color_offset = layout.stride;
|
||||
layout.stride += 4 * sizeof(uchar);
|
||||
}
|
||||
if (uv)
|
||||
{
|
||||
layout.uv_offset = layout.stride;
|
||||
layout.stride += 2 * sizeof(float);
|
||||
}
|
||||
|
||||
// Build interleaved vertex data.
|
||||
if (vtx_vbo)
|
||||
{
|
||||
size_t size = vtx_count * layout.stride;
|
||||
if (size > vtx_vbo->GetSize())
|
||||
if (!vtx_vbo->Create(size, VBO::Vertex, VBO::Dynamic))
|
||||
return false;
|
||||
|
||||
if (char *p_data = (char *)vtx_vbo->Map())
|
||||
{
|
||||
for (uint n = 0; n < vtx_count; ++n)
|
||||
{
|
||||
if (v)
|
||||
{
|
||||
float *p_vtx = (float *)(p_data + layout.vtx_offset);
|
||||
p_vtx[0] = v[n].x;
|
||||
p_vtx[1] = v[n].y;
|
||||
p_vtx[2] = v[n].z;
|
||||
}
|
||||
if (c)
|
||||
{
|
||||
uchar *p_color = (uchar *)(p_data + layout.color_offset);
|
||||
p_color[0] = uchar(c[n].x * 255.f);
|
||||
p_color[1] = uchar(c[n].y * 255.f);
|
||||
p_color[2] = uchar(c[n].z * 255.f);
|
||||
p_color[3] = uchar(c[n].w * 255.f);
|
||||
}
|
||||
if (uv)
|
||||
{
|
||||
float *p_uv = (float *)(p_data + layout.uv_offset);
|
||||
p_uv[0] = uv[n].x;
|
||||
p_uv[1] = uv[n].y;
|
||||
}
|
||||
p_data += layout.stride;
|
||||
}
|
||||
vtx_vbo->Unmap();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::RenderFullscreenQuad(Shader &p, float k_x, float k_y, Render::Texture *t, Core::Light *l)
|
||||
{
|
||||
__NTRACE("RenderFullscreenQuad")
|
||||
|
||||
const size_t stride = sizeof(float) * 5;
|
||||
if (gpu_config.tex_origin_is_top_left)
|
||||
{
|
||||
const float vtx[] = { -1, 1, 1, 0, k_y, -1, -1, 1, 0, 0, 1, -1, 1, k_x, 0, 1, 1, 1, k_x, k_y };
|
||||
helper_vtx_vbo->Update(vtx, 0, stride * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
const float vtx[] = { -1, 1, 1, 0, 1.f - k_y, -1, -1, 1, 0, 1, 1, -1, 1, k_x, 1, 1, 1, 1, k_x, 1.f - k_y };
|
||||
helper_vtx_vbo->Update(vtx, 0, stride * 4);
|
||||
}
|
||||
|
||||
SetDepthFunc(DepthAlways);
|
||||
EnableDepthWrite(false);
|
||||
|
||||
SetShaderProgram(&p);
|
||||
SetIndexSource(helper_idx_vbo);
|
||||
SetVertexSource(helper_vtx_vbo, sizeof(float) * 5);
|
||||
|
||||
ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position),
|
||||
*uv_parm = p.GetInput(Core::ShaderInput::UV0),
|
||||
*texture_parm = p.GetInput(Core::ShaderInput::Texture2D);
|
||||
|
||||
if (!texture_parm) // get cube map (TEMP HACK)
|
||||
texture_parm = p.GetInput(Core::ShaderInput::TextureCube);
|
||||
|
||||
if (vtx_parm)
|
||||
p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, (const void *)0);
|
||||
if (uv_parm)
|
||||
p.Set(*uv_parm->location, 2, Types::ValueFloat, false, stride, (const void *)(sizeof(float) * 3));
|
||||
if (texture_parm && t)
|
||||
texture_parm->SetValue(t);
|
||||
|
||||
if (view_item && l)
|
||||
p.SetLightInputs(*this, *view_item, *l);
|
||||
|
||||
p.SetTransformInputs(Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
p.SetRendererInputs(*this);
|
||||
p.SetConstantInputs();
|
||||
p.SetTextureInputs();
|
||||
p.CommitInputs();
|
||||
|
||||
DrawElements(Types::PrimitiveTriangle, 3 * 2, Types::ValueUShort);
|
||||
|
||||
if (vtx_parm)
|
||||
p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
if (uv_parm)
|
||||
p.Set(*uv_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
|
||||
EnableDepthWrite(true);
|
||||
SetDepthFunc(DepthLess);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::RenderFullscreenQuad(Shader &p, const fRect &src_rect, const fRect &dst_rect, Render::Texture *t, Core::Light *l)
|
||||
{
|
||||
__NTRACE("RenderFullscreenQuad (Region)")
|
||||
|
||||
const size_t stride = sizeof(float) * 5;
|
||||
|
||||
if (gpu_config.tex_origin_is_top_left)
|
||||
{
|
||||
const float vtx[] =
|
||||
{
|
||||
dst_rect.sx * 2.f - 1.f, dst_rect.sy * 2.f - 1.f, 1, src_rect.sx, src_rect.sy,
|
||||
dst_rect.ex * 2.f - 1.f, dst_rect.sy * 2.f - 1.f, 1, src_rect.ex, src_rect.sy,
|
||||
dst_rect.ex * 2.f - 1.f, dst_rect.ey * 2.f - 1.f, 1, src_rect.ex, src_rect.ey,
|
||||
dst_rect.sx * 2.f - 1.f, dst_rect.ey * 2.f - 1.f, 1, src_rect.sx, src_rect.ey
|
||||
};
|
||||
helper_vtx_vbo->Update(vtx, 0, stride * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
const float vtx[] =
|
||||
{
|
||||
dst_rect.sx * 2.f - 1.f, dst_rect.sy * 2.f - 1.f, 1, src_rect.sx, src_rect.ey,
|
||||
dst_rect.ex * 2.f - 1.f, dst_rect.sy * 2.f - 1.f, 1, src_rect.ex, src_rect.ey,
|
||||
dst_rect.ex * 2.f - 1.f, dst_rect.ey * 2.f - 1.f, 1, src_rect.ex, src_rect.sy,
|
||||
dst_rect.sx * 2.f - 1.f, dst_rect.ey * 2.f - 1.f, 1, src_rect.sx, src_rect.sy
|
||||
};
|
||||
helper_vtx_vbo->Update(vtx, 0, stride * 4);
|
||||
}
|
||||
|
||||
SetDepthFunc(DepthAlways);
|
||||
EnableDepthWrite(false);
|
||||
|
||||
SetShaderProgram(&p);
|
||||
SetIndexSource(helper_idx_vbo);
|
||||
SetVertexSource(helper_vtx_vbo, stride);
|
||||
|
||||
ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position),
|
||||
*uv_parm = p.GetInput(Core::ShaderInput::UV0),
|
||||
*texture_parm = p.GetInput(Core::ShaderInput::Texture2D);
|
||||
|
||||
if (!texture_parm) // get cube map (TEMP HACK)
|
||||
texture_parm = p.GetInput(Core::ShaderInput::TextureCube);
|
||||
|
||||
if (vtx_parm)
|
||||
p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, (const void *)0);
|
||||
if (uv_parm)
|
||||
p.Set(*uv_parm->location, 2, Types::ValueFloat, false, stride, (const void *)(sizeof(float) * 3));
|
||||
if (texture_parm && t)
|
||||
texture_parm->SetValue(t);
|
||||
|
||||
if (view_item && l)
|
||||
p.SetLightInputs(*this, *view_item, *l);
|
||||
|
||||
p.SetTransformInputs(Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
p.SetRendererInputs(*this);
|
||||
p.SetConstantInputs();
|
||||
p.SetTextureInputs();
|
||||
p.CommitInputs();
|
||||
|
||||
DrawElements(Types::PrimitiveTriangle, 3 * 2, Types::ValueUShort);
|
||||
|
||||
if (vtx_parm)
|
||||
p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
if (uv_parm)
|
||||
p.Set(*uv_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
|
||||
EnableDepthWrite(true);
|
||||
SetDepthFunc(DepthLess);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
163
include/engine/gpu/gpu_light_volume.cpp
Normal file
163
include/engine/gpu/gpu_light_volume.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "core/camera.h"
|
||||
#include "core/light.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::RenderFullscreenLight(Shader &p, Core::Light &l)
|
||||
{
|
||||
SetDepthFunc(DepthAlways);
|
||||
|
||||
const float h = 1.f, k = 100.f;
|
||||
const float vtx[] = { -k, -k, h, k, -k, h, k, k, h, -k, k, h };
|
||||
|
||||
const size_t stride = sizeof(float) * 3;
|
||||
helper_vtx_vbo->Update(vtx, 0, stride * 4);
|
||||
|
||||
SetShaderProgram(&p);
|
||||
SetIndexSource(helper_idx_vbo);
|
||||
SetVertexSource(helper_vtx_vbo, stride);
|
||||
|
||||
ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position);
|
||||
if (vtx_parm)
|
||||
p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, 0);
|
||||
|
||||
p.SetTransformInputs(Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
p.SetLightInputs(*this, *view_item, l);
|
||||
p.SetRendererInputs(*this);
|
||||
p.CommitInputs();
|
||||
|
||||
DrawElements(Types::PrimitiveTriangle, 3 * 2, Types::ValueUShort);
|
||||
|
||||
if (vtx_parm)
|
||||
p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
|
||||
SetDepthFunc(DepthLess);
|
||||
}
|
||||
void Renderer::RenderSpotLight(Core::Light &l, const DrawContext &dc)
|
||||
{
|
||||
if (!view_item)
|
||||
return;
|
||||
|
||||
Shader &p = (l.shadow == Core::Light::Shadow_Map) && gpu_config.enable_shadow ? *spotlight_shadow_program : *spotlight_program;
|
||||
|
||||
if (!l.volume_range)
|
||||
RenderFullscreenLight(p, l);
|
||||
|
||||
else
|
||||
{
|
||||
const Vector4 *vtx = l.frustum.GetVertices();
|
||||
|
||||
const size_t stride = sizeof(float) * 4;
|
||||
helper_vtx_vbo->Update(vtx, 0, stride * 8);
|
||||
|
||||
SetShaderProgram(&p);
|
||||
SetIndexSource(box_idx_vbo);
|
||||
SetVertexSource(helper_vtx_vbo, stride);
|
||||
|
||||
ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position);
|
||||
if (vtx_parm)
|
||||
p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, 0);
|
||||
|
||||
p.SetTransformInputs(m_projection, m_view, m_iview, &Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
p.SetLightInputs(*this, *view_item, l);
|
||||
p.SetRendererInputs(*this);
|
||||
p.CommitInputs();
|
||||
|
||||
// TODO Boolean operation on light frustum and clipping planes.
|
||||
Vector4 row = view_item->GetMatrix().GetRow(3);
|
||||
Frustum::Visibility viscode = l.frustum.ClassifySet(1, &row, Units::Cm(50.f));
|
||||
|
||||
if (viscode != Frustum::Outside)
|
||||
{
|
||||
SetDepthFunc(DepthGreater);
|
||||
SetCullFunc(CullBack);
|
||||
}
|
||||
|
||||
DrawElements(Types::PrimitiveTriangle, 3 * 2 * 6, Types::ValueUShort);
|
||||
|
||||
if (vtx_parm)
|
||||
p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
|
||||
if (viscode != Frustum::Outside)
|
||||
{
|
||||
SetDepthFunc(DepthLess);
|
||||
SetCullFunc(CullFront);
|
||||
}
|
||||
}
|
||||
}
|
||||
void Renderer::RenderPointLight(Core::Light &l, const DrawContext &dc)
|
||||
{
|
||||
if (!view_item)
|
||||
return;
|
||||
|
||||
Shader &p = (l.shadow == Core::Light::Shadow_Map) && gpu_config.enable_shadow ? *pointlight_shadow_program : *pointlight_program;
|
||||
|
||||
if (!l.volume_range)
|
||||
RenderFullscreenLight(p, l);
|
||||
|
||||
else
|
||||
{
|
||||
float k = l.volume_range;
|
||||
float pointlight_vtx[] =
|
||||
{
|
||||
-k, k, -k, k, k, -k, k, -k, -k, -k, -k, -k,
|
||||
-k, k, k, k, k, k, k, -k, k, -k, -k, k
|
||||
};
|
||||
|
||||
const size_t stride = sizeof(float) * 3;
|
||||
helper_vtx_vbo->Update(pointlight_vtx, 0, stride * 8);
|
||||
|
||||
SetShaderProgram(&p);
|
||||
SetIndexSource(box_idx_vbo);
|
||||
SetVertexSource(helper_vtx_vbo, stride);
|
||||
|
||||
ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position);
|
||||
if (vtx_parm)
|
||||
p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, 0);
|
||||
|
||||
p.SetTransformInputs(m_projection, m_view, m_iview, &l.GetMatrix(), &l.GetInverseMatrix());
|
||||
p.SetLightInputs(*this, *view_item, l);
|
||||
p.SetRendererInputs(*this);
|
||||
p.CommitInputs();
|
||||
|
||||
//
|
||||
MinMax vminmax;
|
||||
vminmax.SetFromPositionSize(l.GetMatrix().GetRow(3), Vector4(l.volume_range, l.volume_range, l.volume_range) * 2.1f);
|
||||
|
||||
bool inside = vminmax.IsInside(view_item->GetMatrix().GetRow(3));
|
||||
|
||||
if (inside)
|
||||
{
|
||||
SetDepthFunc(DepthGreater);
|
||||
SetCullFunc(CullBack);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetDepthFunc(DepthLess);
|
||||
SetCullFunc(CullFront);
|
||||
}
|
||||
|
||||
DrawElements(Types::PrimitiveTriangle, 3 * 12, Types::ValueUShort);
|
||||
|
||||
if (vtx_parm)
|
||||
p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
|
||||
SetDepthFunc(DepthLess);
|
||||
SetCullFunc(CullFront);
|
||||
}
|
||||
}
|
||||
void Renderer::RenderLinearLight(Core::Light &l, const DrawContext &dc)
|
||||
{
|
||||
RenderFullscreenQuad((l.shadow == Core::Light::Shadow_Map) && gpu_config.enable_shadow ? *linearlight_shadow_program : *linearlight_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, NULL, &l);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
172
include/engine/gpu/gpu_material.cpp
Normal file
172
include/engine/gpu/gpu_material.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_material.h"
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "core/material_to_shader_tree.h"
|
||||
#include "core/shader_tree_convert_static_texture_block_to_dynamic.h"
|
||||
#include "core/shader_tree_to_shader.h"
|
||||
#include "core/shader_tree.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Material::ShouldReloadOnDependencyChange(const char *n) const
|
||||
{ return (name == n) || (shader->GetName() == n); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Material::LoadTextureTable(Render::ResourceFactory &rf, const Core::Material &m)
|
||||
{
|
||||
bool r = true;
|
||||
for (uint n = 0; n < Core::Material::max_texture_stage; ++n)
|
||||
{
|
||||
texture_table[n] = rf.LoadTexture(m.texstage[n].t);
|
||||
if (texture_table[n].IsNull())
|
||||
r = false;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
static bool LoadCoreShader(Render::ResourceFactory &rf, const Core::Material &m, Core::Shader &core_shader, const char *name)
|
||||
{
|
||||
using namespace Core;
|
||||
|
||||
if (name == NULL)
|
||||
{
|
||||
// TODO convert material to shader directly (avoid creating an AST).
|
||||
ShaderTree shader_tree;
|
||||
if (!MaterialToShaderTree::Convert(m, shader_tree) || !ConvertShaderTreeToShader(shader_tree, core_shader))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
using namespace NML;
|
||||
|
||||
// Load shader/shader tree.
|
||||
File file;
|
||||
if (!Parser::Load(name, file))
|
||||
return false;
|
||||
|
||||
if (Tag *tag = file.GetTag("Shader"))
|
||||
{
|
||||
if (!core_shader.FromMetaTag(*tag))
|
||||
return false;
|
||||
core_shader.name = m.shader;
|
||||
}
|
||||
else if (Tag *tag = file.GetTag("ShaderMap"))
|
||||
{
|
||||
ShaderTree shader_tree;
|
||||
if (!shader_tree.FromMetaTag(*tag))
|
||||
return false;
|
||||
|
||||
ConvertStaticToDynamicTextureBlocks(shader_tree, m);
|
||||
|
||||
if (!ConvertShaderTreeToShader(shader_tree, core_shader))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
__ERR__(__LOG_E__ << "No shader or shader tree definition found in '" << m.shader << "'.\n", false)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool Material::Create(Render::ResourceFactory &rf, const Core::Material &m)
|
||||
{
|
||||
__LOG_H__ << "Load render material '" << m.name << "'.\n";
|
||||
name = m.name;
|
||||
|
||||
using namespace Core;
|
||||
|
||||
// Transfer basic material informations to the render data.
|
||||
*((BasicMaterial *)this) = ((BasicMaterial &)m);
|
||||
|
||||
// Load texture table.
|
||||
LoadTextureTable(rf, m);
|
||||
|
||||
// Load core shader.
|
||||
MaterialShaderStaticParm parm;
|
||||
|
||||
parm.no_lighting = asbool(m.renderword & Core::Material::Render_Unlit);
|
||||
parm.use_skinning = asbool(m.renderword & Core::Material::Render_Skinned);
|
||||
parm.use_alpha_test = asbool(m.renderword & Core::Material::Render_AlphaTest);
|
||||
parm.use_depth_bias = asbool(m.depth_bias);
|
||||
|
||||
Core::Shader core_shader;
|
||||
bool load_core_shader = LoadCoreShader(rf, m, core_shader, m.shader);
|
||||
|
||||
// Drop current material shader.
|
||||
shader = NULL;
|
||||
|
||||
// Look for a compatible material shader in cache.
|
||||
if (load_core_shader)
|
||||
ListForeachPtr(MaterialShader *, s, renderer.material_shaders)
|
||||
if ((s->GetName() == core_shader.name) && (s->GetStaticParm() == parm))
|
||||
{
|
||||
__LOG_V__ << "Reusing cached material shader for material '" << m.name << "'.\n";
|
||||
|
||||
shader = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Build a new one otherwise.
|
||||
shader = new MaterialShader(renderer);
|
||||
if (shader.IsNull())
|
||||
return false;
|
||||
|
||||
if (!load_core_shader || !shader->Create(rf, core_shader, parm))
|
||||
{
|
||||
if (!LoadCoreShader(rf, m, core_shader, "@core/builtin/shader/shader_error.nsa"))
|
||||
return false;
|
||||
if (!shader->Create(rf, core_shader, parm))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Render::Material *Material::Clone() const
|
||||
{
|
||||
Material *cloned = new Material(renderer);
|
||||
|
||||
// __LOG_W__ << "[MaterialClone] Step 1c: 'new' returned successfully!\n";
|
||||
if (!cloned)
|
||||
{
|
||||
__LOG_E__ << "[MaterialClone] ERROR: Failed to allocate memory for cloned material!\n";
|
||||
return NULL;
|
||||
}
|
||||
// __LOG_W__ << "[MaterialClone] Step 1: Material instance created at " << (void*)cloned << "\n";
|
||||
|
||||
// Copy name (with "_clone" suffix to distinguish it)
|
||||
// __LOG_W__ << "[MaterialClone] Step 2: Copying name...\n";
|
||||
cloned->name = name + "_clone";
|
||||
// __LOG_W__ << "[MaterialClone] Step 2: Name copied: '" << cloned->name << "'\n";
|
||||
|
||||
// Copy basic material properties (diffuse, specular, self, ambient, glossiness, etc.)
|
||||
// __LOG_W__ << "[MaterialClone] Step 3: Copying BasicMaterial properties...\n";
|
||||
*((Core::BasicMaterial *)cloned) = *((Core::BasicMaterial *)this);
|
||||
// __LOG_W__ << "[MaterialClone] Step 3: BasicMaterial properties copied.\n";
|
||||
|
||||
// Copy shader reference
|
||||
// __LOG_W__ << "[MaterialClone] Step 4: Copying shader reference...\n";
|
||||
cloned->shader = shader;
|
||||
// __LOG_W__ << "[MaterialClone] Step 4: Shader reference copied.\n";
|
||||
|
||||
// Copy texture table (smart pointers, so textures are shared, not duplicated)
|
||||
// __LOG_W__ << "[MaterialClone] Step 5: Copying texture table...\n";
|
||||
for (uint n = 0; n < Core::Material::max_texture_stage; ++n)
|
||||
{
|
||||
cloned->texture_table[n] = texture_table[n];
|
||||
}
|
||||
// __LOG_W__ << "[MaterialClone] Step 5: Texture table copied.\n";
|
||||
|
||||
// __LOG_W__ << "[MaterialClone] SUCCESS: Material cloned successfully as '" << cloned->name << "'.\n";
|
||||
|
||||
return cloned;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
429
include/engine/gpu/gpu_material_shader.cpp
Normal file
429
include/engine/gpu/gpu_material_shader.cpp
Normal file
@ -0,0 +1,429 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_material_shader.h"
|
||||
#include "gpu/gpu_material.h"
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::GPU;
|
||||
using GS::NML::Tag;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool MaterialShader::SetUserUniformValue(const char *name, const Vector4 &v)
|
||||
{
|
||||
uint match_count = 0;
|
||||
for (uint n = 0; n < variants.GetCount(); ++n)
|
||||
if (Shader *shader = variants[n].c_ptr())
|
||||
if (ShaderInput *input = shader->GetInput(name))
|
||||
{
|
||||
input->parm_v = v;
|
||||
++match_count;
|
||||
}
|
||||
|
||||
return match_count > 0;
|
||||
}
|
||||
bool MaterialShader::SetUserUniformValue(const char *name, Render::Texture *t)
|
||||
{
|
||||
uint match_count = 0;
|
||||
for (uint n = 0; n < variants.GetCount(); ++n)
|
||||
if (Shader *shader = variants[n].c_ptr())
|
||||
if (ShaderInput *input = shader->GetInput(name))
|
||||
{
|
||||
input->parm_t = t;
|
||||
++match_count;
|
||||
}
|
||||
|
||||
return match_count > 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void MaterialShader::CompileForwardPointLight(Core::Shader &shader, bool shadow)
|
||||
{
|
||||
shader.Define("_POINT_LIGHT", Core::ShaderInput::Pixel);
|
||||
CompileShaderSection("OutputForwardLightModel", shader);
|
||||
|
||||
if (shadow)
|
||||
{
|
||||
shader.Define("_CAST_SHADOW", Core::ShaderInput::Pixel);
|
||||
|
||||
shader.DeclareInput("psm", Core::ShaderInput::DataTextureShadow, Core::ShaderInput::LightShadowMap0, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
for (int n = 0; n < 6; ++n)
|
||||
shader.DeclareInput(String::Format("psm_%d_projection_matrix", n), Core::ShaderInput::Matrix4, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMatrix0 + n), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
shader.DeclareInput("u_iss", Core::ShaderInput::Float, Core::ShaderInput::InverseShadowMapSize, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
shader.DeclareInput("u_lsb", Core::ShaderInput::Float, Core::ShaderInput::LightShadowBias, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
shader.DeclareInput("u_vtl", Core::ShaderInput::Matrix4, Core::ShaderInput::ViewToLightMatrix, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
shader.DeclareInput("u_noise", Core::ShaderInput::DataTexture2D, Core::ShaderInput::NoiseMap, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
|
||||
if (Tag *t = renderer.GetPCFShaderTag())
|
||||
shader.pixel_decl += t->GetString();
|
||||
if (Tag *t = renderer.GetPSMShaderTag())
|
||||
shader.pixel.Replace("%psm_pcf_evaluation%", t->GetString());
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void MaterialShader::CompileForwardSpotLight(Core::Shader &shader, bool shadow, bool projection_map)
|
||||
{
|
||||
shader.Define("_SPOT_LIGHT", Core::ShaderInput::Pixel);
|
||||
CompileShaderSection("OutputForwardLightModel", shader);
|
||||
|
||||
if (shadow)
|
||||
{
|
||||
shader.Define("_CAST_SHADOW", Core::ShaderInput::Pixel);
|
||||
|
||||
shader.DeclareInput("u_ssm", Core::ShaderInput::DataTextureShadow, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMap0), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
shader.DeclareInput("u_ssm_projection", Core::ShaderInput::Matrix4, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMatrix0), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
shader.DeclareInput("u_iss", Core::ShaderInput::Float, Core::ShaderInput::InverseShadowMapSize, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
shader.DeclareInput("u_lsb", Core::ShaderInput::Float, Core::ShaderInput::LightShadowBias, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
shader.DeclareInput("u_noise", Core::ShaderInput::DataTexture2D, Core::ShaderInput::NoiseMap, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
|
||||
if (Tag *t = renderer.GetPCFShaderTag())
|
||||
shader.pixel_decl += t->GetString();
|
||||
}
|
||||
|
||||
if (projection_map)
|
||||
{
|
||||
shader.Define("_PROJECTION_MAP", Core::ShaderInput::Pixel);
|
||||
shader.DeclareInput("u_pjm", Core::ShaderInput::DataTexture2D, Core::ShaderInput::Semantic(Core::ShaderInput::LightProjectionMap), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
shader.DeclareInput("u_pjm_projection", Core::ShaderInput::Matrix4, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMatrix0), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void MaterialShader::CompileForwardLinearLight(Core::Shader &shader, bool shadow)
|
||||
{
|
||||
shader.Define("_LINEAR_LIGHT", Core::ShaderInput::Pixel);
|
||||
CompileShaderSection("OutputForwardLightModel", shader);
|
||||
|
||||
if (shadow)
|
||||
{
|
||||
shader.Define("_CAST_SHADOW", Core::ShaderInput::Pixel);
|
||||
|
||||
shader.DeclareInput("pssm", Core::ShaderInput::DataTextureShadow, Core::ShaderInput::LightShadowMap0, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
for (int n = 0; n < renderer.registry.GetInteger("ShadowMapping:PSSM:Split", 3); ++n)
|
||||
{
|
||||
shader.DeclareInput(String::Format("pssm_%d_slice_distance", n), Core::ShaderInput::Float, Core::ShaderInput::Semantic(Core::ShaderInput::LightPSSMSliceDistance0 + n), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
shader.DeclareInput(String::Format("pssm_%d_projection_matrix", n), Core::ShaderInput::Matrix4, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMatrix0 + n), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
}
|
||||
shader.DeclareInput("u_noise", Core::ShaderInput::DataTexture2D, Core::ShaderInput::NoiseMap, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
shader.DeclareInput("u_iss", Core::ShaderInput::Float, Core::ShaderInput::InverseShadowMapSize, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
shader.DeclareInput("u_lsb", Core::ShaderInput::Float, Core::ShaderInput::LightShadowBias, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
|
||||
if (Tag *t = renderer.GetPCFShaderTag())
|
||||
shader.pixel_decl += t->GetString();
|
||||
|
||||
if (Tag *t = renderer.GetPSSMShaderTag())
|
||||
shader.pixel.Replace("%pssm_pcf_evaluation%", t->GetString());
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool MaterialShader::CompileShaderSection(const char *id, Core::Shader &shader)
|
||||
{
|
||||
Tag *shader_tag = renderer.GetMaterialDict().GetTag(id);
|
||||
if (!shader_tag)
|
||||
__ERR__(__LOG_E__ << "Missing shader section '" << id << "'.\n", false)
|
||||
|
||||
if (Tag *tag = shader_tag->GetTag("Input;"))
|
||||
shader.ParseInputTag(tag);
|
||||
if (Tag *tag = shader_tag->GetTag("Varying;"))
|
||||
shader.ParseVaryingTag(tag);
|
||||
if (Tag *tag = shader_tag->GetTag("Vertex;"))
|
||||
shader.vertex += tag->GetString();
|
||||
if (Tag *tag = shader_tag->GetTag("Fragment;"))
|
||||
shader.pixel += tag->GetString();
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool MaterialShader::SetupMaterialProgram(Variant p, Core::Shader &shader)
|
||||
{
|
||||
if (parm.use_alpha_test)
|
||||
{
|
||||
shader.DeclareInput("u_alpha_threshold", Core::ShaderInput::Float, Core::ShaderInput::MaterialAlphaThreshold, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
shader.pixel += "if (%opacity% < u_alpha_threshold) discard;\n";
|
||||
}
|
||||
|
||||
if (parm.use_depth_bias)
|
||||
shader.DeclareInput("u_depth_bias", Core::ShaderInput::Float, Core::ShaderInput::MaterialDepthBias, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
|
||||
if (parm.use_skinning)
|
||||
{
|
||||
shader.Define("_SKINNED", (Core::ShaderInput::Scope)(Core::ShaderInput::Vertex | Core::ShaderInput::Pixel));
|
||||
shader.DeclareInput("bone_mtx", Core::ShaderInput::Matrix4, Core::ShaderInput::BoneMatrix, Core::ShaderInput::Uniform, Core::ShaderInput::Vertex, __PL_BONE_LIMIT__);
|
||||
shader.DeclareInput("bone_idx", Core::ShaderInput::Vector4, Core::ShaderInput::BoneIndex, Core::ShaderInput::Attribute, Core::ShaderInput::Vertex);
|
||||
shader.DeclareInput("bone_w", Core::ShaderInput::Vector4, Core::ShaderInput::BoneWeight, Core::ShaderInput::Attribute, Core::ShaderInput::Vertex);
|
||||
shader.DeclareVarying("v_skin_mtx", "mat4");
|
||||
}
|
||||
|
||||
CompileShaderSection("InputPosition;", shader);
|
||||
|
||||
// Assemble program shader.
|
||||
switch (p)
|
||||
{
|
||||
case Depth:
|
||||
CompileShaderSection("OutputDepth;", shader);
|
||||
break;
|
||||
|
||||
case FS_Constant:
|
||||
CompileShaderSection("OutputForwardConstant;", shader);
|
||||
break;
|
||||
|
||||
case FS_PointLight:
|
||||
case FS_PointLightShadowMapping:
|
||||
CompileForwardPointLight(shader, p == FS_PointLightShadowMapping);
|
||||
break;
|
||||
|
||||
case FS_SpotLight:
|
||||
case FS_SpotLightShadowMapping:
|
||||
case FS_SpotLightProjection:
|
||||
case FS_SpotLightProjectionShadowMapping:
|
||||
CompileForwardSpotLight(shader, (p == FS_SpotLightShadowMapping) || (p == FS_SpotLightProjectionShadowMapping), (p == FS_SpotLightProjection) || (p == FS_SpotLightProjectionShadowMapping));
|
||||
break;
|
||||
|
||||
case FS_LinearLight:
|
||||
case FS_LinearLightShadowMapping:
|
||||
CompileForwardLinearLight(shader, p == FS_LinearLightShadowMapping);
|
||||
break;
|
||||
|
||||
case DS_GBufferMRT4:
|
||||
CompileShaderSection("OutputDeferred;", shader);
|
||||
break;
|
||||
|
||||
case PP_NormalDepth:
|
||||
CompileShaderSection("OutputNormalDepth;", shader);
|
||||
break;
|
||||
|
||||
case PP_Velocity:
|
||||
if (parm.use_skinning)
|
||||
shader.DeclareInput("previous_bone_mtx", Core::ShaderInput::Matrix4, Core::ShaderInput::PreviousBoneMatrix, Core::ShaderInput::Uniform, Core::ShaderInput::Vertex, __PL_BONE_LIMIT__);
|
||||
CompileShaderSection("OutputVelocity;", shader);
|
||||
break;
|
||||
|
||||
case Last:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static bool FindVarAssignation(const String &src, const char *var) // TODO fix with proper regex, this is proof of concept code at best...
|
||||
{
|
||||
if (src.FindString(String::Format("%s =", var)))
|
||||
return true;
|
||||
if (src.FindString(String::Format("%s\t=", var)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void DeclareVertexPosition(Core::Shader &shader, bool declare_varying)
|
||||
{
|
||||
String decl = "vec4 %position%;\n";
|
||||
|
||||
if (!asbool(FindVarAssignation(shader.vertex, "%position%")))
|
||||
{
|
||||
Core::ShaderInput *input = shader.DeclareInput("a_position", Core::ShaderInput::Vector3, Core::ShaderInput::Position, Core::ShaderInput::Attribute, Core::ShaderInput::Vertex);
|
||||
// Core::ShaderInput *input = shader.GetInput(Core::ShaderInput::Position);
|
||||
decl << "%position% = vec4(" << input->name << ", 1.0);\n";
|
||||
}
|
||||
shader.vertex = decl + shader.vertex;
|
||||
}
|
||||
static void DeclareVertexNormal(Core::Shader &shader)
|
||||
{
|
||||
String decl = "vec3 %normal%;\n";
|
||||
|
||||
if (!asbool(FindVarAssignation(shader.vertex, "%normal%")))
|
||||
{
|
||||
Core::ShaderInput *input = shader.DeclareInput("a_normal", Core::ShaderInput::Vector3, Core::ShaderInput::Normal, Core::ShaderInput::Attribute, Core::ShaderInput::Vertex);
|
||||
// Core::ShaderInput *input = shader.GetInput(Core::ShaderInput::Normal);
|
||||
decl << "%normal% = " << input->name << ";\n";
|
||||
}
|
||||
shader.vertex = decl + shader.vertex;
|
||||
}
|
||||
static void InitializeShader(Core::Shader &shader)
|
||||
{
|
||||
// Position
|
||||
{
|
||||
bool pixel_consumes = asbool(shader.pixel.FindString("%in.position%"));
|
||||
DeclareVertexPosition(shader, pixel_consumes);
|
||||
}
|
||||
|
||||
// Normal
|
||||
{
|
||||
bool pixel_consumes = asbool(shader.pixel.FindString("%in.normal%"));
|
||||
bool pixel_provides = FindVarAssignation(shader.pixel, "%normal%");
|
||||
|
||||
if (pixel_consumes || !pixel_provides)
|
||||
DeclareVertexNormal(shader);
|
||||
|
||||
if (!pixel_provides)
|
||||
shader.pixel << "%normal% = %in.normal%;\n";
|
||||
|
||||
shader.pixel = String("vec3 %normal%;\n") + shader.pixel;
|
||||
}
|
||||
|
||||
{
|
||||
struct ShaderDefault
|
||||
{
|
||||
const char *type, *name, *var;
|
||||
Core::ShaderInput::DataType data_type;
|
||||
Core::ShaderInput::Semantic semantic;
|
||||
};
|
||||
|
||||
static ShaderDefault ps_ic[] =
|
||||
{
|
||||
{ "vec4", "%diffuse%", "_u_mat_diff", Core::ShaderInput::Vector4, Core::ShaderInput::MaterialDiffuse },
|
||||
{ "vec4", "%specular%", "_u_mat_spec", Core::ShaderInput::Vector4, Core::ShaderInput::MaterialSpecular },
|
||||
{ "float", "%glossiness%", "_u_mat_glos", Core::ShaderInput::Float, Core::ShaderInput::MaterialGlossiness },
|
||||
{ "vec4", "%constant%", "_u_mat_const", Core::ShaderInput::Vector4, Core::ShaderInput::MaterialSelf },
|
||||
{ "float", "%opacity%", "_u_mat_opac", Core::ShaderInput::Float, Core::ShaderInput::MaterialOpacity },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
// Declare mandatory outputs and set default value for unprovided entries.
|
||||
String decl;
|
||||
for (uint n = 0; ps_ic[n].type; ++n)
|
||||
{
|
||||
decl << ps_ic[n].type << " " << ps_ic[n].name << ";\n";
|
||||
|
||||
if (!FindVarAssignation(shader.pixel, ps_ic[n].name))
|
||||
{
|
||||
Core::ShaderInput *input = shader.DeclareInput(ps_ic[n].var, ps_ic[n].data_type, ps_ic[n].semantic, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
|
||||
decl << ps_ic[n].name << " = " << input->name << ";\n";
|
||||
}
|
||||
}
|
||||
shader.pixel = decl + shader.pixel;
|
||||
}
|
||||
}
|
||||
static void FinalizeShader(Core::Shader &shader)
|
||||
{
|
||||
if (asbool(shader.pixel.FindString("%in.position%")))
|
||||
{
|
||||
shader.DeclareVarying("_v_position", "vec4");
|
||||
shader.vertex << "_v_position = %position%;\n";
|
||||
}
|
||||
if (asbool(shader.pixel.FindString("%in.normal%")))
|
||||
{
|
||||
shader.DeclareVarying("_v_normal", "vec3");
|
||||
shader.vertex << "_v_normal = %normal%;\n";
|
||||
}
|
||||
|
||||
shader.pixel.ReplaceAll("%in.position%", "_v_position", true);
|
||||
shader.pixel.ReplaceAll("%in.normal%", "_v_normal", true);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static const char *GetShaderVariantName(MaterialShader::Variant v)
|
||||
{
|
||||
switch (v)
|
||||
{
|
||||
case MaterialShader::Depth: return "Depth";
|
||||
|
||||
case MaterialShader::DS_GBufferMRT4: return "DS_GBufferMRT4";
|
||||
case MaterialShader::FS_Constant: return "FS_Constant";
|
||||
|
||||
case MaterialShader::FS_PointLight: return "FS_PointLight";
|
||||
case MaterialShader::FS_PointLightShadowMapping: return "FS_PointLightShadowMapping";
|
||||
|
||||
case MaterialShader::FS_LinearLight: return "FS_LinearLight";
|
||||
case MaterialShader::FS_LinearLightShadowMapping: return "FS_LinearLightShadowMapping";
|
||||
|
||||
case MaterialShader::FS_SpotLight: return "FS_SpotLight";
|
||||
case MaterialShader::FS_SpotLightShadowMapping: return "FS_SpotLightShadowMapping";
|
||||
case MaterialShader::FS_SpotLightProjection: return "FS_SpotLightProjection";
|
||||
case MaterialShader::FS_SpotLightProjectionShadowMapping: return "FS_SpotLightProjectionShadowMapping";
|
||||
|
||||
case MaterialShader::PP_NormalDepth: return "PP_NormalDepth";
|
||||
case MaterialShader::PP_Velocity: return "PP_Velocity";
|
||||
|
||||
case MaterialShader::Last:
|
||||
break;
|
||||
}
|
||||
return "UnknownVariant";
|
||||
}
|
||||
bool MaterialShader::Create(Render::ResourceFactory &rf, const Core::Shader &shader, const MaterialShaderStaticParm &static_parm)
|
||||
{
|
||||
__LOG_V__ << "Creating material shader '" << shader.name << "' variants.\n";
|
||||
|
||||
name = shader.name;
|
||||
parm = static_parm;
|
||||
|
||||
// Build variants.
|
||||
if (!variants.Allocate(Last))
|
||||
return false;
|
||||
|
||||
for (int n = 0; n < Last; ++n)
|
||||
{
|
||||
if (renderer.render_technique == Renderer::TechniqueDeferred)
|
||||
if ((n >= FS_Constant) && (n <= FS_SpotLightProjectionShadowMapping))
|
||||
continue; // no forward
|
||||
|
||||
if (renderer.render_technique == Renderer::TechniqueForward)
|
||||
if (n == DS_GBufferMRT4)
|
||||
continue; // no deferred
|
||||
|
||||
if (!renderer.gpu_config.enable_shadow)
|
||||
if (
|
||||
(n == Depth) ||
|
||||
(n == FS_PointLightShadowMapping) ||
|
||||
(n == FS_LinearLightShadowMapping) ||
|
||||
(n == FS_SpotLightShadowMapping) ||
|
||||
(n == FS_SpotLightProjectionShadowMapping)
|
||||
)
|
||||
continue; // no shadow-mapping
|
||||
|
||||
if (!renderer.gpu_config.use_rtt)
|
||||
if (
|
||||
(n == PP_Velocity) ||
|
||||
(n == PP_NormalDepth)
|
||||
)
|
||||
continue; // no post-processes
|
||||
|
||||
if (parm.no_lighting)
|
||||
if ((n > FS_Constant) && (n <= FS_SpotLightProjectionShadowMapping)) // keep constant!
|
||||
continue; // no lighting pass
|
||||
|
||||
// Specialize shader for this program.
|
||||
Core::Shader shader_variant;
|
||||
if (!shader.Clone(shader_variant))
|
||||
return false;
|
||||
|
||||
InitializeShader(shader_variant);
|
||||
if (!SetupMaterialProgram((Variant)n, shader_variant))
|
||||
return false;
|
||||
FinalizeShader(shader_variant);
|
||||
|
||||
shader_variant.name << "_" << GetShaderVariantName((MaterialShader::Variant)n);
|
||||
|
||||
// Compile it.
|
||||
if ((variants[n] = (Shader *)renderer.NewShader()) != NULL)
|
||||
if (!variants[n]->Create(rf, shader_variant))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
MaterialShader::MaterialShader(Renderer &r) : renderer(r)
|
||||
{ renderer.material_shaders.Add(this); }
|
||||
MaterialShader::~MaterialShader()
|
||||
{ renderer.material_shaders.Remove(this); }
|
||||
//------------------------------------------------------------------------------
|
||||
668
include/engine/gpu/gpu_post_process_chain.cpp
Normal file
668
include/engine/gpu/gpu_post_process_chain.cpp
Normal file
@ -0,0 +1,668 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "gpu/gpu_geometry.h"
|
||||
#include "gpu/gpu_shader_object.h"
|
||||
#include "gpu/gpu_draw_context.h"
|
||||
#include "core/geometry.h"
|
||||
#include "rand/rand.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static bool IsPostProcessExcluded(NML::Tag *exclusion_key, const char *tag)
|
||||
{
|
||||
NML::Tag *__t = exclusion_key ? exclusion_key->GetTypedTag(tag, Variant::VariantBool) : NULL;
|
||||
return __t && __t->GetBool();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------
|
||||
#define _SwapTarget { tmp = t[0]; t[0] = t[1]; t[1] = tmp; }
|
||||
//---------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::GetPostProcessNormalDepth(const Stack <RenderPrimitive *> display_lists[2])
|
||||
{
|
||||
if (normal_depth_updated)
|
||||
return;
|
||||
|
||||
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
|
||||
|
||||
fRect old_viewport = GetViewport();
|
||||
|
||||
// Generate normal/depth buffer.
|
||||
SetViewport(fRect(0, 0, (float)t_fx[2]->GetWidth(), (float)t_fx[2]->GetHeight()));
|
||||
fx_fbo->SetColorTexture(t_fx[2]);
|
||||
|
||||
if (render_technique == TechniqueDeferred)
|
||||
{
|
||||
SetCurrentFBO(fx_fbo);
|
||||
RenderFullscreenQuad(*single_texture_program, old_viewport.GetWidth() / (float)dimensions.x, old_viewport.GetHeight() / (float)dimensions.y, t_gbuffer[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
fx_fbo->SetDepthTexture(t_fx_depth);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
|
||||
Clear(0, 0, 0);
|
||||
DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::PP_NormalDepth);
|
||||
DrawList(display_lists[0], dc);
|
||||
|
||||
fx_fbo->SetDepthTexture(NULL);
|
||||
}
|
||||
|
||||
SetViewport(old_viewport);
|
||||
normal_depth_updated = true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::ApplyDirectionalBlur(Render::Texture *t[2], const Vector2 &d, float attn)
|
||||
{
|
||||
if (!fx_blur_program)
|
||||
return;
|
||||
|
||||
Render::Texture *tmp;
|
||||
|
||||
ShaderInput *u_pass = fx_blur_program->GetInput("u_pass"),
|
||||
*u_blur_d = fx_blur_program->GetInput("u_blur_d"),
|
||||
*u_attenuation = fx_blur_program->GetInput("u_attenuation");
|
||||
|
||||
SetShaderProgram(fx_blur_program);
|
||||
u_blur_d->SetValue(d.x, d.y);
|
||||
u_attenuation->SetValue(attn);
|
||||
|
||||
fRect nviewport(viewport.sx / dimensions.x, viewport.sy / dimensions.y, viewport.ex / dimensions.x, viewport.ey / dimensions.y);
|
||||
|
||||
for (int n = 0; n < 4; ++n)
|
||||
{
|
||||
fx_fbo->SetColorTexture(t[1]);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
u_pass->SetValue(float(n));
|
||||
RenderFullscreenQuad(*fx_blur_program, nviewport, fRect(0, 0, 1, 1), t[0]);
|
||||
_SwapTarget
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::ApplyBloomFilter(Render::Texture *t_in, float strength, float threshold, float exponent, float radius, float strength_white_screen)
|
||||
{
|
||||
if (!strength || !single_texture_cutoff_program || !tone_mapping_program)
|
||||
return;
|
||||
|
||||
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
|
||||
|
||||
Render::Texture *t[] = { t_fx[0], t_fx[1] };
|
||||
ShaderInput *u_cutoff = single_texture_cutoff_program->GetInput("u_cutoff"),
|
||||
*u_strength = tone_mapping_program->GetInput("u_strength"),
|
||||
*u_strength_white_screen = tone_mapping_program->GetInput("u_strength_white_screen");
|
||||
|
||||
if (!u_cutoff || !u_strength || !u_strength_white_screen)
|
||||
return;
|
||||
|
||||
fRect old_viewport = GetViewport();
|
||||
|
||||
fRect nviewport(viewport.sx / dimensions.x, viewport.sy / dimensions.y, viewport.ex / dimensions.x, viewport.ey / dimensions.y);
|
||||
fRect fx_viewport(old_viewport.sx / fx_scale, old_viewport.sy / fx_scale, old_viewport.ex / fx_scale, old_viewport.ey / fx_scale);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
#define CutoffFrameToBloomFX \
|
||||
{ \
|
||||
SetViewport(fx_viewport); \
|
||||
\
|
||||
fx_fbo->SetColorTexture(t[0]); \
|
||||
SetCurrentFBO(fx_fbo);\
|
||||
SetShaderProgram(single_texture_cutoff_program); \
|
||||
u_cutoff->SetValue(threshold); \
|
||||
RenderFullscreenQuad(*single_texture_cutoff_program, nviewport, fRect(0, 0, 1, 1), t_in); \
|
||||
}
|
||||
|
||||
|
||||
#define OutputBloomFXToFrame \
|
||||
{ \
|
||||
SetViewport(old_viewport);\
|
||||
\
|
||||
EnableBlending(true); \
|
||||
SetBlendFunc(BlendOne, BlendOne); \
|
||||
\
|
||||
fx_fbo->SetColorTexture(t_in); \
|
||||
SetCurrentFBO(fx_fbo);\
|
||||
SetShaderProgram(tone_mapping_program); \
|
||||
u_strength->SetValue(strength * 1.25f); \
|
||||
u_strength_white_screen->SetValue(strength_white_screen); \
|
||||
RenderFullscreenQuad(*tone_mapping_program, nviewport, fRect(0, 0, 1, 1), t[0]); \
|
||||
\
|
||||
EnableBlending(false); \
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
if (view_registry->GetBool("PostProcess:Bloom:Streak:Enabled", false))
|
||||
{
|
||||
float angle = view_registry->GetReal("PostProcess:Bloom:Streak:Angle"),
|
||||
attn = view_registry->GetReal("PostProcess:Bloom:Streak:Attenuation", 0.975f);
|
||||
|
||||
for (int n = 0; n < 2; ++n)
|
||||
{
|
||||
Vector2 d = Vector2(cos(angle), sin(angle)) * radius / 12.f;
|
||||
|
||||
CutoffFrameToBloomFX
|
||||
ApplyDirectionalBlur(t, d, attn);
|
||||
OutputBloomFXToFrame
|
||||
|
||||
if (!view_registry->GetBool("PostProcess:Bloom:Streak:CrossShaped", false))
|
||||
break;
|
||||
|
||||
angle += Units::Deg(90.f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CutoffFrameToBloomFX
|
||||
ApplyDirectionalBlur(t, Vector2(radius / 64.f, 0));
|
||||
ApplyDirectionalBlur(t, Vector2(0, radius / 64.f));
|
||||
OutputBloomFXToFrame
|
||||
}
|
||||
|
||||
SetViewport(old_viewport);
|
||||
SetClippingRect(&old_viewport);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::ApplyNoiseFilter(Render::Texture *t_in, Render::Texture *t_out, float strength, float mono, float bias)
|
||||
{
|
||||
if (!strength || !noise_program)
|
||||
return false;
|
||||
|
||||
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
|
||||
|
||||
ShaderInput *u_strength = noise_program->GetInput("u_strength"),
|
||||
*u_mono = noise_program->GetInput("u_mono"),
|
||||
*u_bias = noise_program->GetInput("u_bias"),
|
||||
*u_random = noise_program->GetInput("u_random");
|
||||
|
||||
if (!u_strength || !u_mono || !u_bias || !u_random)
|
||||
return false;
|
||||
|
||||
fx_fbo->SetColorTexture(t_out);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
|
||||
float random[2] = { Random::FRand(), Random::FRand() };
|
||||
|
||||
SetShaderProgram(noise_program);
|
||||
u_strength->SetValue(strength);
|
||||
u_mono->SetValue(mono);
|
||||
u_bias->SetValue(bias);
|
||||
u_random->SetValue(random[0], random[1]);
|
||||
RenderFullscreenQuad(*noise_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::ApplySSAAFilter(Render::Texture *t_in, Render::Texture *t_out)
|
||||
{
|
||||
if (!ssaa_program)
|
||||
return false;
|
||||
|
||||
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
|
||||
|
||||
fx_fbo->SetColorTexture(t_out);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
|
||||
SetShaderProgram(ssaa_program);
|
||||
RenderFullscreenQuad(*ssaa_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::ApplyResolveMSAADepth(Render::Texture *t_depth_msaa, Render::Texture *t_out)
|
||||
{
|
||||
if (!t_depth_msaa || !t_out || !resolve_msaa_depth_program)
|
||||
return false;
|
||||
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
|
||||
|
||||
fx_fbo->SetColorTexture(t_out);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
|
||||
ShaderInput *msaa_depth = resolve_msaa_depth_program->GetInput("u_depthMSAA");
|
||||
SetShaderProgram(resolve_msaa_depth_program);
|
||||
msaa_depth->SetValue(t_depth_msaa);
|
||||
|
||||
RenderFullscreenQuad(*resolve_msaa_depth_program,viewport.GetWidth() / (float)dimensions.x,viewport.GetHeight() / (float)dimensions.y);
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::ApplySSAOFilter(Render::Texture *t_in, Render::Texture *t_out, const Stack <RenderPrimitive *> display_lists[2], float s, float r, float d, float blur_r)
|
||||
{
|
||||
if (!s || !ssao_program)
|
||||
return false;
|
||||
|
||||
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
|
||||
|
||||
ShaderInput *u_strength = ssao_program->GetInput("u_strength"),
|
||||
*u_radius = ssao_program->GetInput("u_radius"),
|
||||
*u_distance_scale = ssao_program->GetInput("u_distance_scale"),
|
||||
*u_iproj2d = ssao_program->GetInput("u_iproj2d");
|
||||
|
||||
if (!u_strength || !u_radius || !u_distance_scale)
|
||||
return false;
|
||||
|
||||
GetPostProcessNormalDepth(display_lists);
|
||||
|
||||
#if 0
|
||||
// Normal/depth debug output.
|
||||
fx_fbo->SetColorTexture(t_in);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
RenderFullscreenQuad(single_texture_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_fx[2]);
|
||||
return false;
|
||||
#endif
|
||||
|
||||
fRect old_viewport = GetViewport();
|
||||
SetViewport(fRect(0, 0, (float)t_fx[2]->GetWidth(), (float)t_fx[2]->GetHeight()));
|
||||
|
||||
// Render raw SSAO to FX texture.
|
||||
SetShaderProgram(ssao_program);
|
||||
u_strength->SetValue(s);
|
||||
u_radius->SetValue(r * (4 / fx_scale));
|
||||
u_distance_scale->SetValue(d);
|
||||
|
||||
float iproj[] = { 1.f / m_projection.m[0][0], 1.f / m_projection.m[1][1] };
|
||||
if (!gpu_config.tex_origin_is_top_left)
|
||||
iproj[1] = -iproj[1];
|
||||
u_iproj2d->SetValue(iproj[0], iproj[1]);
|
||||
|
||||
EnableAlphaTest(false);
|
||||
EnableBlending(false);
|
||||
|
||||
int out_index = 0;
|
||||
fx_fbo->SetColorTexture(t_fx[out_index]);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
Clear(0, 0, 0, 0, 1, ClearColor); // FIXME this is USELESS, but something is killing fragments with 0 alpha in ssao_program and disabling alpha testing does not solves the issue.
|
||||
RenderFullscreenQuad(*ssao_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_fx[2]);
|
||||
|
||||
if (ssao_blur_program)
|
||||
{
|
||||
// SSAO blur.
|
||||
if (blur_r > 0.f)
|
||||
{
|
||||
ShaderInput *u_blur_radius = ssao_blur_program->GetInput("u_blur_radius"),
|
||||
*u_normal_depth = ssao_blur_program->GetInput("u_normal_depth"); // the normal depth texture is not always used, depending on the quality settings.
|
||||
|
||||
if (u_blur_radius && u_normal_depth)
|
||||
{
|
||||
fx_fbo->SetColorTexture(t_fx[1 - out_index]);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
Clear(0, 0, 0, 0, 1, ClearColor); // FIXME this is USELESS, but something is killing fragments with 0 alpha in ssao_program and disabling alpha testing does not solves the issue.
|
||||
|
||||
SetShaderProgram(ssao_blur_program);
|
||||
u_blur_radius->SetValue(blur_r);
|
||||
u_normal_depth->SetValue(t_fx[2].c_ptr());
|
||||
RenderFullscreenQuad(*ssao_blur_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_fx[out_index]);
|
||||
|
||||
out_index = 1 - out_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Composite back over input.
|
||||
SetViewport(old_viewport);
|
||||
|
||||
fx_fbo->SetColorTexture(t_in);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
|
||||
EnableBlending(true);
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
|
||||
RenderFullscreenQuad(*single_texture_program, 1, 1, t_fx[out_index]);
|
||||
EnableBlending(false);
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::ApplySharpenFilter(Render::Texture *t_in, Render::Texture *t_out, float strength)
|
||||
{
|
||||
if (!strength || !sharpen_program)
|
||||
return false;
|
||||
|
||||
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
|
||||
|
||||
ShaderInput *u_strength = sharpen_program->GetInput("u_strength");
|
||||
if (!u_strength)
|
||||
return false;
|
||||
|
||||
fx_fbo->SetColorTexture(t_out);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
|
||||
SetShaderProgram(sharpen_program);
|
||||
u_strength->SetValue(strength);
|
||||
RenderFullscreenQuad(*sharpen_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::ApplyHSL(Render::Texture *t_in, Render::Texture *t_out, float H, float S, float L)
|
||||
{
|
||||
if (((H == 1.f) && (S == 1.f) && (L == 1.f)) || !hsl_program)
|
||||
return false;
|
||||
|
||||
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
|
||||
|
||||
ShaderInput *u_H = hsl_program->GetInput("u_H"),
|
||||
*u_S = hsl_program->GetInput("u_S"),
|
||||
*u_L = hsl_program->GetInput("u_L");
|
||||
if (!u_H || !u_S || !u_L)
|
||||
return false;
|
||||
|
||||
fx_fbo->SetColorTexture(t_out);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
|
||||
SetShaderProgram(hsl_program);
|
||||
u_H->SetValue(H);
|
||||
u_S->SetValue(S);
|
||||
u_L->SetValue(L);
|
||||
RenderFullscreenQuad(*hsl_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::ApplyChromaticDispersion(Render::Texture *t_in, Render::Texture *t_out, float width)
|
||||
{
|
||||
if ((width == 0.f) || !chromatic_dispersion_program)
|
||||
return false;
|
||||
|
||||
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
|
||||
|
||||
ShaderInput *u_width = chromatic_dispersion_program->GetInput("u_width");
|
||||
if (!u_width)
|
||||
return false;
|
||||
|
||||
fx_fbo->SetColorTexture(t_out);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
|
||||
SetShaderProgram(chromatic_dispersion_program);
|
||||
u_width->SetValue(width);
|
||||
RenderFullscreenQuad(*chromatic_dispersion_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::ApplyRadialBlur(Render::Texture *t_in, Render::Texture *t_out, float strength, float center_x, float center_y)
|
||||
{
|
||||
if (!strength || !radial_blur_program)
|
||||
return false;
|
||||
|
||||
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
|
||||
|
||||
ShaderInput *u_strength = radial_blur_program->GetInput("u_strength"),
|
||||
*u_center = radial_blur_program->GetInput("u_center");
|
||||
|
||||
if (!u_strength || !u_center)
|
||||
return false;
|
||||
|
||||
fx_fbo->SetColorTexture(t_out);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
|
||||
SetShaderProgram(radial_blur_program);
|
||||
u_strength->SetValue(strength);
|
||||
float center[] = { center_x * viewport.GetWidth() / (float)dimensions.x, center_y * viewport.GetHeight() / (float)dimensions.y };
|
||||
u_center->SetValue(center[0], center[1]);
|
||||
RenderFullscreenQuad(*radial_blur_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::ApplyMotionBlur(Render::Texture *t_in, Render::Texture *t_out, const Stack <RenderPrimitive *> display_lists[2], float strength, int quality)
|
||||
{
|
||||
if (!strength || !quality || !motion_blur_program)
|
||||
return false;
|
||||
|
||||
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
|
||||
|
||||
float k_u = viewport.GetWidth() / dimensions.x, k_v = viewport.GetHeight() / dimensions.y;
|
||||
|
||||
// Generate a velocity buffer from the opaque surface list.
|
||||
fRect old_viewport = GetViewport();
|
||||
{
|
||||
ScopedPerfEvent event(this, "Render Velocity Buffer", Color::Purple);
|
||||
|
||||
SetViewport(fRect(0, 0, (float)t_fx[0]->GetWidth(), (float)t_fx[0]->GetHeight()));
|
||||
|
||||
fx_fbo->SetColorTexture(t_fx[0]);
|
||||
fx_fbo->SetDepthTexture(t_fx_depth);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
|
||||
Clear(0, 0, 0);
|
||||
|
||||
DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::PP_Velocity);
|
||||
DrawList(display_lists[0], dc);
|
||||
}
|
||||
|
||||
// Vector field blur.
|
||||
#if 0
|
||||
float k_blur = 0.5;
|
||||
Render::Texture *t_pp_0[] = { t_fx[0], t_fx[1] };
|
||||
ApplyDirectionalBlur(t_pp_0, nVector2(k_blur, 0), 0.975f);
|
||||
Render::Texture *t_pp_1[] = { t_fx[1], t_fx[0] };
|
||||
ApplyDirectionalBlur(t_pp_1, nVector2(0, k_blur), 0.975f);
|
||||
#endif
|
||||
|
||||
// Apply velocity field.
|
||||
fx_fbo->SetDepthTexture(NULL);
|
||||
|
||||
// Quality: 0 - No PP, 1 - Lores blur, 2 - Hires blur.
|
||||
if (quality == 1)
|
||||
{
|
||||
fx_fbo->SetColorTexture(t_fx[1]);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
SetShaderProgram(single_texture_program);
|
||||
RenderFullscreenQuad(*single_texture_program, 1, 1, t_in);
|
||||
}
|
||||
else
|
||||
{
|
||||
fx_fbo->SetColorTexture(t_out);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
SetViewport(old_viewport);
|
||||
}
|
||||
|
||||
ShaderInput *u_strength = motion_blur_program->GetInput("u_strength"),
|
||||
*u_pow = motion_blur_program->GetInput("u_pow"),
|
||||
*u_max = motion_blur_program->GetInput("u_max"),
|
||||
*u_source = motion_blur_program->GetInput("u_source"),
|
||||
*u_velocity = motion_blur_program->GetInput("u_velocity");
|
||||
|
||||
SetShaderProgram(motion_blur_program);
|
||||
const float k = 1.f; // stats.bench_fps.GetFps() / 60.f; // Normalize to 60fps.
|
||||
|
||||
u_strength->SetValue(strength * 32.f * k);
|
||||
u_pow->SetValue(2.f);
|
||||
u_max->SetValue(1.f / 128.f); // Works in normalized space, does not need to adjust for variable resolution.
|
||||
|
||||
motion_blur_program->Set(*u_velocity->location, *t_fx[0], u_velocity->index);
|
||||
|
||||
if (quality == 1) // low-res blur path
|
||||
{
|
||||
fx_fbo->SetColorTexture(t_fx[2]);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
u_source->SetValue(t_fx[1].c_ptr());
|
||||
RenderFullscreenQuad(*motion_blur_program, 1, 1);
|
||||
|
||||
fx_fbo->SetColorTexture(t_in);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
SetViewport(old_viewport);
|
||||
|
||||
EnableBlending(true);
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
|
||||
SetShaderProgram(single_texture_program);
|
||||
RenderFullscreenQuad(*single_texture_program, k_u, k_v, t_fx[2]);
|
||||
EnableBlending(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
u_source->SetValue(t_in);
|
||||
RenderFullscreenQuad(*motion_blur_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Render::Texture *Renderer::ApplyPostProcessChain(const Stack <RenderPrimitive *> display_lists[2])
|
||||
{
|
||||
ScopedBenchmark bench(stats.bench_post_process);
|
||||
ScopedPerfEvent event(this, "Apply Post-Processing Chain", Color::Green);
|
||||
|
||||
if (!gpu_config.use_rtt)
|
||||
return t_compose[0];
|
||||
|
||||
normal_depth_updated = false;
|
||||
|
||||
NML::Tag *post_process_registry = view_registry ? view_registry->GetTag("PostProcess") : NULL;
|
||||
if (!post_process_registry)
|
||||
return t_compose[0];
|
||||
NML::Tag *exclusion_key = registry.GetTag("PostProcess:Exclusion");
|
||||
|
||||
fRect old_clipping = GetClippingRect();
|
||||
SetClippingRect(NULL);
|
||||
|
||||
uint i_compose = 0;
|
||||
NML::Tag *tag;
|
||||
|
||||
// SSAO.
|
||||
#if !(__PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__)
|
||||
if (((tag = post_process_registry->GetTag("SSAO")) != NULL) && !IsPostProcessExcluded(exclusion_key, "SSAO"))
|
||||
if (ApplySSAOFilter
|
||||
(
|
||||
t_compose[i_compose],
|
||||
t_compose[1 - i_compose],
|
||||
display_lists,
|
||||
view_registry->GetReal("PostProcess:SSAO:Strength", 1),
|
||||
view_registry->GetReal("PostProcess:SSAO:Radius", 2),
|
||||
view_registry->GetReal("PostProcess:SSAO:DistanceScale", 0.5),
|
||||
view_registry->GetReal("PostProcess:SSAO:BlurRadius", 8)
|
||||
))
|
||||
i_compose = 1 - i_compose;
|
||||
#endif
|
||||
|
||||
// Sharpen.
|
||||
if (((tag = post_process_registry->GetTag("Sharpen")) != NULL) && !IsPostProcessExcluded(exclusion_key, "Sharpen"))
|
||||
if (ApplySharpenFilter
|
||||
(
|
||||
t_compose[i_compose],
|
||||
t_compose[1 - i_compose],
|
||||
view_registry->GetReal("PostProcess:Sharpen:Strength", 0.5f)
|
||||
))
|
||||
i_compose = 1 - i_compose;
|
||||
|
||||
// SSAA.
|
||||
if (render_technique == TechniqueDeferred)
|
||||
if (registry.GetBool("Antialiasing:Enable", false))
|
||||
if (ApplySSAAFilter
|
||||
(
|
||||
t_compose[i_compose],
|
||||
t_compose[1 - i_compose]
|
||||
))
|
||||
i_compose = 1 - i_compose;
|
||||
|
||||
#if !(__PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__)
|
||||
// Motion blur.
|
||||
if (((tag = post_process_registry->GetTag("MotionBlur")) != NULL) && !IsPostProcessExcluded(exclusion_key, "MotionBlur"))
|
||||
if (ApplyMotionBlur
|
||||
(
|
||||
t_compose[i_compose],
|
||||
t_compose[1 - i_compose],
|
||||
display_lists,
|
||||
view_registry->GetReal("PostProcess:MotionBlur:Strength", 0.5),
|
||||
registry.GetInteger("PostProcess:MotionBlur:Quality", 2)
|
||||
))
|
||||
i_compose = 1 - i_compose;
|
||||
#endif
|
||||
|
||||
// Radial blur.
|
||||
if (((tag = post_process_registry->GetTag("RadialBlur")) != NULL) && !IsPostProcessExcluded(exclusion_key, "RadialBlur"))
|
||||
if (ApplyRadialBlur
|
||||
(
|
||||
t_compose[i_compose],
|
||||
t_compose[1 - i_compose],
|
||||
view_registry->GetReal("PostProcess:RadialBlur:Strength", 0.5),
|
||||
view_registry->GetReal("PostProcess:RadialBlur:CenterX", 0.5),
|
||||
view_registry->GetReal("PostProcess:RadialBlur:CenterY", 0.5)
|
||||
))
|
||||
i_compose = 1 - i_compose;
|
||||
|
||||
// Bloom.
|
||||
if (((tag = post_process_registry->GetTag("Bloom")) != NULL) && !IsPostProcessExcluded(exclusion_key, "Bloom"))
|
||||
ApplyBloomFilter
|
||||
(
|
||||
t_compose[i_compose],
|
||||
view_registry->GetReal("PostProcess:Bloom:Strength", 1),
|
||||
view_registry->GetReal("PostProcess:Bloom:Threshold", 0.95f),
|
||||
view_registry->GetReal("PostProcess:Bloom:Exponent", 4),
|
||||
view_registry->GetReal("PostProcess:Bloom:Radius", 12),
|
||||
view_registry->GetReal("PostProcess:Sharpen:Strength", 0.5f)
|
||||
);
|
||||
|
||||
// Noise.
|
||||
#if !(__PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__)
|
||||
// TODO mobile version of this.
|
||||
if (((tag = post_process_registry->GetTag("Noise")) != NULL) && !IsPostProcessExcluded(exclusion_key, "Noise"))
|
||||
if (ApplyNoiseFilter
|
||||
(
|
||||
t_compose[i_compose],
|
||||
t_compose[1 - i_compose],
|
||||
view_registry->GetReal("PostProcess:Noise:Strength", 0.25f),
|
||||
view_registry->GetReal("PostProcess:Noise:Monochromatic", 0.f),
|
||||
view_registry->GetReal("PostProcess:Noise:LumaBias", 0.5f)
|
||||
) )
|
||||
i_compose = 1 - i_compose;
|
||||
#endif
|
||||
|
||||
// HSL.
|
||||
if (((tag = post_process_registry->GetTag("HueSaturation")) != NULL) && !IsPostProcessExcluded(exclusion_key, "HueSaturation"))
|
||||
if (ApplyHSL
|
||||
(
|
||||
t_compose[i_compose],
|
||||
t_compose[1 - i_compose],
|
||||
view_registry->GetReal("PostProcess:HueSaturation:H", 1),
|
||||
view_registry->GetReal("PostProcess:HueSaturation:S", 1),
|
||||
view_registry->GetReal("PostProcess:HueSaturation:L", 1)
|
||||
) )
|
||||
i_compose = 1 - i_compose;
|
||||
|
||||
// Chromatic dispersion.
|
||||
if (((tag = post_process_registry->GetTag("ChromDisp")) != NULL) && !IsPostProcessExcluded(exclusion_key, "ChromDisp"))
|
||||
if (ApplyChromaticDispersion
|
||||
(
|
||||
t_compose[i_compose],
|
||||
t_compose[1 - i_compose],
|
||||
view_registry->GetReal("PostProcess:ChromDisp:Width", 1)
|
||||
) )
|
||||
i_compose = 1 - i_compose;
|
||||
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
|
||||
|
||||
SetClippingRect(&old_clipping);
|
||||
|
||||
SetIndexSource(NULL);
|
||||
SetVertexSource(NULL, 0);
|
||||
return t_compose[i_compose];
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
40
include/engine/gpu/gpu_profiler.cpp
Normal file
40
include/engine/gpu/gpu_profiler.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "core/core_profiler.h"
|
||||
#include "core/raster_font.h"
|
||||
|
||||
using namespace GS::GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::DrawProfilerText(GS::Render::RasterFont *font[2], float &x, float &y)
|
||||
{
|
||||
Color title_color(1.f, 0.9f, 0);
|
||||
WriterConfig config(false);
|
||||
|
||||
Write(*font[1], String::Format("%s - %s (v%s)\n\n", GetName(), GetDescription(), GetVersion()), x, y, config, 1, &title_color);
|
||||
|
||||
Write(*font[0], String::Format("Device: %s\n", stats.adapter.c_str()), x, y, config);
|
||||
Write(*font[0], String::Format("Vendor: %s\n\n", stats.vendor.c_str()), x, y, config);
|
||||
|
||||
Write(*font[0], String::Format("Texture: %s Geometry: %s\n\n", Core::FormatNumber(stats.texture_memory, Core::MemorySize).c_str(), Core::FormatNumber(stats.geometry_memory, Core::MemorySize).c_str()), x, y, config);
|
||||
|
||||
Render::Renderer::DrawProfilerText(font, x, y);
|
||||
|
||||
Write(*font[1], "Batching System\n\n", x, y, config, 1, &title_color);
|
||||
Write(*font[0], String::Format("Program change = %0.02f%% (%d)\n", gpu_stats.prg_change ? (gpu_stats.prg_change * 100.f) / stats.list_drawn : 0, gpu_stats.prg_change), x, y, config);
|
||||
Write(*font[0], String::Format("List change = %0.02f%% (%d)\n", gpu_stats.dls_change ? (gpu_stats.dls_change * 100.f) / stats.list_drawn : 0, gpu_stats.dls_change), x, y, config);
|
||||
Write(*font[0], String::Format("Material change = %0.02f%% (%d)\n", gpu_stats.mat_change ? (gpu_stats.mat_change * 100.f) / stats.list_drawn : 0, gpu_stats.mat_change), x, y, config);
|
||||
Write(*font[0], String::Format("Item change = %0.02f%% (%d)\n\n", gpu_stats.item_change ? (gpu_stats.item_change * 100.f) / stats.list_drawn : 0, gpu_stats.item_change), x, y, config);
|
||||
|
||||
Write(*font[1], "Terrain System\n\n", x, y, config, 1, &title_color);
|
||||
Write(*font[0], String::Format("Cache miss = %0.02f%% (%d query/frame)\n\n", gpu_stats.terrain_page_query_count ? (gpu_stats.terrain_page_query_miss * 100.f) / gpu_stats.terrain_page_query_count : 0, gpu_stats.terrain_page_query_count), x, y, config);
|
||||
|
||||
Write(*font[1], String::Format("Technique: %s\n\n", render_technique == TechniqueDeferred ? "Deferred" : "Forward"), x, y, config, 1, &title_color);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
71
include/engine/gpu/gpu_registry.cpp
Normal file
71
include/engine/gpu/gpu_registry.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_renderer.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
RegistryRValue Renderer::ProcessMessage(RegistryMessage m, const Registry *registry, const void *parm)
|
||||
{
|
||||
switch (m)
|
||||
{
|
||||
case RegistryMsg_StartKeyChangeBatch:
|
||||
break;
|
||||
|
||||
case RegistryMsg_EndKeyChangeBatch:
|
||||
if (pending_shadow_map_refresh)
|
||||
CreateShadowMaps();
|
||||
if (pending_core_shader_refresh)
|
||||
LoadCoreShaders();
|
||||
|
||||
if (pending_technique_refresh)
|
||||
SetRenderTechnique();
|
||||
if (pending_fx_refresh)
|
||||
SetPostProcess();
|
||||
|
||||
pending_shadow_map_refresh = false;
|
||||
pending_core_shader_refresh = false;
|
||||
pending_technique_refresh = false;
|
||||
pending_fx_refresh = false;
|
||||
break;
|
||||
|
||||
case RegistryMsg_KeyChange:
|
||||
if (RegistryKeyChange *key = (RegistryKeyChange *)parm)
|
||||
{
|
||||
if ((key->key == "ShadowMapping:Enable") || (key->key == "ShadowMapping:Size"))
|
||||
{
|
||||
gpu_config.enable_shadow = registry->GetBool("ShadowMapping:Enable", true);
|
||||
pending_shadow_map_refresh = true;
|
||||
}
|
||||
else if (key->key == "ShadowMapping:PSSM:Split")
|
||||
{
|
||||
pending_shadow_map_refresh = true;
|
||||
pending_core_shader_refresh = true;
|
||||
}
|
||||
else if (key->key == "ShadowMapping:PCF:Quality")
|
||||
pending_core_shader_refresh = true;
|
||||
|
||||
else if (key->key == "Texture:Float:Enable")
|
||||
pending_technique_refresh = true;
|
||||
else if (key->key == "Technique")
|
||||
pending_technique_refresh = true;
|
||||
else if (key->key.StartsWith("Antialiasing"))
|
||||
pending_technique_refresh = true;
|
||||
|
||||
else if (key->key == "PostProcess:FX:Scale")
|
||||
pending_fx_refresh = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return RegistryReturn_Ok;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
155
include/engine/gpu/gpu_render_display_list.cpp
Normal file
155
include/engine/gpu/gpu_render_display_list.cpp
Normal file
@ -0,0 +1,155 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "gpu/gpu_geometry.h"
|
||||
#include "gpu/gpu_shader_object.h"
|
||||
#include "gpu/gpu_draw_context.h"
|
||||
#include "core/object.h"
|
||||
#include "core/camera.h"
|
||||
#include "core/geometry.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#define ENABLE_PERFORMANCE_TOOLS
|
||||
|
||||
#ifdef ENABLE_PERFORMANCE_TOOLS
|
||||
#define _ProgramCacheMiss ++gpu_stats.prg_change;
|
||||
#define _DisplayListCacheMiss ++gpu_stats.dls_change;
|
||||
#define _MaterialCacheMiss ++gpu_stats.mat_change;
|
||||
#define _ItemCacheMiss ++gpu_stats.item_change;
|
||||
|
||||
#define _SetPerfWire if (performance_tools.show_wireframe) SetFillMode(FillWireframe);
|
||||
#define _UnsetPerfWire if (performance_tools.show_wireframe) SetFillMode(FillSolid);
|
||||
#else
|
||||
#define _ProgramCacheMiss
|
||||
#define _DisplayListCacheMiss
|
||||
#define _MaterialCacheMiss
|
||||
#define _ItemCacheMiss
|
||||
|
||||
#define _SetPerfWire
|
||||
#define _UnsetPerfWire
|
||||
#endif
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::ResetDisplayListCache()
|
||||
{
|
||||
if (dls_cache.dls)
|
||||
SetDisplayList(NULL);
|
||||
dls_cache.dls = NULL;
|
||||
|
||||
if (dls_cache.mat)
|
||||
UnsetMaterial(*dls_cache.mat, dls_cache.ctx);
|
||||
dls_cache.mat = NULL;
|
||||
|
||||
if (dls_cache.shd)
|
||||
SetShaderProgram(NULL, dls_cache.shd);
|
||||
dls_cache.shd = NULL;
|
||||
|
||||
dls_cache.item = NULL;
|
||||
}
|
||||
bool Renderer::SetDrawContext(DisplayList &dls, const Core::Item &item, const DrawContext &dc)
|
||||
{
|
||||
Material *mat = dls.material.IsValid() ? (Material *)dls.material.c_ptr() : NULL;
|
||||
if (!mat || !mat->shader)
|
||||
return false;
|
||||
Shader *shd = mat->shader->variants[dc.ctx.variant];
|
||||
if (!shd)
|
||||
return false;
|
||||
|
||||
#define ProgramChange (1 << 0)
|
||||
#define DisplayListChange (1 << 1)
|
||||
#define MaterialChange (1 << 2)
|
||||
#define ItemChange (1 << 3)
|
||||
#define OpacityChange (1 << 4)
|
||||
|
||||
uint change_mask = 0;
|
||||
change_mask |= (shd != dls_cache.shd) ? ProgramChange : 0;
|
||||
change_mask |= (&dls != dls_cache.dls) ? DisplayListChange : 0;
|
||||
change_mask |= (mat != dls_cache.mat) ? MaterialChange : 0;
|
||||
change_mask |= (&item != dls_cache.item) ? ItemChange : 0;
|
||||
change_mask |= (item.opacity != dls_cache.opacity) ? OpacityChange : 0;
|
||||
|
||||
if (change_mask & ProgramChange)
|
||||
{
|
||||
if (!SetShaderProgram(shd, dls_cache.shd))
|
||||
return false;
|
||||
_ProgramCacheMiss
|
||||
}
|
||||
|
||||
if (change_mask & ProgramChange)
|
||||
shd->SetConstantInputs();
|
||||
|
||||
if (change_mask & DisplayListChange)
|
||||
{
|
||||
SetDisplayList(&dls);
|
||||
_DisplayListCacheMiss
|
||||
}
|
||||
if (change_mask & MaterialChange)
|
||||
{
|
||||
if (dls_cache.mat)
|
||||
UnsetMaterial(*dls_cache.mat, dc.ctx);
|
||||
SetMaterial(*mat, dc.ctx, asbool(item.opacity < 1.f));
|
||||
_MaterialCacheMiss
|
||||
}
|
||||
|
||||
// Program inputs.
|
||||
if (change_mask & ProgramChange)
|
||||
{
|
||||
shd->SetRendererInputs(*this, mat);
|
||||
if (dc.light)
|
||||
shd->SetLightInputs(*this, *view_item, *dc.light);
|
||||
}
|
||||
if (change_mask & (ProgramChange | MaterialChange | OpacityChange))
|
||||
shd->SetMaterialOpacityInputs(*mat, item.opacity);
|
||||
if (change_mask & (ProgramChange | MaterialChange))
|
||||
{
|
||||
shd->SetMaterialInputs(*mat);
|
||||
shd->SetTextureInputs();
|
||||
}
|
||||
if (change_mask & (ProgramChange | DisplayListChange))
|
||||
shd->SetVertexStreamInputs(dls);
|
||||
if (change_mask & (ProgramChange | ItemChange))
|
||||
{
|
||||
shd->SetTransformInputs(m_projection, m_view, m_iview, &m_world, &m_iworld);
|
||||
shd->SetPreviousTransformInputs(m_projection, m_previous_iview, &m_previous_world);
|
||||
_ItemCacheMiss
|
||||
}
|
||||
if (change_mask & (ProgramChange | DisplayListChange | ItemChange))
|
||||
if (Core::Skin *skin = ((Core::Object &)item).GetSkin())
|
||||
shd->SetSkinInputs(dls, *skin);
|
||||
|
||||
shd->CommitInputs();
|
||||
|
||||
// Sync cache.
|
||||
dls_cache.ctx = dc.ctx;
|
||||
dls_cache.shd = shd;
|
||||
dls_cache.mat = mat;
|
||||
dls_cache.dls = &dls;
|
||||
dls_cache.item = &item;
|
||||
dls_cache.opacity = item.opacity;
|
||||
|
||||
return true;
|
||||
}
|
||||
void Renderer::DrawDisplayListCached(DisplayList &dls, const Core::Item &item, const DrawContext &dc)
|
||||
{
|
||||
if (!SetDrawContext(dls, item, dc))
|
||||
return;
|
||||
|
||||
// Draw.
|
||||
_SetPerfWire
|
||||
dls.Draw();
|
||||
_UnsetPerfWire
|
||||
|
||||
stats.list_drawn++;
|
||||
stats.triangle_drawn += dls.index_count / 3;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
298
include/engine/gpu/gpu_render_queue.cpp
Normal file
298
include/engine/gpu/gpu_render_queue.cpp
Normal file
@ -0,0 +1,298 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "gpu/gpu_geometry.h"
|
||||
#include "core/renderer_environment_interface.h"
|
||||
#include "core/terrain.h"
|
||||
#include "core/geometry.h"
|
||||
#include "sort/sort.h"
|
||||
#include "container/container_sort.h"
|
||||
#include "platform_config.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::DrawList(const Stack <RenderPrimitive *> &dl_list, const DrawContext &dc)
|
||||
{
|
||||
ResetDisplayListCache();
|
||||
DecayTerrainCache();
|
||||
|
||||
m_previous_iview = view_item->GetPreviousMatrix().InversedFast();
|
||||
|
||||
for (uint n = 0; n < dl_list.GetCount(); ++n)
|
||||
{
|
||||
RenderPrimitive *dl = dl_list[n];
|
||||
|
||||
if (!dl->item)
|
||||
continue;
|
||||
|
||||
switch (dl->type)
|
||||
{
|
||||
case RenderPrimitive::TypeDisplayList:
|
||||
if (dl->dlst)
|
||||
{
|
||||
m_previous_world = dl->item->GetPreviousMatrix();
|
||||
SetWorldMatrix(dl->item->GetMatrix(), &dl->item->GetInverseMatrix());
|
||||
DrawDisplayListCached(*dl->dlst, *dl->item, dc);
|
||||
}
|
||||
break;
|
||||
|
||||
case RenderPrimitive::TypeTerrainPatch:
|
||||
if (dl->patch)
|
||||
RenderTerrainPatch(*dl->patch, *dl->item, dc);
|
||||
break;
|
||||
|
||||
case RenderPrimitive::TypeEmitter:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ResetDisplayListCache();
|
||||
stats.queue_pass++;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static inline uint GetRenderPrimitiveSortKey(RenderPrimitive *a)
|
||||
{
|
||||
// Watch nRenderMaterial and nGPUDisplayList size in bytes so that this key stays optimal.
|
||||
return ((((uint)a->dlst->material.c_ptr() >> 8) & 0xffff) << 16) + (((uint)a->dlst >> 7) & 0xffff);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::SortDisplayList(AutoStack <RenderPrimitive *> &d_list) const
|
||||
{
|
||||
const uint count = d_list.GetCount();
|
||||
if (count == 0)
|
||||
return;
|
||||
try {
|
||||
// Byte sort list.
|
||||
typedef Sort<uint, RenderPrimitive *> SortPrimitive;
|
||||
Array <SortPrimitive::Entry> sort_a(count), sort_b(count);
|
||||
|
||||
for (uint n = 0; n < count; ++n)
|
||||
{
|
||||
sort_a[n].o = d_list[n];
|
||||
sort_a[n].v = GetRenderPrimitiveSortKey(d_list[n]);
|
||||
}
|
||||
Array <SortPrimitive::Entry> *out = SortPrimitive::ByteSort(count, &sort_a, &sort_b);
|
||||
|
||||
// Empty the source stack, prevent primitives cleanup...
|
||||
d_list.DropContentOwnership();
|
||||
|
||||
// ...as we now insert them back in sorted order.
|
||||
for (uint n = 0; n < count; ++n)
|
||||
d_list.Push((*out)[n].o);
|
||||
}
|
||||
catch(char *e)
|
||||
{
|
||||
__LOG__ << "Failed to SortDisplayList.\n";
|
||||
}
|
||||
}
|
||||
static int GetMaterialDrawPassIndex(Render::Material *m, float opacity = 1.f)
|
||||
{
|
||||
if (m->renderword & Core::Material::Render_UseFramebuffer)
|
||||
return 2;
|
||||
if ((m->blendop != Core::Material::Blend_None) || (opacity < 1.0))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
void Renderer::BuildDisplayLists(const AutoStack <Render::Primitive *> &p_list, AutoStack <RenderPrimitive *> *d_list) const
|
||||
{
|
||||
for (uint n = 0; n < p_list.GetCount(); ++n)
|
||||
{
|
||||
Render::Primitive *p = p_list[n];
|
||||
|
||||
switch (p->type)
|
||||
{
|
||||
case Render::Primitive::Type_Geometry:
|
||||
if (Geometry *geo = (Geometry *)p->geometry.c_ptr())
|
||||
for (uint n = 0; n < geo->display_list.GetCount(); ++n)
|
||||
{
|
||||
DisplayList *dlist = geo->display_list[n];
|
||||
if (dlist->material.IsNull())
|
||||
continue;
|
||||
|
||||
int draw_pass_index = GetMaterialDrawPassIndex(dlist->material, p->item->opacity);
|
||||
d_list[draw_pass_index].Push(new RenderPrimitive(p->item, dlist));
|
||||
}
|
||||
break;
|
||||
|
||||
case Render::Primitive::Type_TerrainPatch:
|
||||
if (p->patch->terrain->render_data->material.IsNull())
|
||||
continue;
|
||||
d_list[GetMaterialDrawPassIndex(p->patch->terrain->render_data->material, p->patch->terrain->opacity)].Push(new RenderPrimitive(p->item, p->patch));
|
||||
break;
|
||||
|
||||
case Render::Primitive::Type_Emitter:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int n = 0; n < 3; ++n)
|
||||
SortDisplayList(d_list[n]);
|
||||
}
|
||||
void Renderer::RenderList()
|
||||
{
|
||||
ScopedBenchmark bench(stats.bench_render);
|
||||
PerfSetMarker("RenderList", Color::Green);
|
||||
|
||||
// Build primitive list.
|
||||
stats.renderable_processed += BuildRenderablePrimitiveList(*view_item, *view_item, frustum_rlist.primitive_list);
|
||||
stats.renderable_drawn += frustum_rlist.primitive_list.GetCount();
|
||||
|
||||
// Build display lists.
|
||||
BuildDisplayLists(frustum_rlist.primitive_list, frustum_rlist.display_lists);
|
||||
|
||||
// Render opaque display lists.
|
||||
switch (render_technique)
|
||||
{
|
||||
case TechniqueDeferred:
|
||||
RenderListDeferred(frustum_rlist.display_lists[0]);
|
||||
break;
|
||||
|
||||
case TechniqueForward:
|
||||
RenderListForward(frustum_rlist.display_lists[0], DrawContext::Opaque);
|
||||
|
||||
if (environment_interface) // draw skybox
|
||||
{
|
||||
Render::Shader *user_skybox_shader = environment_interface->GetSkyboxShader();
|
||||
|
||||
Render::sTexture skybox_layers[2];
|
||||
if (environment_interface->GetSkyboxLayers(skybox_layers) || user_skybox_shader)
|
||||
DrawSkybox(skybox_layers, user_skybox_shader);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Render transparent display lists.
|
||||
RenderListForward(frustum_rlist.display_lists[1], DrawContext::Alpha);
|
||||
|
||||
// Render frame buffer dependent display lists.
|
||||
if (gpu_config.use_rtt && ((frustum_rlist.display_lists[2].GetCount() > 0) || registry.GetBool("FrameBufferAsTexture;", false)))
|
||||
{
|
||||
GrabDisplay(t_fx[0]);
|
||||
RenderListForward(frustum_rlist.display_lists[2], DrawContext::Opaque);
|
||||
}
|
||||
|
||||
// Render direct user primitives hook.
|
||||
if (environment_interface)
|
||||
environment_interface->OnRenderUser(this);
|
||||
|
||||
//
|
||||
switch (render_technique)
|
||||
{
|
||||
case TechniqueDeferred:
|
||||
// Render fog.
|
||||
if (environment_interface)
|
||||
if (environment_interface->IsFogEnabled() && !performance_tools.disable_fog)
|
||||
{
|
||||
EnableBlending(true);
|
||||
RenderFullscreenQuad(*ds_fog_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y);
|
||||
EnableBlending(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case TechniqueForward:
|
||||
// Resolve MSSA target.
|
||||
if (gpu_config.enable_aa)
|
||||
render_fbo->Blit(resolve_fbo, iRect(0, 0, dimensions.x, dimensions.y), iRect(0, 0, dimensions.x, dimensions.y), true, true);
|
||||
break;
|
||||
}
|
||||
|
||||
// Post-processes.
|
||||
if (gpu_config.use_rtt)
|
||||
{
|
||||
t_final = ApplyPostProcessChain(frustum_rlist.display_lists);
|
||||
resolve_fbo->SetColorTexture(t_final);
|
||||
}
|
||||
|
||||
// Drop render list.
|
||||
frustum_rlist.Clear(false);
|
||||
|
||||
// Subsequent draws should render to the final texture.
|
||||
if (gpu_config.use_rtt)
|
||||
SetCurrentFBO(resolve_fbo);
|
||||
render_fbo = resolve_fbo;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::BeginDrawList()
|
||||
{
|
||||
PerfSetMarker("BeginDrawList", Color::Green);
|
||||
|
||||
// Cache per-frame values for coherency.
|
||||
frame_clock = environment_interface ? environment_interface->GetClock() : 0;
|
||||
pcf_radius = registry.GetReal("ShadowMapping:PCF:Radius", 1.75f);
|
||||
|
||||
if (environment_interface)
|
||||
if (Core::Camera *c = environment_interface->GetCurrentCamera())
|
||||
{
|
||||
SetCamera(c);
|
||||
ApplyCamera();
|
||||
}
|
||||
|
||||
/*
|
||||
MSAA needs a special resolve step so we work on a different FBO which
|
||||
will later be resolved to the resolve FBO.
|
||||
*/
|
||||
render_fbo = gpu_config.enable_aa ? buffer_fbo : resolve_fbo;
|
||||
|
||||
if (render_technique == TechniqueForward)
|
||||
{
|
||||
if (gpu_config.use_rtt)
|
||||
SetCurrentFBO(render_fbo);
|
||||
|
||||
// Clear output, if a skybox is set it will be rendered after the opaque pass.
|
||||
if (environment_interface)
|
||||
{
|
||||
Render::Shader *user_skybox_shader = environment_interface->GetSkyboxShader();
|
||||
|
||||
Render::sTexture skybox_layers[2];
|
||||
if (environment_interface->GetSkyboxLayers(skybox_layers) || user_skybox_shader)
|
||||
Clear(0, 0, 0, 1, 1, ClearDepth);
|
||||
else
|
||||
{
|
||||
Color bg = environment_interface->GetClearColor();
|
||||
Clear(bg.x, bg.y, bg.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t_final = t_compose[0];
|
||||
return true;
|
||||
}
|
||||
void Renderer::EndDrawList()
|
||||
{
|
||||
PerfSetMarker("EndDrawList", Color::Green);
|
||||
|
||||
// when entering this function the current FBO is expected to be render_FBO.
|
||||
if (gpu_config.use_rtt)
|
||||
{
|
||||
// Blit result to user output_fbo.
|
||||
if (output_fbo)
|
||||
render_fbo->Blit(output_fbo, viewport.AsInt(), output_fbo_rect, true, false);
|
||||
|
||||
// Blit to frame buffer.
|
||||
SetCurrentFBO(NULL);
|
||||
fRect src(viewport.sx / dimensions.x, viewport.sy / dimensions.y, viewport.ex / dimensions.x, viewport.ey / dimensions.y);
|
||||
RenderFullscreenQuad(*single_texture_program, src, fRect(0, 0, 1, 1), t_final); // viewport is already set for destination so use the full rect output
|
||||
|
||||
// Restore default output.
|
||||
resolve_fbo->SetColorTexture(t_final = t_compose[0]);
|
||||
}
|
||||
|
||||
SetIndexSource(NULL);
|
||||
SetVertexSource(NULL, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
82
include/engine/gpu/gpu_render_queue_deferred.cpp
Normal file
82
include/engine/gpu/gpu_render_queue_deferred.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "gpu/gpu_geometry.h"
|
||||
#include "gpu/gpu_draw_context.h"
|
||||
#include "core/renderer_environment_interface.h"
|
||||
#include "core/camera.h"
|
||||
#include "core/light.h"
|
||||
|
||||
using namespace GS::GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::RenderGBufferPass(const GS::Stack <RenderPrimitive *> &display_lists)
|
||||
{
|
||||
SetCurrentFBO(buffer_fbo);
|
||||
Clear(0, 0, 0, 0);
|
||||
|
||||
DrawContext dc(DrawContext::Deferred, DrawContext::Base, MaterialShader::DS_GBufferMRT4);
|
||||
DrawList(display_lists, dc);
|
||||
}
|
||||
void Renderer::RenderDeferredLightPass(const GS::Stack <RenderPrimitive *> &)
|
||||
{
|
||||
SetCurrentFBO(resolve_fbo);
|
||||
|
||||
DrawContext dc(DrawContext::Deferred, DrawContext::Light);
|
||||
|
||||
// Clear to ambient.
|
||||
EnableDepthWrite(false);
|
||||
RenderFullscreenQuad(*ambient_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y);
|
||||
EnableDepthWrite(true);
|
||||
|
||||
// For each light in frustum, render affected primitives with a light specific shader.
|
||||
SetBlendFunc(BlendOne, BlendOne);
|
||||
|
||||
List <Core::Light *> light_list;
|
||||
environment_interface->GetLightsInFrustum(view_item->GetMatrix().GetRow(3), frustum, light_list);
|
||||
|
||||
ListForeachPtr(Core::Light *, l, light_list)
|
||||
{
|
||||
// Render shadow map.
|
||||
if (gpu_config.enable_shadow && l->shadow == Core::Light::Shadow_Map)
|
||||
{
|
||||
if (PrepareShadowMap(*l, true)) // TODO move to job
|
||||
RenderShadowMap(*l);
|
||||
|
||||
SetCurrentFBO(resolve_fbo);
|
||||
SetBlendFunc(BlendOne, BlendOne);
|
||||
}
|
||||
|
||||
// Compose light.
|
||||
EnableDepthWrite(false);
|
||||
EnableBlending(true);
|
||||
|
||||
switch (l->model)
|
||||
{
|
||||
case Core::Light::Model_Point: RenderPointLight(*l, dc); break;
|
||||
case Core::Light::Model_Linear: RenderLinearLight(*l, dc); break;
|
||||
case Core::Light::Model_Spot: RenderSpotLight(*l, dc); break;
|
||||
}
|
||||
|
||||
EnableBlending(false);
|
||||
EnableDepthWrite(true);
|
||||
stats.light_processed++;
|
||||
}
|
||||
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::RenderListDeferred(const GS::Stack <RenderPrimitive *> &display_lists)
|
||||
{
|
||||
RenderGBufferPass(display_lists);
|
||||
if (environment_interface)
|
||||
RenderDeferredLightPass(display_lists);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
318
include/engine/gpu/gpu_render_queue_forward.cpp
Normal file
318
include/engine/gpu/gpu_render_queue_forward.cpp
Normal file
@ -0,0 +1,318 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "gpu/gpu_shader_object.h"
|
||||
#include "gpu/gpu_geometry.h"
|
||||
#include "gpu/gpu_draw_context.h"
|
||||
#include "core/renderer_environment_interface.h"
|
||||
#include "core/geometry.h"
|
||||
#include "core/terrain.h"
|
||||
#include "core/camera.h"
|
||||
#include "core/light.h"
|
||||
#include "async/job.h"
|
||||
#include "alloc/ialloc.h"
|
||||
#include "platform.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::LightCullDisplayList(const Core::Light &light, const Stack <RenderPrimitive *> &in, Stack <RenderPrimitive *> &out)
|
||||
{
|
||||
for (uint n = 0; n < in.GetCount(); ++n)
|
||||
{
|
||||
RenderPrimitive *dl = in[n];
|
||||
|
||||
switch (light.model)
|
||||
{
|
||||
case Core::Light::Model_Point:
|
||||
if (light.range > 0)
|
||||
{
|
||||
MinMax &mm = dl->dlst->minmax;
|
||||
Vector4 dl_lpos = light.GetMatrix().GetRow(3) * dl->item->GetInverseMatrix();
|
||||
|
||||
if ( ((dl_lpos.x - light.range) < mm.mx.x) &&
|
||||
((dl_lpos.x + light.range) > mm.mn.x) &&
|
||||
((dl_lpos.y - light.range) < mm.mx.y) &&
|
||||
((dl_lpos.y + light.range) > mm.mn.y) &&
|
||||
((dl_lpos.z - light.range) < mm.mx.z) &&
|
||||
((dl_lpos.z + light.range) > mm.mn.z) )
|
||||
out.Push(dl);
|
||||
}
|
||||
else
|
||||
out.Push(dl);
|
||||
break;
|
||||
|
||||
case Core::Light::Model_Spot:
|
||||
if (light.frustum.ClassifyMinMax(dl->dlst->minmax, &dl->item->GetMatrix()) != Frustum::Outside)
|
||||
out.Push(dl);
|
||||
break;
|
||||
|
||||
case Core::Light::Model_Linear:
|
||||
default:
|
||||
out.Push(dl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::SetupForwardContext(const DrawContext::Context &ctx)
|
||||
{
|
||||
switch (ctx.draw)
|
||||
{
|
||||
case DrawContext::Base:
|
||||
switch (ctx.render)
|
||||
{
|
||||
case DrawContext::Opaque:
|
||||
EnableDepthTest(true);
|
||||
break;
|
||||
|
||||
case DrawContext::Alpha:
|
||||
EnableDepthWrite(false);
|
||||
EnableBlending(true);
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case DrawContext::Light:
|
||||
switch (ctx.render)
|
||||
{
|
||||
case DrawContext::Opaque:
|
||||
EnableBlending(true);
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOne);
|
||||
break;
|
||||
|
||||
case DrawContext::Alpha:
|
||||
EnableDepthWrite(false);
|
||||
EnableBlending(true);
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOne);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
class GPU::PrepareLightJob : public ASync::Job
|
||||
{
|
||||
const Renderer &renderer;
|
||||
const Core::Light &light;
|
||||
|
||||
const Stack <RenderPrimitive *> ∈
|
||||
Stack <RenderPrimitive *> &out;
|
||||
|
||||
DrawContext::Render rc;
|
||||
|
||||
public:
|
||||
|
||||
NPLACEMENT_NEW(RendererJob)
|
||||
|
||||
bool use_shadow;
|
||||
|
||||
bool CullPrimitive(const Render::Material *m, const MinMax &mm, const Core::Item *item) const
|
||||
{
|
||||
if (m->renderword & Core::Material::Render_Unlit)
|
||||
return false; // exclude from lighting
|
||||
|
||||
switch (light.model)
|
||||
{
|
||||
case Core::Light::Model_Point:
|
||||
if (light.range > 0)
|
||||
{
|
||||
Vector4 dl_lpos = light.GetMatrix().GetRow(3) * item->GetInverseMatrix();
|
||||
|
||||
if ( ((dl_lpos.x - light.range) < mm.mx.x) &&
|
||||
((dl_lpos.y - light.range) < mm.mx.y) &&
|
||||
((dl_lpos.z - light.range) < mm.mx.z) &&
|
||||
((dl_lpos.x + light.range) > mm.mn.x) &&
|
||||
((dl_lpos.y + light.range) > mm.mn.y) &&
|
||||
((dl_lpos.z + light.range) > mm.mn.z) )
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
break;
|
||||
|
||||
case Core::Light::Model_Spot:
|
||||
if (light.frustum.ClassifyMinMax(mm, &item->GetMatrix()) != Frustum::Outside)
|
||||
return true;
|
||||
break;
|
||||
|
||||
default:
|
||||
case Core::Light::Model_Linear:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void Execute(uint)
|
||||
{
|
||||
for (uint n = 0; n < in.GetCount(); ++n)
|
||||
{
|
||||
RenderPrimitive *dl = in[n];
|
||||
|
||||
bool r = false;
|
||||
switch (dl->type)
|
||||
{
|
||||
case RenderPrimitive::TypeDisplayList:
|
||||
r = CullPrimitive(dl->dlst->material, dl->dlst->minmax, dl->item);
|
||||
break;
|
||||
case RenderPrimitive::TypeTerrainPatch:
|
||||
r = CullPrimitive(dl->patch->terrain->render_data->material, dl->patch->minmax, dl->item);
|
||||
break;
|
||||
|
||||
default:
|
||||
case RenderPrimitive::TypeEmitter:
|
||||
break;
|
||||
}
|
||||
if (r)
|
||||
out.Push(dl);
|
||||
}
|
||||
|
||||
// Prepare shadow map if required.
|
||||
if (out.GetCount() == 0)
|
||||
use_shadow = false;
|
||||
|
||||
else
|
||||
{
|
||||
use_shadow = renderer.gpu_config.enable_shadow && (light.shadow == Core::Light::Shadow_Map);
|
||||
if ((rc == DrawContext::Alpha) && (light.shadow_cast_all == false))
|
||||
use_shadow = false;
|
||||
|
||||
bool use_shadow_matrix = false;
|
||||
if ((light.model == Core::Light::Model_Spot) && !light.projection_texture.IsEmpty())
|
||||
use_shadow_matrix = true;
|
||||
|
||||
if (use_shadow || use_shadow_matrix)
|
||||
renderer.PrepareShadowMap(light, use_shadow);
|
||||
}
|
||||
}
|
||||
|
||||
PrepareLightJob(const Renderer &_renderer, const Core::Light &_light, const Stack <RenderPrimitive *> &_in, Stack <RenderPrimitive *> &_out, DrawContext::Render _rc) : Job("Light Draw Setup"), renderer(_renderer), light(_light), in(_in), out(_out), rc(_rc) {}
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static MaterialShader::Variant DispatchLightShaderVariant(Core::Light::Model type, bool shadow, bool projection_map)
|
||||
{
|
||||
static MaterialShader::Variant table[2][2][Core::Light::Model_Last] =
|
||||
{
|
||||
{
|
||||
{ MaterialShader::Last, MaterialShader::FS_PointLight, MaterialShader::FS_LinearLight, MaterialShader::FS_SpotLight },
|
||||
{ MaterialShader::Last, MaterialShader::FS_PointLightShadowMapping, MaterialShader::FS_LinearLightShadowMapping, MaterialShader::FS_SpotLightShadowMapping }
|
||||
},
|
||||
{
|
||||
{ MaterialShader::Last, MaterialShader::FS_PointLight, MaterialShader::FS_LinearLight, MaterialShader::FS_SpotLightProjection },
|
||||
{ MaterialShader::Last, MaterialShader::FS_PointLightShadowMapping, MaterialShader::FS_LinearLightShadowMapping, MaterialShader::FS_SpotLightProjectionShadowMapping }
|
||||
}
|
||||
};
|
||||
return table[projection_map ? 1 : 0][shadow ? 1 : 0][type];
|
||||
}
|
||||
void Renderer::DrawForwardBaseConstant(DrawContext::Render rc, const Stack <RenderPrimitive *> &dl_list)
|
||||
{
|
||||
ScopedPerfEvent event(this, __FUNCTION__, Color::Blue);
|
||||
|
||||
DrawContext dc(rc, DrawContext::Base, MaterialShader::FS_Constant);
|
||||
SetupForwardContext(dc.ctx);
|
||||
DrawList(dl_list, dc);
|
||||
}
|
||||
void Renderer::DrawForwardLightContribution(DrawContext::Render rc, const List <Core::Light *> &light_list, const AutoStack <PrepareLightJob *> &setup_jobs, const Array <Stack <RenderPrimitive *> > &dl_list)
|
||||
{
|
||||
ScopedPerfEvent event(this, "Render light contribution", Color::Blue);
|
||||
|
||||
DrawContext dc(rc, DrawContext::Light, MaterialShader::FS_Constant);
|
||||
SetupForwardContext(dc.ctx);
|
||||
|
||||
EnableDepthWrite(false);
|
||||
SetDepthFunc(DepthLessEqual);
|
||||
|
||||
uint job_count = 0;
|
||||
ListForeachPtr(Core::Light *, l, light_list)
|
||||
{
|
||||
if (l->model == Core::Light::Model_None)
|
||||
continue;
|
||||
|
||||
// Wait for the light setup job to complete.
|
||||
Platform::Get().job_manager->JoinJob(setup_jobs[job_count]);
|
||||
|
||||
if (dl_list[job_count].GetCount())
|
||||
{
|
||||
bool use_shadow = setup_jobs[job_count]->use_shadow;
|
||||
|
||||
if (use_shadow)
|
||||
{
|
||||
PerfBeginEvent("Render shadow map", Color::Yellow);
|
||||
|
||||
EnableDepthWrite(true);
|
||||
RenderShadowMap(*l);
|
||||
EnableDepthWrite(false);
|
||||
|
||||
SetCurrentFBO(gpu_config.use_rtt ? render_fbo : NULL);
|
||||
SetupForwardContext(dc.ctx);
|
||||
|
||||
PerfEndEvent();
|
||||
}
|
||||
|
||||
// Add contribution.
|
||||
dc.ctx.variant = DispatchLightShaderVariant(l->model, use_shadow, asbool(l->projection_texture));
|
||||
dc.light = l;
|
||||
|
||||
DrawList(dl_list[job_count], dc);
|
||||
}
|
||||
|
||||
++stats.light_processed;
|
||||
++job_count;
|
||||
}
|
||||
}
|
||||
void Renderer::RenderListForward(Stack <RenderPrimitive *> &dl_list, DrawContext::Render rc)
|
||||
{
|
||||
ScopedPerfEvent event(this, __FUNCTION__, Color::Red);
|
||||
|
||||
if (dl_list.GetCount() == 0)
|
||||
return;
|
||||
|
||||
// Setup all lights.
|
||||
List <Core::Light *> frustum_light_list;
|
||||
environment_interface->GetLightsInFrustum(view_item->GetMatrix().GetRow(3), frustum, frustum_light_list);
|
||||
|
||||
Array <Stack <RenderPrimitive *> > light_dl_list(frustum_light_list.GetCount());
|
||||
AutoStack <PrepareLightJob *> light_setup_job(frustum_light_list.GetCount());
|
||||
|
||||
uint job_count = 0;
|
||||
ListForeachPtr(Core::Light *, l, frustum_light_list)
|
||||
{
|
||||
if (l->model == Core::Light::Model_None)
|
||||
continue;
|
||||
|
||||
if (PrepareLightJob *job = new PrepareLightJob(*this, *l, dl_list, light_dl_list[job_count++], rc))
|
||||
{
|
||||
light_setup_job.Push(job);
|
||||
Platform::Get().job_manager->EnqueueJob(job);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw all passes.
|
||||
DrawForwardBaseConstant(rc, dl_list);
|
||||
DrawForwardLightContribution(rc, frustum_light_list, light_setup_job, light_dl_list);
|
||||
|
||||
//
|
||||
CollectJobsPerf(light_setup_job, job_count, stats.bench_prepare_light);
|
||||
|
||||
// Restore default state.
|
||||
SetCullFunc(CullFront);
|
||||
|
||||
SetDepthFunc(DepthLess);
|
||||
EnableDepthWrite(true);
|
||||
|
||||
EnableBlending(false);
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
462
include/engine/gpu/gpu_renderer.cpp
Normal file
462
include/engine/gpu/gpu_renderer.cpp
Normal file
@ -0,0 +1,462 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "core/renderer_environment_interface.h"
|
||||
#include "core/renderer_resource_factory.h"
|
||||
#include "core/camera.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::GPU;
|
||||
using Render::TextureParm;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::SetViewMatrix(const Matrix4 &m, const Matrix4 *inverse)
|
||||
{
|
||||
m_view = m;
|
||||
m_iview = inverse ? *inverse : m.InversedFast();
|
||||
}
|
||||
void Renderer::SetProjectionMatrix(const Matrix4 &m)
|
||||
{ m_projection = m; }
|
||||
void Renderer::SetWorldMatrix(const Matrix4 &m, const Matrix4 *inverse)
|
||||
{
|
||||
m_world = m;
|
||||
m_iworld = inverse ? *inverse : m.InversedFast();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int Renderer::GetAnisotropySampleCount(TextureParm::Anisotropy aniso) const
|
||||
{
|
||||
uint sample = 1;
|
||||
|
||||
switch (aniso)
|
||||
{
|
||||
default:
|
||||
sample = registry.GetInteger("Texture:Filtering:Sample", 4);
|
||||
break;
|
||||
|
||||
case TextureParm::AnisotropyNone: sample = 1; break;
|
||||
case TextureParm::Anisotropy2x: sample = 2; break;
|
||||
case TextureParm::Anisotropy4x: sample = 4; break;
|
||||
case TextureParm::Anisotropy8x: sample = 8; break;
|
||||
case TextureParm::Anisotropy16x: sample = 16; break;
|
||||
}
|
||||
|
||||
if (sample > gpu_config.max_anisotropy)
|
||||
sample = gpu_config.max_anisotropy;
|
||||
|
||||
return sample;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::SetDisplayList(DisplayList *dlist)
|
||||
{
|
||||
SetIndexSource(dlist ? dlist->idx.c_ptr() : NULL);
|
||||
SetVertexSource(dlist ? dlist->vtx.c_ptr() : NULL, dlist ? dlist->stride : 0);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::SetMaterial(const GPU::Material &m, const DrawContext::Context &ctx, bool force_alpha)
|
||||
{
|
||||
bool fog_enabled = (environment_interface != NULL) && environment_interface->IsFogEnabled();
|
||||
|
||||
if (m.renderword & Core::Material::Render_DoubleSided)
|
||||
EnableCulling(false);
|
||||
|
||||
if (m.renderword & Core::Material::Render_AlphaTest)
|
||||
if (gpu_config.enable_aa)
|
||||
EnableAlphaToCoverage(true);
|
||||
|
||||
uint blendop = m.blendop;
|
||||
if ((blendop == Core::Material::Blend_None) && force_alpha)
|
||||
blendop = Core::Material::Blend_Alpha;
|
||||
|
||||
switch (ctx.render)
|
||||
{
|
||||
case DrawContext::Deferred:
|
||||
case DrawContext::Opaque:
|
||||
break;
|
||||
|
||||
case DrawContext::Alpha:
|
||||
switch (ctx.draw)
|
||||
{
|
||||
case DrawContext::Base:
|
||||
switch (blendop)
|
||||
{
|
||||
case Core::Material::Blend_Add:
|
||||
if (fog_enabled)
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOne);
|
||||
else
|
||||
SetBlendFunc(BlendOne, BlendOne);
|
||||
break;
|
||||
|
||||
case Core::Material::Blend_Alpha:
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case DrawContext::Light:
|
||||
switch (blendop)
|
||||
{
|
||||
case Core::Material::Blend_Add:
|
||||
if (fog_enabled)
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOne);
|
||||
else
|
||||
SetBlendFunc(BlendOne, BlendOne);
|
||||
break;
|
||||
|
||||
case Core::Material::Blend_Alpha:
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOne);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Renderer::UnsetMaterial(const GPU::Material &m, const DrawContext::Context &ctx)
|
||||
{
|
||||
if (m.renderword & Core::Material::Render_DoubleSided)
|
||||
EnableCulling(true);
|
||||
|
||||
if (m.renderword & Core::Material::Render_AlphaTest)
|
||||
if (gpu_config.enable_aa)
|
||||
EnableAlphaToCoverage(false);
|
||||
|
||||
switch (ctx.render)
|
||||
{
|
||||
case DrawContext::Deferred:
|
||||
case DrawContext::Opaque:
|
||||
break;
|
||||
|
||||
case DrawContext::Alpha:
|
||||
switch (ctx.draw)
|
||||
{
|
||||
case DrawContext::Base:
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
|
||||
break;
|
||||
|
||||
case DrawContext::Light:
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOne);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::DrawSetState(Core::Material::BlendOperator bo, Core::Material::RenderWord f)
|
||||
{
|
||||
switch (bo)
|
||||
{
|
||||
default:
|
||||
case Core::Material::Blend_None:
|
||||
break;
|
||||
|
||||
case Core::Material::Blend_Alpha:
|
||||
EnableBlending(true);
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
|
||||
break;
|
||||
case Core::Material::Blend_Add:
|
||||
EnableBlending(true);
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOne);
|
||||
break;
|
||||
}
|
||||
|
||||
EnableDepthWrite(!asbool(f & Core::Material::Render_NoZWrite));
|
||||
EnableDepthTest(!asbool(f & Core::Material::Render_NoZTest));
|
||||
EnableCulling(!asbool(f & Core::Material::Render_DoubleSided));
|
||||
}
|
||||
void Renderer::DrawRestoreState(Core::Material::BlendOperator bo, Core::Material::RenderWord f)
|
||||
{
|
||||
EnableCulling(true);
|
||||
EnableDepthTest(true);
|
||||
EnableDepthWrite(true);
|
||||
|
||||
if (bo != Core::Material::Blend_None)
|
||||
{
|
||||
EnableBlending(false);
|
||||
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
|
||||
}
|
||||
}
|
||||
void Renderer::DrawLine(uint count, const Vector4 *v, const Color *c, Core::Material::BlendOperator bo, Core::Material::RenderWord f, Render::Shader *r_p)
|
||||
{
|
||||
DrawSetState(bo, f);
|
||||
|
||||
// Dispatch inputs to the correct program.
|
||||
Shader *p = (Shader *)r_p;
|
||||
|
||||
if (p == NULL)
|
||||
{
|
||||
p = c ? single_color_program : simple_program;
|
||||
|
||||
if (!p)
|
||||
return;
|
||||
}
|
||||
SetShaderProgram(p);
|
||||
|
||||
ShaderInput *vtx_parm = p->GetInput(Core::ShaderInput::Position),
|
||||
*color_parm = p->GetInput(Core::ShaderInput::VertexColor);
|
||||
|
||||
SetIndexSource(NULL); // FIXME broken on the DirectX back-end
|
||||
SetVertexSource(NULL, 0); // FIXME broken on the DirectX back-end
|
||||
|
||||
if (vtx_parm)
|
||||
p->Set(*vtx_parm->location, 3, Types::ValueFloat, false, sizeof(Vector4), (const void *)v);
|
||||
if (color_parm && c)
|
||||
p->Set(*color_parm->location, 4, Types::ValueFloat, false, sizeof(Color), (const void *)c);
|
||||
|
||||
p->SetRendererInputs(*this);
|
||||
p->SetTransformInputs(m_projection, m_view, m_iview, &m_world, &m_iworld);
|
||||
|
||||
DrawElements(Types::PrimitiveLine, 2 * count);
|
||||
|
||||
if (vtx_parm)
|
||||
p->Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
if (color_parm)
|
||||
p->Set(*color_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
|
||||
DrawRestoreState(bo, f);
|
||||
}
|
||||
void Renderer::DrawTriangle(uint count, const Vector4 *v, const ushort *idx, const Color *c, const Vector2 *uv, const Render::Texture *t, Core::Material::BlendOperator bo, Core::Material::RenderWord f, Render::Shader *r_p)
|
||||
{
|
||||
DrawSetState(bo, f);
|
||||
|
||||
// Dispatch inputs to the correct program.
|
||||
Shader *p = (Shader *)r_p;
|
||||
|
||||
if (p == NULL)
|
||||
{
|
||||
if (c)
|
||||
p = t ? single_texture_color_program : single_color_program;
|
||||
else
|
||||
if (t)
|
||||
p = single_texture_program;
|
||||
|
||||
if (!p)
|
||||
return;
|
||||
}
|
||||
SetShaderProgram(p);
|
||||
|
||||
// If no indice were provided, assume a linear attribute array.
|
||||
Array <ushort> indice;
|
||||
if (!idx)
|
||||
if (indice.Allocate(count * 3))
|
||||
{
|
||||
idx = indice;
|
||||
for (uint n = 0; n < count * 3; ++n)
|
||||
indice[n] = ushort(n);
|
||||
}
|
||||
|
||||
// Set program inputs.
|
||||
ShaderInput *vtx_parm = p->GetInput(Core::ShaderInput::Position),
|
||||
*uv_parm = p->GetInput(Core::ShaderInput::UV0),
|
||||
*color_parm = p->GetInput(Core::ShaderInput::VertexColor),
|
||||
*texture_parm = p->GetInput(Core::ShaderInput::Texture2D);
|
||||
|
||||
if (gpu_config.can_stream_vertex_from_memory)
|
||||
{
|
||||
SetIndexSource(NULL);
|
||||
SetVertexSource(NULL, 0);
|
||||
|
||||
if (vtx_parm)
|
||||
p->Set(*vtx_parm->location, 3, Types::ValueFloat, false, sizeof(Vector4), (const void *)v);
|
||||
if (uv_parm && uv)
|
||||
p->Set(*uv_parm->location, 2, Types::ValueFloat, false, sizeof(Vector2), (const void *)uv);
|
||||
if (color_parm && c)
|
||||
p->Set(*color_parm->location, 4, Types::ValueFloat, false, sizeof(Color), (const void *)c);
|
||||
}
|
||||
else
|
||||
{
|
||||
DirectVertexLayout layout;
|
||||
BuildDirectVertexLayout(count * 3, idx, v, c, uv, layout, direct_idx_vbo, direct_vtx_vbo);
|
||||
|
||||
SetIndexSource(direct_idx_vbo);
|
||||
SetVertexSource(direct_vtx_vbo, layout.stride);
|
||||
|
||||
if (vtx_parm)
|
||||
p->Set(*vtx_parm->location, 3, Types::ValueFloat, false, layout.stride, (const void *)layout.vtx_offset);
|
||||
if (uv_parm && uv)
|
||||
p->Set(*uv_parm->location, 2, Types::ValueFloat, false, layout.stride, (const void *)layout.uv_offset);
|
||||
if (color_parm && c)
|
||||
p->Set(*color_parm->location, 4, Types::ValueUByte, true, layout.stride, (const void *)layout.color_offset);
|
||||
}
|
||||
|
||||
if (texture_parm && t)
|
||||
p->Set(*texture_parm->location, *t, texture_parm->index);
|
||||
|
||||
p->SetRendererInputs(*this);
|
||||
p->SetTransformInputs(m_projection, m_view, m_iview, &m_world, &m_iworld);
|
||||
p->CommitInputs();
|
||||
|
||||
DrawElements(Types::PrimitiveTriangle, count * 3, Types::ValueUShort, gpu_config.can_stream_vertex_from_memory ? idx : 0);
|
||||
|
||||
if (vtx_parm)
|
||||
p->Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
if (uv_parm)
|
||||
p->Set(*uv_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
if (color_parm)
|
||||
p->Set(*color_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
|
||||
DrawRestoreState(bo, f);
|
||||
stats.triangle_drawn += count;
|
||||
}
|
||||
void Renderer::DrawSprite(uint count, const Vector4 *v, const Color *c, const float *s, const Render::Texture *t, float g_size, Core::Material::BlendOperator bo, Core::Material::RenderWord f, Render::Shader *r_p)
|
||||
{
|
||||
Array <Vector4> sprite_p;
|
||||
Array <Color> sprite_c;
|
||||
Array <Vector2> sprite_uv;
|
||||
Array <ushort> sprite_idx;
|
||||
|
||||
Shader *p = (Shader *)r_p;
|
||||
|
||||
if (p == NULL)
|
||||
p = simple_program;
|
||||
|
||||
// Setup display list.
|
||||
if (!sprite_p.Allocate(count * 4) || !sprite_idx.Allocate(count * 3 * 2))
|
||||
return;
|
||||
|
||||
Vector4 *p_v = sprite_p.c_ptr();
|
||||
ushort *idx = sprite_idx.c_ptr();
|
||||
|
||||
Vector4 left = m_view.GetRow(0),
|
||||
up = m_view.GetRow(1);
|
||||
|
||||
if (s)
|
||||
for (uint n = 0; n < count; ++n)
|
||||
{
|
||||
register float k = g_size * s[n];
|
||||
Vector4 uml = (up - left) * k;
|
||||
Vector4 lpu = (left + up) * k;
|
||||
Vector4 lmu = (left - up) * k;
|
||||
*p_v++ = v[n] + uml;
|
||||
*p_v++ = v[n] + lpu;
|
||||
*p_v++ = v[n] + lmu;
|
||||
*p_v++ = v[n] - lpu;
|
||||
}
|
||||
else
|
||||
for (uint n = 0; n < count; ++n)
|
||||
{
|
||||
Vector4 uml = (up - left) * g_size;
|
||||
Vector4 lpu = (left + up) * g_size;
|
||||
Vector4 lmu = (left - up) * g_size;
|
||||
*p_v++ = v[n] + uml;
|
||||
*p_v++ = v[n] + lpu;
|
||||
*p_v++ = v[n] + lmu;
|
||||
*p_v++ = v[n] - lpu;
|
||||
}
|
||||
|
||||
for (ushort n = 0; n < count; ++n)
|
||||
{
|
||||
ushort s = (ushort)(n << 2);
|
||||
*idx++ = s; *idx++ = s + 1; *idx++ = s + 2;
|
||||
*idx++ = s; *idx++ = s + 2; *idx++ = s + 3;
|
||||
}
|
||||
|
||||
if (c) // Color.
|
||||
{
|
||||
if (!sprite_c.Allocate(count * 4))
|
||||
return;
|
||||
|
||||
Color *p_c = sprite_c.c_ptr();
|
||||
for (uint n = 0; n < count; ++n)
|
||||
{
|
||||
const Color &cl = c[n];
|
||||
for (uint i = 0; i < 4; ++i)
|
||||
*p_c++ = cl;
|
||||
}
|
||||
|
||||
p = single_color_program;
|
||||
}
|
||||
|
||||
if (t) // Texture.
|
||||
{
|
||||
if (!sprite_uv.Allocate(count * 4))
|
||||
return;
|
||||
|
||||
Vector2 *p_uv = sprite_uv.c_ptr();
|
||||
for (uint n = 0; n < count; ++n)
|
||||
{
|
||||
p_uv[0].Set(0, 0); p_uv[1].Set(1, 0);
|
||||
p_uv[2].Set(1, 1); p_uv[3].Set(0, 1);
|
||||
p_uv += 4;
|
||||
}
|
||||
|
||||
p = c ? single_texture_color_program : single_texture_program;
|
||||
}
|
||||
|
||||
// Compute aspect ratio.
|
||||
Matrix4 m_ar;
|
||||
if (view_item)
|
||||
m_ar = Matrix4::ScaleMatrix(view_item->ComputeAspectRatioCorrection(viewport));
|
||||
else m_ar = Matrix4::ScaleMatrix(Vector4(viewport.GetHeight() / viewport.GetWidth(), 1, 0, 1));
|
||||
|
||||
// Set program inputs.
|
||||
DrawSetState(bo, f);
|
||||
SetShaderProgram(p);
|
||||
|
||||
ShaderInput *vtx_parm = p->GetInput(Core::ShaderInput::Position),
|
||||
*uv_parm = p->GetInput(Core::ShaderInput::UV0),
|
||||
*color_parm = p->GetInput(Core::ShaderInput::VertexColor),
|
||||
*texture_parm = p->GetInput(Core::ShaderInput::Texture2D);
|
||||
|
||||
if (vtx_parm)
|
||||
p->Set(*vtx_parm->location, 3, Types::ValueFloat, false, sizeof(Vector4), (const void *)sprite_p.c_ptr());
|
||||
if (uv_parm && t)
|
||||
p->Set(*uv_parm->location, 2, Types::ValueFloat, false, sizeof(Vector2), (const void *)sprite_uv.c_ptr());
|
||||
if (color_parm && c)
|
||||
p->Set(*color_parm->location, 4, Types::ValueFloat, false, sizeof(Color), (const void *)sprite_c.c_ptr());
|
||||
if (texture_parm && t)
|
||||
p->Set(*texture_parm->location, *t, texture_parm->index);
|
||||
|
||||
p->SetRendererInputs(*this);
|
||||
p->SetTransformInputs(m_projection, m_view, m_iview, &m_world, &m_iworld);
|
||||
p->CommitInputs();
|
||||
|
||||
DrawElements(Types::PrimitiveTriangle, sprite_idx.GetCount(), Types::ValueUShort, sprite_idx.c_ptr());
|
||||
|
||||
if (vtx_parm)
|
||||
p->Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
if (uv_parm)
|
||||
p->Set(*uv_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
if (color_parm)
|
||||
p->Set(*color_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
|
||||
DrawRestoreState(bo, f);
|
||||
stats.triangle_drawn += count * 2;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Renderer::Renderer() : terrain_patch_vtx(Alloc::RendererTerrain), terrain_patch_cache(Alloc::RendererTerrain)
|
||||
{
|
||||
core_resource_factory = new Render::RendererResourceFactory(*this);
|
||||
|
||||
pending_shadow_map_refresh = false;
|
||||
pending_core_shader_refresh = false;
|
||||
|
||||
render_technique = TechniqueForward;
|
||||
|
||||
m_view = Matrix4::IdentityMatrix();
|
||||
m_iview = Matrix4::IdentityMatrix();
|
||||
m_world = Matrix4::IdentityMatrix();
|
||||
m_iworld = Matrix4::IdentityMatrix();
|
||||
|
||||
m_projection = Matrix4::IdentityMatrix();
|
||||
|
||||
pcf_radius = 1.75f;
|
||||
frame_clock = 0.f;
|
||||
|
||||
clipping.Set(-1, -1, -1, -1);
|
||||
output_fbo = NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@ -218,6 +218,7 @@ protected:
|
||||
radial_blur_program,
|
||||
motion_blur_program,
|
||||
|
||||
resolve_msaa_depth_program,
|
||||
skybox_program;
|
||||
|
||||
/*!
|
||||
@ -325,6 +326,7 @@ public:
|
||||
bool ApplySSAOFilter(Render::Texture *t_in, Render::Texture *t_out, const Stack <RenderPrimitive *> [2], float strength, float radius, float clip_distance, float blur_radius);
|
||||
bool ApplyRadialBlur(Render::Texture *t_in, Render::Texture *t_out, float strength, float center_x, float center_y);
|
||||
bool ApplyMotionBlur(Render::Texture *t_in, Render::Texture *t_out, const Stack <RenderPrimitive *> [2], float strength, int quality = 1);
|
||||
bool ApplyResolveMSAADepth(Render::Texture *t_depth_msaa, Render::Texture *t_out);
|
||||
|
||||
void GetPostProcessNormalDepth(const Stack <RenderPrimitive *> display_lists[2]);
|
||||
|
||||
|
||||
674
include/engine/gpu/gpu_shader.cpp
Normal file
674
include/engine/gpu/gpu_shader.cpp
Normal file
@ -0,0 +1,674 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#ifndef __PLATFORM_IOS__
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "core/renderer_environment_interface.h"
|
||||
#include "core/light.h"
|
||||
#include "core/camera.h"
|
||||
#include "core/object.h"
|
||||
#include "core/shader.h"
|
||||
#include "container/narray.h"
|
||||
#include "platform_config.h"
|
||||
#include "platform.h"
|
||||
#include "log/file_log.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Shader::SetVertexStreamInputs(DisplayList &dls)
|
||||
{
|
||||
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryVertexStream].GetCount(); ++n)
|
||||
{
|
||||
ShaderInput *input = &input_list[Core::ShaderInput::CategoryVertexStream][n];
|
||||
|
||||
switch (input->semantic)
|
||||
{
|
||||
case Core::ShaderInput::Position:
|
||||
Set(*input->location, 3, Types::ValueHalfFloat, false, dls.stride, (const void *)dls.vertex_offset);
|
||||
break;
|
||||
case Core::ShaderInput::Normal:
|
||||
Set(*input->location, 3, Types::ValueByte, true, dls.stride, (const void *)dls.normal_offset);
|
||||
break;
|
||||
case Core::ShaderInput::VertexColor:
|
||||
Set(*input->location, 4, Types::ValueUByte, true, dls.stride, (const void *)dls.rgb_offset);
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::Tangent:
|
||||
Set(*input->location, 3, Types::ValueByte, true, dls.stride, (const void *)dls.tangent_offset);
|
||||
break;
|
||||
case Core::ShaderInput::Bitangent:
|
||||
Set(*input->location, 3, Types::ValueByte, true, dls.stride, (const void *)(dls.tangent_offset + 4 * sizeof(char)));
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::BoneIndex:
|
||||
Set(*input->location, 4, Types::ValueUByte, false, dls.stride, (const void *)dls.skinning_offset);
|
||||
break;
|
||||
case Core::ShaderInput::BoneWeight:
|
||||
Set(*input->location, 4, Types::ValueUByte, true, dls.stride, (const void *)(dls.skinning_offset + 4 * sizeof(char)));
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::UV0:
|
||||
case Core::ShaderInput::UV1:
|
||||
case Core::ShaderInput::UV2:
|
||||
{
|
||||
int uv_index = (int)input->semantic - (int)Core::ShaderInput::UV0;
|
||||
Set(*input->location, 2, Types::ValueHalfFloat, false, dls.stride, (const void *)dls.uv_offset[uv_index]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Shader::SetSkinInputs(DisplayList &dls, Core::Skin &skin)
|
||||
{
|
||||
for (uint n = 0; n < input_list[Core::ShaderInput::CategorySkin].GetCount(); ++n)
|
||||
{
|
||||
ShaderInput *input = &input_list[Core::ShaderInput::CategorySkin][n];
|
||||
|
||||
switch (input->semantic)
|
||||
{
|
||||
case Core::ShaderInput::BoneMatrix:
|
||||
if (float *m = (float *)alloca(4 * 4 * sizeof(float) * dls.bone.GetCount()))
|
||||
{
|
||||
float *p_m = m;
|
||||
for (uint n = 0; n < dls.bone.GetCount(); ++n)
|
||||
{
|
||||
Memory::Copy(p_m, skin.bones_mtx[dls.bone[n]].m, 4 * 4 * sizeof(float));
|
||||
p_m += 4 * 4;
|
||||
}
|
||||
|
||||
Set(*input->location, (Matrix4 *)m, dls.bone.GetCount());
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::PreviousBoneMatrix:
|
||||
if (float *m = (float *)alloca(4 * 4 * sizeof(float) * dls.bone.GetCount()))
|
||||
{
|
||||
float *p_m = m;
|
||||
for (uint n = 0; n < dls.bone.GetCount(); ++n)
|
||||
{
|
||||
Memory::Copy(p_m, skin.previous_bones_mtx[dls.bone[n]].m, 4 * 4 * sizeof(float));
|
||||
p_m += 4 * 4;
|
||||
}
|
||||
|
||||
Set(*input->location, (Matrix4 *)m, dls.bone.GetCount());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Shader::SetConstantInputs()
|
||||
{
|
||||
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryConstant].GetCount(); ++n)
|
||||
{
|
||||
ShaderInput *input = &input_list[Core::ShaderInput::CategoryConstant][n];
|
||||
|
||||
switch (input->semantic)
|
||||
{
|
||||
case Core::ShaderInput::Constant:
|
||||
switch (input->data_type)
|
||||
{
|
||||
default:
|
||||
case Core::ShaderInput::Matrix3:
|
||||
case Core::ShaderInput::Matrix4:
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::DataTexture2D:
|
||||
case Core::ShaderInput::DataTexture3D:
|
||||
case Core::ShaderInput::DataTextureCube:
|
||||
Set(*input->location, *input->parm_t, input->index);
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::Int:
|
||||
Set(*input->location, (int *)&input->parm_v.x);
|
||||
break;
|
||||
case Core::ShaderInput::Float:
|
||||
Set(*input->location, &input->parm_v.x);
|
||||
break;
|
||||
case Core::ShaderInput::Vector2:
|
||||
Set(*input->location, &input->parm_v.x, 2);
|
||||
break;
|
||||
case Core::ShaderInput::Vector3:
|
||||
Set(*input->location, &input->parm_v.x, 3);
|
||||
break;
|
||||
case Core::ShaderInput::Vector4:
|
||||
Set(*input->location, &input->parm_v.x, 4);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Shader::SetTextureInputs()
|
||||
{
|
||||
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryTexture].GetCount(); ++n)
|
||||
{
|
||||
ShaderInput *input = &input_list[Core::ShaderInput::CategoryTexture][n];
|
||||
|
||||
switch (input->semantic)
|
||||
{
|
||||
case Core::ShaderInput::Texture2D:
|
||||
case Core::ShaderInput::Texture3D:
|
||||
case Core::ShaderInput::TextureCube:
|
||||
if (input->parm_t)
|
||||
Set(*input->location, *input->parm_t, input->index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Shader::SetRendererInputs(Renderer &r, Material *m)
|
||||
{
|
||||
Color fog_color;
|
||||
float fog_near = 0, fog_far = 0;
|
||||
|
||||
bool fog_enabled = r.environment_interface ? r.environment_interface->GetFogConfiguration(fog_color, fog_near, fog_far) : false;
|
||||
|
||||
if (r.performance_tools.disable_fog)
|
||||
fog_enabled = false;
|
||||
if (m && (m->blendop == Core::Material::Blend_Add))
|
||||
fog_enabled = false;
|
||||
|
||||
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryRenderer].GetCount(); ++n)
|
||||
{
|
||||
ShaderInput *input = &input_list[Core::ShaderInput::CategoryRenderer][n];
|
||||
|
||||
switch (input->semantic)
|
||||
{
|
||||
case Core::ShaderInput::Clock:
|
||||
Set(*input->location, r.frame_clock);
|
||||
break;
|
||||
case Core::ShaderInput::TimeOfDay:
|
||||
Set(*input->location, r.environment_interface->GetTimeOfDay());
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::ViewVector:
|
||||
{
|
||||
Vector4 tmp = r.GetCamera()->GetMatrix().GetRow(2);
|
||||
Set(*input->location, &tmp.x, 3);
|
||||
}
|
||||
break;
|
||||
case Core::ShaderInput::ViewPosition:
|
||||
{
|
||||
Vector4 tmp = r.GetCamera()->GetMatrix().GetRow(3);
|
||||
Set(*input->location, &tmp.x, 4);
|
||||
}
|
||||
break;
|
||||
case Core::ShaderInput::Viewport:
|
||||
{
|
||||
const fRect viewport = r.GetViewport();
|
||||
float v[4] = { viewport.sx, viewport.sy, viewport.GetWidth(), viewport.GetHeight() };
|
||||
Set(*input->location, v, 4);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::ZNear:
|
||||
Set(*input->location, r.GetCamera()->GetNearClippingPlane());
|
||||
break;
|
||||
case Core::ShaderInput::ZFar:
|
||||
Set(*input->location, r.GetCamera()->GetFarClippingPlane());
|
||||
break;
|
||||
case Core::ShaderInput::ZoomFactor:
|
||||
Set(*input->location, r.GetCamera()->zoom_factor);
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::DisplayBufferRatio:
|
||||
{
|
||||
float v[] = { r.GetOutputAspectRatio(), 1.f };
|
||||
Set(*input->location, v, 2);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::ViewportRatio:
|
||||
{
|
||||
float v[] = { r.GetViewport().GetHeight() / r.GetViewport().GetWidth(), 1.f };
|
||||
Set(*input->location, v, 2);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::FxScale:
|
||||
Set(*input->location, float(r.fx_scale));
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::InverseBufferSize:
|
||||
{
|
||||
tVector2 <uint> d = r.GetOutputDimensions();
|
||||
float v[] = { 1.f / d.x, 1.f / d.y };
|
||||
Set(*input->location, v, 2);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::InverseViewportSize:
|
||||
{
|
||||
float v[] = { 1.f / r.GetViewport().GetWidth(), 1.f / r.GetViewport().GetHeight() };
|
||||
Set(*input->location, v, 2);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::ViewDepthOffset:
|
||||
{
|
||||
float k = 0.f;
|
||||
Set(*input->location, &k);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::AmbientColor:
|
||||
{
|
||||
Color ambient = r.environment_interface->GetAmbientColor();
|
||||
Set(*input->location, &ambient.x, 3);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::FogColor:
|
||||
Set(*input->location, &fog_color.x, 3);
|
||||
break;
|
||||
case Core::ShaderInput::FogNear:
|
||||
Set(*input->location, fog_near);
|
||||
break;
|
||||
case Core::ShaderInput::FogFar:
|
||||
Set(*input->location, fog_far);
|
||||
break;
|
||||
case Core::ShaderInput::FogInverseRange:
|
||||
{
|
||||
bool use_fog = fog_enabled && (fog_far > 0.0);
|
||||
if (m && (m->renderword & Core::Material::Render_NoFog))
|
||||
use_fog = false;
|
||||
|
||||
Set(*input->location, use_fog ? 1.f / (fog_far - fog_near) : -1.f);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::DepthBuffer:
|
||||
if (r.render_technique == Renderer::TechniqueDeferred)
|
||||
Set(*input->location, *r.t_gbuffer[0], input->index);
|
||||
else Set(*input->location, *r.t_depth, input->index);
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::FrameBuffer:
|
||||
if (r.t_fx[0].IsValid())
|
||||
Set(*input->location, *r.t_fx[0], input->index);
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::GBuffer0:
|
||||
case Core::ShaderInput::GBuffer1:
|
||||
case Core::ShaderInput::GBuffer2:
|
||||
case Core::ShaderInput::GBuffer3:
|
||||
Set(*input->location, *r.t_gbuffer[input->semantic - Core::ShaderInput::GBuffer0], input->index);
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::NoiseMap:
|
||||
if (r.t_noise.IsValid())
|
||||
Set(*input->location, *r.t_noise, input->index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Shader::SetTransformInputs(const Matrix4 &v_pm, const Matrix4 &v_m, const Matrix4 &v_im, const Matrix4 *i_m, const Matrix4 *i_im, uint count)
|
||||
{
|
||||
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryTransform].GetCount(); ++n)
|
||||
{
|
||||
ShaderInput *input = &input_list[Core::ShaderInput::CategoryTransform][n];
|
||||
|
||||
switch (input->semantic)
|
||||
{
|
||||
case Core::ShaderInput::NormalMatrix:
|
||||
if (Matrix3 *n_m = (Matrix3 *)alloca(sizeof(Matrix3) * count))
|
||||
{
|
||||
for (uint n = 0; n < count; ++n)
|
||||
n_m[n] = Matrix3::FromMatrix4(i_m[n]).Normalized();
|
||||
Set(*input->location, n_m, count);
|
||||
}
|
||||
break;
|
||||
case Core::ShaderInput::NormalViewMatrix:
|
||||
if (Matrix3 *nv_m = (Matrix3 *)alloca(sizeof(Matrix3) * count))
|
||||
{
|
||||
Matrix3 vn_m = Matrix3::FromMatrix4(v_m).Normalized().Transposed();
|
||||
for (uint n = 0; n < count; ++n)
|
||||
nv_m[n] = vn_m * Matrix3::FromMatrix4(i_m[n]).Normalized();
|
||||
Set(*input->location, nv_m, count);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::ModelMatrix:
|
||||
Set(*input->location, i_m, count);
|
||||
break;
|
||||
case Core::ShaderInput::ViewMatrix:
|
||||
Set(*input->location, v_im);
|
||||
break;
|
||||
case Core::ShaderInput::ProjectionMatrix:
|
||||
Set(*input->location, v_pm);
|
||||
break;
|
||||
case Core::ShaderInput::ModelViewMatrix:
|
||||
if (Matrix4 *mv_m = (Matrix4 *)alloca(sizeof(Matrix4) * count))
|
||||
{
|
||||
for (uint n = 0; n < count; ++n)
|
||||
mv_m[n] = v_im * i_m[n];
|
||||
Set(*input->location, mv_m, count);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::ModelViewProjectionMatrix:
|
||||
if (Matrix4 *mvp_m = (Matrix4 *)alloca(sizeof(Matrix4) * count))
|
||||
{
|
||||
for (uint n = 0; n < count; ++n)
|
||||
mvp_m[n] = v_pm * (v_im * i_m[n]);
|
||||
Set(*input->location, mvp_m, count);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::InverseViewProjectionMatrix:
|
||||
{
|
||||
Matrix4 vpm = v_pm * v_im, ivpm;
|
||||
vpm.Inverse(ivpm);
|
||||
Set(*input->location, ivpm);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::InverseViewProjectionMatrixAtOrigin:
|
||||
{
|
||||
Matrix4 v_im_o = v_im;
|
||||
v_im_o.SetRow(3, Vector4(0, 0, 0, 1));
|
||||
|
||||
Matrix4 vpm = v_pm * v_im_o, ivpm;
|
||||
vpm.Inverse(ivpm);
|
||||
Set(*input->location, ivpm);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Shader::SetPreviousTransformInputs(const Matrix4 &v_pm, const Matrix4 &v_im, const Matrix4 *i_m, uint count)
|
||||
{
|
||||
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryPreviousTransform].GetCount(); ++n)
|
||||
{
|
||||
ShaderInput *input = &input_list[Core::ShaderInput::CategoryPreviousTransform][n];
|
||||
|
||||
switch (input->semantic)
|
||||
{
|
||||
case Core::ShaderInput::PreviousModelViewMatrix:
|
||||
if (Matrix4 *mv_m = (Matrix4 *)alloca(sizeof(Matrix4) * count))
|
||||
{
|
||||
for (uint n = 0; n < count; ++n)
|
||||
mv_m[n] = v_im * i_m[n];
|
||||
Set(*input->location, mv_m, count);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::PreviousModelViewProjectionMatrix:
|
||||
if (Matrix4 *mvp_m = (Matrix4 *)alloca(sizeof(Matrix4) * count))
|
||||
{
|
||||
for (uint n = 0; n < count; ++n)
|
||||
mvp_m[n] = v_pm * (v_im * i_m[n]);
|
||||
Set(*input->location, mvp_m, count);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Shader::SetMaterialOpacityInputs(Material &m, float opacity)
|
||||
{
|
||||
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryMaterialOpacity].GetCount(); ++n)
|
||||
{
|
||||
ShaderInput *input = &input_list[Core::ShaderInput::CategoryMaterialOpacity][n];
|
||||
|
||||
switch (input->semantic)
|
||||
{
|
||||
case Core::ShaderInput::MaterialOpacity:
|
||||
Set(*input->location, m.opacity * opacity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Shader::SetMaterialInputs(Material &m)
|
||||
{
|
||||
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryMaterial].GetCount(); ++n)
|
||||
{
|
||||
ShaderInput *input = &input_list[Core::ShaderInput::CategoryMaterial][n];
|
||||
|
||||
switch (input->semantic)
|
||||
{
|
||||
case Core::ShaderInput::MaterialDiffuse:
|
||||
Set(*input->location, &m.diffuse.x, 4);
|
||||
break;
|
||||
case Core::ShaderInput::MaterialSpecular:
|
||||
Set(*input->location, &m.specular.x, 4);
|
||||
break;
|
||||
case Core::ShaderInput::MaterialAmbient:
|
||||
Set(*input->location, &m.ambient.x, 4);
|
||||
break;
|
||||
case Core::ShaderInput::MaterialSelf:
|
||||
Set(*input->location, &m.self.x, 4);
|
||||
break;
|
||||
case Core::ShaderInput::MaterialGlossiness:
|
||||
Set(*input->location, m.glossiness);
|
||||
break;
|
||||
case Core::ShaderInput::MaterialReflection:
|
||||
Set(*input->location, m.reflection);
|
||||
break;
|
||||
case Core::ShaderInput::MaterialAlphaThreshold:
|
||||
Set(*input->location, m.athreshold);
|
||||
break;
|
||||
case Core::ShaderInput::MaterialDepthBias:
|
||||
Set(*input->location, m.depth_bias);
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::MaterialTexture0:
|
||||
case Core::ShaderInput::MaterialTexture1:
|
||||
case Core::ShaderInput::MaterialTexture2:
|
||||
case Core::ShaderInput::MaterialTexture3:
|
||||
case Core::ShaderInput::MaterialTexture4:
|
||||
case Core::ShaderInput::MaterialTexture5:
|
||||
case Core::ShaderInput::MaterialTexture6:
|
||||
case Core::ShaderInput::MaterialTexture7:
|
||||
if (Render::Texture *t = m.texture_table[input->semantic - Core::ShaderInput::MaterialTexture0])
|
||||
Set(*input->location, *t, input->index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Shader::SetLightInputs(Renderer &r, Core::Camera &view_item, Core::Light &l)
|
||||
{
|
||||
float k_clip_fade = 1.f;
|
||||
|
||||
if (l.range > 0.f) // [EJ] fade on last 10% of clip range
|
||||
{
|
||||
float c = l.clip_distance + l.range;
|
||||
float d = Vector4::Dist(view_item.GetMatrix().GetRow(3), l.GetMatrix().GetRow(3));
|
||||
|
||||
k_clip_fade = 1.f - GS::Types::Clamp((d - c * 0.9f) / (c * 0.1f));
|
||||
}
|
||||
|
||||
if (Core::Light::RenderData *light_render_data = (Core::Light::RenderData *)l.render_data.c_ptr())
|
||||
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryLight].GetCount(); ++n)
|
||||
{
|
||||
ShaderInput *input = &input_list[Core::ShaderInput::CategoryLight][n];
|
||||
|
||||
switch (input->semantic)
|
||||
{
|
||||
case Core::ShaderInput::LightRange:
|
||||
Set(*input->location, l.range);
|
||||
break;
|
||||
case Core::ShaderInput::LightSpotEdge:
|
||||
Set(*input->location, Math::Cos(l.edge_angle + l.cone_angle));
|
||||
break;
|
||||
case Core::ShaderInput::LightSpotCone:
|
||||
Set(*input->location, Math::Cos(l.cone_angle));
|
||||
break;
|
||||
case Core::ShaderInput::LightShadowBias:
|
||||
Set(*input->location, l.shadow_bias);
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::LightDiffuseColor:
|
||||
{
|
||||
Color c = l.diffuse_color * l.diffuse_intensity * k_clip_fade;
|
||||
Set(*input->location, &c.x, 3);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::LightSpecularColor:
|
||||
{
|
||||
Color c = l.specular_color * l.specular_intensity * k_clip_fade;
|
||||
Set(*input->location, &c.x, 3);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::LightShadowColor:
|
||||
Set(*input->location, &l.shadow_color.x, 3);
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::LightViewPosition:
|
||||
{
|
||||
Vector4 p = l.GetMatrix().GetRow(3) * view_item.GetInverseMatrix();
|
||||
Set(*input->location, &p.x, 3);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::LightViewDirection:
|
||||
{
|
||||
Vector4 d = l.GetMatrix().GetRow(2) * Matrix3::FromMatrix4(view_item.GetMatrix()).Normalized().Transposed();
|
||||
Set(*input->location, &d.x, 3);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::LightShadowMatrix0:
|
||||
case Core::ShaderInput::LightShadowMatrix1:
|
||||
case Core::ShaderInput::LightShadowMatrix2:
|
||||
case Core::ShaderInput::LightShadowMatrix3:
|
||||
case Core::ShaderInput::LightShadowMatrix4:
|
||||
case Core::ShaderInput::LightShadowMatrix5:
|
||||
{
|
||||
uint n = input->semantic - Core::ShaderInput::LightShadowMatrix0;
|
||||
if (n < light_render_data->shadow_data.GetCount())
|
||||
Set(*input->location, light_render_data->shadow_data[n].pmatrix * (light_render_data->shadow_data[n].imatrix * view_item.GetMatrix()));
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::InverseShadowMapSize:
|
||||
{
|
||||
float k = r.pcf_radius / r.gpu_config.shadow_size;
|
||||
Set(*input->location, k);
|
||||
}
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::LightShadowMap0:
|
||||
case Core::ShaderInput::LightShadowMap1:
|
||||
case Core::ShaderInput::LightShadowMap2:
|
||||
case Core::ShaderInput::LightShadowMap3:
|
||||
case Core::ShaderInput::LightShadowMap4:
|
||||
case Core::ShaderInput::LightShadowMap5:
|
||||
Set(*input->location, *r.shadow_map, input->index);
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::LightPSSMSliceDistance0:
|
||||
case Core::ShaderInput::LightPSSMSliceDistance1:
|
||||
case Core::ShaderInput::LightPSSMSliceDistance2:
|
||||
case Core::ShaderInput::LightPSSMSliceDistance3:
|
||||
if (light_render_data->shadow_data)
|
||||
Set(*input->location, light_render_data->shadow_data[input->semantic - Core::ShaderInput::LightPSSMSliceDistance0].slice_distance);
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::ViewToLightMatrix:
|
||||
Set(*input->location, l.GetInverseMatrix() * view_item.GetMatrix());
|
||||
break;
|
||||
|
||||
case Core::ShaderInput::LightProjectionMap:
|
||||
if (light_render_data->projection_texture.IsValid())
|
||||
Set(*input->location, *light_render_data->projection_texture, input->index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Shader::GetSemanticInputList(Core::ShaderInput::Semantic semantic)
|
||||
{
|
||||
return Core::ShaderInput::semantic_desc[semantic].category;
|
||||
}
|
||||
ShaderInput *Shader::GetInput(Core::ShaderInput::Semantic semantic) const
|
||||
{
|
||||
uint cat = GetSemanticInputList(semantic);
|
||||
for (uint n = 0; n < input_list[cat].GetCount(); ++n)
|
||||
if (input_list[cat][n].semantic == semantic)
|
||||
return &input_list[cat][n];
|
||||
return NULL;
|
||||
}
|
||||
ShaderInput *Shader::GetInput(const char *n) const
|
||||
{
|
||||
String name(n);
|
||||
for (uint l = 0; l < Core::ShaderInput::CategoryLast; ++l) // need to check all categories here
|
||||
for (uint n = 0; n < input_list[l].GetCount(); ++n)
|
||||
if (!input_list[l][n].name.IsEmpty() && (input_list[l][n].name == name))
|
||||
return &input_list[l][n];
|
||||
return NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Shader::Create(Render::ResourceFactory &rf, const Core::Shader &shader)
|
||||
{
|
||||
Free();
|
||||
|
||||
__RASSERT_MSG__(renderer.shader_compiler != NULL, String::Format("No shader compiler available for this renderer ('%s').", renderer.GetName()));
|
||||
|
||||
if (!renderer.shader_compiler->Compile(shader, *this))
|
||||
return false;
|
||||
|
||||
// Solve uniforms and attributes.
|
||||
Array <AutoPtr <ShaderInputLocation> > locations(shader.input_list.GetCount());
|
||||
|
||||
uint solved_count[Core::ShaderInput::CategoryLast], n = 0;
|
||||
Memory::Set(solved_count, 0, sizeof(uint) * Core::ShaderInput::CategoryLast);
|
||||
ListForeachPtr(Core::ShaderInput *, input, shader.input_list)
|
||||
{
|
||||
uint input_index = GetSemanticInputList(input->semantic);
|
||||
|
||||
locations[n] = NewGPUShaderLocation();
|
||||
if (GetLocation(input->name, *locations[n], input->type))
|
||||
solved_count[input_index]++;
|
||||
else
|
||||
locations[n] = NULL;
|
||||
|
||||
++n;
|
||||
}
|
||||
|
||||
uint texture_count = 0;
|
||||
for (uint l = 0; l < Core::ShaderInput::CategoryLast; ++l)
|
||||
{
|
||||
uint n = 0, i = 0;
|
||||
|
||||
if (input_list[l].Allocate(solved_count[l]))
|
||||
ListForeachPtr(Core::ShaderInput *, input, shader.input_list)
|
||||
{
|
||||
if ((l != GetSemanticInputList(input->semantic)) || locations[i].IsNull())
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
ShaderInput *gpu_input = &input_list[l][n];
|
||||
gpu_input->location = locations[i].Detach();
|
||||
gpu_input->Set(input);
|
||||
|
||||
// Allocate texture unit index and load render resource.
|
||||
if (input->type == Core::ShaderInput::Uniform)
|
||||
if (input->ConsumesTextureUnit())
|
||||
{
|
||||
if (!input->parm_t.IsEmpty())
|
||||
gpu_input->parm_t = rf.LoadTexture(input->parm_t);
|
||||
gpu_input->index = texture_count++;
|
||||
}
|
||||
|
||||
++i; ++n;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
65
include/engine/gpu/gpu_shader_compiler.cpp
Normal file
65
include/engine/gpu/gpu_shader_compiler.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
nEngine - GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_shader_compiler.h"
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "log/file_log.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool IShaderCompiler::Compile(const Core::Shader &shader, Shader &gpu_shader, const char *binary_cache_id)
|
||||
{
|
||||
if (!shader.name.IsEmpty())
|
||||
__LOG_V__ << "Creating shader '" << shader.name << "'...\n";
|
||||
gpu_shader.name = shader.name;
|
||||
|
||||
// Compile shader objects.
|
||||
String vertex_source, pixel_source;
|
||||
if (!renderer.TranslateShader(shader, vertex_source, pixel_source))
|
||||
return false;
|
||||
|
||||
gpu_shader.vertex = renderer.NewGPUShaderObject();
|
||||
gpu_shader.pixel = renderer.NewGPUShaderObject();
|
||||
if (gpu_shader.vertex.IsNull() || gpu_shader.pixel.IsNull())
|
||||
return false;
|
||||
|
||||
String vertex_error, pixel_error;
|
||||
if (
|
||||
!CompileObject(vertex_source, *gpu_shader.vertex, ShaderObject::Vertex, shader.name, &vertex_error) ||
|
||||
!CompileObject(pixel_source, *gpu_shader.pixel, ShaderObject::Pixel, shader.name, &pixel_error)
|
||||
)
|
||||
{
|
||||
__LOG_E__ << "Shader '" << shader.name << "' failed to compile.\n";
|
||||
|
||||
//#ifdef _DEBUG
|
||||
FileLog file_log("c:/shader_error.log");
|
||||
file_log << "--------------------------------------------------------------------------------\n";
|
||||
file_log << "Failed to compile shader '" << shader.name << "'.\n\n";
|
||||
file_log << "Vertex error:" << vertex_error << "\n\n";
|
||||
file_log << "Pixel error:" << pixel_error << "\n\n";
|
||||
file_log << "--------------------------------------------------------------------------------\n";
|
||||
file_log << "\n";
|
||||
file_log << "VERTEX:\n";
|
||||
file_log << "\n";
|
||||
file_log << vertex_source.NormalizedEOL(String::EOLUnix);
|
||||
file_log << "\n";
|
||||
file_log << "PIXEL:\n";
|
||||
file_log << "\n";
|
||||
file_log << pixel_source.NormalizedEOL(String::EOLUnix);
|
||||
file_log << "\n";
|
||||
file_log << "--------------------------------------------------------------------------------\n";
|
||||
file_log << "\n";
|
||||
//#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
return Link(shader, gpu_shader);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
376
include/engine/gpu/gpu_shadow_map.cpp
Normal file
376
include/engine/gpu/gpu_shadow_map.cpp
Normal file
@ -0,0 +1,376 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "core/light.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::Core;
|
||||
using namespace GS::GPU;
|
||||
|
||||
|
||||
struct UVOffset
|
||||
{ float x, y; };
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::CreateShadowMaps()
|
||||
{
|
||||
gpu_config.enable_shadow = registry.GetBool("ShadowMapping:Enable", true);
|
||||
gpu_config.shadow_size = registry.GetInteger("ShadowMapping:Size", 1024);
|
||||
|
||||
__LOG__ << "Creating shadow maps (" << gpu_config.shadow_size << "x" << gpu_config.shadow_size << ").\n";
|
||||
|
||||
if (gpu_config.enable_shadow)
|
||||
{
|
||||
shadow_map = NewTexture("shadow_map");
|
||||
shadow_map->Create(NULL, gpu_config.shadow_size, gpu_config.shadow_size, Render::Texture::FormatDepth, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource));
|
||||
shadow_map->ConfigureAsShadowMap();
|
||||
|
||||
shadow_map_fbo->SetDepthTexture(shadow_map);
|
||||
}
|
||||
}
|
||||
void Renderer::FreeShadowMaps()
|
||||
{
|
||||
shadow_map = NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
struct ShadowRenderData : public Light::RenderData::ShadowRenderData
|
||||
{
|
||||
Renderer::ShadowMapSplit split;
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static float ComputeSplitZBoundary(const Camera &lod_view, const Light &light, int index, int count)
|
||||
{
|
||||
if (index <= 0)
|
||||
return lod_view.GetNearClippingPlane();
|
||||
|
||||
float k = float(index) / count;
|
||||
|
||||
float z_log = lod_view.GetNearClippingPlane() * powf(light.shadow_range / lod_view.GetNearClippingPlane(), k);
|
||||
float z_lin = lod_view.GetNearClippingPlane() + (light.shadow_range - lod_view.GetNearClippingPlane()) * k;
|
||||
|
||||
return z_log * light.shadow_distribution + z_lin * (1.f - light.shadow_distribution);
|
||||
}
|
||||
static void FitViewToFrustum(Camera &view, const GS::Frustum &slice, float d)
|
||||
{
|
||||
using namespace GS;
|
||||
|
||||
/*
|
||||
Compute split view item properties so that the split view frustum act as
|
||||
a perfect fit for the split frustum.
|
||||
*/
|
||||
const Vector4 *fv = slice.GetVertices();
|
||||
|
||||
Vector4 vfv[8];
|
||||
view.GetInverseMatrix().Apply(vfv, fv, 8);
|
||||
const Matrix4 &m = view.GetMatrix();
|
||||
|
||||
Vector4 mm[2];
|
||||
mm[0] = mm[1] = vfv[0];
|
||||
for (int n = 1; n < 8; ++n)
|
||||
{
|
||||
mm[0] = Vector4::Minimum(mm[0], vfv[n]);
|
||||
mm[1] = Vector4::Maximum(mm[1], vfv[n]);
|
||||
}
|
||||
Vector4 focus = ((mm[0] + mm[1]) * 0.5) * m;
|
||||
|
||||
// Position the shadow map light item,
|
||||
view.SetPosition(focus - m.GetRow(2) * d);
|
||||
|
||||
// View clipping planes.
|
||||
Vector4 view_front = m.GetRow(2);
|
||||
view.z_far = view_front.Dot(fv[0] - view.GetPosition());
|
||||
for (int n = 1; n < 8; ++n)
|
||||
view.z_far = GS::Types::Max(view.z_far, view_front.Dot(fv[n] - view.GetPosition()));
|
||||
|
||||
// View dimensions.
|
||||
Vector4 view_left = view.GetMatrix().GetRow(0),
|
||||
view_top = view.GetMatrix().GetRow(1);
|
||||
|
||||
Vector4 dt = fv[0] - view.GetPosition();
|
||||
float h_width = std::fabs(view_left.Dot(dt)),
|
||||
h_height = std::fabs(view_top.Dot(dt));
|
||||
|
||||
for (int n = 1; n < 8; ++n)
|
||||
{
|
||||
dt = fv[n] - view.GetPosition();
|
||||
h_width = GS::Types::Max <float> (h_width, std::fabs(view_left.Dot(dt)));
|
||||
h_height = GS::Types::Max <float> (h_height, std::fabs(view_top.Dot(dt)));
|
||||
}
|
||||
view.ortho_w = h_width * 2.f;
|
||||
view.ortho_h = h_height * 2.f;
|
||||
}
|
||||
bool Renderer::LightPreparePSSM(const Light &l, bool build_dlist) const
|
||||
{
|
||||
Light::RenderData *light_render_data = (Light::RenderData *)l.render_data.c_ptr();
|
||||
if (!light_render_data || !light_render_data->shadow_data.Allocate(4))
|
||||
return false;
|
||||
|
||||
int split_count = registry.GetInteger("ShadowMapping:PSSM:Split", 3);
|
||||
|
||||
for (int n = 0; n < split_count; ++n)
|
||||
{
|
||||
Light::RenderData::ShadowData &data = light_render_data->shadow_data[n];
|
||||
|
||||
if (data.render_data.IsNull())
|
||||
data.render_data = new ShadowRenderData;
|
||||
ShadowRenderData *pssm_data = (ShadowRenderData *)data.render_data.c_ptr();
|
||||
|
||||
ShadowMapSplit &split = pssm_data->split;
|
||||
|
||||
static UVOffset split_offset[] = { { 0.0, 0.0 }, { 0.5, 0.0 }, { 0.0, 0.5 }, { 0.5, 0.5 } };
|
||||
split.rect = fRect::FromWidthHeight(split_offset[n].x * gpu_config.shadow_size, split_offset[n].y * gpu_config.shadow_size, gpu_config.shadow_size / 2.f, gpu_config.shadow_size / 2.f);
|
||||
|
||||
// Compute frustum slice.
|
||||
Frustum slice;
|
||||
float znear = ComputeSplitZBoundary(*view_item, l, n, split_count), zfar = ComputeSplitZBoundary(*view_item, l, n + 1, split_count);
|
||||
view_item->ComputeFrustum(slice, GetViewport(), znear, zfar);
|
||||
|
||||
// Adjust view item.
|
||||
split.view.is_orthographic = true;
|
||||
split.view.aspect_ratio = 1;
|
||||
split.view.SnapshotTransformation(l.GetMatrix());
|
||||
|
||||
FitViewToFrustum(split.view, slice, l.shadow_range * 4.f);
|
||||
split.view.ComputeFrustum(split.view.frustum, split.rect);
|
||||
|
||||
// Cull.
|
||||
if (build_dlist)
|
||||
{
|
||||
BuildRenderablePrimitiveList(split.view, *view_item, split.list.primitive_list, Renderable::Context_Shadow);
|
||||
BuildDisplayLists(split.list.primitive_list, split.list.display_lists);
|
||||
}
|
||||
|
||||
// Store slice view to light matrix.
|
||||
data.slice_distance = zfar;
|
||||
data.imatrix = split.view.GetInverseMatrix();
|
||||
split.view.ComputeProjectionMatrix(split.rect, data.pmatrix);
|
||||
if (!gpu_config.tex_origin_is_top_left)
|
||||
data.pmatrix = Matrix4::ScaleMatrix(Vector4(1.0, -1.0, 1.0)) * data.pmatrix;
|
||||
data.pmatrix = Matrix4::ScaleMatrix(Vector4(0.5, 0.5, 0.5)) * Matrix4::TranslationMatrix(Vector4(1, 1, 1)) * data.pmatrix;
|
||||
|
||||
// Crop on PSSM region.
|
||||
data.pmatrix = Matrix4::TranslationMatrix(Vector4(split_offset[n].x, split_offset[n].y, 0.0)) * Matrix4::ScaleMatrix(Vector4(0.5, 0.5, 1.0)) * data.pmatrix;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::LightRenderPSSM(Light &l)
|
||||
{
|
||||
Light::RenderData *data = (Light::RenderData *)l.render_data.c_ptr();
|
||||
ViewConfig view = BackupView();
|
||||
|
||||
SetViewport(fRect(0, 0, (float)gpu_config.shadow_size, (float)gpu_config.shadow_size));
|
||||
Clear(0, 0, 0, 0, 1, ClearDepth);
|
||||
|
||||
int split_count = registry.GetInteger("ShadowMapping:PSSM:Split", 3);
|
||||
for (int n = 0; n < split_count; ++n)
|
||||
{
|
||||
ShadowRenderData *shd_data = (ShadowRenderData *)data->shadow_data[n].render_data.c_ptr();
|
||||
|
||||
SetViewport(shd_data->split.rect);
|
||||
SetClippingRect(&shd_data->split.rect);
|
||||
|
||||
DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::Depth);
|
||||
|
||||
view_item = &shd_data->split.view;
|
||||
ApplyCamera();
|
||||
DrawList(shd_data->split.list.display_lists[0], dc);
|
||||
DrawList(shd_data->split.list.display_lists[2], dc);
|
||||
shd_data->split.list.Clear(false); // limit dynamic allocations
|
||||
}
|
||||
RestoreView(view);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::LightPreparePSM(const Light &l, bool build_dlist) const
|
||||
{
|
||||
Light::RenderData *light_render_data = (Light::RenderData *)l.render_data.c_ptr();
|
||||
if (!light_render_data || !light_render_data->shadow_data.Allocate(6))
|
||||
return false;
|
||||
|
||||
for (uint n = 0; n < 6; ++n)
|
||||
{
|
||||
Light::RenderData::ShadowData &data = light_render_data->shadow_data[n];
|
||||
|
||||
if (data.render_data.IsNull())
|
||||
data.render_data = new ShadowRenderData;
|
||||
ShadowRenderData *shd_data = (ShadowRenderData *)data.render_data.c_ptr();
|
||||
|
||||
ShadowMapSplit &split = shd_data->split;
|
||||
|
||||
static Vector4 view_angle[6] = { Vector4(0, 0, 0), Vector4(0, Units::Deg(90.f), 0), Vector4(0, Units::Deg(180.f), 0), Vector4(0, Units::Deg(270.f), 0), Vector4(Units::Deg(90.f), 0, 0), Vector4(Units::Deg(270.f), 0, 0) };
|
||||
static UVOffset split_offset[] = { { 0, 0 }, { 0.33f, 0 }, { 0.66f, 0 }, { 0, 0.5f }, { 0.33f, 0.5f }, { 0.66f, 0.5f } };
|
||||
|
||||
split.view.aspect_ratio = 1;
|
||||
split.view.SetFov(Units::Deg(95.f)); // Add a 5<> safe area to avoid artifacts at transition.
|
||||
split.view.SnapshotTransformation(l.GetMatrix() * Matrix4::FromMatrix3(Matrix3::FromEuler(view_angle[n])));
|
||||
|
||||
split.rect = fRect::FromWidthHeight(split_offset[n].x * gpu_config.shadow_size, split_offset[n].y * gpu_config.shadow_size, gpu_config.shadow_size / 3.f, gpu_config.shadow_size / 2.f);
|
||||
|
||||
if (build_dlist)
|
||||
{
|
||||
BuildRenderablePrimitiveList(split.view, *view_item, split.list.primitive_list, Renderable::Context_Shadow);
|
||||
BuildDisplayLists(split.list.primitive_list, split.list.display_lists);
|
||||
}
|
||||
|
||||
// Store cube view to light matrix.
|
||||
data.imatrix = split.view.GetInverseMatrix();
|
||||
split.view.ComputeProjectionMatrix(split.rect, data.pmatrix); // FIXME watch out, do we need the split viewport (split.rect) or the complete viewport???
|
||||
if (!gpu_config.tex_origin_is_top_left)
|
||||
data.pmatrix = Matrix4::ScaleMatrix(Vector4(1, -1, 1)) * data.pmatrix;
|
||||
data.pmatrix = Matrix4::ScaleMatrix(Vector4(0.5f, 0.5f, 0.5f)) * Matrix4::TranslationMatrix(Vector4(1, 1, 1)) * data.pmatrix;
|
||||
|
||||
// Crop on view region.
|
||||
data.pmatrix = Matrix4::TranslationMatrix(Vector4(split_offset[n].x, split_offset[n].y, 0.f)) * Matrix4::ScaleMatrix(Vector4(0.33f, 0.5f, 1)) * data.pmatrix;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::LightRenderPSM(Light &l)
|
||||
{
|
||||
Light::RenderData *data = (Light::RenderData *)l.render_data.c_ptr();
|
||||
ViewConfig view = BackupView();
|
||||
|
||||
SetViewport(fRect(0, 0, (float)gpu_config.shadow_size, (float)gpu_config.shadow_size));
|
||||
Clear(0, 0, 0, 0, 1, ClearDepth);
|
||||
|
||||
for (uint n = 0; n < 6; ++n)
|
||||
{
|
||||
ShadowRenderData *psm_data = (ShadowRenderData *)data->shadow_data[n].render_data.c_ptr();
|
||||
|
||||
SetViewport(psm_data->split.rect);
|
||||
SetClippingRect(&psm_data->split.rect);
|
||||
|
||||
view_item = &psm_data->split.view;
|
||||
ApplyCamera();
|
||||
|
||||
DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::Depth);
|
||||
DrawList(psm_data->split.list.display_lists[0], dc);
|
||||
DrawList(psm_data->split.list.display_lists[2], dc);
|
||||
psm_data->split.list.Clear(false); // limit dynamic allocations
|
||||
}
|
||||
RestoreView(view);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::LightPrepareSSM(const Light &l, bool build_dlist) const
|
||||
{
|
||||
Light::RenderData *data = (Light::RenderData *)l.render_data.c_ptr();
|
||||
if (!data || !data->shadow_data.Allocate(1))
|
||||
return false;
|
||||
|
||||
if (data->shadow_data[0].render_data.IsNull())
|
||||
data->shadow_data[0].render_data = new ShadowRenderData;
|
||||
ShadowRenderData *shd_data = (ShadowRenderData *)data->shadow_data[0].render_data.c_ptr();
|
||||
|
||||
Renderer::ShadowMapSplit &split = shd_data->split;
|
||||
split.view.AlignTo(l);
|
||||
|
||||
if (build_dlist)
|
||||
{
|
||||
BuildRenderablePrimitiveList(split.view, *view_item, split.list.primitive_list, Renderable::Context_Shadow);
|
||||
BuildDisplayLists(split.list.primitive_list, split.list.display_lists);
|
||||
}
|
||||
|
||||
// Store shadow matrices.
|
||||
data->shadow_data[0].imatrix = split.view.GetInverseMatrix();
|
||||
l.ComputeProjectionMatrix(data->shadow_data[0].pmatrix);
|
||||
if (!gpu_config.tex_origin_is_top_left)
|
||||
data->shadow_data[0].pmatrix = Matrix4::ScaleMatrix(Vector4(1.0, -1.0, 1.0)) * data->shadow_data[0].pmatrix;
|
||||
data->shadow_data[0].pmatrix = Matrix4::ScaleMatrix(Vector4(0.5, 0.5, 0.5)) * Matrix4::TranslationMatrix(Vector4(1, 1, 1)) * data->shadow_data[0].pmatrix;
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::LightRenderSSM(Light &l)
|
||||
{
|
||||
Light::RenderData *data = (Light::RenderData *)l.render_data.c_ptr();
|
||||
ViewConfig view = BackupView();
|
||||
|
||||
SetViewport(fRect(0, 0, (float)gpu_config.shadow_size, (float)gpu_config.shadow_size));
|
||||
Clear(0, 0, 0, 0, 1, ClearDepth);
|
||||
|
||||
ShadowRenderData *render_data = (ShadowRenderData *)data->shadow_data[0].render_data.c_ptr();
|
||||
|
||||
view_item = &render_data->split.view;
|
||||
ApplyCamera();
|
||||
|
||||
DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::Depth);
|
||||
DrawList(render_data->split.list.display_lists[0], dc);
|
||||
DrawList(render_data->split.list.display_lists[2], dc);
|
||||
render_data->split.list.Clear(false); // limit dynamic allocations
|
||||
|
||||
RestoreView(view);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Renderer::ViewConfig Renderer::BackupView() const
|
||||
{
|
||||
return ViewConfig(view_item, viewport);
|
||||
}
|
||||
void Renderer::RestoreViewport(const ViewConfig &config)
|
||||
{
|
||||
SetViewport(config.viewport);
|
||||
}
|
||||
void Renderer::RestoreView(const ViewConfig &config)
|
||||
{
|
||||
RestoreViewport(config);
|
||||
view_item = config.camera;
|
||||
ApplyCamera();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Renderer::PrepareShadowMap(const Light &l, bool build_dlist) const
|
||||
{
|
||||
switch (l.model)
|
||||
{
|
||||
case Light::Model_Spot: return LightPrepareSSM(l, build_dlist);
|
||||
case Light::Model_Point: return LightPreparePSM(l, build_dlist);
|
||||
case Light::Model_Linear: return LightPreparePSSM(l, build_dlist);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void Renderer::RenderShadowMap(Light &l)
|
||||
{
|
||||
PerfBeginEvent(__FUNCTION__, Color::Blue);
|
||||
|
||||
SetCurrentFBO(shadow_map_fbo);
|
||||
SetCullFunc(CullBack);
|
||||
|
||||
fRect old_clipping = GetClippingRect();
|
||||
SetClippingRect(NULL);
|
||||
|
||||
switch (l.model)
|
||||
{
|
||||
case Light::Model_Spot: LightRenderSSM(l); break;
|
||||
case Light::Model_Point: LightRenderPSM(l); break;
|
||||
case Light::Model_Linear: LightRenderPSSM(l); break;
|
||||
}
|
||||
|
||||
SetClippingRect(&old_clipping);
|
||||
|
||||
SetCullFunc(CullFront);
|
||||
SetCurrentFBO(NULL);
|
||||
|
||||
PerfEndEvent();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
48
include/engine/gpu/gpu_skybox.cpp
Normal file
48
include/engine/gpu/gpu_skybox.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "core/camera.h"
|
||||
|
||||
using namespace GS::GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::DrawSkybox(GS::Render::sTexture t[2], GS::Render::Shader *s)
|
||||
{
|
||||
static const size_t stride = sizeof(float) * 3;
|
||||
|
||||
SetDepthFunc(DepthEqual); // only draw on Z = 1
|
||||
EnableDepthWrite(false);
|
||||
|
||||
Shader &p = s ? (Shader &)*s : *skybox_program;
|
||||
|
||||
SetShaderProgram(&p);
|
||||
SetIndexSource(skybox_idx_vbo);
|
||||
SetVertexSource(skybox_vtx_vbo, stride);
|
||||
|
||||
ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position),
|
||||
*layer0_parm = p.GetInput("u_layer0"),
|
||||
*layer1_parm = p.GetInput("u_layer1");
|
||||
|
||||
p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, (const void *)0);
|
||||
if (layer0_parm && t[0].IsValid())
|
||||
p.Set(*layer0_parm->location, (Render::Texture &)*t[0], layer0_parm->index);
|
||||
if (layer1_parm && t[1].IsValid())
|
||||
p.Set(*layer1_parm->location, (Render::Texture &)*t[1], layer1_parm->index);
|
||||
|
||||
p.SetRendererInputs(*this);
|
||||
p.SetTransformInputs(m_projection, m_view, m_iview, &Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
|
||||
p.CommitInputs();
|
||||
|
||||
DrawElements(Types::PrimitiveTriangle, 3 * 2, Types::ValueUShort);
|
||||
|
||||
p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
|
||||
|
||||
EnableDepthWrite(true);
|
||||
SetDepthFunc(DepthLess);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
539
include/engine/gpu/gpu_spline.cpp
Normal file
539
include/engine/gpu/gpu_spline.cpp
Normal file
@ -0,0 +1,539 @@
|
||||
#include "gpu/gpu_types.h"
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "log/log.h"
|
||||
#include "math/nmath.h"
|
||||
#include "memory/memory.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::GPU;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Render::Texture *Renderer::CreateRenderTexture(
|
||||
const char *name,
|
||||
uint width,
|
||||
uint height,
|
||||
Render::Texture::Format format,
|
||||
Render::Texture::AA aa
|
||||
)
|
||||
{
|
||||
__NTRACE("CreateRenderTexture")
|
||||
// __LOG_W__ << "CreateRenderTexture: name=" << name << ", size=" << width << "x" << height << "\n";
|
||||
|
||||
Render::Texture *tex = NewTexture(name);
|
||||
if (!tex)
|
||||
{
|
||||
__LOG_E__ << "CreateRenderTexture: NewTexture failed!\n";
|
||||
return nullptr;
|
||||
}
|
||||
// __LOG_W__ << "CreateRenderTexture: NewTexture succeeded, tex ptr=" << (void*)tex << "\n";
|
||||
|
||||
const Render::Texture::Usage usage =
|
||||
Render::Texture::Usage(
|
||||
Render::Texture::IsRenderTarget |
|
||||
Render::Texture::IsShaderResource
|
||||
);
|
||||
|
||||
// __LOG_W__ << "CreateRenderTexture: Calling tex->Create with format=" << format << ", aa=" << aa << "\n";
|
||||
if (tex->Create(nullptr, width, height, format, aa, usage))
|
||||
{
|
||||
// __LOG_W__ << "CreateRenderTexture: Success! Returning texture.\n";
|
||||
return tex;
|
||||
}
|
||||
|
||||
__LOG_E__ << "CreateRenderTexture: tex->Create failed!\n";
|
||||
tex->Free();
|
||||
return nullptr;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::ClearTexture(Render::Texture *target, const Color &clear_color)
|
||||
{
|
||||
__NTRACE("ClearTexture")
|
||||
// __LOG_W__ << "ClearTexture: Clearing target texture with color (" << clear_color.x << ", " << clear_color.y << ", " << clear_color.z << ", " << clear_color.w << ")\n";
|
||||
|
||||
if (!target)
|
||||
{
|
||||
__LOG_E__ << "ClearTexture: target is null!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
const uint tex_w = target->GetWidth();
|
||||
const uint tex_h = target->GetHeight();
|
||||
|
||||
// Use DrawSplineToTexture with clear_target=true to clear the texture
|
||||
// This is a workaround since Blit() doesn't work on RenderTarget textures
|
||||
// __LOG_W__ << "ClearTexture: Using DrawSplineToTexture method with clear...\n";
|
||||
|
||||
// Create a dummy 2-point array (not used since we're clearing)
|
||||
Array<Vector2> dummy_points;
|
||||
dummy_points.Allocate(2);
|
||||
dummy_points[0] = Vector2(0, 0);
|
||||
dummy_points[1] = Vector2(1, 1);
|
||||
|
||||
// Call DrawSplineToTexture with clear_target=true and zero width (no drawing)
|
||||
// This will fill the buffer with the clear color
|
||||
const uint pitch = tex_w * 4;
|
||||
const size_t buffer_size = pitch * tex_h;
|
||||
unsigned char *pixels = new unsigned char[buffer_size];
|
||||
|
||||
// Convert color to 8-bit RGBA
|
||||
const auto r = (unsigned char)(clear_color.x * 255.0f);
|
||||
const auto g = (unsigned char)(clear_color.y * 255.0f);
|
||||
const auto b = (unsigned char)(clear_color.z * 255.0f);
|
||||
const auto a = (unsigned char)(clear_color.w * 255.0f);
|
||||
|
||||
// __LOG_W__ << "ClearTexture: Filling buffer with RGBA(" << (int)r << ", " << (int)g << ", " << (int)b << ", " << (int)a << ")...\n";
|
||||
|
||||
// Fill with clear color (ABGR format)
|
||||
unsigned int pixel_value = (a << 24) | (b << 16) | (g << 8) | r;
|
||||
unsigned int *pixel_buffer = (unsigned int *)pixels;
|
||||
for (uint i = 0; i < tex_w * tex_h; ++i)
|
||||
{
|
||||
pixel_buffer[i] = pixel_value;
|
||||
}
|
||||
|
||||
// __LOG_W__ << "ClearTexture: Uploading via Blit...\n";
|
||||
target->Blit((const char *)pixels, tex_w, tex_h, 0, 0, Render::Texture::FormatRGBA8);
|
||||
|
||||
delete[] pixels;
|
||||
|
||||
// __LOG_W__ << "ClearTexture: Complete!\n";
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::BlitTextureToTexture(
|
||||
Render::Texture *src,
|
||||
Render::Texture *dst,
|
||||
const fRect *src_rect,
|
||||
const fRect *dst_rect
|
||||
)
|
||||
{
|
||||
__NTRACE("BlitTextureToTexture")
|
||||
|
||||
if (!src || !dst)
|
||||
return;
|
||||
|
||||
// Default rectangles (full texture)
|
||||
const fRect src_r = src_rect ? *src_rect : fRect(0, 0, 1, 1);
|
||||
const fRect dst_r = dst_rect ? *dst_rect : fRect(0, 0, 1, 1);
|
||||
|
||||
// Save current state
|
||||
const fRect old_viewport = viewport;
|
||||
|
||||
// Setup FBO for destination
|
||||
fx_fbo->SetColorTexture(dst);
|
||||
fx_fbo->SetDepthTexture(nullptr);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
SetViewport(fRect(0, 0, (float)dst->GetWidth(), (float)dst->GetHeight()));
|
||||
|
||||
// Use existing RenderFullscreenQuad infrastructure
|
||||
RenderFullscreenQuad(*single_texture_program, src_r, dst_r, src);
|
||||
|
||||
// Restore state
|
||||
SetCurrentFBO(nullptr);
|
||||
SetViewport(old_viewport);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Vector2 Renderer::CatmullRomInterpolate(
|
||||
const Vector2 &p0,
|
||||
const Vector2 &p1,
|
||||
const Vector2 &p2,
|
||||
const Vector2 &p3,
|
||||
float t
|
||||
) const
|
||||
{
|
||||
// Catmull-Rom spline with tau = 0.5
|
||||
const float t2 = t * t;
|
||||
const float t3 = t2 * t;
|
||||
|
||||
// Manual calculation for Vector2
|
||||
Vector2 result;
|
||||
result.x = 0.5f * (
|
||||
(2.0f * p1.x) +
|
||||
(-p0.x + p2.x) * t +
|
||||
(2.0f * p0.x - 5.0f * p1.x + 4.0f * p2.x - p3.x) * t2 +
|
||||
(-p0.x + 3.0f * p1.x - 3.0f * p2.x + p3.x) * t3
|
||||
);
|
||||
result.y = 0.5f * (
|
||||
(2.0f * p1.y) +
|
||||
(-p0.y + p2.y) * t +
|
||||
(2.0f * p0.y - 5.0f * p1.y + 4.0f * p2.y - p3.y) * t2 +
|
||||
(-p0.y + 3.0f * p1.y - 3.0f * p2.y + p3.y) * t3
|
||||
);
|
||||
return result;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Renderer::TessellateSplineRibbon(
|
||||
const Array<Vector2> &control_points,
|
||||
float width_normalized,
|
||||
const Color &color,
|
||||
int samples_per_segment,
|
||||
bool soft_edge,
|
||||
SplineRibbon &out_ribbon
|
||||
)
|
||||
{
|
||||
__NTRACE("TessellateSplineRibbon")
|
||||
|
||||
const int num_control = control_points.GetCount();
|
||||
if (num_control < 2)
|
||||
return;
|
||||
|
||||
// For Catmull-Rom: we have num_control-1 segments
|
||||
const int num_segments = num_control - 1;
|
||||
if (num_segments < 1)
|
||||
return;
|
||||
|
||||
// Allocate arrays for sampled points
|
||||
const int total_samples = num_segments * samples_per_segment + 1;
|
||||
Array<Vector2> samples;
|
||||
Array<Vector2> tangents;
|
||||
samples.Allocate(total_samples);
|
||||
tangents.Allocate(total_samples);
|
||||
|
||||
// Sample the spline using Catmull-Rom interpolation
|
||||
int sample_idx = 0;
|
||||
for (int seg = 0; seg < num_segments; seg++)
|
||||
{
|
||||
// Get 4 control points (with duplication at boundaries)
|
||||
Vector2 p0 = (seg > 0) ? control_points[seg - 1] : control_points[seg];
|
||||
Vector2 p1 = control_points[seg];
|
||||
Vector2 p2 = control_points[seg + 1];
|
||||
Vector2 p3 = (seg < num_segments - 1) ? control_points[seg + 2] : control_points[seg + 1];
|
||||
|
||||
for (int s = 0; s < samples_per_segment; s++)
|
||||
{
|
||||
const float t = (float)s / (float)samples_per_segment;
|
||||
samples[sample_idx] = CatmullRomInterpolate(p0, p1, p2, p3, t);
|
||||
|
||||
// Compute tangent (derivative approximation)
|
||||
const float dt = 0.01f;
|
||||
const float t_next = (t + dt > 1.0f) ? 1.0f : t + dt;
|
||||
Vector2 p_next = CatmullRomInterpolate(p0, p1, p2, p3, t_next);
|
||||
Vector2 diff = p_next - samples[sample_idx];
|
||||
tangents[sample_idx] = diff.Normalized();
|
||||
|
||||
sample_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
// Add final point
|
||||
samples[total_samples - 1] = control_points[num_control - 1];
|
||||
tangents[total_samples - 1] = tangents[total_samples - 2]; // Reuse last tangent
|
||||
|
||||
// Build ribbon geometry
|
||||
out_ribbon.positions.Allocate(total_samples * 2);
|
||||
out_ribbon.colors.Allocate(total_samples * 2);
|
||||
|
||||
for (int i = 0; i < total_samples; i++)
|
||||
{
|
||||
const Vector2 pos = samples[i];
|
||||
const Vector2 tangent = tangents[i];
|
||||
|
||||
// Perpendicular normal (2D rotation by 90 degrees)
|
||||
Vector2 normal(-tangent.y, tangent.x);
|
||||
|
||||
// Convert from normalized [0..1] texture space to clip space [-1..1]
|
||||
const float x_clip = pos.x * 2.0f - 1.0f;
|
||||
const float y_clip = 1.0f - pos.y * 2.0f; // Flip Y (texture origin top-left)
|
||||
|
||||
// Offset in clip space
|
||||
const Vector2 offset = normal * width_normalized;
|
||||
|
||||
// Left and right vertices
|
||||
out_ribbon.positions[i * 2 + 0] = Vector4(
|
||||
x_clip - offset.x,
|
||||
y_clip - offset.y,
|
||||
0.0f,
|
||||
1.0f
|
||||
);
|
||||
out_ribbon.positions[i * 2 + 1] = Vector4(
|
||||
x_clip + offset.x,
|
||||
y_clip + offset.y,
|
||||
0.0f,
|
||||
1.0f
|
||||
);
|
||||
|
||||
// Colors with soft edge via alpha gradient
|
||||
if (soft_edge)
|
||||
{
|
||||
// Apply alpha gradient on outer edges (30% opacity) vs center (full opacity)
|
||||
// But since we're using a ribbon with 2 vertices per sample, both get same alpha
|
||||
// To get true soft edge, we'd need a center vertex too, but for now just use the color as-is
|
||||
// TODO: Implement true soft edge with centerline vertex if needed
|
||||
out_ribbon.colors[i * 2 + 0] = color;
|
||||
out_ribbon.colors[i * 2 + 1] = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_ribbon.colors[i * 2 + 0] = color;
|
||||
out_ribbon.colors[i * 2 + 1] = color;
|
||||
}
|
||||
}
|
||||
|
||||
// Build indices (triangle list)
|
||||
const int num_quads = total_samples - 1;
|
||||
out_ribbon.indices.Allocate(num_quads * 6);
|
||||
|
||||
for (int i = 0; i < num_quads; i++)
|
||||
{
|
||||
const ushort i0 = (ushort)(i * 2);
|
||||
const ushort i1 = (ushort)(i * 2 + 1);
|
||||
const ushort i2 = (ushort)((i + 1) * 2);
|
||||
const ushort i3 = (ushort)((i + 1) * 2 + 1);
|
||||
|
||||
// Triangle 1
|
||||
out_ribbon.indices[i * 6 + 0] = i0;
|
||||
out_ribbon.indices[i * 6 + 1] = i2;
|
||||
out_ribbon.indices[i * 6 + 2] = i1;
|
||||
|
||||
// Triangle 2
|
||||
out_ribbon.indices[i * 6 + 3] = i1;
|
||||
out_ribbon.indices[i * 6 + 4] = i2;
|
||||
out_ribbon.indices[i * 6 + 5] = i3;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper: Draw a simple line to texture pixel data (Bresenham-like)
|
||||
static void DrawLineToPixels(unsigned char *pixels, uint width, uint height, uint pitch,
|
||||
int x0, int y0, int x1, int y1,
|
||||
unsigned char r, unsigned char g, unsigned char b, unsigned char a)
|
||||
{
|
||||
const int dx = abs(x1 - x0);
|
||||
const int dy = abs(y1 - y0);
|
||||
const int sx = (x0 < x1) ? 1 : -1;
|
||||
const int sy = (y0 < y1) ? 1 : -1;
|
||||
int err = dx - dy;
|
||||
|
||||
int x = x0, y = y0;
|
||||
while (true)
|
||||
{
|
||||
if (x >= 0 && x < (int)width && y >= 0 && y < (int)height)
|
||||
{
|
||||
int *pixel = (int*)(pixels + y * pitch + x * 4);
|
||||
|
||||
// Read destination pixel components (packed as in existing code: ABGR)
|
||||
const unsigned int dst = *pixel;
|
||||
const unsigned char dst_r = (unsigned char)(dst & 0xFF);
|
||||
const unsigned char dst_g = (unsigned char)((dst >> 8) & 0xFF);
|
||||
const unsigned char dst_b = (unsigned char)((dst >> 16) & 0xFF);
|
||||
const unsigned char dst_a = (unsigned char)((dst >> 24) & 0xFF);
|
||||
|
||||
// Source alpha (0..1)
|
||||
const float src_af = (float)a / 255.0f;
|
||||
const float inv = 1.0f - src_af;
|
||||
|
||||
// Standard 'over' compositing: out = src*src_a + dst*(1-src_a)
|
||||
const unsigned char out_r = (unsigned char)(r * src_af + dst_r * inv + 0.5f);
|
||||
const unsigned char out_g = (unsigned char)(g * src_af + dst_g * inv + 0.5f);
|
||||
const unsigned char out_b = (unsigned char)(b * src_af + dst_b * inv + 0.5f);
|
||||
const unsigned char out_a = (unsigned char)(a + dst_a * inv + 0.5f);
|
||||
|
||||
*pixel = (out_a << 24) | (out_b << 16) | (out_g << 8) | out_r; // ABGR packing (kept as original)
|
||||
}
|
||||
|
||||
if (x == x1 && y == y1) break;
|
||||
|
||||
const int e2 = 2 * err;
|
||||
if (e2 > -dy) { err -= dy; x += sx; }
|
||||
if (e2 < dx) { err += dx; y += sy; }
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::DrawSplineToTexture(
|
||||
Render::Texture *target,
|
||||
const Array<Vector2> &control_points,
|
||||
float width_pixels,
|
||||
const Color &color,
|
||||
int samples_per_segment,
|
||||
bool clear_target,
|
||||
bool soft_edge,
|
||||
const Color *border_color,
|
||||
float border_width,
|
||||
float margin
|
||||
)
|
||||
{
|
||||
__NTRACE("DrawSplineToTexture")
|
||||
// __LOG_W__ << "Drawing spline to texture (CPU-based method)...\n";
|
||||
|
||||
if (!target) {
|
||||
__LOG_E__ << "DrawSplineToTexture: target texture is null!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (control_points.GetCount() < 2) {
|
||||
__LOG_E__ << "DrawSplineToTexture: not enough control points!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
const uint tex_w = target->GetWidth();
|
||||
const uint tex_h = target->GetHeight();
|
||||
// __LOG_W__ << "Target texture size: " << tex_w << "x" << tex_h << "\n";
|
||||
|
||||
// Convert color to 8-bit RGBA
|
||||
const auto r = (unsigned char)(color.x * 255.0f);
|
||||
const auto g = (unsigned char)(color.y * 255.0f);
|
||||
const auto b = (unsigned char)(color.z * 255.0f);
|
||||
const auto a = (unsigned char)(color.w * 255.0f);
|
||||
|
||||
// __LOG_W__ << "Color: R=" << (int)r << " G=" << (int)g << " B=" << (int)b << " A=" << (int)a << "\n";
|
||||
|
||||
// Convert border color if provided
|
||||
unsigned char border_r = 0, border_g = 0, border_b = 0, border_a = 0;
|
||||
if (border_color && border_width > 0.0f) {
|
||||
border_r = (unsigned char)(border_color->x * 255.0f);
|
||||
border_g = (unsigned char)(border_color->y * 255.0f);
|
||||
border_b = (unsigned char)(border_color->z * 255.0f);
|
||||
border_a = (unsigned char)(border_color->w * 255.0f);
|
||||
// __LOG_W__ << "Border Color: R=" << (int)border_r << " G=" << (int)border_g << " B=" << (int)border_b << " A=" << (int)border_a << " Width=" << border_width << "\n";
|
||||
}
|
||||
|
||||
const int num_control = control_points.GetCount();
|
||||
const int num_segments = num_control - 1;
|
||||
|
||||
// Adjust control points with margin to avoid clipping
|
||||
Array<Vector2> adjusted_points;
|
||||
adjusted_points.Allocate(num_control);
|
||||
|
||||
if (margin > 0.0f) {
|
||||
// __LOG_W__ << "Applying margin: " << margin << "\n";
|
||||
|
||||
// Calculate the margin in normalized coordinates [0..1]
|
||||
const float margin_x = margin / (float)tex_w;
|
||||
const float margin_y = margin / (float)tex_h;
|
||||
|
||||
// Scale and offset control points to fit within margin bounds
|
||||
float min_x = 1.0f, max_x = 0.0f, min_y = 1.0f, max_y = 0.0f;
|
||||
for (int i = 0; i < num_control; i++) {
|
||||
if (control_points[i].x < min_x) min_x = control_points[i].x;
|
||||
if (control_points[i].x > max_x) max_x = control_points[i].x;
|
||||
if (control_points[i].y < min_y) min_y = control_points[i].y;
|
||||
if (control_points[i].y > max_y) max_y = control_points[i].y;
|
||||
}
|
||||
|
||||
const float range_x = max_x - min_x;
|
||||
const float range_y = max_y - min_y;
|
||||
const float scale_x = (range_x > 0.0f) ? (1.0f - 2.0f * margin_x) / range_x : 1.0f;
|
||||
const float scale_y = (range_y > 0.0f) ? (1.0f - 2.0f * margin_y) / range_y : 1.0f;
|
||||
|
||||
for (int i = 0; i < num_control; i++) {
|
||||
adjusted_points[i].x = margin_x + (control_points[i].x - min_x) * scale_x;
|
||||
adjusted_points[i].y = margin_y + (control_points[i].y - min_y) * scale_y;
|
||||
}
|
||||
} else {
|
||||
// No margin, use original points
|
||||
for (int i = 0; i < num_control; i++) {
|
||||
adjusted_points[i] = control_points[i];
|
||||
}
|
||||
}
|
||||
|
||||
Array<Vector2> samples;
|
||||
samples.Allocate(num_segments * samples_per_segment + 1);
|
||||
|
||||
// Sample the spline using Catmull-Rom interpolation with adjusted points
|
||||
int sample_idx = 0;
|
||||
for (int seg = 0; seg < num_segments; seg++)
|
||||
{
|
||||
Vector2 p0 = (seg > 0) ? adjusted_points[seg - 1] : adjusted_points[seg];
|
||||
Vector2 p1 = adjusted_points[seg];
|
||||
Vector2 p2 = adjusted_points[seg + 1];
|
||||
Vector2 p3 = (seg < num_segments - 1) ? adjusted_points[seg + 2] : adjusted_points[seg + 1];
|
||||
|
||||
for (int s = 0; s < samples_per_segment; s++)
|
||||
{
|
||||
const float t = (float)s / (float)samples_per_segment;
|
||||
samples[sample_idx] = CatmullRomInterpolate(p0, p1, p2, p3, t);
|
||||
sample_idx++;
|
||||
}
|
||||
}
|
||||
samples[num_segments * samples_per_segment] = adjusted_points[num_control - 1];
|
||||
|
||||
// __LOG_W__ << "Generated " << samples.GetCount() << " sampled points\n";
|
||||
|
||||
// Create pixel buffer for drawing
|
||||
// __LOG_W__ << "Creating pixel buffer for CPU rendering...\n";
|
||||
|
||||
const uint pitch = tex_w * 4; // RGBA = 4 bytes per pixel
|
||||
const size_t buffer_size = pitch * tex_h;
|
||||
unsigned char *pixels = new unsigned char[buffer_size];
|
||||
|
||||
// __LOG_W__ << "Pixel buffer created. Size=" << buffer_size << " Pitch=" << pitch << "\n";
|
||||
|
||||
if (clear_target)
|
||||
{
|
||||
// Initialize with transparent black
|
||||
memset(pixels, 0, buffer_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read back the existing texture contents into the buffer so we can composite on top.
|
||||
// __LOG_W__ << "Reading existing texture pixels into buffer (for compositing)...\n";
|
||||
|
||||
// Setup FBO for readback
|
||||
fx_fbo->SetColorTexture(target);
|
||||
fx_fbo->SetDepthTexture(nullptr);
|
||||
SetCurrentFBO(fx_fbo);
|
||||
|
||||
fx_fbo->ReadColorPixels((char*)pixels, 0, 0, tex_w, tex_h);
|
||||
|
||||
// Restore
|
||||
SetCurrentFBO(nullptr);
|
||||
}
|
||||
|
||||
// __LOG_W__ << "Drawing spline lines...\n";
|
||||
|
||||
// Draw border first (if specified)
|
||||
if (border_color && border_width > 0.0f) {
|
||||
// __LOG_W__ << "Drawing border with width: " << border_width << "\n";
|
||||
const float total_width = width_pixels + border_width * 2.0f;
|
||||
|
||||
for (uint i = 0; i + 1 < samples.GetCount(); i++)
|
||||
{
|
||||
const int x0 = (int)(samples[i].x * (float)tex_w);
|
||||
const int y0 = (int)(samples[i].y * (float)tex_h);
|
||||
const int x1 = (int)(samples[i + 1].x * (float)tex_w);
|
||||
const int y1 = (int)(samples[i + 1].y * (float)tex_h);
|
||||
|
||||
for (int w = -(int)total_width/2; w <= (int)total_width/2; w++)
|
||||
{
|
||||
DrawLineToPixels(pixels, tex_w, tex_h, pitch,
|
||||
x0 + w, y0, x1 + w, y1, border_r, border_g, border_b, border_a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw main spline on top
|
||||
for (uint i = 0; i + 1 < samples.GetCount(); i++)
|
||||
{
|
||||
// Convert from normalized [0..1] to pixel coordinates
|
||||
const int x0 = (int)(samples[i].x * (float)tex_w);
|
||||
const int y0 = (int)(samples[i].y * (float)tex_h);
|
||||
const int x1 = (int)(samples[i + 1].x * (float)tex_w);
|
||||
const int y1 = (int)(samples[i + 1].y * (float)tex_h);
|
||||
|
||||
// Draw line with width by drawing multiple parallel lines
|
||||
for (int w = -(int)width_pixels/2; w <= (int)width_pixels/2; w++)
|
||||
{
|
||||
DrawLineToPixels(pixels, tex_w, tex_h, pitch,
|
||||
x0 + w, y0, x1 + w, y1, r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
// __LOG_W__ << "Uploading pixel buffer to texture...\n";
|
||||
// Upload to texture using Blit
|
||||
target->Blit((const char *)pixels, tex_w, tex_h, 0, 0, Render::Texture::FormatRGBA8);
|
||||
|
||||
// __LOG_W__ << "Freeing pixel buffer...\n";
|
||||
delete[] pixels;
|
||||
// __LOG_W__ << "Buffer freed!\n";
|
||||
|
||||
// __LOG_W__ << "DrawSplineToTexture complete!\n";
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
264
include/engine/gpu/gpu_terrain.cpp
Normal file
264
include/engine/gpu/gpu_terrain.cpp
Normal file
@ -0,0 +1,264 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_renderer.h"
|
||||
#include "core/terrain.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::GPU;
|
||||
|
||||
// Terrain patch size.
|
||||
#define _PatchSize 64
|
||||
// Terrain page count.
|
||||
#if __PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__
|
||||
#define _PageCount 4
|
||||
#else
|
||||
#define _PageCount 32
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool Renderer::CreateTerrainPatch()
|
||||
{
|
||||
// Allocate terrain cache.
|
||||
if (!terrain_patch_cache.Allocate(_PageCount))
|
||||
__ERR__(__LOG_E__ << "Failed to allocate terrain cache.\n", false)
|
||||
|
||||
// Compute page stride.
|
||||
size_t stride = 0;
|
||||
|
||||
size_t vertex_offset = stride;
|
||||
stride += 3 * 4; // Vertex buffer (3x float).
|
||||
size_t normal_offset = stride;
|
||||
stride += (3 + 1) * 1; // Normal buffer (3x byte + 1 padding).
|
||||
size_t uv_offset = stride;
|
||||
stride += 2 * 4; // Large UV (2x float).
|
||||
|
||||
// Setup default page layout.
|
||||
size_t byte_size = stride * (_PatchSize + 1) * (_PatchSize + 1);
|
||||
|
||||
__LOG_H__ << "Setup terrain cache: " << terrain_patch_cache.GetCount() << " pages (" << uint(byte_size * terrain_patch_cache.GetCount()) << " bytes).\n";
|
||||
|
||||
terrain_patch_vtx.Allocate((uint)byte_size);
|
||||
if (char *p = terrain_patch_vtx)
|
||||
{
|
||||
Vector4 wp(0, 0, 0);
|
||||
for (int v = 0; v < (_PatchSize + 1); ++v)
|
||||
{
|
||||
for (int u = 0; u < (_PatchSize + 1); ++u)
|
||||
{
|
||||
float *pv = (float *)p;
|
||||
pv[0] = wp.x; pv[1] = 0; pv[2] = wp.z;
|
||||
|
||||
p += stride;
|
||||
wp.x += 1.f;
|
||||
}
|
||||
wp.x = 0.f;
|
||||
wp.z += 1.f;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate pages.
|
||||
for (uint n = 0; n < terrain_patch_cache.GetCount(); ++n)
|
||||
{
|
||||
DisplayList *patch = terrain_patch_cache[n].patch = NewDisplayList();
|
||||
|
||||
// Create the patch index buffer.
|
||||
patch->idx = NewVBO();
|
||||
|
||||
if (patch->idx->Create(6 * _PatchSize * _PatchSize * sizeof(ushort), VBO::Index, VBO::Static))
|
||||
{
|
||||
SetIndexSource(patch->idx);
|
||||
if (ushort *p_idx = (ushort *)patch->idx->Map())
|
||||
{
|
||||
for (int v = 0; v < _PatchSize; ++v)
|
||||
for (int u = 0; u < _PatchSize; ++u)
|
||||
{
|
||||
*p_idx++ = (ushort)(u + v * (_PatchSize + 1));
|
||||
*p_idx++ = (ushort)(u + (v + 1) * (_PatchSize + 1) + 1);
|
||||
*p_idx++ = (ushort)(u + v * (_PatchSize + 1) + 1);
|
||||
*p_idx++ = (ushort)(u + v * (_PatchSize + 1));
|
||||
*p_idx++ = (ushort)(u + (v + 1) * (_PatchSize + 1));
|
||||
*p_idx++ = (ushort)(u + (v + 1) * (_PatchSize + 1) + 1);
|
||||
}
|
||||
|
||||
patch->idx->Unmap();
|
||||
patch->index_count = 6 * _PatchSize * _PatchSize;
|
||||
}
|
||||
SetIndexSource(NULL);
|
||||
}
|
||||
|
||||
// Create the patch vertex buffer.
|
||||
patch->vertex_offset = vertex_offset;
|
||||
patch->normal_offset = normal_offset;
|
||||
patch->uv_offset[0] = uv_offset;
|
||||
patch->stride = stride;
|
||||
|
||||
patch->vtx = NewVBO();
|
||||
if (patch->vtx->Create(byte_size, VBO::Vertex, VBO::Dynamic))
|
||||
{
|
||||
SetVertexSource(patch->vtx, patch->stride);
|
||||
patch->vtx->Update(terrain_patch_vtx);
|
||||
SetVertexSource(NULL, 0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void Renderer::InvalidateTerrainCache(int, int, int, int)
|
||||
{
|
||||
// Very naive invalidation that ignores the input region.
|
||||
for (uint n = 0; n < terrain_patch_cache.GetCount(); ++n)
|
||||
terrain_patch_cache[n].terrain = NULL;
|
||||
}
|
||||
void Renderer::DecayTerrainCache()
|
||||
{
|
||||
for (uint n = 0; n < terrain_patch_cache.GetCount(); ++n)
|
||||
if (terrain_patch_cache[n].score > 0)
|
||||
--terrain_patch_cache[n].score;
|
||||
}
|
||||
void Renderer::UploadTerrainPatchToPage(CachedTerrainPatch &page, const GS::Core::Item &item, const GS::Core::Patch &patch, const DrawContext &dc)
|
||||
{
|
||||
if (!patch.terrain || !page.patch)
|
||||
return;
|
||||
|
||||
Core::Terrain &terrain = *patch.terrain;
|
||||
DisplayList &terrain_patch = *page.patch;
|
||||
|
||||
float pixel_size = terrain.GetUnit();
|
||||
Vector4 patch_scale(pixel_size * patch.decimation, 1, pixel_size * patch.decimation);
|
||||
|
||||
#if 1
|
||||
|
||||
// Update patch CPU-side data.
|
||||
Core::Terrain::Attrib *psa = terrain.GetAttributesMap() + patch.v * terrain.GetHeightmapPitch() + patch.u;
|
||||
float *psh = terrain.GetHeightmap() + patch.v * terrain.GetHeightmapPitch() + patch.u;
|
||||
|
||||
char *p = terrain_patch_vtx;
|
||||
|
||||
Vector4 uv(0, 0, patch.v * pixel_size);
|
||||
for (int v = 0; v < (_PatchSize + 1); ++v)
|
||||
{
|
||||
uv.x = patch.u * pixel_size;
|
||||
|
||||
Core::Terrain::Attrib *pa = psa;
|
||||
float *ph = psh;
|
||||
|
||||
for (int u = 0; u < (_PatchSize + 1); ++u)
|
||||
{
|
||||
// Vertex.
|
||||
float *pv = (float *)(p + terrain_patch.vertex_offset);
|
||||
pv[1] = ph[0];
|
||||
|
||||
// Normal.
|
||||
char *pn = (char *)(p + terrain_patch.normal_offset);
|
||||
pn[0] = pa[0].nx;
|
||||
pn[1] = pa[0].ny;
|
||||
pn[2] = pa[0].nz;
|
||||
|
||||
// UV.
|
||||
float *pu = (float *)(p + terrain_patch.uv_offset[0]);
|
||||
pu[0] = uv.x / terrain.GetWidth();
|
||||
uv.x += patch_scale.x;
|
||||
pu[1] = uv.z / terrain.GetDepth();
|
||||
|
||||
p += terrain_patch.stride;
|
||||
|
||||
pa += patch.decimation;
|
||||
ph += patch.decimation;
|
||||
}
|
||||
|
||||
uv.z += patch_scale.z;
|
||||
|
||||
psa += terrain.GetHeightmapPitch() * patch.decimation;
|
||||
psh += terrain.GetHeightmapPitch() * patch.decimation;
|
||||
}
|
||||
|
||||
// Update VBO.
|
||||
SetVertexSource(terrain_patch.vtx, terrain_patch.stride);
|
||||
terrain_patch.vtx->Update(terrain_patch_vtx);
|
||||
|
||||
// [EJ 9 Mar] the display list cache MUST be reset here otherwise an invalid
|
||||
// VBO reference will be used by the next draw calls for this patch.
|
||||
dls_cache.dls = NULL;
|
||||
|
||||
#endif
|
||||
|
||||
// Synchronize cache.
|
||||
page.terrain = patch.terrain;
|
||||
page.u = patch.u; page.v = patch.v;
|
||||
page.w = patch.w; page.h = patch.h;
|
||||
page.decimation = patch.decimation;
|
||||
}
|
||||
void Renderer::RenderTerrainPatch(const GS::Core::Patch &patch, const GS::Core::Item &item, const DrawContext &dc)
|
||||
{
|
||||
// Cache query.
|
||||
++gpu_stats.terrain_page_query_count;
|
||||
|
||||
DisplayList *dlist = NULL;
|
||||
CachedTerrainPatch *page = NULL;
|
||||
uint lowest_score = (uint)~0;
|
||||
|
||||
for (uint n = 0; n < terrain_patch_cache.GetCount(); ++n)
|
||||
if (CachedTerrainPatch *c_page = &terrain_patch_cache[n])
|
||||
{
|
||||
if (
|
||||
(c_page->terrain == patch.terrain) &&
|
||||
(c_page->u == patch.u) && (c_page->v == patch.v) &&
|
||||
(c_page->w == patch.w) && (c_page->h == patch.h) &&
|
||||
(c_page->decimation == patch.decimation)
|
||||
)
|
||||
{
|
||||
dlist = c_page->patch;
|
||||
c_page->score++; // Increase score.
|
||||
break;
|
||||
}
|
||||
|
||||
// Track the lowest scoring page.
|
||||
if (c_page->score <= lowest_score)
|
||||
{
|
||||
lowest_score = c_page->score;
|
||||
page = c_page;
|
||||
}
|
||||
}
|
||||
|
||||
// Cache miss.
|
||||
if (!dlist)
|
||||
{
|
||||
++gpu_stats.terrain_page_query_miss;
|
||||
|
||||
if (!page || !page->patch)
|
||||
__ERRRAW__(__LOG_E__ << "No page to upload terrain patch.\n") // Unexpected...
|
||||
|
||||
// Create new page.
|
||||
UploadTerrainPatchToPage(*page, item, patch, dc);
|
||||
dlist = page->patch;
|
||||
page->score = 1; // Reset score (+1 for being used).
|
||||
}
|
||||
|
||||
// Draw patch scaled and translated on the GPU.
|
||||
float pixel_size = patch.terrain->GetUnit();
|
||||
Vector4 patch_scale(pixel_size * patch.decimation, 1, pixel_size * patch.decimation);
|
||||
|
||||
Matrix4 patch_matrix =
|
||||
Matrix4::TranslationMatrix(Vector4(patch.u * pixel_size - patch.terrain->GetWidth() * 0.5f, 0, patch.v * pixel_size - patch.terrain->GetDepth() * 0.5f)) *
|
||||
Matrix4::ScaleMatrix(patch_scale);
|
||||
|
||||
SetWorldMatrix(item.GetMatrix() * patch_matrix);
|
||||
m_previous_world = item.GetPreviousMatrix() * patch_matrix;
|
||||
|
||||
dls_cache.item = NULL; // Force matrices upload to program.
|
||||
|
||||
// Draw.
|
||||
if (patch.terrain->render_data)
|
||||
{
|
||||
dlist->material = patch.terrain->render_data->material;
|
||||
DrawDisplayListCached(*dlist, item, dc);
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
68
include/engine/gpu/gpu_triangle_batch.cpp
Normal file
68
include/engine/gpu/gpu_triangle_batch.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "gpu/gpu_triangle_batch.h"
|
||||
#include "gpu/gpu_renderer.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::GPU;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void TriangleBatch::Flush()
|
||||
{
|
||||
if (batch_triangle_count > 0)
|
||||
renderer.DrawTriangle(batch_triangle_count, batch_vtx, batch_idx, batch_col, batch_uv, batch_t, batch_blend_op, batch_render_word);
|
||||
|
||||
batch_triangle_count = 0;
|
||||
batch_attrib_offset = 0;
|
||||
}
|
||||
void TriangleBatch::DrawTriangle(uint triangle_count, uint attrib_count, const Vector4 *vtx, const ushort *idx, const Color *col, const Vector2 *uv, const Render::Texture *t, Core::Material::BlendOperator blend_op, Core::Material::RenderWord render_word)
|
||||
{
|
||||
bool batch_broken = (batch_triangle_count == 0) || (batch_t != t) || (batch_blend_op != blend_op) || (batch_render_word != render_word);
|
||||
|
||||
if (batch_triangle_count + triangle_count > batch_max_triangle_count)
|
||||
batch_broken = true;
|
||||
|
||||
if (batch_broken)
|
||||
Flush();
|
||||
|
||||
// Transfer and fix-up indices.
|
||||
for (uint n = 0; n < triangle_count; ++n)
|
||||
{
|
||||
batch_idx[batch_triangle_count * 3 + 0] = (ushort)(idx[n * 3 + 0] + batch_attrib_offset);
|
||||
batch_idx[batch_triangle_count * 3 + 1] = (ushort)(idx[n * 3 + 1] + batch_attrib_offset);
|
||||
batch_idx[batch_triangle_count * 3 + 2] = (ushort)(idx[n * 3 + 2] + batch_attrib_offset);
|
||||
++batch_triangle_count;
|
||||
}
|
||||
|
||||
// Transfer attributes.
|
||||
Memory::Copy(&batch_vtx[batch_attrib_offset], vtx, sizeof(Vector4) * attrib_count);
|
||||
Memory::Copy(&batch_col[batch_attrib_offset], col, sizeof(Color) * attrib_count);
|
||||
Memory::Copy(&batch_uv[batch_attrib_offset], uv, sizeof(Vector2) * attrib_count);
|
||||
batch_attrib_offset += attrib_count;
|
||||
|
||||
batch_render_word = render_word;
|
||||
batch_blend_op = blend_op;
|
||||
batch_t = t;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
TriangleBatch::TriangleBatch(Renderer &r, uint max) : renderer(r)
|
||||
{
|
||||
batch_t = NULL;
|
||||
batch_render_word = Core::Material::Render_None;
|
||||
batch_blend_op = Core::Material::Blend_None;
|
||||
|
||||
batch_max_triangle_count = max;
|
||||
batch_triangle_count = 0;
|
||||
batch_attrib_offset = 0;
|
||||
|
||||
batch_idx.Allocate(3 * batch_max_triangle_count);
|
||||
batch_vtx.Allocate(3 * batch_max_triangle_count);
|
||||
batch_col.Allocate(3 * batch_max_triangle_count);
|
||||
batch_uv.Allocate(3 * batch_max_triangle_count);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user