#include "Tringula.h"
#include <Glasses/ZHashList.h>
#include <Glasses/ZVector.h>
#include <Glasses/RectTerrain.h>
#include <Glasses/ZImage.h>
#include <Stones/TringTvor.h>
#include "TriMesh.h"
#include "ParaSurf.h"
#include "Statico.h"
#include "Dynamico.h"
#include "Crawler.h"
#include "Flyer.h"
#include "Airplane.h"
#include "Chopper.h"
#include "LandMark.h"
#include "ExtendioExplosion.h"
#include "LaserTraceExplosion.h"
#include "Tringula.c7"
#include <Glasses/WSTube.h>
#include <Glasses/ZQueen.h>
#include <Opcode/Opcode.h>
#include <TMath.h>
ClassImp(Tringula);
void Tringula::_init()
{
bUseDispList = true;
mMaxFlyerH = 0;
mMaxCameraH = 0;
bRnrBBoxes = false;
mEdgeRule = ER_Stop;
mRndGen.SetSeed(0);
mBoxPruner = new Opcode::BipartiteBoxPruner;
mStatosLTS = mDynosLTS = mFlyersLTS = 0;
}
Tringula::~Tringula()
{
delete mBoxPruner;
}
void Tringula::AdEnlightenment()
{
PARENT_GLASS::AdEnlightenment();
if (mStatos == 0) {
assign_link<ZHashList>(mStatos, FID(), "Statos", GForm("Statos of Tringula %s", GetName()));
mStatos->SetElementFID(Statico::FID());
}
if (mDynos == 0) {
assign_link<ZHashList>(mDynos, FID(), "Dynos", GForm("Dynos of Tringula %s", GetName()));
mDynos->SetElementFID(Dynamico::FID());
}
if (mFlyers == 0) {
assign_link<ZHashList>(mFlyers, FID(), "Flyers", GForm("Flyers of Tringula %s", GetName()));
mFlyers->SetElementFID(Dynamico::FID());
}
if (mLandMarks == 0) {
assign_link<ZHashList>(mLandMarks, FID(), "LandMarks", GForm("LandMarks of Tringula %s", GetName()));
mLandMarks->SetElementFID(LandMark::FID());
}
if (mTubes == 0) {
assign_link<ZHashList>(mTubes, FID(), "Tubes", GForm("Tubes of Tringula %s", GetName()));
mTubes->SetElementFID(WSTube::FID());
}
if (mExplodios == 0) {
assign_link<ZHashList>(mExplodios, FID(), "Explodios", GForm("Exploding Extendios of Tringula %s", GetName()));
mExplodios->SetElementFID(Extendio::FID());
}
if (mExplosions == 0) {
assign_link<ZHashList>(mExplosions, FID(), "Explosions", GForm("Explosions of Tringula %s", GetName()));
mExplosions->SetElementFID(Explosion::FID());
}
}
Bool_t Tringula::RayCollide(const Opcode::Ray& ray, Float_t ray_length,
Bool_t cull_p, Bool_t closest_p,
Opcode::CollisionFaces& col_faces)
{
static const Exc_t _eh("Tringula::RayCollide ");
if (mMesh == 0 || mMesh->GetOPCModel() == 0)
throw _eh + "Opcode model not created.";
using namespace Opcode;
RayCollider RC;
RC.SetCulling(cull_p);
RC.SetClosestHit(closest_p);
RC.SetDestination(&col_faces);
if (ray_length > 0) RC.SetMaxDist(ray_length);
const char* setval = RC.ValidateSettings();
if (setval != 0)
throw _eh + "setting validation failed: " + setval;
return RC.Collide(ray, *mMesh->GetOPCModel());
}
Float_t Tringula::RayCollideClosestHit(const Opcode::Ray& ray, Bool_t cull_p)
{
static const Exc_t _eh("Tringula::RayCollideClosestHit ");
Opcode::CollisionFaces col_faces;
if (RayCollide(ray, 0, cull_p, true, col_faces))
{
if (col_faces.GetNbFaces())
return col_faces[0].mDistance;
else
return Opcode::MAX_FLOAT;
}
else
{
printf("%scollide status=<failed>.", _eh.Data());
return Opcode::MAX_FLOAT;
}
}
void Tringula::prepick_extendios(AList* extendios, const Opcode::Ray& ray, Float_t ray_length,
lPickResult_t& candidates)
{
Opcode::Point dpos;
Float_t t;
Stepper<> stepper(extendios);
while (stepper.step())
{
Extendio* ext = (Extendio*) *stepper;
dpos.Sub(ray.mOrig, ext->ref_last_aabb().Center());
t = - (ray.mDir | dpos);
if (t > 0 && t < ray_length && dpos.SquareMagnitude() - t*t <= ext->ref_last_aabb().GetSphereSquareRadius())
{
candidates.push_back(PickResult(ext, t));
}
}
}
Extendio* Tringula::PickExtendios(const Opcode::Ray& ray, Float_t ray_length,
Float_t* ext_distance)
{
static const Exc_t _eh("Tringula::PickExtendios ");
if (ray_length <= 0) ray_length = Opcode::MAX_FLOAT;
lPickResult_t candidates;
prepick_extendios(*mStatos, ray, ray_length, candidates);
prepick_extendios(*mDynos, ray, ray_length, candidates);
prepick_extendios(*mFlyers, ray, ray_length, candidates);
prepick_extendios(*mLandMarks, ray, ray_length, candidates);
candidates.sort();
Opcode::RayCollider RC;
RC.SetFirstContact(true);
for (lPickResult_i res = candidates.begin(); res != candidates.end(); ++res)
{
Extendio *ext = res->fExtendio;
if (RC.Collide(ray, * ext->get_opc_model(), ext->RefLastTrans()))
{
if (RC.GetContactStatus())
{
if (ext_distance) *ext_distance = res->fTime;
return ext;
}
}
else
{
printf("%scollide status=<failed>, extendio='%s'.", _eh.Data(), ext->GetName());
}
}
return 0;
}
void Tringula::ResetCollisionStuff()
{
mMesh->BuildOpcStructs();
Stepper<Dynamico> stepper(*mDynos);
while (stepper.step())
{
stepper->mOPCRCCache = Opcode::OPC_INVALID_ID;
}
}
Float_t Tringula::PlaceAboveTerrain(ZTrans& trans, Float_t height, Float_t dh_fac)
{
static const Exc_t _eh("Tringula::PlaceAboveTerrain ");
using namespace Opcode;
RayCollider RC;
RC.SetClosestHit(true);
CollisionFaces CF;
RC.SetDestination(&CF);
Point pos; trans.GetPos(pos);
Opcode::Ray R;
Float_t ray_offset = mParaSurf->pos2hray(pos, R);
Float_t abs_height = height + dh_fac*mParaSurf->GetDeltaH();
Bool_t status = RC.Collide(R, *mMesh->GetOPCModel());
if (status && CF.GetNbFaces() == 1)
{
const CollisionFace& cf = CF.GetFaces()[0];
pos.TMac(R.mDir, cf.mDistance - ray_offset - abs_height);
trans.SetPos(pos);
}
else
{
throw _eh + RC.CollideInfo(status, R);
}
return abs_height;
}
Statico* Tringula::NewStatico(const Text_t* sname)
{
if (sname == 0)
sname = GForm("Statico %d", mStatos->GetSize() + 1);
Statico* s = new Statico(sname);
mParaSurf->origin_trans(s->ref_trans());
place_on_terrain(s, *mDefStaMesh, false);
mQueen->CheckIn(s);
s->SetMesh(*mDefStaMesh);
mStatos->Add(s);
s->SetTringula(this);
s->update_aabb();
return s;
}
Statico* Tringula::RandomStatico(ZVector *mesh_list,
Bool_t check_inside,
Int_t max_tries)
{
static const Exc_t _eh("Tringula::RandomStatico ");
Statico* s = new Statico(GForm("Statico %d", mStatos->GetSize() + 1));
TriMesh* mesh;
if (mesh_list) {
mesh = dynamic_cast<TriMesh*>
(mesh_list->GetElementById(mRndGen.Integer(mesh_list->GetSize())));
} else {
mesh = *mDefStaMesh;
}
setup_stato_pruner();
Opcode::AABB bbox;
Opcode::AABBTreeCollider collider;
Opcode::BVTCache cache;
cache.Model0 = mesh->GetOPCModel();
Int_t top_cnt = 0;
place:
if (++top_cnt > max_tries)
return 0;
mParaSurf->random_trans(mRndGen, s->ref_trans());
s->ref_trans().RotateLF(1, 2, mRndGen.Uniform(0, TMath::TwoPi()));
Bool_t place_status = place_on_terrain(s, mesh, check_inside,
0.5f * mesh->ref_mesh_bbox().GetZSize());
if (check_inside && ! place_status)
goto place;
mesh->ref_mesh_bbox().Rotate(s->ref_trans(), bbox);
Opcode::Container hits;
mBoxPruner->SinglePruning(hits, 0, bbox);
for (UInt_t i = 0; i < hits.GetNbEntries(); ++i)
{
Statico *hit = (Statico*) mBoxPruner->GetUserData(0, hits.GetEntry(i));
cache.Model1 = hit->get_opc_model();
collider.Collide(cache, s->ref_trans(), hit->ref_trans());
if (collider.GetContactStatus())
goto place;
}
mQueen->CheckIn(s);
s->SetMesh(mesh);
mStatos->Add(s);
s->SetTringula(this);
s->update_aabb();
return s;
}
void Tringula::RegisterCrawler(Crawler* d)
{
mParaSurf->origin_trans(d->ref_trans());
place_on_terrain(d, d->GetLevH());
mQueen->CheckIn(d);
d->SetMesh(*mDefDynMesh);
mDynos->Add(d);
d->SetTringula(this);
d->update_aabb();
d->update_last_data();
}
Crawler* Tringula::NewDynamico(const Text_t* dname)
{
if (dname == 0)
dname = GForm("Dynamico %d", mDynos->GetSize() + 1);
Crawler* d = new Crawler(dname);
RegisterCrawler(d);
return d;
}
Crawler* Tringula::RandomDynamico(ZVector* mesh_list,
Float_t v_min, Float_t v_max, Float_t w_max)
{
Crawler* d = new Crawler(GForm("Dynamico %d", mDynos->GetSize() + 1));
HTransF& t = d->ref_trans();
TriMesh* mesh;
if (mesh_list) {
mesh = dynamic_cast<TriMesh*>
(mesh_list->GetElementById(mRndGen.Integer(mesh_list->GetSize())));
} else {
mesh = *mDefDynMesh;
}
mParaSurf->random_trans(mRndGen, t);
Float_t phi = mRndGen.Uniform(0, TMath::TwoPi());
t.RotateLF(1, 2, phi);
d->SetV(mRndGen.Uniform( v_min, v_max));
d->SetW(mRndGen.Uniform(-w_max, w_max));
place_on_terrain(d, d->GetLevH());
mQueen->CheckIn(d);
d->SetMesh(mesh);
mDynos->Add(d);
d->SetTringula(this);
d->update_aabb();
d->update_last_data();
return d;
}
Dynamico* Tringula::RandomAirplane(Float_t v_min, Float_t v_max,
Float_t w_max,
Float_t h_min_fac, Float_t h_max_fac)
{
static const Exc_t _eh("Tringula::RandomAirplane ");
Flyer* d = new Airplane(GForm("Airplane %d", mFlyers->GetSize() + 1));
HTransF& t = d->ref_trans();
mParaSurf->random_trans(mRndGen, t);
Float_t phi = mRndGen.Uniform(0, TMath::TwoPi());
t.RotateLF(1, 2, phi);
Float_t point_h, terrain_h;
if ( ! terrain_height(t.ref_pos(), point_h, terrain_h))
throw _eh + "problem determining terrain height.";
Float_t h = mRndGen.Uniform(h_min_fac * mMaxFlyerH, h_max_fac * mMaxFlyerH);
h = TMath::Max(h, terrain_h + mDefFlyMesh->GetTTvor()->BoundingBoxDiagonal());
t.MoveLF(3, h);
d->SetHeight(h);
d->SetV(mRndGen.Uniform( v_min, v_max));
d->SetW(mRndGen.Uniform(-w_max, w_max));
mQueen->CheckIn(d);
d->SetMesh(*mDefFlyMesh);
mFlyers->Add(d);
d->SetTringula(this);
d->update_aabb();
d->update_last_data();
return d;
}
Dynamico* Tringula::RandomChopper(Float_t v_min, Float_t v_max,
Float_t w_max,
Float_t h_min_fac, Float_t h_max_fac)
{
static const Exc_t _eh("Tringula::RandomChopper ");
Flyer* d = new Chopper(GForm("Chopper %d", mFlyers->GetSize() + 1));
HTransF& t = d->ref_trans();
mParaSurf->random_trans(mRndGen, t);
Float_t phi = mRndGen.Uniform(0, TMath::TwoPi());
t.RotateLF(1, 2, phi);
Float_t point_h, terrain_h;
if ( ! terrain_height(t.ref_pos(), point_h, terrain_h))
throw _eh + "problem determining terrain height.";
Float_t h = mRndGen.Uniform(h_min_fac * mMaxFlyerH, h_max_fac * mMaxFlyerH);
h = TMath::Max(h, terrain_h + mDefChopMesh->GetTTvor()->BoundingBoxDiagonal());
t.MoveLF(3, h);
d->SetHeight(h);
d->SetV(mRndGen.Uniform( v_min, v_max));
d->SetW(mRndGen.Uniform(-w_max, w_max));
mQueen->CheckIn(d);
d->SetMesh(*mDefChopMesh);
mFlyers->Add(d);
d->SetTringula(this);
d->update_aabb();
d->update_last_data();
return d;
}
LandMark* Tringula::AddLandMark(TriMesh* mesh, const Float_t* pos)
{
LandMark* d = new LandMark(GForm("LandMark %d", mLandMarks->GetSize() + 1));
HTransF& t = d->ref_trans();
Float_t fgh[3];
mParaSurf->pos2fgh(pos, fgh);
mParaSurf->fgh2trans(fgh, t);
mQueen->CheckIn(d);
d->SetMesh(mesh);
mLandMarks->Add(d);
d->SetTringula(this);
d->SetFGH(fgh[0], fgh[1], fgh[2]);
d->update_aabb();
d->update_last_data();
return d;
}
Bool_t Tringula::CheckBoundaries(Dynamico* dyno, Float_t& safety)
{
Bool_t trans_changed = false;
Int_t np = mParaSurf->n_edge_planes();
Float_t min_d = 1e5;
for (Int_t p = 0; p < np; ++p)
{
Opcode::Point& pos = * (Opcode::Point*) dyno->ref_trans().ArrT();
Opcode::Point& old_pos = * (Opcode::Point*) dyno->ref_last_trans().ArrT();
Float_t d = mParaSurf->edge_planes()[p].Distance(pos);
if (d > 0)
{
trans_changed = true;
handle_edge_crossing(*dyno, old_pos, pos, p, d);
d = mParaSurf->edge_planes()[p].Distance(pos);
}
d = -d;
if (d < min_d)
min_d = d;
}
safety = min_d;
return trans_changed;
}
void Tringula::DoFullBoxPrunning(vector<Opcode::Segment>& its_lines,
Bool_t accumulate, Bool_t verbose)
{
static const Exc_t _eh("Tringula::DoFullBoxPrunning ");
using namespace Opcode;
GTime time(GTime::I_Now);
UInt_t nboxes = mStatos->GetSize() + mDynos->GetSize() + mFlyers->GetSize();
Extendio *extarr[nboxes];
const AABB *bboxes[nboxes];
{
int n = 0;
fill_pruning_list(*mStatos, n, bboxes, (void**) extarr);
fill_pruning_list(*mDynos, n, bboxes, (void**) extarr);
fill_pruning_list(*mFlyers, n, bboxes, (void**) extarr);
}
Pairs pairs;
Axes axes(AXES_XZY);
Bool_t res = CompleteBoxPruning(nboxes, bboxes, pairs, axes);
printf("%son %3u: res=%d, npairs=%3u, time=%f\n", _eh.Data(),
nboxes, res, pairs.GetNbPairs(), time.TimeUntilNow().ToDouble());
if (accumulate)
{
its_lines.reserve(its_lines.size() + 2*pairs.GetNbPairs());
}
else
{
its_lines.clear();
its_lines.reserve(2*pairs.GetNbPairs());
}
const Text_t* debug_prefix = verbose ? " " : 0;
AABBTreeCollider collider;
for (UInt_t i = 0; i < pairs.GetNbPairs(); ++i)
{
const Pair& p = * pairs.GetPair(i);
if (verbose)
printf(" %3u:", i);
Extendio::intersect_extendios(extarr[p.id0], extarr[p.id1], collider,
its_lines, debug_prefix);
}
printf(" Vector size = %zu, segments per pair = %f\n",
its_lines.size(), (float)its_lines.size()/pairs.GetNbPairs());
}
void Tringula::DoSplitBoxPrunning()
{
static const Exc_t _eh("Tringula::DoSplitBoxPrunning ");
setup_box_pruner();
Opcode::Pairs ds_pairs;
mBoxPruner->BipartitePruning(ds_pairs, 0, 1);
Opcode::Pairs dd_pairs;
mBoxPruner->CompletePruning(dd_pairs, 1);
Opcode::AABBTreeCollider collider;
Extendio::CollisionSegments segments;
Opcode::RayCollider RC;
Opcode::CollisionFaces CF;
RC.SetDestination(&CF);
RC.SetCulling(false);
for (UInt_t i = 0; i < ds_pairs.GetNbPairs(); ++i)
{
using namespace Opcode;
const Pair *p = ds_pairs.GetPair(i);
Statico *stato = (Statico*) mBoxPruner->GetUserData(0, p->id0);
Dynamico *dyno = (Dynamico*) mBoxPruner->GetUserData(1, p->id1);
Int_t ns = Extendio::intersect_extendios(stato, dyno, collider, segments);
if (ns > 0)
{
++stato->mNDynoColls;
HTransF& t_dyno = dyno->ref_trans();
Point com_dyno;
t_dyno.MultiplyVec3(dyno->GetMesh()->RefCOM(), 1.0f, com_dyno);
HTransF& t_stato = stato->ref_trans();
Point com_stato;
t_stato.MultiplyVec3(stato->GetMesh()->RefCOM(), 1.0f, com_stato);
Opcode::Ray R;
R.mOrig = com_dyno;
R.mDir = com_dyno - com_stato;
Bool_t ok_p = Dynamico::handle_collision(dyno, stato, RC, R, com_dyno,
segments);
if (!ok_p)
printf("%sDyno-Stato dyno->handle_collision failed.\n", _eh.Data());
segments.Clear();
}
}
for (UInt_t i = 0; i < dd_pairs.GetNbPairs(); ++i)
{
using namespace Opcode;
const Pair *p = dd_pairs.GetPair(i);
Dynamico *dyno0 = (Dynamico*) mBoxPruner->GetUserData(1, p->id0);
Dynamico *dyno1 = (Dynamico*) mBoxPruner->GetUserData(1, p->id1);
Int_t ns = Extendio::intersect_extendios(dyno0, dyno1, collider, segments);
if (ns > 0)
{
Point up_dir;
mParaSurf->pos2hdir(segments.RefCenter(), up_dir);
Dynamico::handle_collision(dyno0, dyno1, up_dir, segments);
segments.Clear();
}
}
}
void Tringula::TimeTick(Double_t t, Double_t dt)
{
static const Exc_t _eh("Tringula::TimeTick ");
GLensWriteHolder wlck(this);
{
Stepper<> dyno_stepper(*mDynos);
while (dyno_stepper.step())
{
Dynamico& D = * (Dynamico*) *dyno_stepper;
D.TimeTick(t, dt);
}
Stepper<> flyo_stepper(*mFlyers);
while (flyo_stepper.step())
{
Dynamico& D = * (Dynamico*) *flyo_stepper;
D.TimeTick(t, dt);
}
Stepper<> tube_stepper(*mTubes);
while (tube_stepper.step())
{
WSTube& D = * (WSTube*) *tube_stepper;
D.TimeTick(t, dt);
}
}
{
Stepper<> explosion_stepper(*mExplosions);
while (explosion_stepper.step())
{
Explosion& E = * (Explosion*) *explosion_stepper;
E.TimeTick(t, dt);
}
Stepper<> explodio_stepper(*mExplodios);
while (explodio_stepper.step())
{
Extendio& E = * (Extendio*) *explodio_stepper;
E.TimeTick(t, dt);
}
}
{
GMutexHolder _elck(mExplosionMutex);
for (lpZGlass_i i = mFreshExplodingExtendios.begin(); i != mFreshExplodingExtendios.end(); ++i)
{
Extendio *ext = (Extendio*) *i;
ExtendioExplosion *exp = new ExtendioExplosion;
mQueen->CheckIn(exp);
exp->SetExplodeDuration(1.0f + TMath::Log10(ext->GetMesh()->GetM() + 1.0f));
exp->SetTringula(this);
exp->SetExtendio(ext);
mExplosions->Add(exp);
mExplodios->Add(ext);
if (mDynos->RemoveAll(ext) == 0)
{
if (mFlyers->RemoveAll(ext) == 0)
if (mStatos->RemoveAll(ext) == 0)
mLandMarks->RemoveAll(ext);
}
EmitExtendioExplodingRay(ext, exp);
}
mFreshExplodingExtendios.clear();
for (lpZGlass_i i = mFinishedExtendioExplosions.begin(); i != mFinishedExtendioExplosions.end(); ++i)
{
ExtendioExplosion *exp = (ExtendioExplosion*) *i;
Extendio *ext = exp->GetExtendio();
EmitExtendioDyingRay(ext);
mExplosions->RemoveAll(exp);
mExplodios->RemoveAll(ext);
delete_lens_if_alive(exp);
delete_lens_if_alive(ext);
}
mFinishedExtendioExplosions.clear();
for (lpZGlass_i i = mFreshExplosions.begin(); i != mFreshExplosions.end(); ++i)
{
mExplosions->Add(*i);
}
mFreshExplosions.clear();
for (lpZGlass_i i =mFinishedExplosions.begin(); i != mFinishedExplosions.end(); ++i)
{
mExplosions->RemoveAll(*i);
delete_lens_if_alive(*i);
}
mFinishedExplosions.clear();
}
DoSplitBoxPrunning();
{
Stepper<> dyno_stepper(*mDynos);
while (dyno_stepper.step())
{
Dynamico& D = * (Dynamico*) *dyno_stepper;
D.update_aabb();
D.update_last_data();
}
Stepper<> flyo_stepper(*mFlyers);
while (flyo_stepper.step())
{
Dynamico& D = * (Dynamico*) *flyo_stepper;
D.update_aabb();
D.update_last_data();
}
Stepper<> explodio_stepper(*mExplodios);
while (explodio_stepper.step())
{
Extendio& E = * (Extendio*) *explodio_stepper;
E.update_aabb();
E.update_last_data();
}
}
{
Stepper<TimeMakerClient> tcl(this);
while (tcl.step())
tcl->TimeTick(t, dt);
}
}
void Tringula::ExtendioExploding(Extendio* ext)
{
GMutexHolder _lck(mExplosionMutex);
mFreshExplodingExtendios.push_back(ext);
}
void Tringula::ExtendioExplosionFinished(Explosion* exp)
{
GMutexHolder _lck(mExplosionMutex);
mFinishedExtendioExplosions.push_back(exp);
}
void Tringula::ExplosionStarted(Explosion* exp)
{
GMutexHolder _lck(mExplosionMutex);
mFreshExplosions.push_back(exp);
}
void Tringula::ExplosionFinished(Explosion* exp)
{
GMutexHolder _lck(mExplosionMutex);
mFinishedExplosions.push_back(exp);
}
void Tringula::LaserShot(Extendio* ext, const Opcode::Ray& ray, Float_t power)
{
static const Exc_t _eh("Tringula::LaserShot ");
Bool_t terrain_hit = false;
Float_t ray_length = 0;
Opcode::CollisionFaces col_faces;
if (RayCollide(ray, 0, true, true, col_faces))
{
if (col_faces.GetNbFaces())
{
terrain_hit = true;
ray_length = col_faces[0].mDistance;
}
}
else
{
printf("%scollide status=<failed>.", _eh.Data());
}
Extendio* dmg_ext = PickExtendios(ray, ray_length, &ray_length);
if (dmg_ext)
{
dmg_ext->TakeDamage(power);
}
if (ray_length == 0)
{
ray_length = 10.0f * power;
}
Opcode::Point end;
end.Mac(ray.mOrig, ray.mDir, ray_length);
LaserTraceExplosion* e = new LaserTraceExplosion("LaserShot");
e->SetTringula(this);
e->RefA().Set(ray.mOrig);
e->RefB().Set(end);
if (terrain_hit || dmg_ext)
{
e->SetEndRadius(0.03125f*sqrtf(power));
}
mQueen->CheckIn(e);
ExplosionStarted(e);
EmitExtendioSoundRay(ext, "PewPew");
}
void Tringula::fill_pruning_list(AList* extendios, Int_t& n,
const Opcode::AABB** boxes, void** user_data)
{
Stepper<> stepper(extendios);
while (stepper.step())
{
boxes[n] = &((Extendio*)*stepper)->RefAABB();
user_data[n] = *stepper;
++n;
}
}
void Tringula::fill_pruning_list(AList* extendios, Int_t& n, Int_t l)
{
fill_pruning_list(extendios, n, mBoxPruner->BoxList(l), mBoxPruner->UserData(l));
}
void Tringula::setup_box_pruner()
{
setup_stato_pruner();
setup_dyno_pruner();
}
void Tringula::setup_stato_pruner()
{
if (mStatosLTS < mStatos->GetListTimeStamp())
{
mBoxPruner->InitList(0, mStatos->Size());
int n = 0;
fill_pruning_list(*mStatos, n, 0);
mStatosLTS = mStatos->GetListTimeStamp();
mBoxPruner->Sort(0);
}
}
void Tringula::setup_dyno_pruner()
{
if (mDynosLTS < mDynos ->GetListTimeStamp() ||
mFlyersLTS < mFlyers->GetListTimeStamp())
{
mBoxPruner->InitList(1, mDynos->Size() + mFlyers->Size());
int n = 0;
fill_pruning_list(*mDynos, n, 1);
fill_pruning_list(*mFlyers, n, 1);
mDynosLTS = mDynos ->GetListTimeStamp();
mFlyersLTS = mFlyers->GetListTimeStamp();
}
mBoxPruner->Sort(1);
}
void Tringula::handle_edge_crossing
( Dynamico& D, Opcode::Point& old_pos, Opcode::Point& pos,
Int_t plane, Float_t dist )
{
using namespace Opcode;
Plane& P = mParaSurf->edge_planes()[plane];
switch (mEdgeRule)
{
case ER_Stop:
{
pos -= dist*P.n;
D.mVVec.Zero();
D.mWVec.Zero();
D.Stamp(D.FID());
break;
}
case ER_Hold:
{
Point step = pos; step -= old_pos;
Float_t depth = fabsf(P.Distance(old_pos));
pos = old_pos + depth*step/(depth+dist);
break;
}
case ER_Wrap:
{
if (mParaSurf->support_wrap())
{
mParaSurf->wrap(pos, plane, dist);
break;
}
}
case ER_Bounce:
{
pos -= 2*dist*P.n;
HTransF& t = D.ref_trans();
Point& fwd = * (Point*) t.PtrBaseVec(1);
fwd += -2 * (fwd | P.n) * P.n;
t.OrtoNorm3Column(3, 1);
t.SetBaseVecViaCross(2);
break;
}
}
}
Bool_t Tringula::terrain_height(const Opcode::Point& pos, Float_t& point_h, Float_t& terrain_h)
{
static const Exc_t _eh("Tringula::terrain_height ");
Opcode::RayCollider RC;
RC.SetClosestHit(true);
Opcode::CollisionFaces CF;
RC.SetDestination(&CF);
Opcode::Point fgh;
Opcode::Point hzero_pos;
mParaSurf->pos2fgh(pos, fgh);
point_h = fgh.z;
fgh.z = 0;
mParaSurf->fgh2pos(fgh, hzero_pos);
Opcode::Ray R;
Float_t ray_offset = mParaSurf->pos2hray(hzero_pos, R);
Bool_t cs = RC.Collide(R, *mMesh->GetOPCModel());
if (cs && CF.GetNbFaces() == 1)
{
const Opcode::CollisionFace& cf = CF.GetFaces()[0];
terrain_h = ray_offset - cf.mDistance;
return true;
}
else
{
ISwarn(_eh + RC.CollideInfo(cs, R));
return false;
}
}
Bool_t Tringula::place_on_terrain(Statico* S, TriMesh* M, Bool_t check_inside,
Float_t min_h_above)
{
static const Exc_t _eh("Tringula::place_on_terrain ");
if (M == 0)
{
M = S->GetMesh();
if (M == 0)
throw(_eh + "TriMesh not passed as argument nor available from Statico.\n");
}
HTransF& trans = S->ref_trans();
Opcode::AABB& aabb = M->ref_mesh_bbox();
Float_t ray_offset = mParaSurf->GetMaxH() - aabb.GetMin(Opcode::_Z) +
mParaSurf->GetEpsilon() +
0.1f*(mParaSurf->GetMaxH() - mParaSurf->GetMinH());
Float_t max_dist = 0, min_dist = ray_offset;
Opcode::Point fdir (trans.ArrX());
Opcode::Point gdir (trans.ArrY());
Opcode::Point center(trans.ArrT());
center.TMac(trans.ArrZ(), ray_offset);
Opcode::Ray R;
R.mDir.Set(trans.ArrZ()); R.mDir.Neg();
Opcode::RayCollider RC;
RC.SetClosestHit(true);
Opcode::CollisionFaces CF;
RC.SetDestination(&CF);
static const Float_t sample[] = { -1, 0, 1 };
static const Int_t ns = 3;
for (Int_t sf = 0; sf < ns; ++sf)
{
for (Int_t sg = 0; sg < ns; ++sg)
{
R.mOrig.Mac2(center,
fdir, sample[sf]*aabb.GetExtents(Opcode::_X),
gdir, sample[sg]*aabb.GetExtents(Opcode::_Y));
Bool_t coll_status = RC.Collide(R, *mMesh->GetOPCModel());
if (coll_status && CF.GetNbFaces() == 1)
{
max_dist = TMath::Max(max_dist, CF.GetFaces()[0].mDistance);
min_dist = TMath::Min(min_dist, CF.GetFaces()[0].mDistance);
}
else
{
if (check_inside && coll_status)
return false;
else
printf("%s(Statico*) sample_id %2d,%2d; status=%s, nfaces=%d\n"
" nbvt=%d, nprt=%d, ni=%d\n"
" ray_orig = %6.2f, %6.2f, %6.2f; ray_dir = %6.2f, %6.2f, %6.2f\n",
_eh.Data(), sf, sg, coll_status ? "ok" : "failed", CF.GetNbFaces(),
RC.GetNbRayBVTests(), RC.GetNbRayPrimTests(), RC.GetNbIntersections(),
R.mOrig.x, R.mOrig.y, R.mOrig.z, R.mDir.x, R.mDir.y, R.mDir.z);
}
}
}
if (aabb.GetZSize() - (max_dist - min_dist) < min_h_above)
return false;
trans.MoveLF(3, ray_offset - max_dist);
return true;
}
Bool_t Tringula::place_on_terrain(Dynamico* D, Float_t h_above)
{
static const Exc_t _eh("Tringula::place_on_terrain ");
Opcode::RayCollider RC;
RC.SetClosestHit(true);
Opcode::CollisionFaces CF;
RC.SetDestination(&CF);
Opcode::Point& pos = D->ref_pos();
Opcode::Ray R;
Float_t ray_offset = mParaSurf->pos2hray(pos, R);
Int_t cs = RC.Collide(R, *mMesh->GetOPCModel());
if (cs && CF.GetNbFaces() == 1)
{
const Opcode::CollisionFace& cf = CF.GetFaces()[0];
pos.TMac(R.mDir, cf.mDistance - ray_offset - h_above);
Float_t* n = mMesh->GetTTvor()->TriangleNormal(cf.mFaceID);
HTransF& trans = D->ref_trans();
trans.SetBaseVec(3, n);
trans.OrtoNorm3Column(1, 3);
trans.SetBaseVecViaCross(2);
return true;
}
else
{
printf("%s(Dynamico*) status=%s, nfaces=%d\n"
" nbvt=%d, nprt=%d, ni=%d\n"
" ray_orig = %6.2f, %6.2f, %6.2f; ray_dir = %6.2f, %6.2f, %6.2f\n",
_eh.Data(), cs ? "ok" : "failed", CF.GetNbFaces(),
RC.GetNbRayBVTests(), RC.GetNbRayPrimTests(), RC.GetNbIntersections(),
R.mOrig.x, R.mOrig.y, R.mOrig.z, R.mDir.x, R.mDir.y, R.mDir.z);
return false;
}
}
void Tringula::delete_lens_if_alive(ZGlass* lens)
{
if (!lens->CheckBit(kDyingBit))
{
auto_ptr<ZMIR> mir(mQueen->S_RemoveLens(lens));
mSaturn->ShootMIR(mir);
}
}
void Tringula::EmitExtendioExplodingRay(Extendio* ext, Explosion* exp)
{
if (mQueen && mSaturn->AcceptsRays())
{
auto_ptr<Ray> ray
(Ray::PtrCtor(this, PRQN_extendio_exploding, mTimeStamp, FID()));
TBufferFile cbuff(TBuffer::kWrite);
GledNS::WriteLensID(cbuff, ext);
GledNS::WriteLensID(cbuff, exp);
ray->SetCustomBuffer(cbuff);
mQueen->EmitRay(ray);
}
}
void Tringula::EmitExtendioDyingRay(Extendio* ext)
{
if (mQueen && mSaturn->AcceptsRays())
{
auto_ptr<Ray> ray
(Ray::PtrCtor(this, PRQN_extendio_dying, mTimeStamp, FID()));
TBufferFile cbuff(TBuffer::kWrite);
GledNS::WriteLensID(cbuff, ext);
ray->SetCustomBuffer(cbuff);
mQueen->EmitRay(ray);
}
}
void Tringula::EmitExtendioSoundRay(Extendio* ext, const TString& effect)
{
if (mQueen && mSaturn->AcceptsRays())
{
auto_ptr<Ray> ray
(Ray::PtrCtor(this, PRQN_extendio_sound, mTimeStamp, FID()));
TBufferFile cbuff(TBuffer::kWrite);
GledNS::WriteLensID(cbuff, ext);
cbuff << effect;
ray->SetCustomBuffer(cbuff);
mQueen->EmitRay(ray);
}
}