Files
2026-01-10 15:16:00 +01:00

715 lines
35 KiB
C++

#pragma once
#include "CommonInclude.h"
#include "wiJobSystem.h"
#include "wiSpinLock.h"
#include "wiGPUBVH.h"
#include "wiSprite.h"
#include "wiSpriteFont.h"
#include "wiMath.h"
#include "wiECS.h"
#include "wiScene_Components.h"
#include "wiEmittedParticle.h"
#include "wiHairParticle.h"
#include "wiTerrain.h"
#include "wiBVH.h"
#include "wiUnorderedSet.h"
#include "wiVoxelGrid.h"
#include "wiPathQuery.h"
#include <string>
#include <memory>
namespace wi::scene
{
struct Scene
{
virtual ~Scene() = default;
wi::ecs::ComponentLibrary componentLibrary;
wi::ecs::ComponentManager<NameComponent>& names = componentLibrary.Register<NameComponent>("wi::scene::Scene::names");
wi::ecs::ComponentManager<LayerComponent>& layers = componentLibrary.Register<LayerComponent>("wi::scene::Scene::layers");
wi::ecs::ComponentManager<TransformComponent>& transforms = componentLibrary.Register<TransformComponent>("wi::scene::Scene::transforms");
wi::ecs::ComponentManager<HierarchyComponent>& hierarchy = componentLibrary.Register<HierarchyComponent>("wi::scene::Scene::hierarchy");
wi::ecs::ComponentManager<MaterialComponent>& materials = componentLibrary.Register<MaterialComponent>("wi::scene::Scene::materials", 11); // version = 11
wi::ecs::ComponentManager<MeshComponent>& meshes = componentLibrary.Register<MeshComponent>("wi::scene::Scene::meshes", 4); // version = 4
wi::ecs::ComponentManager<ImpostorComponent>& impostors = componentLibrary.Register<ImpostorComponent>("wi::scene::Scene::impostors");
wi::ecs::ComponentManager<ObjectComponent>& objects = componentLibrary.Register<ObjectComponent>("wi::scene::Scene::objects", 4); // version = 4
wi::ecs::ComponentManager<RigidBodyPhysicsComponent>& rigidbodies = componentLibrary.Register<RigidBodyPhysicsComponent>("wi::scene::Scene::rigidbodies", 6); // version = 6
wi::ecs::ComponentManager<SoftBodyPhysicsComponent>& softbodies = componentLibrary.Register<SoftBodyPhysicsComponent>("wi::scene::Scene::softbodies", 3); // version = 3
wi::ecs::ComponentManager<ArmatureComponent>& armatures = componentLibrary.Register<ArmatureComponent>("wi::scene::Scene::armatures");
wi::ecs::ComponentManager<LightComponent>& lights = componentLibrary.Register<LightComponent>("wi::scene::Scene::lights", 5); // version = 5
wi::ecs::ComponentManager<CameraComponent>& cameras = componentLibrary.Register<CameraComponent>("wi::scene::Scene::cameras", 2); // version = 2
wi::ecs::ComponentManager<EnvironmentProbeComponent>& probes = componentLibrary.Register<EnvironmentProbeComponent>("wi::scene::Scene::probes", 2); // version = 2
wi::ecs::ComponentManager<ForceFieldComponent>& forces = componentLibrary.Register<ForceFieldComponent>("wi::scene::Scene::forces", 1); // version = 1
wi::ecs::ComponentManager<DecalComponent>& decals = componentLibrary.Register<DecalComponent>("wi::scene::Scene::decals", 1); // version = 1
wi::ecs::ComponentManager<AnimationComponent>& animations = componentLibrary.Register<AnimationComponent>("wi::scene::Scene::animations", 2); // version = 2
wi::ecs::ComponentManager<AnimationDataComponent>& animation_datas = componentLibrary.Register<AnimationDataComponent>("wi::scene::Scene::animation_datas");
wi::ecs::ComponentManager<EmittedParticleSystem>& emitters = componentLibrary.Register<EmittedParticleSystem>("wi::scene::Scene::emitters", 2); // version = 2
wi::ecs::ComponentManager<HairParticleSystem>& hairs = componentLibrary.Register<HairParticleSystem>("wi::scene::Scene::hairs", 3); // version = 3
wi::ecs::ComponentManager<WeatherComponent>& weathers = componentLibrary.Register<WeatherComponent>("wi::scene::Scene::weathers", 6); // version = 6
wi::ecs::ComponentManager<SoundComponent>& sounds = componentLibrary.Register<SoundComponent>("wi::scene::Scene::sounds", 1); // version = 1
wi::ecs::ComponentManager<VideoComponent>& videos = componentLibrary.Register<VideoComponent>("wi::scene::Scene::videos", 1); // version = 1
wi::ecs::ComponentManager<InverseKinematicsComponent>& inverse_kinematics = componentLibrary.Register<InverseKinematicsComponent>("wi::scene::Scene::inverse_kinematics");
wi::ecs::ComponentManager<SpringComponent>& springs = componentLibrary.Register<SpringComponent>("wi::scene::Scene::springs", 1); // version = 1
wi::ecs::ComponentManager<ColliderComponent>& colliders = componentLibrary.Register<ColliderComponent>("wi::scene::Scene::colliders", 2); // version = 2
wi::ecs::ComponentManager<ScriptComponent>& scripts = componentLibrary.Register<ScriptComponent>("wi::scene::Scene::scripts");
wi::ecs::ComponentManager<ExpressionComponent>& expressions = componentLibrary.Register<ExpressionComponent>("wi::scene::Scene::expressions");
wi::ecs::ComponentManager<HumanoidComponent>& humanoids = componentLibrary.Register<HumanoidComponent>("wi::scene::Scene::humanoids", 3); // version = 3
wi::ecs::ComponentManager<wi::terrain::Terrain>& terrains = componentLibrary.Register<wi::terrain::Terrain>("wi::scene::Scene::terrains", 6); // version = 6
wi::ecs::ComponentManager<wi::Sprite>& sprites = componentLibrary.Register<wi::Sprite>("wi::scene::Scene::sprites", 2); // version = 2
wi::ecs::ComponentManager<wi::SpriteFont>& fonts = componentLibrary.Register<wi::SpriteFont>("wi::scene::Scene::fonts");
wi::ecs::ComponentManager<wi::VoxelGrid>& voxel_grids = componentLibrary.Register<wi::VoxelGrid>("wi::scene::Scene::voxel_grids");
wi::ecs::ComponentManager<MetadataComponent>& metadatas = componentLibrary.Register<MetadataComponent>("wi::scene::Scene::metadatas");
wi::ecs::ComponentManager<CharacterComponent>& characters = componentLibrary.Register<CharacterComponent>("wi::scene::Scene::characters");
wi::ecs::ComponentManager<PhysicsConstraintComponent>& constraints = componentLibrary.Register<PhysicsConstraintComponent>("wi::scene::Scene::constraints", 6); // version = 6
wi::ecs::ComponentManager<SplineComponent>& splines = componentLibrary.Register<SplineComponent>("wi::scene::Scene::splines", 3); // version = 3
// Non-serialized attributes:
float dt = 0;
enum FLAGS
{
EMPTY = 0,
};
uint32_t flags = EMPTY;
float time = 0;
CameraComponent camera; // only for LOD and 3D sound update; use GetCamera() or set RenderPath3D's camera to your own
wi::allocator::shared_ptr<void> physics_scene;
wi::SpinLock locker;
wi::primitive::AABB bounds;
wi::vector<wi::primitive::AABB> parallel_bounds;
WeatherComponent weather;
wi::graphics::RaytracingAccelerationStructure TLAS;
wi::graphics::GPUBuffer TLAS_instancesUpload[wi::graphics::GraphicsDevice::GetBufferCount()];
void* TLAS_instancesMapped = nullptr;
wi::GPUBVH BVH; // this is for non-hardware accelerated raytracing
mutable bool acceleration_structure_update_requested = false;
void SetAccelerationStructureUpdateRequested(bool value = true) { acceleration_structure_update_requested = value; }
bool IsAccelerationStructureUpdateRequested() const { return acceleration_structure_update_requested; }
bool IsLightmapUpdateRequested() const { return lightmap_request_allocator.load() > 0; }
wi::Archive optimized_instatiation_data;
wi::vector<wi::primitive::Capsule> character_capsules;
wi::vector<wi::primitive::Sphere> character_dedicated_shadows;
wi::unordered_map<wi::ecs::Entity, wi::vector<wi::ecs::Entity>> topdown_hierarchy; // managed by BuildTopDownHierarchy() in every Update(), allows parent->children traversal
wi::jobsystem::context topdown_hierarchy_workload;
uint32_t cpu_gpu_mapped_resource_index = 0;
// AABB culling streams:
wi::vector<wi::primitive::AABB> aabb_objects;
wi::vector<wi::primitive::AABB> aabb_lights;
wi::vector<wi::primitive::AABB> aabb_probes;
wi::vector<wi::primitive::AABB> aabb_decals;
wi::vector<wi::primitive::AABB> aabb_fonts;
// Separate stream of world matrices:
wi::vector<XMFLOAT4X4> matrix_objects;
wi::vector<XMFLOAT4X4> matrix_objects_prev;
// Shader visible scene parameters:
ShaderScene shaderscene;
// Instances for bindless visiblity indexing:
// contains in order:
// 1) objects
// 2) hair particles
// 3) emitted particles
// 4) impostors
wi::graphics::GPUBuffer instanceUploadBuffer[wi::graphics::GraphicsDevice::GetBufferCount()];
ShaderMeshInstance* instanceArrayMapped = nullptr;
size_t instanceArraySize = 0;
wi::graphics::GPUBuffer instanceBuffer;
// Geometries for bindless visiblity indexing:
// contains in order:
// 1) meshes * mesh.subsetCount
// 2) hair particles * 1
// 3) emitted particles * 1
// 4) impostors * 1
wi::graphics::GPUBuffer geometryUploadBuffer[wi::graphics::GraphicsDevice::GetBufferCount()];
ShaderGeometry* geometryArrayMapped = nullptr;
size_t geometryArraySize = 0;
wi::graphics::GPUBuffer geometryBuffer;
std::atomic<uint32_t> geometryAllocator{ 0 };
// Materials for bindless visibility indexing:
wi::graphics::GPUBuffer materialUploadBuffer[wi::graphics::GraphicsDevice::GetBufferCount()];
ShaderMaterial* materialArrayMapped = nullptr;
size_t materialArraySize = 0;
wi::graphics::GPUBuffer materialBuffer;
wi::graphics::GPUBuffer textureStreamingFeedbackBuffer;
wi::graphics::GPUBuffer textureStreamingFeedbackBuffer_readback[wi::graphics::GraphicsDevice::GetBufferCount()];
const uint32_t* textureStreamingFeedbackMapped = nullptr;
// Meshlets:
wi::graphics::GPUBuffer meshletBuffer;
std::atomic<uint32_t> meshletAllocator{ 0 };
// Skinning GPU data containining all bones, all morph descriptions:
wi::graphics::GPUBuffer skinningUploadBuffer[wi::graphics::GraphicsDevice::GetBufferCount()];
void* skinningDataMapped = nullptr;
size_t skinningDataSize = 0;
wi::graphics::GPUBuffer skinningBuffer;
std::atomic<uint32_t> skinningAllocator{ 0 };
// Occlusion query state:
struct OcclusionResult
{
int occlusionQueries[wi::graphics::GraphicsDevice::GetBufferCount()];
// occlusion result history bitfield (32 bit->32 frame history)
uint32_t occlusionHistory = ~0u;
constexpr bool IsOccluded() const
{
// Perform a conservative occlusion test:
// If it is visible in any frames in the history, it is determined visible in this frame
// But if all queries failed in the history, it is occluded.
// If it pops up for a frame after occluded, it is visible again for some frames
return occlusionHistory == 0;
}
};
mutable wi::vector<OcclusionResult> occlusion_results_objects;
wi::graphics::GPUQueryHeap queryHeap;
wi::graphics::GPUBuffer queryResultBuffer[arraysize(OcclusionResult::occlusionQueries)];
wi::graphics::GPUBuffer queryPredicationBuffer;
uint32_t queryheap_idx = 0;
mutable std::atomic<uint32_t> queryAllocator{ 0 };
// Surfel GI resources:
struct SurfelGI
{
mutable bool cleared = false;
wi::graphics::GPUBuffer surfelBuffer;
wi::graphics::GPUBuffer dataBuffer;
wi::graphics::GPUBuffer varianceBuffer;
wi::graphics::GPUBuffer aliveBuffer[2];
wi::graphics::GPUBuffer deadBuffer;
wi::graphics::GPUBuffer statsBuffer;
wi::graphics::GPUBuffer indirectBuffer;
wi::graphics::GPUBuffer gridBuffer;
wi::graphics::GPUBuffer cellBuffer;
wi::graphics::GPUBuffer rayBuffer;
wi::graphics::Texture momentsTexture;
} surfelgi;
// DDGI resources:
struct DDGI
{
uint frame_index = 0;
uint3 grid_dimensions = uint3(32, 8, 32); // The scene extents will be subdivided into a grid of this resolution, each grid cell will have one probe
float3 grid_min = float3(-1, -1, -1);
float3 grid_max = float3(1, 1, 1);
float smooth_backface = 0.01f; // smoothness of backface test
wi::graphics::GPUBuffer ray_buffer;
wi::graphics::GPUBuffer variance_buffer;
wi::graphics::GPUBuffer raycount_buffer;
wi::graphics::GPUBuffer rayallocation_buffer;
wi::graphics::GPUBuffer probe_buffer;
wi::graphics::Texture depth_texture;
void Serialize(wi::Archive& archive);
} ddgi;
// Voxel GI resources:
struct VXGI
{
uint32_t res = 64;
float rayStepSize = 1;
float maxDistance = 100.0f;
struct ClipMap
{
float voxelsize = 0.125;
XMFLOAT3 center = XMFLOAT3(0, 0, 0);
XMINT3 offsetfromPrevFrame = XMINT3(0, 0, 0);
XMFLOAT3 extents = XMFLOAT3(0, 0, 0);
} clipmaps[VXGI_CLIPMAP_COUNT];
uint32_t clipmap_to_update = 0;
wi::graphics::Texture radiance;
wi::graphics::Texture prev_radiance;
wi::graphics::Texture render_atomic;
wi::graphics::Texture sdf;
wi::graphics::Texture sdf_temp;
mutable bool pre_clear = true;
} vxgi;
EnvironmentProbeComponent global_dynamic_probe; // when no envprobes are placed, this will be the fallback
// Impostor state:
static constexpr uint32_t maxImpostorCount = 8;
static constexpr uint32_t impostorTextureDim = 128;
wi::graphics::Texture impostorDepthStencil;
wi::graphics::Texture impostorRenderTarget_Albedo_MSAA;
wi::graphics::Texture impostorRenderTarget_Normal_MSAA;
wi::graphics::Texture impostorRenderTarget_Surface_MSAA;
wi::graphics::Texture impostorRenderTarget_Albedo;
wi::graphics::Texture impostorRenderTarget_Normal;
wi::graphics::Texture impostorRenderTarget_Surface;
wi::graphics::Texture impostorArray;
wi::graphics::GPUBuffer impostorBuffer;
uint32_t allocated_impostor_capacity = 0;
MeshComponent::BufferView impostor_ib32;
MeshComponent::BufferView impostor_ib16;
MeshComponent::BufferView impostor_vb_pos;
MeshComponent::BufferView impostor_vb_nor;
MeshComponent::BufferView impostor_data;
MeshComponent::BufferView impostor_indirect;
wi::graphics::Format impostor_ib_format = wi::graphics::Format::R32_UINT;
uint32_t impostorInstanceOffset = ~0u;
uint32_t impostorGeometryOffset = ~0u;
uint32_t impostorMaterialOffset = ~0u;
wi::EmittedParticleSystem rainEmitter;
MaterialComponent rainMaterial;
uint32_t rainInstanceOffset = ~0u;
uint32_t rainGeometryOffset = ~0u;
uint32_t rainMaterialOffset = ~0u;
LightComponent rain_blocker_dummy_light;
std::atomic<uint32_t> lightmap_request_allocator{ 0 };
wi::vector<uint32_t> lightmap_requests;
wi::vector<TransformComponent> transforms_temp;
// CPU/GPU Colliders:
wi::vector<uint8_t> collider_deinterleaved_data;
uint32_t collider_count_cpu = 0;
uint32_t collider_count_gpu = 0;
wi::primitive::AABB* aabb_colliders_cpu = nullptr;
wi::primitive::AABB* aabb_colliders_gpu = nullptr;
ColliderComponent* colliders_cpu = nullptr;
ColliderComponent* colliders_gpu = nullptr;
wi::BVH collider_bvh;
wi::BVH collider_bvh_next;
wi::jobsystem::context collider_bvh_workload;
void CountCPUandGPUColliders();
// Ocean GPU state:
wi::Ocean ocean;
void OceanRegenerate() { ocean.Create(weather.oceanParameters); }
// Simple water ripple sprites:
mutable wi::vector<wi::Sprite> waterRipples;
void PutWaterRipple(const XMFLOAT3& pos);
void PutWaterRipple(const std::string& image, const XMFLOAT3& pos);
wi::graphics::GPUBuffer voxelgrid_gpu; // primary CPU voxelgrid uploaded to GPU
// Animation processing optimizer:
struct AnimationQueue
{
// The animations within one queue must be processed on the same thread in order
wi::vector<AnimationComponent*> animations; // pointers for one frame only!
wi::unordered_set<wi::ecs::Entity> entities;
};
wi::vector<AnimationQueue> animation_queues; // different animation queues can be processed in different threads in any order
size_t animation_queue_count = 0; // to avoid resizing animation queues downwards because the internals for them needs to be reallocated in that case
wi::jobsystem::context animation_dependency_scan_workload;
void ScanAnimationDependencies();
wi::vector<SpringComponent*> spring_queues; // these indicate which chains can be updated on separate threads
wi::jobsystem::context spring_dependency_scan_workload;
void ScanSpringDependencies();
void UpdateSpringsTopDownRecursive(SpringComponent* parent_spring, SpringComponent& spring);
float wetmap_fadeout_time = 0;
bool IsWetmapProcessingRequired() const;
void StartBuildTopDownHierarchy();
void WaitBuildTopDownHierarchy() const;
void RefreshHierarchyTopdownFromParent(wi::ecs::Entity entity);
// Update all components by a given timestep (in seconds):
// This is an expensive function, prefer to call it only once per frame!
virtual void Update(float dt);
// Remove everything from the scene that it owns:
virtual void Clear();
// Merge an other scene into this.
// The contents of the other scene will be lost (and moved to this)!
// Any references to entities or components from the other scene will now reference them in this scene.
virtual void Merge(Scene& other);
// Similar to merge but skipping some things that are safe to skip within the Update loop
void MergeFastInternal(Scene& other);
// Create a copy of prefab and merge it into this.
// prefab : source scene to be copied from
// attached : if true, everything from prefab will be attached to a root entity
// returns new root entity if attached is set to true, otherwise returns INVALID_ENTITY
virtual wi::ecs::Entity Instantiate(Scene& prefab, bool attached = false);
// Finds all entities in the scene that have any components attached
void FindAllEntities(wi::unordered_set<wi::ecs::Entity>& entities) const;
// Removes (deletes) a specific entity from the scene (if it exists):
// recursive : also removes children if true
// keep_sorted : remove all components while keeping sorted order (slow)
void Entity_Remove(wi::ecs::Entity entity, bool recursive = true, bool keep_sorted = false);
// Finds the first entity by the name (if it exists, otherwise returns INVALID_ENTITY):
// ancestor : you can specify an ancestor entity if you only want to find entities that are descendants of ancestor entity
wi::ecs::Entity Entity_FindByName(const std::string& name, wi::ecs::Entity ancestor = wi::ecs::INVALID_ENTITY);
// Duplicates all of an entity's components and creates a new entity with them (recursively keeps hierarchy):
wi::ecs::Entity Entity_Duplicate(wi::ecs::Entity entity);
// Check whether entity is a descendant of ancestor
// returns true if entity is in the hierarchy tree of ancestor, false otherwise
bool Entity_IsDescendant(wi::ecs::Entity entity, wi::ecs::Entity ancestor) const;
enum class EntitySerializeFlags
{
NONE = 0,
RECURSIVE = 1 << 0, // children entities will be also serialized
KEEP_INTERNAL_ENTITY_REFERENCES = 1 << 1, // entity handles inside components will be kept intact, they won't use remapping of wi::ecs::EntitySerializer
};
// Serializes entity and all of its components to archive:
// archive : archive used for serializing data
// seri : serializer state for entity component system
// entity : if archive is in write mode, this is the entity to serialize. If archive is in read mode, it should be INVALID_ENTITY
// flags : specify options as EntitySerializeFlags bits to control internal behaviour
//
// Returns either the new entity that was read, or the original entity that was written
wi::ecs::Entity Entity_Serialize(
wi::Archive& archive,
wi::ecs::EntitySerializer& seri,
wi::ecs::Entity entity = wi::ecs::INVALID_ENTITY,
EntitySerializeFlags flags = EntitySerializeFlags::RECURSIVE
);
wi::ecs::Entity Entity_CreateTransform(
const std::string& name
);
wi::ecs::Entity Entity_CreateMaterial(
const std::string& name
);
wi::ecs::Entity Entity_CreateObject(
const std::string& name
);
wi::ecs::Entity Entity_CreateMesh(
const std::string& name
);
wi::ecs::Entity Entity_CreateLight(
const std::string& name,
const XMFLOAT3& position = XMFLOAT3(0, 0, 0),
const XMFLOAT3& color = XMFLOAT3(1, 1, 1),
float intensity = 1,
float range = 10,
LightComponent::LightType type = LightComponent::POINT,
float outerConeAngle = XM_PIDIV4,
float innerConeAngle = 0
);
wi::ecs::Entity Entity_CreateForce(
const std::string& name,
const XMFLOAT3& position = XMFLOAT3(0, 0, 0)
);
wi::ecs::Entity Entity_CreateEnvironmentProbe(
const std::string& name,
const XMFLOAT3& position = XMFLOAT3(0, 0, 0)
);
wi::ecs::Entity Entity_CreateDecal(
const std::string& name,
const std::string& textureName,
const std::string& normalMapName = ""
);
wi::ecs::Entity Entity_CreateCamera(
const std::string& name,
float width, float height, float nearPlane = 0.01f, float farPlane = 1000.0f, float fov = XM_PIDIV4
);
wi::ecs::Entity Entity_CreateEmitter(
const std::string& name,
const XMFLOAT3& position = XMFLOAT3(0, 0, 0)
);
wi::ecs::Entity Entity_CreateHair(
const std::string& name,
const XMFLOAT3& position = XMFLOAT3(0, 0, 0)
);
wi::ecs::Entity Entity_CreateSound(
const std::string& name,
const std::string& filename,
const XMFLOAT3& position = XMFLOAT3(0, 0, 0)
);
wi::ecs::Entity Entity_CreateVideo(
const std::string& name,
const std::string& filename
);
wi::ecs::Entity Entity_CreateCube(
const std::string& name
);
wi::ecs::Entity Entity_CreatePlane(
const std::string& name
);
wi::ecs::Entity Entity_CreateSphere(
const std::string& name,
float radius = 1,
uint32_t latitudeBands = 64,
uint32_t longitudeBands = 64
);
wi::ecs::Entity Entity_CreateMeshFromData(
const std::string& name,
size_t index_count,
const uint32_t* indices,
size_t vertex_count,
const XMFLOAT3* positions
);
// Attaches an entity to a parent:
// child_already_in_local_space : child won't be transformed from world space to local space
void Component_Attach(wi::ecs::Entity entity, wi::ecs::Entity parent, bool child_already_in_local_space = false);
// Detaches the entity from its parent (if it is attached):
void Component_Detach(wi::ecs::Entity entity);
// Detaches all children from an entity (if there are any):
void Component_DetachChildren(wi::ecs::Entity parent);
// Bakes the transform's current world matrix back to hierarchy local space (if it is part of a hierarchy)
void Component_TransformWorldToHierarchy(wi::ecs::Entity entity);
// Gathers all direct and indirect children of an entity
void GatherChildren(wi::ecs::Entity parent, wi::vector<wi::ecs::Entity>& children) const;
// Iterates over each child of an entity and executes a lambda function for each child
// parent : the parent entity whose children will be iterated
// function : lambda function that takes a child entity as parameter
// If function returns bool, iteration stops when it returns false
// If function returns void, iteration continues for all children
template<typename Func>
void ForEachChild(const wi::ecs::Entity parent, Func function) const
{
for (size_t i = 0; i < hierarchy.GetCount(); ++i)
{
wi::ecs::Entity child = hierarchy.GetEntity(i);
if (Entity_IsDescendant(child, parent))
{
if constexpr (std::is_same_v<decltype(function(child)), bool>)
{
// Function returns bool - stop iteration if it returns false
if (!function(child))
break;
}
else
{
// Function returns void - continue iteration
function(child);
}
}
}
}
// Read/write whole scene into an archive
void Serialize(wi::Archive& archive);
void RunAnimationUpdateSystem(wi::jobsystem::context& ctx);
void RunTransformUpdateSystem(wi::jobsystem::context& ctx);
void RunHierarchyUpdateSystem(wi::jobsystem::context& ctx);
void RunExpressionUpdateSystem(wi::jobsystem::context& ctx);
void RunProceduralAnimationUpdateSystem(wi::jobsystem::context& ctx);
void RunArmatureUpdateSystem(wi::jobsystem::context& ctx);
void RunMeshUpdateSystem(wi::jobsystem::context& ctx);
void RunMaterialUpdateSystem(wi::jobsystem::context& ctx);
void RunImpostorUpdateSystem(wi::jobsystem::context& ctx);
void RunObjectUpdateSystem(wi::jobsystem::context& ctx);
void RunCameraUpdateSystem(wi::jobsystem::context& ctx);
void RunDecalUpdateSystem(wi::jobsystem::context& ctx);
void RunProbeUpdateSystem(wi::jobsystem::context& ctx);
void RunForceUpdateSystem(wi::jobsystem::context& ctx);
void RunLightUpdateSystem(wi::jobsystem::context& ctx);
void RunParticleUpdateSystem(wi::jobsystem::context& ctx);
void RunWeatherUpdateSystem(wi::jobsystem::context& ctx);
void RunSoundUpdateSystem(wi::jobsystem::context& ctx);
void RunVideoUpdateSystem(wi::jobsystem::context& ctx);
void RunScriptUpdateSystem(wi::jobsystem::context& ctx);
void RunSpriteUpdateSystem(wi::jobsystem::context& ctx);
void RunFontUpdateSystem(wi::jobsystem::context& ctx);
void RunCharacterUpdateSystem(wi::jobsystem::context& ctx);
void RunSplineUpdateSystem(wi::jobsystem::context& ctx);
struct RayIntersectionResult
{
wi::ecs::Entity entity = wi::ecs::INVALID_ENTITY;
XMFLOAT3 position = XMFLOAT3(0, 0, 0);
XMFLOAT3 normal = XMFLOAT3(0, 0, 0);
XMFLOAT4 uv = XMFLOAT4(0, 0, 0, 0);
XMFLOAT3 velocity = XMFLOAT3(0, 0, 0);
float distance = FLT_MAX;
int subsetIndex = -1;
int vertexID0 = -1;
int vertexID1 = -1;
int vertexID2 = -1;
XMFLOAT2 bary = XMFLOAT2(0, 0);
XMFLOAT4X4 orientation = wi::math::IDENTITY_MATRIX;
HumanoidComponent::HumanoidBone humanoid_bone = HumanoidComponent::HumanoidBone::Count;
constexpr bool operator==(const RayIntersectionResult& other) const
{
return entity == other.entity;
}
};
// Given a ray, finds the closest intersection point against all mesh instances or collliders
// ray : the incoming ray that will be traced
// filterMask : filter based on type
// layerMask : filter based on layer
// lod : specify min level of detail for meshes
RayIntersectionResult Intersects(const wi::primitive::Ray& ray, uint32_t filterMask = wi::enums::FILTER_OPAQUE, uint32_t layerMask = ~0, uint32_t lod = 0) const;
// Given a ray, finds the first intersection point against all mesh instances or colliders
// returns true immediately if intersection was found, false otherwise
// ray : the incoming ray that will be traced
// filterMask : filter based on type
// layerMask : filter based on layer
// lod : specify min level of detail for meshes
bool IntersectsFirst(const wi::primitive::Ray& ray, uint32_t filterMask = wi::enums::FILTER_OPAQUE, uint32_t layerMask = ~0, uint32_t lod = 0) const;
// Given a ray, finds all intersections against all mesh instances or collliders
void IntersectsAll(wi::vector<RayIntersectionResult>& results, const wi::primitive::Ray& ray, uint32_t filterMask = wi::enums::FILTER_OPAQUE, uint32_t layerMask = ~0, uint32_t lod = 0) const;
struct SphereIntersectionResult
{
wi::ecs::Entity entity = wi::ecs::INVALID_ENTITY;
XMFLOAT3 position = XMFLOAT3(0, 0, 0);
XMFLOAT3 normal = XMFLOAT3(0, 0, 0);
XMFLOAT3 velocity = XMFLOAT3(0, 0, 0);
float depth = 0;
int subsetIndex = -1;
XMFLOAT4X4 orientation = wi::math::IDENTITY_MATRIX;
HumanoidComponent::HumanoidBone humanoid_bone = HumanoidComponent::HumanoidBone::Count;
};
// Closest sphere intersection
SphereIntersectionResult Intersects(const wi::primitive::Sphere& sphere, uint32_t filterMask = wi::enums::FILTER_OPAQUE, uint32_t layerMask = ~0, uint32_t lod = 0) const;
// All sphere intersections
void IntersectsAll(wi::vector<SphereIntersectionResult>& results, const wi::primitive::Sphere& sphere, uint32_t filterMask = wi::enums::FILTER_OPAQUE, uint32_t layerMask = ~0, uint32_t lod = 0) const;
using CapsuleIntersectionResult = SphereIntersectionResult;
// Closest capsule intersection
CapsuleIntersectionResult Intersects(const wi::primitive::Capsule& capsule, uint32_t filterMask = wi::enums::FILTER_OPAQUE, uint32_t layerMask = ~0, uint32_t lod = 0) const;
// All capsule intersections
void IntersectsAll(wi::vector<CapsuleIntersectionResult>& results, const wi::primitive::Capsule& capsule, uint32_t filterMask = wi::enums::FILTER_OPAQUE, uint32_t layerMask = ~0, uint32_t lod = 0) const;
// Goes through the hierarchy backwards and computes entity's world space matrix:
XMMATRIX ComputeEntityMatrixRecursive(wi::ecs::Entity entity) const;
// Goes through the hierarchy backwards and computes parent's world space matrix:
XMMATRIX ComputeParentMatrixRecursive(wi::ecs::Entity entity) const;
// Retargets an animation from a Humanoid to an other Humanoid such that the new animation will play back on the destination humanoid
// dst : destination humanoid that the animation will be fit onto
// src : the animation to copy, it should already target humanoid bones
// bake_data : if true, the retargeted data will be baked into a new animation data.
// if false, it will reuse the source animation data without creating a new one and retargeting will be applied at runtime on every Update
// src_scene : (optional) specify if you want to retarget from an other scene. This scene must be kept alive while you use the resulting animation if the data is not baked!
// If you use a separate scene, then you mustn't serialize the scene while there are animations referencing the src_scene!
//
// returns entity ID of the new animation or INVALID_ENTITY if retargeting was not successful
wi::ecs::Entity RetargetAnimation(wi::ecs::Entity dst, wi::ecs::Entity src, bool bake_data, const Scene* src_scene = nullptr);
// If you don't know which armature the bone is contained in, this function can be used to find the first such armature and return the bone's rest matrix
// If not found, and entity has a transform, it returns transform matrix
// Otherwise, returns identity matrix
XMMATRIX GetRestPose(wi::ecs::Entity entity) const;
XMMATRIX FindBoneRestPose(wi::ecs::Entity bone) { return GetRestPose(bone); }; // back-compat of GetRestPose
// All triangles of the object will be injected into the voxel grid
// subtract: if false (default), voxels will be added, if true then voxels will be removed
void VoxelizeObject(size_t objectIndex, wi::VoxelGrid& grid, bool subtract = false, uint32_t lod = 0);
// Voxelize all meshes that match the filters into a voxel grid
void VoxelizeScene(wi::VoxelGrid& voxelgrid, bool subtract = false, uint32_t filterMask = wi::enums::FILTER_ALL, uint32_t layerMask = ~0, uint32_t lod = 0);
// Get the current position on the surface of an object, tracked by the triangle barycentrics
XMFLOAT3 GetPositionOnSurface(wi::ecs::Entity objectEntity, int vertexID0, int vertexID1, int vertexID2, const XMFLOAT2& bary) const;
// Resets pose of the specified entity to bind pose
// this will search for all armatures that are descendants of the entity and set all bone matrices to the their bind matrix
void ResetPose(wi::ecs::Entity entity);
// Returns the approximate position on the ocean surface seen from a position in world space.
// If current weather doesn't have ocean enabled, returns the world position itself.
// The result position is approximate because it involves reading back from GPU to the CPU, so the result can be delayed compared to the current GPU simulation.
// Note that the input position to this function will be taken on the XZ plane and modified by the displacement map's XZ value, and the Y (vertical) position will be taken from the ocean water height and displacement map only.
XMFLOAT3 GetOceanPosAt(const XMFLOAT3& worldPosition) const;
// Computes the LOD for an object AABB for a given view projection matrix
uint32_t ComputeObjectLODForView(const ObjectComponent& object, const wi::primitive::AABB& aabb, const MeshComponent& mesh, const XMMATRIX& ViewProjection) const;
// If somehow NANs happened in TransformComponents, this will clear them up and rename them with _nanfix postfix to help filtering them
void FixupNans();
// Duplicate colliders will be removed from the scene
void DeleteDuplicateColliders();
private:
void UpdateHumanoidFacings();
};
// Returns skinned vertex position
// N : normal (out, optional)
XMVECTOR SkinVertex(const MeshComponent& mesh, const wi::vector<ShaderTransform>& boneData, uint32_t index, XMVECTOR* N = nullptr);
// Returns skinned vertex position in armature local space
// N : normal (out, optional)
XMVECTOR SkinVertex(const MeshComponent& mesh, const ArmatureComponent& armature, uint32_t index, XMVECTOR* N = nullptr);
// Returns skinned vertex position of soft body in world space
// N : normal (out, optional)
XMVECTOR SkinVertex(const MeshComponent& mesh, const SoftBodyPhysicsComponent& softbody, uint32_t index, XMVECTOR* N = nullptr);
// Helper that manages a global scene
// (You don't need to use it, but it's an option for simplicity)
inline Scene& GetScene()
{
static Scene scene;
return scene;
}
// Helper that manages a global camera
// (You don't need to use it, but it's an option for simplicity)
inline CameraComponent& GetCamera()
{
static CameraComponent camera;
return camera;
}
// Helper function to open a wiscene file and add the contents to the global scene
// fileName : file path
// transformMatrix : everything will be transformed by this matrix (optional)
// attached : if true, everything will be attached to a base entity
//
// returns INVALID_ENTITY if attached argument was false, else it returns the base entity handle
wi::ecs::Entity LoadModel(const std::string& fileName, const XMMATRIX& transformMatrix = XMMatrixIdentity(), bool attached = false);
// Helper function to open a wiscene file and add the contents to the specified scene. This is thread safe as it doesn't modify global scene
// scene : the scene that will contain the model
// fileName : file path
// transformMatrix : everything will be transformed by this matrix (optional)
// attached : if true, everything will be attached to a base entity
//
// returns INVALID_ENTITY if attached argument was false, else it returns the base entity handle
wi::ecs::Entity LoadModel(Scene& scene, const std::string& fileName, const XMMATRIX& transformMatrix = XMMatrixIdentity(), bool attached = false);
// Helper function to open a wiscene file and add the contents to the global scene
// fileName : file path
// transformMatrix : everything will be transformed by this matrix (optional)
// rootEntity : specify entity to attach whole scene to (optional)
void LoadModel2(const std::string& fileName, const XMMATRIX& transformMatrix = XMMatrixIdentity(), wi::ecs::Entity rootEntity = wi::ecs::INVALID_ENTITY);
// Helper function to open a wiscene file and add the contents to the specified scene. This is thread safe as it doesn't modify global scene
// scene : the scene that will contain the model
// fileName : file path
// transformMatrix : everything will be transformed by this matrix (optional)
// rootEntity : specify entity to attach whole scene to (optional)
void LoadModel2(Scene& scene, const std::string& fileName, const XMMATRIX& transformMatrix = XMMatrixIdentity(), wi::ecs::Entity rootEntity = wi::ecs::INVALID_ENTITY);
// Deprecated, use Scene::Intersects() function instead
using PickResult = Scene::RayIntersectionResult;
PickResult Pick(const wi::primitive::Ray& ray, uint32_t filterMask = wi::enums::FILTER_OPAQUE, uint32_t layerMask = ~0, const Scene& scene = GetScene(), uint32_t lod = 0);
// Deprecated, use Scene::Intersects() function instead
using SceneIntersectSphereResult = Scene::SphereIntersectionResult;
SceneIntersectSphereResult SceneIntersectSphere(const wi::primitive::Sphere& sphere, uint32_t filterMask = wi::enums::FILTER_OPAQUE, uint32_t layerMask = ~0, const Scene& scene = GetScene(), uint32_t lod = 0);
// Deprecated, use Scene::Intersects() function instead
using SceneIntersectCapsuleResult = Scene::SphereIntersectionResult;
SceneIntersectCapsuleResult SceneIntersectCapsule(const wi::primitive::Capsule& capsule, uint32_t filterMask = wi::enums::FILTER_OPAQUE, uint32_t layerMask = ~0, const Scene& scene = GetScene(), uint32_t lod = 0);
}
template<>
struct enable_bitmask_operators<wi::scene::Scene::EntitySerializeFlags> {
static constexpr bool enable = true;
};