#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 #include namespace wi::scene { struct Scene { virtual ~Scene() = default; wi::ecs::ComponentLibrary componentLibrary; wi::ecs::ComponentManager& names = componentLibrary.Register("wi::scene::Scene::names"); wi::ecs::ComponentManager& layers = componentLibrary.Register("wi::scene::Scene::layers"); wi::ecs::ComponentManager& transforms = componentLibrary.Register("wi::scene::Scene::transforms"); wi::ecs::ComponentManager& hierarchy = componentLibrary.Register("wi::scene::Scene::hierarchy"); wi::ecs::ComponentManager& materials = componentLibrary.Register("wi::scene::Scene::materials", 11); // version = 11 wi::ecs::ComponentManager& meshes = componentLibrary.Register("wi::scene::Scene::meshes", 4); // version = 4 wi::ecs::ComponentManager& impostors = componentLibrary.Register("wi::scene::Scene::impostors"); wi::ecs::ComponentManager& objects = componentLibrary.Register("wi::scene::Scene::objects", 4); // version = 4 wi::ecs::ComponentManager& rigidbodies = componentLibrary.Register("wi::scene::Scene::rigidbodies", 6); // version = 6 wi::ecs::ComponentManager& softbodies = componentLibrary.Register("wi::scene::Scene::softbodies", 3); // version = 3 wi::ecs::ComponentManager& armatures = componentLibrary.Register("wi::scene::Scene::armatures"); wi::ecs::ComponentManager& lights = componentLibrary.Register("wi::scene::Scene::lights", 5); // version = 5 wi::ecs::ComponentManager& cameras = componentLibrary.Register("wi::scene::Scene::cameras", 2); // version = 2 wi::ecs::ComponentManager& probes = componentLibrary.Register("wi::scene::Scene::probes", 2); // version = 2 wi::ecs::ComponentManager& forces = componentLibrary.Register("wi::scene::Scene::forces", 1); // version = 1 wi::ecs::ComponentManager& decals = componentLibrary.Register("wi::scene::Scene::decals", 1); // version = 1 wi::ecs::ComponentManager& animations = componentLibrary.Register("wi::scene::Scene::animations", 2); // version = 2 wi::ecs::ComponentManager& animation_datas = componentLibrary.Register("wi::scene::Scene::animation_datas"); wi::ecs::ComponentManager& emitters = componentLibrary.Register("wi::scene::Scene::emitters", 2); // version = 2 wi::ecs::ComponentManager& hairs = componentLibrary.Register("wi::scene::Scene::hairs", 3); // version = 3 wi::ecs::ComponentManager& weathers = componentLibrary.Register("wi::scene::Scene::weathers", 6); // version = 6 wi::ecs::ComponentManager& sounds = componentLibrary.Register("wi::scene::Scene::sounds", 1); // version = 1 wi::ecs::ComponentManager& videos = componentLibrary.Register("wi::scene::Scene::videos", 1); // version = 1 wi::ecs::ComponentManager& inverse_kinematics = componentLibrary.Register("wi::scene::Scene::inverse_kinematics"); wi::ecs::ComponentManager& springs = componentLibrary.Register("wi::scene::Scene::springs", 1); // version = 1 wi::ecs::ComponentManager& colliders = componentLibrary.Register("wi::scene::Scene::colliders", 2); // version = 2 wi::ecs::ComponentManager& scripts = componentLibrary.Register("wi::scene::Scene::scripts"); wi::ecs::ComponentManager& expressions = componentLibrary.Register("wi::scene::Scene::expressions"); wi::ecs::ComponentManager& humanoids = componentLibrary.Register("wi::scene::Scene::humanoids", 3); // version = 3 wi::ecs::ComponentManager& terrains = componentLibrary.Register("wi::scene::Scene::terrains", 6); // version = 6 wi::ecs::ComponentManager& sprites = componentLibrary.Register("wi::scene::Scene::sprites", 2); // version = 2 wi::ecs::ComponentManager& fonts = componentLibrary.Register("wi::scene::Scene::fonts"); wi::ecs::ComponentManager& voxel_grids = componentLibrary.Register("wi::scene::Scene::voxel_grids"); wi::ecs::ComponentManager& metadatas = componentLibrary.Register("wi::scene::Scene::metadatas"); wi::ecs::ComponentManager& characters = componentLibrary.Register("wi::scene::Scene::characters"); wi::ecs::ComponentManager& constraints = componentLibrary.Register("wi::scene::Scene::constraints", 6); // version = 6 wi::ecs::ComponentManager& splines = componentLibrary.Register("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 physics_scene; wi::SpinLock locker; wi::primitive::AABB bounds; wi::vector 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 character_capsules; wi::vector character_dedicated_shadows; wi::unordered_map> 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 aabb_objects; wi::vector aabb_lights; wi::vector aabb_probes; wi::vector aabb_decals; wi::vector aabb_fonts; // Separate stream of world matrices: wi::vector matrix_objects; wi::vector 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 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 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 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 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 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 lightmap_request_allocator{ 0 }; wi::vector lightmap_requests; wi::vector transforms_temp; // CPU/GPU Colliders: wi::vector 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 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 animations; // pointers for one frame only! wi::unordered_set entities; }; wi::vector 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 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& 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& 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 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) { // 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& 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& 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& 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& 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 { static constexpr bool enable = true; };