16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-20 00:59:19 +02:00
Files
maszyna/scene.cpp

999 lines
37 KiB
C++

/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "scene.h"
#include "globals.h"
#include "renderer.h"
#include "logs.h"
namespace scene {
// legacy method, updates sounds and polls event launchers within radius around specified point
void
basic_cell::update() {
/*
// renderowanie obiektów aktywnych a niewidocznych
for( auto node = subcell->nRenderHidden; node; node = node->nNext3 ) {
node->RenderHidden();
}
*/
// TBD, TODO: move to sound renderer
for( auto *path : m_paths ) {
// dźwięki pojazdów, również niewidocznych
path->RenderDynSounds();
}
}
// adds provided shape to the cell
void
basic_cell::insert( shape_node Shape ) {
m_active = true;
auto const &shapedata { Shape.data() };
auto &shapes = (
shapedata.translucent ?
m_shapestranslucent :
m_shapesopaque );
for( auto &targetshape : shapes ) {
// try to merge shapes with matching view ranges...
auto const &targetshapedata { targetshape.data() };
if( ( shapedata.rangesquared_min == targetshapedata.rangesquared_min )
&& ( shapedata.rangesquared_max == targetshapedata.rangesquared_max )
// ...and located close to each other (within arbitrary limit of 25m)
&& ( glm::length( shapedata.area.center - targetshapedata.area.center ) < 25.0 ) ) {
if( true == targetshape.merge( Shape ) ) {
// if the shape was merged there's nothing left to do
return;
}
}
}
// otherwise add the shape to the relevant list
Shape.origin( m_area.center );
shapes.emplace_back( Shape );
}
// adds provided path to the cell
void
basic_cell::insert( TTrack *Path ) {
m_active = true;
// TODO: add animation hook
Path->origin( m_area.center );
m_paths.emplace_back( Path );
}
// adds provided traction piece to the cell
void
basic_cell::insert( TTraction *Traction ) {
m_active = true;
Traction->origin( m_area.center );
m_traction.emplace_back( Traction );
}
// adds provided model instance to the cell
void
basic_cell::insert( TAnimModel *Instance ) {
m_active = true;
auto const flags = Instance->Flags();
auto alpha =
( Instance->Material() != nullptr ?
Instance->Material()->textures_alpha :
0x30300030 );
// assign model to appropriate render phases
if( alpha & flags & 0x2F2F002F ) {
// translucent pieces
m_instancetranslucent.emplace_back( Instance );
}
alpha ^= 0x0F0F000F; // odwrócenie flag tekstur, aby wyłapać nieprzezroczyste
if( alpha & flags & 0x1F1F001F ) {
// opaque pieces
m_instancesopaque.emplace_back( Instance );
}
}
// registers provided path in the lookup directory of the cell
void
basic_cell::register_end( TTrack *Path ) {
m_directories.paths.emplace( Path );
}
// registers provided traction piece in the lookup directory of the cell
void
basic_cell::register_end( TTraction *Traction ) {
m_directories.traction.emplace( Traction );
}
// find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance
std::tuple<TDynamicObject *, float>
basic_cell::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) {
TDynamicObject *vehiclenearest { nullptr };
float leastdistance { std::numeric_limits<float>::max() };
float distance;
float const distancecutoff { Radius * Radius }; // we'll ignore vehicles farther than this
for( auto *path : m_paths ) {
for( auto *vehicle : path->Dynamics ) {
if( ( true == Onlycontrolled )
&& ( vehicle->Mechanik == nullptr ) ) {
continue;
}
distance = glm::length2( glm::dvec3{ vehicle->GetPosition() } - Point );
if( ( distance > distancecutoff )
|| ( distance > leastdistance ) ){
continue;
}
std::tie( vehiclenearest, leastdistance ) = std::tie( vehicle, distance );
}
}
return std::tie( vehiclenearest, leastdistance );
}
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
std::tuple<TTrack *, int>
basic_cell::find( glm::dvec3 const &Point, TTrack const *Exclude ) {
Math3D::vector3 point { Point.x, Point.y, Point.z }; // sad workaround until math classes unification
int endpointid;
for( auto *path : m_directories.paths ) {
if( path == Exclude ) { continue; }
endpointid = path->TestPoint( &point );
if( endpointid >= 0 ) {
return std::tie( path, endpointid );
}
}
return { nullptr, -1 };
}
// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint
std::tuple<TTraction *, int>
basic_cell::find( glm::dvec3 const &Point, TTraction const *Exclude ) {
int endpointid;
for( auto *traction : m_directories.traction ) {
if( traction == Exclude ) { continue; }
endpointid = traction->TestPoint( Point );
if( endpointid >= 0 ) {
return std::tie( traction, endpointid );
}
}
return { nullptr, -1 };
}
// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece
std::tuple<TTraction *, int, float>
basic_cell::find( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ) {
TTraction
*tractionnearest { nullptr };
float
distance,
distancenearest { std::numeric_limits<float>::max() };
int endpoint,
endpointnearest { -1 };
for( auto *traction : m_directories.traction ) {
if( ( traction == Other )
|| ( traction->psSection != Other->psSection )
|| ( traction == Other->hvNext[ 0 ] )
|| ( traction == Other->hvNext[ 1 ] ) ) {
// ignore pieces from different sections, and ones connected to the other piece
continue;
}
endpoint = (
glm::dot( traction->vParametric, Other->vParametric ) >= 0.0 ?
Currentdirection ^ 1 :
Currentdirection );
if( ( traction->psPower[ endpoint ] == nullptr )
|| ( traction->fResistance[ endpoint ] < 0.0 ) ) {
continue;
}
distance = glm::length2( traction->location() - Point );
if( distance < distancenearest ) {
std::tie( tractionnearest, endpointnearest, distancenearest ) = std::tie( traction, endpoint, distance );
}
}
return { tractionnearest, endpointnearest, distancenearest };
}
// sets center point of the section
void
basic_cell::center( glm::dvec3 Center ) {
m_area.center = Center;
// NOTE: we should also update origin point for the contained nodes, but in practice we can skip this
// as all nodes will be added only after the proper center point was set, and won't change
}
// generates renderable version of held non-instanced geometry
void
basic_cell::create_geometry( geometrybank_handle const &Bank ) {
if( false == m_active ) { return; } // nothing to do here
for( auto &shape : m_shapesopaque ) { shape.create_geometry( Bank ); }
for( auto &shape : m_shapestranslucent ) { shape.create_geometry( Bank ); }
#ifndef EU07_USE_OLD_GROUNDCODE
for( auto *path : m_paths ) { path->create_geometry( Bank ); }
for( auto *traction : m_traction ) { traction->create_geometry( Bank ); }
#endif
}
// legacy method, updates sounds and polls event launchers within radius around specified point
void
basic_section::update( glm::dvec3 const &Location, float const Radius ) {
auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + Radius, 2 ) };
for( auto &cell : m_cells ) {
if( glm::length2( cell.area().center - Location ) < squaredradii ) {
// we reject cells which aren't within our area of interest
cell.update();
}
}
}
// adds provided shape to the section
void
basic_section::insert( shape_node Shape ) {
auto const &shapedata = Shape.data();
if( ( true == shapedata.translucent )
|| ( shapedata.rangesquared_max <= 90000.0 )
|| ( shapedata.rangesquared_min > 0.0 ) ) {
// small, translucent or not always visible shapes are placed in the sub-cells
cell( shapedata.area.center ).insert( Shape );
}
else {
// large, opaque shapes are placed on section level
for( auto &shape : m_shapes ) {
// check first if the shape can't be merged with one of the shapes already present in the section
if( true == shape.merge( Shape ) ) {
// if the shape was merged there's nothing left to do
return;
}
}
// otherwise add the shape to the section's list
Shape.origin( m_area.center );
m_shapes.emplace_back( Shape );
}
}
// adds provided path to the section
void
basic_section::insert( TTrack *Path ) {
// pass the node to the appropriate partitioning cell
// NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done
cell( Path->location() ).insert( Path );
}
// adds provided path to the section
void
basic_section::insert( TTraction *Traction ) {
// pass the node to the appropriate partitioning cell
// NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done
cell( Traction->location() ).insert( Traction );
}
// adds provided model instance to the section
void
basic_section::insert( TAnimModel *Instance ) {
// pass the node to the appropriate partitioning cell
// NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done
cell( Instance->location() ).insert( Instance );
}
// registers specified end point of the provided path in the lookup directory of the region
void
basic_section::register_end( TTrack *Path, glm::dvec3 const &Point ) {
cell( Point ).register_end( Path );
}
// registers specified end point of the provided traction piece in the lookup directory of the region
void
basic_section::register_end( TTraction *Traction, glm::dvec3 const &Point ) {
cell( Point ).register_end( Traction );
}
// find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance
std::tuple<TDynamicObject *, float>
basic_section::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) {
// go through sections within radius of interest, and pick the nearest candidate
TDynamicObject
*vehiclefound,
*vehiclenearest { nullptr };
float
distancefound,
distancenearest { std::numeric_limits<float>::max() };
auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + Radius, 2 ) };
for( auto &cell : m_cells ) {
// we reject early cells which aren't within our area of interest
if( glm::length2( cell.area().center - Point ) > squaredradii ) {
continue;
}
std::tie( vehiclefound, distancefound ) = cell.find( Point, Radius, Onlycontrolled );
if( ( vehiclefound != nullptr )
&& ( distancefound < distancenearest ) ) {
std::tie( vehiclenearest, distancenearest ) = std::tie( vehiclefound, distancefound );
}
}
return std::tie( vehiclenearest, distancenearest );
}
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
std::tuple<TTrack *, int>
basic_section::find( glm::dvec3 const &Point, TTrack const *Exclude ) {
return cell( Point ).find( Point, Exclude );
}
// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint
std::tuple<TTraction *, int>
basic_section::find( glm::dvec3 const &Point, TTraction const *Exclude ) {
return cell( Point ).find( Point, Exclude );
}
// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece
std::tuple<TTraction *, int, float>
basic_section::find( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ) {
// go through sections within radius of interest, and pick the nearest candidate
TTraction
*tractionfound,
*tractionnearest { nullptr };
float
distancefound,
distancenearest { std::numeric_limits<float>::max() };
int
endpointfound,
endpointnearest { -1 };
auto const radius { 0.0 }; // { EU07_CELLSIZE * 0.5 }; // experimentally limited, check if it has any negative effect
auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + radius, 2 ) };
for( auto &cell : m_cells ) {
// we reject early cells which aren't within our area of interest
if( glm::length2( cell.area().center - Point ) > squaredradii ) {
continue;
}
std::tie( tractionfound, endpointfound, distancefound ) = cell.find( Point, Other, Currentdirection );
if( ( tractionfound != nullptr )
&& ( distancefound < distancenearest ) ) {
std::tie( tractionnearest, endpointnearest, distancenearest ) = std::tie( tractionfound, endpointfound, distancefound );
}
}
return { tractionnearest, endpointnearest, distancenearest };
}
// sets center point of the section
void
basic_section::center( glm::dvec3 Center ) {
m_area.center = Center;
// set accordingly center points of the section's partitioning cells
// NOTE: we should also update origin point for the contained nodes, but in practice we can skip this
// as all nodes will be added only after the proper center point was set, and won't change
auto const centeroffset = -( EU07_SECTIONSIZE / EU07_CELLSIZE / 2 * EU07_CELLSIZE ) + EU07_CELLSIZE / 2;
glm::dvec3 sectioncornercenter { m_area.center + glm::dvec3{ centeroffset, 0, centeroffset } };
auto row { 0 }, column { 0 };
for( auto &cell : m_cells ) {
cell.center( sectioncornercenter + glm::dvec3{ column * EU07_CELLSIZE, 0.0, row * EU07_CELLSIZE } );
if( ++column >= EU07_SECTIONSIZE / EU07_CELLSIZE ) {
++row;
column = 0;
}
}
}
// generates renderable version of held non-instanced geometry
void
basic_section::create_geometry() {
if( true == m_geometrycreated ) { return; }
else {
// mark it done for future checks
m_geometrycreated = true;
}
// since sections can be empty, we're doing lazy initialization of the geometry bank, when something may actually use it
if( m_geometrybank == null_handle ) {
m_geometrybank = GfxRenderer.Create_Bank();
}
for( auto &shape : m_shapes ) {
shape.create_geometry( m_geometrybank );
}
for( auto &cell : m_cells ) {
cell.create_geometry( m_geometrybank );
}
}
// provides access to section enclosing specified point
basic_cell &
basic_section::cell( glm::dvec3 const &Location ) {
auto const column = static_cast<int>( std::floor( ( Location.x - ( m_area.center.x - EU07_SECTIONSIZE / 2 ) ) / EU07_CELLSIZE ) );
auto const row = static_cast<int>( std::floor( ( Location.z - ( m_area.center.z - EU07_SECTIONSIZE / 2 ) ) / EU07_CELLSIZE ) );
return
m_cells[
clamp( row, 0, ( EU07_SECTIONSIZE / EU07_CELLSIZE ) - 1 ) * ( EU07_SECTIONSIZE / EU07_CELLSIZE )
+ clamp( column, 0, ( EU07_SECTIONSIZE / EU07_CELLSIZE ) - 1 ) ] ;
}
basic_region::basic_region() {
m_sections.fill( nullptr );
/*
// initialize centers of sections:
// calculate center of 'top left' region section...
auto const centeroffset = -( EU07_REGIONSIDESECTIONCOUNT / 2 * EU07_SECTIONSIZE ) + EU07_SECTIONSIZE / 2;
glm::dvec3 regioncornercenter { centeroffset, 0, centeroffset };
auto row { 0 }, column { 0 };
// ...move through section array assigning centers left to right, front/top to back/bottom
for( auto &section : m_sections ) {
section.center( regioncornercenter + glm::dvec3{ column * EU07_SECTIONSIZE, 0.0, row * EU07_SECTIONSIZE } );
if( ++column >= EU07_REGIONSIDESECTIONCOUNT ) {
++row;
column = 0;
}
}
*/
}
basic_region::~basic_region() {
for( auto section : m_sections ) { if( section != nullptr ) { delete section; } }
}
// legacy method, updates sounds and polls event launchers around camera
void
basic_region::update() {
// render events and sounds from sectors near enough to the viewer
auto const range = 2750.f; // audible range of 100 db sound
auto const &sectionlist = sections( Global::pCameraPosition, range );
for( auto *section : sectionlist ) {
section->update( Global::pCameraPosition, range );
}
}
void
basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad ) {
// shape might need to be split into smaller pieces, so we create list of nodes instead of just single one
// using deque so we can do single pass iterating and addding generated pieces without invalidating anything
std::deque<shape_node> shapes { Shape };
auto &shape = shapes.front();
if( shape.m_data.vertices.empty() ) { return; }
// adjust input if necessary:
if( Scratchpad.location_rotation != glm::vec3( 0, 0, 0 ) ) {
// rotate...
auto const rotation = glm::radians( Scratchpad.location_rotation );
for( auto &vertex : shape.m_data.vertices ) {
vertex.position = glm::rotateZ<double>( vertex.position, rotation.z );
vertex.position = glm::rotateX<double>( vertex.position, rotation.x );
vertex.position = glm::rotateY<double>( vertex.position, rotation.y );
vertex.normal = glm::rotateZ( vertex.normal, rotation.z );
vertex.normal = glm::rotateX( vertex.normal, rotation.x );
vertex.normal = glm::rotateY( vertex.normal, rotation.y );
}
}
if( ( false == Scratchpad.location_offset.empty() )
&& ( Scratchpad.location_offset.top() != glm::dvec3( 0, 0, 0 ) ) ) {
// ...and move
auto const offset = Scratchpad.location_offset.top();
for( auto &vertex : shape.m_data.vertices ) {
vertex.position += offset;
}
}
// calculate bounding area
for( auto const &vertex : shape.m_data.vertices ) {
shape.m_data.area.center += vertex.position;
}
shape.m_data.area.center /= shape.m_data.vertices.size();
// trim the shape if needed. trimmed parts will be added to list as separate nodes
for( std::size_t index = 0; index < shapes.size(); ++index ) {
while( true == RaTriangleDivider( shapes[ index ], shapes ) ) {
; // all work is done during expression check
}
// with the trimming done we can calculate shape's bounding radius
shape.compute_radius();
}
// move the data into appropriate section(s)
for( auto &shape : shapes ) {
if( point_inside( shape.m_data.area.center ) ) {
// NOTE: nodes placed outside of region boundaries are discarded
section( shape.m_data.area.center ).insert( shape );
}
else {
ErrorLog(
"Bad scenario: shape node" + (
shape.m_name.empty() ?
"" :
" \"" + shape.m_name + "\"" )
+ " placed in location outside region bounds (" + to_string( shape.m_data.area.center ) + ")" );
}
}
}
// inserts provided track in the region
void
basic_region::insert_path( TTrack *Path, scratch_data &Scratchpad ) {
// NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done
auto center = Path->location();
if( point_inside( center ) ) {
// NOTE: nodes placed outside of region boundaries are discarded
section( center ).insert( Path );
}
else {
// tracks are guaranteed to hava a name so we can skip the check
ErrorLog( "Bad scenario: track node \"" + Path->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" );
}
// also register path ends in appropriate sections, for path merging lookups
// TODO: clean this up during track refactoring
for( auto &point : Path->endpoints() ) {
register_path( Path, point );
}
}
// inserts provided track in the region
void
basic_region::insert_traction( TTraction *Traction, scratch_data &Scratchpad ) {
// NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done
auto center = Traction->location();
if( point_inside( center ) ) {
// NOTE: nodes placed outside of region boundaries are discarded
section( center ).insert( Traction );
}
else {
// tracks are guaranteed to hava a name so we can skip the check
ErrorLog( "Bad scenario: traction node \"" + Traction->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" );
}
// also register traction ends in appropriate sections, for path merging lookups
// TODO: clean this up during track refactoring
for( auto &point : Traction->endpoints() ) {
register_traction( Traction, point );
}
}
// inserts provided instance of 3d model in the region
void
basic_region::insert_instance( TAnimModel *Instance, scratch_data &Scratchpad ) {
// NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done
auto center = Instance->location();
if( point_inside( center ) ) {
// NOTE: nodes placed outside of region boundaries are discarded
section( center ).insert( Instance );
}
else {
// tracks are guaranteed to hava a name so we can skip the check
ErrorLog( "Bad scenario: model node \"" + Instance->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" );
}
}
// inserts provided sound in the region
void
basic_region::insert_sound( TTextSound *Sound, scratch_data &Scratchpad ) {
/*
// TODO: implement
// NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done
auto center = Sound->location();
if( point_inside( center ) ) {
// NOTE: nodes placed outside of region boundaries are discarded
section( center ).insert( Instance );
}
else {
// tracks are guaranteed to hava a name so we can skip the check
ErrorLog( "Bad scenario: model node \"" + Instance->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" );
}
*/
}
// find a vehicle located neares to specified location, within specified radius, optionally discarding vehicles without drivers
std::tuple<TDynamicObject *, float>
basic_region::find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) {
auto const &sectionlist = sections( Point, Radius );
// go through sections within radius of interest, and pick the nearest candidate
TDynamicObject
*foundvehicle,
*nearestvehicle { nullptr };
float
founddistance,
nearestdistance { std::numeric_limits<float>::max() };
for( auto *section : sectionlist ) {
std::tie( foundvehicle, founddistance ) = section->find( Point, Radius, Onlycontrolled );
if( ( foundvehicle != nullptr )
&& ( founddistance < nearestdistance ) ) {
std::tie( nearestvehicle, nearestdistance ) = std::tie( foundvehicle, founddistance );
}
}
return std::tie( nearestvehicle, nearestdistance );
}
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
std::tuple<TTrack *, int>
basic_region::find_path( glm::dvec3 const &Point, TTrack const *Exclude ) {
// TBD: throw out of bounds exception instead of checks all over the place..?
if( point_inside( Point ) ) {
return section( Point ).find( Point, Exclude );
}
return std::make_tuple<TTrack *, int>( nullptr, -1 );
}
// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint
std::tuple<TTraction *, int>
basic_region::find_traction( glm::dvec3 const &Point, TTraction const *Exclude ) {
// TBD: throw out of bounds exception instead of checks all over the place..?
if( point_inside( Point ) ) {
return section( Point ).find( Point, Exclude );
}
return std::make_tuple<TTraction *, int>( nullptr, -1 );
}
// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece
std::tuple<TTraction *, int>
basic_region::find_traction( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ) {
auto const &sectionlist = sections( Point, 0.f );
// go through sections within radius of interest, and pick the nearest candidate
TTraction
*tractionfound,
*tractionnearest { nullptr };
float
distancefound,
distancenearest { std::numeric_limits<float>::max() };
int
endpointfound,
endpointnearest { -1 };
for( auto *section : sectionlist ) {
std::tie( tractionfound, endpointfound, distancefound ) = section->find( Point, Other, Currentdirection );
if( ( tractionfound != nullptr )
&& ( distancefound < distancenearest ) ) {
std::tie( tractionnearest, endpointnearest, distancenearest ) = std::tie( tractionfound, endpointfound, distancefound );
}
}
return { tractionnearest, endpointnearest };
}
// finds sections inside specified sphere. returns: list of sections
std::vector<basic_section *> const &
basic_region::sections( glm::dvec3 const &Point, float const Radius ) {
m_scratchpad.sections.clear();
auto const centerx { static_cast<int>( std::floor( Point.x / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ) };
auto const centerz { static_cast<int>( std::floor( Point.z / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ) };
auto const sectioncount { 2 * static_cast<int>( std::ceil( Radius / EU07_SECTIONSIZE ) ) };
int const originx = centerx - sectioncount / 2;
int const originz = centerz - sectioncount / 2;
auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_SECTIONSIZE + 0.25 * EU07_SECTIONSIZE ) + Radius, 2 ) };
for( int row = originz; row <= originz + sectioncount; ++row ) {
if( row < 0 ) { continue; }
if( row >= EU07_REGIONSIDESECTIONCOUNT ) { break; }
for( int column = originx; column <= originx + sectioncount; ++column ) {
if( column < 0 ) { continue; }
if( column >= EU07_REGIONSIDESECTIONCOUNT ) { break; }
auto *section { m_sections[ row * EU07_REGIONSIDESECTIONCOUNT + column ] };
if( ( section != nullptr )
&& ( glm::length2( section->area().center - Point ) < squaredradii ) ) {
m_scratchpad.sections.emplace_back( section );
}
}
}
return m_scratchpad.sections;
}
// registers specified path in the lookup directory of a cell enclosing specified point
void
basic_region::register_path( TTrack *Path, glm::dvec3 const &Point ) {
if( point_inside( Point ) ) {
section( Point ).register_end( Path, Point );
}
}
// registers specified end point of the provided traction piece in the lookup directory of the region
void
basic_region::register_traction( TTraction *Traction, glm::dvec3 const &Point ) {
if( point_inside( Point ) ) {
section( Point ).register_end( Traction, Point );
}
}
// checks whether specified point is within boundaries of the region
bool
basic_region::point_inside( glm::dvec3 const &Location ) {
double const regionboundary = EU07_REGIONSIDESECTIONCOUNT / 2 * EU07_SECTIONSIZE;
return ( ( Location.x > -regionboundary ) && ( Location.x < regionboundary )
&& ( Location.z > -regionboundary ) && ( Location.z < regionboundary ) );
}
// trims provided shape to fit into a section, adds trimmed part at the end of provided list
// NOTE: legacy function. TBD, TODO: clean it up?
bool
basic_region::RaTriangleDivider( shape_node &Shape, std::deque<shape_node> &Shapes ) {
if( Shape.m_data.vertices.size() != 3 ) {
// tylko gdy jeden trójkąt
return false;
}
auto const margin { 200.0 };
auto x0 = EU07_SECTIONSIZE * std::floor( 0.001 * Shape.m_data.area.center.x ) - margin;
auto x1 = x0 + EU07_SECTIONSIZE + margin * 2;
auto z0 = EU07_SECTIONSIZE * std::floor( 0.001 * Shape.m_data.area.center.z ) - margin;
auto z1 = z0 + EU07_SECTIONSIZE + margin * 2;
if( ( Shape.m_data.vertices[ 0 ].position.x >= x0 ) && ( Shape.m_data.vertices[ 0 ].position.x <= x1 )
&& ( Shape.m_data.vertices[ 0 ].position.z >= z0 ) && ( Shape.m_data.vertices[ 0 ].position.z <= z1 )
&& ( Shape.m_data.vertices[ 1 ].position.x >= x0 ) && ( Shape.m_data.vertices[ 1 ].position.x <= x1 )
&& ( Shape.m_data.vertices[ 1 ].position.z >= z0 ) && ( Shape.m_data.vertices[ 1 ].position.z <= z1 )
&& ( Shape.m_data.vertices[ 2 ].position.x >= x0 ) && ( Shape.m_data.vertices[ 2 ].position.x <= x1 )
&& ( Shape.m_data.vertices[ 2 ].position.z >= z0 ) && ( Shape.m_data.vertices[ 2 ].position.z <= z1 ) ) {
// trójkąt wystający mniej niż 200m z kw. kilometrowego jest do przyjęcia
return false;
}
// Ra: przerobić na dzielenie na 2 trójkąty, podział w przecięciu z siatką kilometrową
// Ra: i z rekurencją będzie dzielić trzy trójkąty, jeśli będzie taka potrzeba
int divide { -1 }; // bok do podzielenia: 0=AB, 1=BC, 2=CA; +4=podział po OZ; +8 na x1/z1
double
min { 0.0 },
mul; // jeśli przechodzi przez oś, iloczyn będzie ujemny
x0 += margin;
x1 -= margin; // przestawienie na siatkę
z0 += margin;
z1 -= margin;
// AB na wschodzie
mul = ( Shape.m_data.vertices[ 0 ].position.x - x0 ) * ( Shape.m_data.vertices[ 1 ].position.x - x0 );
if( mul < min ) {
min = mul;
divide = 0;
}
// BC na wschodzie
mul = ( Shape.m_data.vertices[ 1 ].position.x - x0 ) * ( Shape.m_data.vertices[ 2 ].position.x - x0 );
if( mul < min ) {
min = mul;
divide = 1;
}
// CA na wschodzie
mul = ( Shape.m_data.vertices[ 2 ].position.x - x0 ) * ( Shape.m_data.vertices[ 0 ].position.x - x0 );
if( mul < min ) {
min = mul;
divide = 2;
}
// AB na zachodzie
mul = ( Shape.m_data.vertices[ 0 ].position.x - x1 ) * ( Shape.m_data.vertices[ 1 ].position.x - x1 );
if( mul < min ) {
min = mul;
divide = 8;
}
// BC na zachodzie
mul = ( Shape.m_data.vertices[ 1 ].position.x - x1 ) * ( Shape.m_data.vertices[ 2 ].position.x - x1 );
if( mul < min ) {
min = mul;
divide = 9;
}
// CA na zachodzie
mul = ( Shape.m_data.vertices[ 2 ].position.x - x1 ) * ( Shape.m_data.vertices[ 0 ].position.x - x1 );
if( mul < min ) {
min = mul;
divide = 10;
}
// AB na południu
mul = ( Shape.m_data.vertices[ 0 ].position.z - z0 ) * ( Shape.m_data.vertices[ 1 ].position.z - z0 );
if( mul < min ) {
min = mul;
divide = 4;
}
// BC na południu
mul = ( Shape.m_data.vertices[ 1 ].position.z - z0 ) * ( Shape.m_data.vertices[ 2 ].position.z - z0 );
if( mul < min ) {
min = mul;
divide = 5;
}
// CA na południu
mul = ( Shape.m_data.vertices[ 2 ].position.z - z0 ) * ( Shape.m_data.vertices[ 0 ].position.z - z0 );
if( mul < min ) {
min = mul;
divide = 6;
}
// AB na północy
mul = ( Shape.m_data.vertices[ 0 ].position.z - z1 ) * ( Shape.m_data.vertices[ 1 ].position.z - z1 );
if( mul < min ) {
min = mul;
divide = 12;
}
// BC na północy
mul = ( Shape.m_data.vertices[ 1 ].position.z - z1 ) * ( Shape.m_data.vertices[ 2 ].position.z - z1 );
if( mul < min ) {
min = mul;
divide = 13;
}
// CA na północy
mul = (Shape.m_data.vertices[2].position.z - z1) * (Shape.m_data.vertices[0].position.z - z1);
if( mul < min ) {
divide = 14;
}
// tworzymy jeden dodatkowy trójkąt, dzieląc jeden bok na przecięciu siatki kilometrowej
Shapes.emplace_back( Shape ); // copy current shape
auto &newshape = Shapes.back();
switch (divide & 3) {
// podzielenie jednego z boków, powstaje wierzchołek D
case 0: {
// podział AB (0-1) -> ADC i DBC
newshape.m_data.vertices[ 2 ] = Shape.m_data.vertices[ 2 ]; // wierzchołek C jest wspólny
newshape.m_data.vertices[ 1 ] = Shape.m_data.vertices[ 1 ]; // wierzchołek B przechodzi do nowego
if( divide & 4 ) {
Shape.m_data.vertices[ 1 ].set_from_z(
Shape.m_data.vertices[ 0 ],
Shape.m_data.vertices[ 1 ],
( ( divide & 8 ) ?
z1 :
z0 ) );
}
else {
Shape.m_data.vertices[ 1 ].set_from_x(
Shape.m_data.vertices[ 0 ],
Shape.m_data.vertices[ 1 ],
( ( divide & 8 ) ?
x1 :
x0 ) );
}
newshape.m_data.vertices[ 0 ] = Shape.m_data.vertices[ 1 ]; // wierzchołek D jest wspólny
break;
}
case 1: {
// podział BC (1-2) -> ABD i ADC
newshape.m_data.vertices[ 0 ] = Shape.m_data.vertices[ 0 ]; // wierzchołek A jest wspólny
newshape.m_data.vertices[ 2 ] = Shape.m_data.vertices[ 2 ]; // wierzchołek C przechodzi do nowego
if( divide & 4 ) {
Shape.m_data.vertices[ 2 ].set_from_z(
Shape.m_data.vertices[ 1 ],
Shape.m_data.vertices[ 2 ],
( ( divide & 8 ) ?
z1 :
z0 ) );
}
else {
Shape.m_data.vertices[ 2 ].set_from_x(
Shape.m_data.vertices[ 1 ],
Shape.m_data.vertices[ 2 ],
( ( divide & 8 ) ?
x1 :
x0 ) );
}
newshape.m_data.vertices[ 1 ] = Shape.m_data.vertices[ 2 ]; // wierzchołek D jest wspólny
break;
}
case 2: {
// podział CA (2-0) -> ABD i DBC
newshape.m_data.vertices[ 1 ] = Shape.m_data.vertices[ 1 ]; // wierzchołek B jest wspólny
newshape.m_data.vertices[ 2 ] = Shape.m_data.vertices[ 2 ]; // wierzchołek C przechodzi do nowego
if( divide & 4 ) {
Shape.m_data.vertices[ 2 ].set_from_z(
Shape.m_data.vertices[ 2 ],
Shape.m_data.vertices[ 0 ],
( ( divide & 8 ) ?
z1 :
z0 ) );
}
else {
Shape.m_data.vertices[ 2 ].set_from_x(
Shape.m_data.vertices[ 2 ],
Shape.m_data.vertices[ 0 ],
( ( divide & 8 ) ?
x1 :
x0 ) );
}
newshape.m_data.vertices[ 0 ] = Shape.m_data.vertices[ 2 ]; // wierzchołek D jest wspólny
break;
}
}
// przeliczenie środków ciężkości obu
Shape.m_data.area.center = ( Shape.m_data.vertices[ 0 ].position + Shape.m_data.vertices[ 1 ].position + Shape.m_data.vertices[ 2 ].position ) / 3.0;
newshape.m_data.area.center = ( newshape.m_data.vertices[ 0 ].position + newshape.m_data.vertices[ 1 ].position + newshape.m_data.vertices[ 2 ].position ) / 3.0;
return true;
}
// provides access to section enclosing specified point
basic_section &
basic_region::section( glm::dvec3 const &Location ) {
auto const column { static_cast<int>( std::floor( Location.x / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ) };
auto const row { static_cast<int>( std::floor( Location.z / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ) };
auto &section =
m_sections[
clamp( row, 0, EU07_REGIONSIDESECTIONCOUNT - 1 ) * EU07_REGIONSIDESECTIONCOUNT
+ clamp( column, 0, EU07_REGIONSIDESECTIONCOUNT - 1 ) ] ;
if( section == nullptr ) {
// there's no guarantee the section exists at this point, so check and if needed, create it
section = new basic_section();
// assign center of the section
auto const centeroffset = -( EU07_REGIONSIDESECTIONCOUNT / 2 * EU07_SECTIONSIZE ) + EU07_SECTIONSIZE / 2;
glm::dvec3 regioncornercenter { centeroffset, 0, centeroffset };
section->center( regioncornercenter + glm::dvec3{ column * EU07_SECTIONSIZE, 0.0, row * EU07_SECTIONSIZE } );
}
return *section;
}
} // scene
//---------------------------------------------------------------------------