Torque3D/Engine/source/afx/afxResidueMgr.cpp
2017-07-26 19:38:34 +01:00

470 lines
12 KiB
C++

//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "afx/arcaneFX.h"
#include "ts/tsShapeInstance.h"
#include "afx/ce/afxZodiacMgr.h"
#include "afx/ce/afxModel.h"
#include "afx/afxResidueMgr.h"
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
int QSORT_CALLBACK afxResidueMgr::ResidueList::compare_residue(const void* p1, const void* p2)
{
const afxResidueMgr::Residue** pd1 = (const afxResidueMgr::Residue**)p1;
const afxResidueMgr::Residue** pd2 = (const afxResidueMgr::Residue**)p2;
return int(((char*)(*pd1)->data.simobject) - ((char*)(*pd2)->data.simobject));
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
inline void afxResidueMgr::ResidueList::swap_array_ptrs()
{
Vector<Residue*>* tmp = m_array;
m_array = m_scratch_array;
m_scratch_array = tmp;
}
void afxResidueMgr::ResidueList::free_residue(Residue* residue)
{
if (the_mgr == NULL)
return;
if (the_mgr->requires_delete_tracking(residue))
the_mgr->disable_delete_tracking(residue);
the_mgr->free_residue(residue);
}
afxResidueMgr::ResidueList::ResidueList()
{
VECTOR_SET_ASSOCIATION(m_array_a);
VECTOR_SET_ASSOCIATION(m_array_b);
m_array = &m_array_a;
m_scratch_array = &m_array_b ;
m_dirty = false;
m_pending = -1;
}
afxResidueMgr::ResidueList::~ResidueList()
{
clear();
}
void afxResidueMgr::ResidueList::clear()
{
if (the_mgr)
{
for (S32 i = 0; i < m_array->size(); i++)
{
Residue* r = (*m_array)[i];
the_mgr->free_residue(r);
}
}
m_array_a.clear();
m_array_b.clear();
}
void afxResidueMgr::ResidueList::sort()
{
dQsort(m_array->address(), m_array->size(), sizeof(Residue*), compare_residue);
m_dirty = false;
}
void afxResidueMgr::ResidueList::fadeAndCull(U32 now)
{
for (S32 i = 0; i < m_array->size(); i++)
{
Residue* r = (*m_array)[i];
// done
if (now >= r->stop_time)
{
free_residue(r);
}
// fading
else if (now >= r->fade_time)
{
r->fade = 1.0f - ((F32)(now - r->fade_time))/((F32)(r->stop_time - r->fade_time));
m_scratch_array->push_back(r);
}
// opaque
else
{
r->fade = 1.0f;
m_scratch_array->push_back(r);
}
}
m_array->clear();
swap_array_ptrs();
}
// removes all residue with datablock matching obj
void afxResidueMgr::ResidueList::stripMatchingObjects(SimObject* db, bool del_notify)
{
if (del_notify)
{
for (S32 i = 0; i < m_array->size(); i++)
{
Residue* r = (*m_array)[i];
if (db == r->data.simobject && the_mgr != NULL)
the_mgr->free_residue(r);
else
m_scratch_array->push_back(r);
}
}
else
{
for (S32 i = 0; i < m_array->size(); i++)
{
Residue* r = (*m_array)[i];
if (db == r->data.simobject)
free_residue(r);
else
m_scratch_array->push_back(r);
}
}
m_array->clear();
swap_array_ptrs();
}
void afxResidueMgr::ResidueList::add(Residue* residue)
{
m_array->push_back(residue);
m_dirty = true;
}
void afxResidueMgr::manage_residue(const Residue* r)
{
if (r == NULL || r->fade < 0.01f)
return;
if (r->type == ZODIAC)
{
LinearColorF zode_color = ColorI(r->params.zodiac.r, r->params.zodiac.g, r->params.zodiac.b, r->params.zodiac.a);
afxZodiacData* zd = (afxZodiacData*) r->data.zodiac;
if (zd->blend_flags == afxZodiacDefs::BLEND_SUBTRACTIVE)
zode_color *= r->fade;
else
zode_color.alpha *= r->fade;
Point3F zode_pos(r->params.zodiac.pos_x, r->params.zodiac.pos_y, r->params.zodiac.pos_z);
Point2F zode_vrange(r->params.zodiac.vrange_dn, r->params.zodiac.vrange_dn);
if (r->params.zodiac.on_terrain)
{
afxZodiacMgr::addTerrainZodiac(zode_pos, r->params.zodiac.rad, zode_color, r->params.zodiac.ang, zd);
}
else
{
afxZodiacMgr::addInteriorZodiac(zode_pos, r->params.zodiac.rad, zode_vrange, zode_color, r->params.zodiac.ang, zd);
}
}
else if (r->type == MODEL)
{
r->data.model->setFadeAmount(r->fade);
}
}
void afxResidueMgr::ResidueList::manage()
{
if (the_mgr == NULL)
return;
S32 n_residue = m_array->size();
for (S32 x = 0; x < n_residue; x++)
the_mgr->manage_residue((*m_array)[x]);
}
U32 afxResidueMgr::ResidueList::findPendingBestBump(U32 look_max)
{
U32 soonest = 1000*60*60*24;
m_pending = -1;
U32 n = m_array->size();
for (U32 i = 0; i < n && i < look_max; i++)
{
Residue* r = (*m_array)[i];
if (r->stop_time < soonest)
{
soonest = r->stop_time;
m_pending = i;
}
}
return soonest;
}
void afxResidueMgr::ResidueList::bumpPending()
{
if (m_pending >= 0 && m_pending < m_array->size())
{
Residue* r = (*m_array)[m_pending];
m_array->erase(m_pending);
free_residue(r);
}
m_pending = -1;
}
bool afxResidueMgr::requires_delete_tracking(Residue* r)
{
return (r->type == MODEL);
}
void afxResidueMgr::enable_delete_tracking(Residue* r)
{
deleteNotify(r->data.simobject);
}
void afxResidueMgr::disable_delete_tracking(Residue* r)
{
clearNotify(r->data.simobject);
r->data.simobject->deleteObject();
r->data.simobject = 0;
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
afxResidueMgr* afxResidueMgr::the_mgr = NULL;
U32 afxResidueMgr::m_max_residue_objs = 256;
bool afxResidueMgr::enabled = true;
IMPLEMENT_CONOBJECT(afxResidueMgr);
ConsoleDocClass( afxResidueMgr,
"@brief A class that manages certain AFX effects that can persist for long durations.\n\n"
"A class that manages certain AFX effects that can persist much longer than the duration of choreographers.\n"
"@ingroup AFX\n"
);
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// free-list management
afxResidueMgr::Residue* afxResidueMgr::alloc_free_pool_block()
{
// allocate new block for the free-list
m_free_pool_blocks.push_back(new Residue[FREE_POOL_BLOCK_SIZE]);
// link them onto the free-list
Residue* new_block = m_free_pool_blocks.last();
for (U32 i = 0; i < FREE_POOL_BLOCK_SIZE - 1; i++)
new_block[i].next = &new_block[i + 1];
// tail of free-list points to NULL
new_block[FREE_POOL_BLOCK_SIZE - 1].next = NULL;
return new_block;
}
afxResidueMgr::Residue* afxResidueMgr::alloc_residue()
{
// need new free-list-block if m_next_free is null
if (!m_next_free)
m_next_free = alloc_free_pool_block();
// pop new residue from head of free-list
Residue* residue = m_next_free;
m_next_free = residue->next;
residue->next = NULL;
return residue;
}
void afxResidueMgr::free_residue(Residue* residue)
{
if (residue && residue->type == ZODIAC)
{
if (residue->data.zodiac && residue->data.zodiac->isTempClone())
{
delete residue->data.zodiac;
residue->data.zodiac = 0;
}
}
// push residue onto head of free-list
residue->next = m_next_free;
m_next_free = residue;
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
void afxResidueMgr::deleteResidueObject(SimObject* obj, bool del_notify)
{
m_managed.stripMatchingObjects(obj, del_notify);
}
void afxResidueMgr::bump_residue()
{
if (m_managed.findPendingBestBump())
m_managed.bumpPending();
}
void afxResidueMgr::add_residue(Residue* residue)
{
AssertFatal(residue != NULL, "residue pointer is NULL.");
if (m_managed.size() >= m_max_residue_objs)
bump_residue();
m_managed.add(residue);
manage_residue(residue);
if (requires_delete_tracking(residue))
enable_delete_tracking(residue);
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
afxResidueMgr::afxResidueMgr()
{
mObjBox.minExtents.set(-1e7, -1e7, -1e7);
mObjBox.maxExtents.set( 1e7, 1e7, 1e7);
mWorldBox.minExtents.set(-1e7, -1e7, -1e7);
mWorldBox.maxExtents.set( 1e7, 1e7, 1e7);
m_next_free = NULL;
VECTOR_SET_ASSOCIATION(m_free_pool_blocks);
}
afxResidueMgr::~afxResidueMgr()
{
cleanup();
}
void afxResidueMgr::cleanup()
{
m_managed.clear();
m_next_free = NULL;
for (S32 i = 0; i < m_free_pool_blocks.size(); i++)
delete [] m_free_pool_blocks[i];
m_free_pool_blocks.clear();
}
void afxResidueMgr::onDeleteNotify(SimObject* obj)
{
deleteResidueObject(obj, true);
Parent::onDeleteNotify(obj);
}
void afxResidueMgr::residueAdvanceTime()
{
U32 now = Platform::getVirtualMilliseconds();
m_managed.fadeAndCull(now);
m_managed.sortIfDirty();
m_managed.manage();
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//
// add ZODIAC residue
void afxResidueMgr::add_interior_zodiac(F32 dur, F32 fade_dur, afxZodiacData* zode, const Point3F& pos,
F32 rad, const Point2F& vrange, const LinearColorF& col, F32 ang)
{
add_zodiac(dur, fade_dur, zode, pos, rad, vrange, col, ang, false);
}
void afxResidueMgr::add_terrain_zodiac(F32 dur, F32 fade_dur, afxZodiacData* zode, const Point3F& pos,
F32 rad, const LinearColorF& col, F32 ang)
{
static Point2F vrange(0.0, 0.0);
add_zodiac(dur, fade_dur, zode, pos, rad, vrange, col, ang, true);
}
void afxResidueMgr::add_zodiac(F32 dur, F32 fade_dur, afxZodiacData* zode, const Point3F& pos,
F32 rad, const Point2F& vrange, const LinearColorF& col, F32 ang, bool on_terrain)
{
if (m_max_residue_objs == 0 || dur <= 0 || the_mgr == NULL)
return;
ColorI col_i = LinearColorF(col).toColorI();
U32 now = Platform::getVirtualMilliseconds();
Residue* residue = the_mgr->alloc_residue();
//
residue->type = ZODIAC;
residue->data.zodiac = zode;
residue->fade_time = now + (U32)(dur*1000);
residue->stop_time = residue->fade_time + (U32)(fade_dur*1000);
residue->fade = 1.0f;
//
residue->params.zodiac.pos_x = pos.x;
residue->params.zodiac.pos_y = pos.y;
residue->params.zodiac.pos_z = pos.z;
residue->params.zodiac.rad = rad;
residue->params.zodiac.vrange_dn = vrange.x;
residue->params.zodiac.vrange_up = vrange.y;
residue->params.zodiac.r = col_i.red;
residue->params.zodiac.g = col_i.green;
residue->params.zodiac.b = col_i.blue;
residue->params.zodiac.a = col_i.alpha;
residue->params.zodiac.ang = ang;
residue->params.zodiac.on_terrain = on_terrain;
//
residue->next = 0;
the_mgr->add_residue(residue);
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//
// add MODEL residue
void afxResidueMgr::add(F32 dur, F32 fade_dur, afxModel* model)
{
if (m_max_residue_objs == 0 || dur <= 0 || the_mgr == NULL)
return;
U32 now = Platform::getVirtualMilliseconds();
Residue* residue = the_mgr->alloc_residue();
//
residue->type = MODEL;
residue->data.model = model;
residue->fade_time = now + (U32)(dur*1000);
residue->stop_time = residue->fade_time + (U32)(fade_dur*1000);
residue->fade = 1.0f;
//
residue->next = 0;
the_mgr->add_residue(residue);
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//