Compare commits

3 Commits

625 changed files with 117342 additions and 4445 deletions

View File

@ -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

Binary file not shown.

View 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; }
//------------------------------------------------------------------------------

View 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();
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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() {}
//------------------------------------------------------------------------------

View 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
View 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)
}
//------------------------------------------------------------------------------

View 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();
}

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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
}
//--------------------------------------------------------------------------

View 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/");
}

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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();
}
//------------------------------------------------------------------------------

View 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();
}
//------------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View 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;
}
//------------------------------------------------------------------------------

View 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);
}
//------------------------------------------------------------------------------

View 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;
}

View 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);
}
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//-------------------------------------------------------------------------------

View 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;
}

View 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);
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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();
}
//------------------------------------------------------------------------------

View 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);
}
//------------------------------------------------------------------------------

View 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();
}

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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) {}

View 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);
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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();
}
//------------------------------------------------------------------------------

View 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);
}
//------------------------------------------------------------------------------

View 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);
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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"); }
//------------------------------------------------------------------------------

View 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

View 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;
}
//------------------------------------------------------------------------------

View 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;
}

View 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; }

View 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;
}

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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(); }
//------------------------------------------------------------------------------

View 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(); }
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View File

@ -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);
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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);
}
//------------------------------------------------------------------------------

View 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();
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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); }
//------------------------------------------------------------------------------

View 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);
}
}
//------------------------------------------------------------------------------

View 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;
}

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View File

@ -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;

View 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;
}
//------------------------------------------------------------------------------

View 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
//------------------------------------------------------------------------------

View 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);
}
//------------------------------------------------------------------------------

View 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);
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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); }
//------------------------------------------------------------------------------

View 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];
}
//------------------------------------------------------------------------------

View 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);
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View 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);
}
//------------------------------------------------------------------------------

View 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);
}
//------------------------------------------------------------------------------

View 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 *> &in;
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);
}
//------------------------------------------------------------------------------

View 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;
}
//------------------------------------------------------------------------------

View File

@ -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]);

View 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;
}
//------------------------------------------------------------------------------

View 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);
}
//------------------------------------------------------------------------------

View 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();
}
//------------------------------------------------------------------------------

View 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);
}
//------------------------------------------------------------------------------

View 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";
}
//------------------------------------------------------------------------------

View 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);
}
}
//-----------------------------------------------------------------------------

View 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