mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 04:34:48 +00:00
470 lines
12 KiB
C++
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);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
|