engine/ext/behaviors/region/behavior.cpp

204 lines
7.7 KiB
C++

#include "behavior.h"
#include "./chunk/behavior.h"
#include <uf/utils/hook/hook.h>
#include <uf/utils/time/time.h>
#include <uf/utils/serialize/serializer.h>
#include <uf/utils/userdata/userdata.h>
#include <uf/utils/window/window.h>
#include <uf/utils/window/payloads.h>
#include <uf/utils/camera/camera.h>
#include <uf/utils/audio/audio.h>
#include <uf/ext/openvr/openvr.h>
#include <uf/engine/graph/graph.h>
#include <uf/engine/asset/asset.h>
#include <uf/utils/math/physics.h>
#include <uf/spec/controller/controller.h>
#include <uf/utils/graphic/graphic.h>
#include <uf/utils/io/inputs.h>
#include <sstream>
UF_BEHAVIOR_REGISTER_CPP(ext::RegionBehavior)
UF_BEHAVIOR_TRAITS_CPP(ext::RegionBehavior, ticks = true, renders = false, multithread = false)
#define this (&self)
namespace {
pod::Vector3i getIndex( const pod::Vector3f& position, const pod::Vector3ui& size ) {
pod::Vector3f index = { position.x / size.x, position.y / size.y, position.z / size.z };
// index.x += index.x > 0 ? 0.5f : -0.5f;
// index.y += index.y > 0 ? 0.5f : -0.5f;
// index.z += index.z > 0 ? 0.5f : -0.5f;
return { index.x, index.y, index.z };
}
bool outOfRange( const pod::Vector3f& a, const pod::Vector3f& b, const pod::Vector3f& range ) {
auto distance = uf::vector::distance( a, b );
auto length = uf::vector::norm( range );
UF_MSG_DEBUG("{}\t\t||{} - {}|| > ||{}|| // ({} : {})",
distance > length,
uf::vector::toString(a),
uf::vector::toString(b),
uf::vector::toString(range),
distance,
length
);
return distance > length;
/*
float dot = uf::vector::dot( a, b );
float mag = uf::vector::magnitude( range );
return dot > mag;
*/
}
void generateChunk( uf::Object& self, pod::Vector3i index ) {
auto& metadata = this->getComponent<ext::RegionBehavior::Metadata>();
if ( metadata.chunks.count( index ) > 0 ) return;
auto& child = this->loadChild("/region-chunk.json", false);
auto& serializer = child.getComponent<uf::Serializer>();
auto& chunkMetadata = child.getComponent<ext::RegionChunkBehavior::Metadata>();
auto& chunkTransform = child.getComponent<pod::Transform<>>();
metadata.chunks[index] = &child;
serializer["region"]["index"] = uf::vector::encode( index );
chunkTransform.position = index * metadata.settings.chunkSize;
child.initialize();
UF_MSG_DEBUG("Generated chunk: {} ({})", uf::vector::toString( index ), child.getUid());
}
void generateChunks( uf::Object& self ) {
auto& metadata = this->getComponent<ext::RegionBehavior::Metadata>();
if ( metadata.last.checkTime >= 0 && metadata.last.checkTime >= uf::physics::time::current ) return;
auto& scene = uf::scene::getCurrentScene();
auto& controller = scene.getController();
auto& controllerTransform = controller.getComponent<pod::Transform<>>();
pod::Vector3i controllerIndex = getIndex( controllerTransform.position, metadata.settings.chunkSize );
pod::Vector3i halfRange = {
metadata.settings.chunkRange.x / 2,
metadata.settings.chunkRange.y / 2,
metadata.settings.chunkRange.z / 2,
};
for ( int y = 0; y < metadata.settings.chunkRange.y * 2; ++y ) {
for ( int z = 0; z < metadata.settings.chunkRange.z * 2; ++z ) {
for ( int x = 0; x < metadata.settings.chunkRange.x * 2; ++x ) {
// UF_MSG_DEBUG("{}, {}, {}", x, y, z);
// skip loading if it would just degenerate
pod::Vector3i index = pod::Vector3i{ x, y, z } + controllerIndex - halfRange;
if ( outOfRange( controllerTransform.position, index * metadata.settings.chunkSize, metadata.settings.chunkSize * metadata.settings.chunkRange ) ) continue;
generateChunk( self, index );
}
}
}
metadata.last.checkTime = uf::physics::time::current + metadata.settings.cooldown;
metadata.last.controllerIndex = controllerIndex;
}
void degenerateChunk( uf::Object& self, const pod::Vector3i& index ) {
auto& metadata = this->getComponent<ext::RegionBehavior::Metadata>();
UF_MSG_DEBUG("Degenerating: {}", uf::vector::toString( index ));
if ( metadata.chunks.count( index ) <= 0 ) return;
auto& child = *metadata.chunks[index];
child.queueDeletion();
metadata.chunks.erase( index );
UF_MSG_DEBUG("Degenerated chunk: {} ({})", uf::vector::toString( index ), child.getUid());
}
void degenerateChunks( uf::Object& self ) {
auto& metadata = this->getComponent<ext::RegionBehavior::Metadata>();
auto& scene = uf::scene::getCurrentScene();
auto& controller = scene.getController();
auto& controllerTransform = controller.getComponent<pod::Transform<>>();
uf::stl::vector<pod::Vector3i> degenerates;
for ( auto& pair : metadata.chunks ) {
auto& index = pair.first;
if ( !outOfRange( controllerTransform.position, index * metadata.settings.chunkSize, metadata.settings.chunkSize * metadata.settings.chunkRange ) ) continue;
degenerates.emplace_back( index );
}
for ( auto& index : degenerates ) {
degenerateChunk( self, index );
}
}
}
void ext::RegionBehavior::initialize( uf::Object& self ) {
auto& metadata = this->getComponent<ext::RegionBehavior::Metadata>();
auto& metadataJson = this->getComponent<uf::Serializer>();
UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson);
auto& transform = this->getComponent<pod::Transform<>>();
// generate chunks
this->addHook( "region:GenerateChunks.%UID%", [&](){
UF_MSG_DEBUG("GENERATE");
generateChunks( self );
});
this->addHook( "region:DegenerateChunks.%UID%", [&](){
UF_MSG_DEBUG("DEGENERATE");
degenerateChunks( self );
});
this->addHook( "region:Update.%UID%", [&](){
generateChunks( self );
degenerateChunks( self );
});
this->addHook( "controller:Ready", [&](ext::json::Value& payload){
metadata.ready = true;
UF_MSG_DEBUG("READY");
this->queueHook( "region:GenerateChunks.%UID%" );
});
}
void ext::RegionBehavior::tick( uf::Object& self ) {
auto& metadata = this->getComponent<ext::RegionBehavior::Metadata>();
auto& metadataJson = this->getComponent<uf::Serializer>();
if ( !metadata.ready ) return;
auto& scene = uf::scene::getCurrentScene();
auto& controller = scene.getController();
auto& controllerTransform = controller.getComponent<pod::Transform<>>();
pod::Vector3i controllerIndex = getIndex( controllerTransform.position, metadata.settings.chunkSize );
// controller moved indices, regenerate
if ( controllerIndex != metadata.last.controllerIndex || metadata.chunks.size() == 0 ) {
if ( metadata.last.checkTime >= 0 && metadata.last.checkTime >= uf::physics::time::current ) return;
degenerateChunks( self );
generateChunks( self );
metadata.last.checkTime = uf::physics::time::current + metadata.settings.cooldown;
}
}
void ext::RegionBehavior::render( uf::Object& self ){}
void ext::RegionBehavior::destroy( uf::Object& self ){
auto& metadata = this->getComponent<ext::RegionBehavior::Metadata>();
for ( auto& pair : metadata.chunks ) {
degenerateChunk( self, pair.first );
}
metadata.chunks.clear();
}
void ext::RegionBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& serializer ){
serializer["region"]["range"] = uf::vector::encode( /*this->*/settings.chunkRange );
serializer["region"]["size"] = uf::vector::encode( /*this->*/settings.chunkSize );
serializer["region"]["cooldown"] = /*this->*/settings.cooldown;
}
void ext::RegionBehavior::Metadata::deserialize( uf::Object& self, uf::Serializer& serializer ){
/*this->*/settings.chunkRange = uf::vector::decode( serializer["region"]["range"], /*this->*/settings.chunkRange );
/*this->*/settings.chunkSize = uf::vector::decode( serializer["region"]["size"], /*this->*/settings.chunkSize );
/*this->*/settings.cooldown = serializer["region"]["cooldown"].as( /*this->*/settings.cooldown );
}
#undef this