diff --git a/Content/Documentation/ScriptingAPI-Documentation.md b/Content/Documentation/ScriptingAPI-Documentation.md index 350ce8181..a0e0bfcb6 100644 --- a/Content/Documentation/ScriptingAPI-Documentation.md +++ b/Content/Documentation/ScriptingAPI-Documentation.md @@ -842,6 +842,8 @@ The scene holds components. Entity handles can be used to retrieve associated co - RetargetAnimation(Entity dst, src, bool bake_data) : Entity entity -- 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. Returns entity ID of the new animation or INVALID_ENTITY if retargeting was not successful +- ResetPose(Entity entity) -- resets the pose of the specified entity to bind pose. The bind pose is taken from the bind matrices of bones of an ArmatureComponent. If the entity does not have an armature, then it will find the child armatures of the entity. + - VoxelizeObject(int objectIndex, VoxelGrid voxelgrid, opt bool subtract = false, opt int lod = 0) -- voxelizes a single object into the voxel grid. Subtract parameter controls whether the voxels are added (true) or removed (false). Lod argument selects object's level of detail - VoxelizeScene(VoxelGrid voxelgrid, opt bool subtract = false, opt uint filterMask = ~0u, opt uint layerMask = ~0u, opt uint lod = 0) -- voxelizes all entities in the scene which intersect the voxel grid volume and match the filterMask and layerMask. Subtract parameter controls whether the voxels are added (true) or removed (false). Lod argument selects object's level of detail @@ -1834,7 +1836,6 @@ Playstation button codes: - ApplyImpulseAt(RigidBodyPhysicsComponent component, Vector impulse, Vector at) -- Apply impulse at body local position - ApplyImpulseAt(HumanoidComponent humanoid, HumanoidBone bone, Vector impulse, Vector at) -- Apply impulse at body local position of ragdoll bone - ApplyTorque(RigidBodyPhysicsComponent component, Vector torque) -- Apply torque at body center -- ApplyTorqueImpulse(RigidBodyPhysicsComponent component, Vector torque) -- Apply torque impulse at body center - SetActivationState(RigidBodyPhysicsComponent component, int state) -- Force set activation state to rigid body. Use a value ACTIVATION_STATE_ACTIVE or ACTIVATION_STATE_INACTIVE - SetActivationState(SoftBodyPhysicsComponent component, int state) -- Force set activation state to soft body. Use a value ACTIVATION_STATE_ACTIVE or ACTIVATION_STATE_INACTIVE - [outer]ACTIVATION_STATE_ACTIVE : int diff --git a/Content/Documentation/WickedEngine-Documentation.md b/Content/Documentation/WickedEngine-Documentation.md index 7c5f73342..4975540ba 100644 --- a/Content/Documentation/WickedEngine-Documentation.md +++ b/Content/Documentation/WickedEngine-Documentation.md @@ -1227,9 +1227,6 @@ The [ObjectComponent](#objectcomponent)'s transform matrix is used as a manipula The pinned vertices can also be manipulated via skinning animation. If the soft body mesh is skinned and an animation is playing, then the pinned vertices will follow the animation. -Deactivation happens after a while for soft bodies that participated in the simulation for a while, this means after that they will no longer participate in the simulation. This behaviour can be disabled with the `SoftBodyPhysicsComponent::DISABLE_DEACTIVATION` flag. - -Resetting a soft body can be accomplished by setting the `SoftBodyPhysicsComponent::FORCE_RESET` flag. This means that the next physics update will reset the soft body mesh to the initial pose. diff --git a/Editor/ArmatureWindow.cpp b/Editor/ArmatureWindow.cpp index bd17c8737..8e566a321 100644 --- a/Editor/ArmatureWindow.cpp +++ b/Editor/ArmatureWindow.cpp @@ -32,7 +32,7 @@ void ArmatureWindow::Create(EditorComponent* _editor) float wid = 220; infoLabel.Create(""); - infoLabel.SetSize(XMFLOAT2(100, 50)); + infoLabel.SetSize(XMFLOAT2(100, 80)); infoLabel.SetText("This window will stay open even if you select other entities until it is collapsed, so you can select other bone entities."); AddWidget(&infoLabel); @@ -41,27 +41,8 @@ void ArmatureWindow::Create(EditorComponent* _editor) resetPoseButton.SetSize(XMFLOAT2(wid, hei)); resetPoseButton.OnClick([=](wi::gui::EventArgs args) { wi::scene::Scene& scene = editor->GetCurrentScene(); - const ArmatureComponent* armature = scene.armatures.GetComponent(entity); - if (armature == nullptr) - return; - - for (size_t i = 0; i < armature->boneCollection.size(); ++i) - { - Entity bone = armature->boneCollection[i]; - TransformComponent* transform = scene.transforms.GetComponent(bone); - if (transform != nullptr) - { - transform->ClearTransform(); - transform->MatrixTransform(XMMatrixInverse(nullptr, XMLoadFloat4x4(&armature->inverseBindMatrices[i]))); - transform->UpdateTransform(); - const HierarchyComponent* hier = scene.hierarchy.GetComponent(bone); - if (hier != nullptr && hier->parentID != INVALID_ENTITY) - { - scene.Component_Attach(bone, hier->parentID, false); - } - } - } - }); + scene.ResetPose(entity); + }); AddWidget(&resetPoseButton); createHumanoidButton.Create("Try to create humanoid rig"); diff --git a/Editor/CMakeLists.txt b/Editor/CMakeLists.txt index b637629f7..352a800fa 100644 --- a/Editor/CMakeLists.txt +++ b/Editor/CMakeLists.txt @@ -51,8 +51,6 @@ set (SOURCE_FILES EmbeddedResources.cpp ) -add_subdirectory(meshoptimizer) - if (WIN32) list (APPEND SOURCE_FILES Editor.rc @@ -62,7 +60,6 @@ if (WIN32) target_link_libraries(WickedEngineEditor PUBLIC WickedEngine_Windows - meshoptimizer ) set_property(TARGET WickedEngineEditor PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") @@ -72,7 +69,6 @@ else () target_link_libraries(WickedEngineEditor PUBLIC WickedEngine - meshoptimizer ) set(LIB_DXCOMPILER "libdxcompiler.so") diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index c6dd09f8b..c2b34f5d9 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -1615,7 +1615,6 @@ void EditorComponent::Update(float dt) { if ( wi::input::Down(wi::input::MOUSE_BUTTON_LEFT) || - wi::input::Down(wi::input::MOUSE_BUTTON_MIDDLE) || inspector_mode ) { @@ -1638,6 +1637,7 @@ void EditorComponent::Update(float dt) // Interactions: { // Interact: + bool interaction_happened = false; if (wi::input::Down((wi::input::BUTTON)'P')) { if (wi::input::Press(wi::input::MOUSE_BUTTON_MIDDLE)) @@ -1646,6 +1646,7 @@ void EditorComponent::Update(float dt) wi::physics::RayIntersectionResult result = wi::physics::Intersects(scene, pickRay); if (result.IsValid()) { + interaction_happened = true; XMFLOAT3 impulse; XMStoreFloat3(&impulse, XMVector3Normalize(XMLoadFloat3(&pickRay.direction)) * 20); if (result.humanoid_ragdoll_entity != INVALID_ENTITY) @@ -1676,6 +1677,10 @@ void EditorComponent::Update(float dt) if (wi::input::Down(wi::input::MOUSE_BUTTON_MIDDLE)) { wi::physics::PickDrag(scene, pickRay, physicsDragOp); + if (physicsDragOp.IsValid()) + { + interaction_happened = true; + } } else { @@ -1714,50 +1719,53 @@ void EditorComponent::Update(float dt) } // Other: - if (hovered.entity != INVALID_ENTITY && wi::input::Down(wi::input::MOUSE_BUTTON_MIDDLE)) + if (!interaction_happened && wi::input::Down(wi::input::MOUSE_BUTTON_MIDDLE)) { - if (dummy_enabled) + hovered = wi::scene::Pick(pickRay, wi::enums::FILTER_OBJECT_ALL, ~0u, scene); + if (hovered.entity != INVALID_ENTITY) { - dummy_pos = hovered.position; - } - else - { - const ObjectComponent* object = scene.objects.GetComponent(hovered.entity); - if (object != nullptr) + if (dummy_enabled) { - if (object->GetFilterMask() & wi::enums::FILTER_WATER) + dummy_pos = hovered.position; + } + else + { + const ObjectComponent* object = scene.objects.GetComponent(hovered.entity); + if (object != nullptr) { - // if water, then put a water ripple onto it: - scene.PutWaterRipple(hovered.position); - } - else - { - // Check for interactive grass (hair particle that is child of hovered object: - for (size_t i = 0; i < scene.hairs.GetCount(); ++i) + if (object->GetFilterMask() & wi::enums::FILTER_WATER) { - Entity entity = scene.hairs.GetEntity(i); - HierarchyComponent* hier = scene.hierarchy.GetComponent(entity); - if (hier != nullptr && hier->parentID == hovered.entity) + // if water, then put a water ripple onto it: + scene.PutWaterRipple(hovered.position); + } + else + { + // Check for interactive grass (hair particle that is child of hovered object: + for (size_t i = 0; i < scene.hairs.GetCount(); ++i) { - XMVECTOR P = XMLoadFloat3(&hovered.position); - P += XMLoadFloat3(&hovered.normal) * 2; - if (grass_interaction_entity == INVALID_ENTITY) + Entity entity = scene.hairs.GetEntity(i); + HierarchyComponent* hier = scene.hierarchy.GetComponent(entity); + if (hier != nullptr && hier->parentID == hovered.entity) { - grass_interaction_entity = CreateEntity(); + XMVECTOR P = XMLoadFloat3(&hovered.position); + P += XMLoadFloat3(&hovered.normal) * 2; + if (grass_interaction_entity == INVALID_ENTITY) + { + grass_interaction_entity = CreateEntity(); + } + ForceFieldComponent& force = scene.forces.Create(grass_interaction_entity); + TransformComponent& transform = scene.transforms.Create(grass_interaction_entity); + force.type = ForceFieldComponent::Type::Point; + force.gravity = -80; + force.range = 3; + transform.Translate(P); + break; } - ForceFieldComponent& force = scene.forces.Create(grass_interaction_entity); - TransformComponent& transform = scene.transforms.Create(grass_interaction_entity); - force.type = ForceFieldComponent::Type::Point; - force.gravity = -80; - force.range = 3; - transform.Translate(P); - break; } } } } } - } } @@ -2285,6 +2293,10 @@ void EditorComponent::Update(float dt) if (navtest_enabled) { + if (wi::input::Down(wi::input::MOUSE_BUTTON_MIDDLE)) + { + hovered = wi::scene::Pick(pickRay, wi::enums::FILTER_OBJECT_ALL, ~0u, scene); + } if (hovered.entity != INVALID_ENTITY && wi::input::Down(wi::input::KEYBOARD_BUTTON_F5)) { navtest_start_pick = hovered; @@ -2558,7 +2570,7 @@ void EditorComponent::Render() const if (renderPath->getMSAASampleCount() > 1) { RenderPassImage rp[] = { - RenderPassImage::RenderTarget(&rt_selectionOutline_MSAA, RenderPassImage::LoadOp::CLEAR, RenderPassImage::StoreOp::DONTCARE), + RenderPassImage::RenderTarget(&rt_selectionOutline_MSAA, RenderPassImage::LoadOp::CLEAR), RenderPassImage::Resolve(&rt_selectionOutline[0]), RenderPassImage::DepthStencil( renderPath->GetDepthStencil(), @@ -2593,7 +2605,7 @@ void EditorComponent::Render() const if (renderPath->getMSAASampleCount() > 1) { RenderPassImage rp[] = { - RenderPassImage::RenderTarget(&rt_selectionOutline_MSAA, RenderPassImage::LoadOp::CLEAR, RenderPassImage::StoreOp::DONTCARE), + RenderPassImage::RenderTarget(&rt_selectionOutline_MSAA, RenderPassImage::LoadOp::CLEAR), RenderPassImage::Resolve(&rt_selectionOutline[1]), RenderPassImage::DepthStencil( renderPath->GetDepthStencil(), @@ -2742,7 +2754,7 @@ void EditorComponent::Render() const RenderPassImage::RenderTarget( &editor_rendertarget, RenderPassImage::LoadOp::CLEAR, - RenderPassImage::StoreOp::DONTCARE, + RenderPassImage::StoreOp::STORE, ResourceState::RENDERTARGET, ResourceState::RENDERTARGET ), diff --git a/Editor/Editor_SOURCE.vcxitems b/Editor/Editor_SOURCE.vcxitems index 8fe65fda4..f7acf0a36 100644 --- a/Editor/Editor_SOURCE.vcxitems +++ b/Editor/Editor_SOURCE.vcxitems @@ -37,66 +37,6 @@ - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - @@ -167,7 +107,6 @@ - diff --git a/Editor/Editor_SOURCE.vcxitems.filters b/Editor/Editor_SOURCE.vcxitems.filters index 9affc6c9d..3aa9d7395 100644 --- a/Editor/Editor_SOURCE.vcxitems.filters +++ b/Editor/Editor_SOURCE.vcxitems.filters @@ -27,51 +27,6 @@ - - meshoptimizer - - - meshoptimizer - - - meshoptimizer - - - meshoptimizer - - - meshoptimizer - - - meshoptimizer - - - meshoptimizer - - - meshoptimizer - - - meshoptimizer - - - meshoptimizer - - - meshoptimizer - - - meshoptimizer - - - meshoptimizer - - - meshoptimizer - - - meshoptimizer - @@ -123,9 +78,6 @@ - - meshoptimizer - @@ -156,9 +108,6 @@ - - {49c521c0-5dca-4fce-b8c5-a334d1746d98} - {beae58b9-9c06-4b1f-9e03-a27712d6a579} diff --git a/Editor/HumanoidWindow.cpp b/Editor/HumanoidWindow.cpp index 1af266e2f..4a56a0577 100644 --- a/Editor/HumanoidWindow.cpp +++ b/Editor/HumanoidWindow.cpp @@ -32,7 +32,7 @@ void HumanoidWindow::Create(EditorComponent* _editor) float wid = 220; infoLabel.Create(""); - infoLabel.SetSize(XMFLOAT2(100, 50)); + infoLabel.SetSize(XMFLOAT2(100, 80)); infoLabel.SetText("This window will stay open even if you select other entities until it is collapsed, so you can select other bone entities."); AddWidget(&infoLabel); diff --git a/Editor/MeshWindow.cpp b/Editor/MeshWindow.cpp index 04c06fc16..10b079e85 100644 --- a/Editor/MeshWindow.cpp +++ b/Editor/MeshWindow.cpp @@ -2,8 +2,7 @@ #include "MeshWindow.h" #include "Utility/stb_image.h" - -#include "meshoptimizer/meshoptimizer.h" +#include "Utility/meshoptimizer/meshoptimizer.h" using namespace wi::ecs; using namespace wi::scene; diff --git a/Editor/ModelImporter_FBX.cpp b/Editor/ModelImporter_FBX.cpp index ca1d40d90..34c9dad5f 100644 --- a/Editor/ModelImporter_FBX.cpp +++ b/Editor/ModelImporter_FBX.cpp @@ -258,6 +258,8 @@ void ImportModel_FBX(const std::string& filename, wi::scene::Scene& scene) wi::vector colors; wi::vector boneindices; wi::vector boneweights; + wi::vector boneindices2; + wi::vector boneweights2; wi::vector morphs(meshcomponent.morph_targets.size()); for (uint32_t face_index : part.face_indices) @@ -299,24 +301,29 @@ void ImportModel_FBX(const std::string& filename, wi::scene::Scene& scene) ufbx_skin_vertex skin_vertex = skin->vertices[vertex]; uint32_t num_weights = skin_vertex.num_weights; - num_weights = std::min(num_weights, 4u); + num_weights = std::min(num_weights, 8u); + + if (num_weights > 4) + { + boneindices2.resize(boneindices.size()); + boneweights2.resize(boneweights.size()); + } - float total_weight = 0; for (uint32_t i = 0; i < num_weights; ++i) { ufbx_skin_weight skin_weight = skin->weights[skin_vertex.weight_begin + i]; - (&boneindices.back().x)[i] = skin_weight.cluster_index; - (&boneweights.back().x)[i] = skin_weight.weight; - total_weight += skin_weight.weight; - } - - if (total_weight > 0) - { - for (uint32_t i = 0; i < num_weights; ++i) + if (i < 4) { - (&boneweights.back().x)[i] /= total_weight; + (&boneindices.back().x)[i] = skin_weight.cluster_index; + (&boneweights.back().x)[i] = skin_weight.weight; + } + else + { + (&boneindices2.back().x)[i] = skin_weight.cluster_index; + (&boneweights2.back().x)[i] = skin_weight.weight; } } + // Note: normalization of bone weights will be done in MeshComponent::CreateRenderData() } for (const ufbx_blend_deformer* deformer : mesh->blend_deformers) @@ -369,6 +376,18 @@ void ImportModel_FBX(const std::string& filename, wi::scene::Scene& scene) { streams.push_back({ boneweights.data(), boneweights.size(), sizeof(boneweights[0]) }); } + assert(boneindices.size() == boneweights.size()); + if (!boneindices2.empty()) + { + boneindices2.resize(boneindices.size()); // ensure same size as first bone stream, we only resized so far on demand + streams.push_back({ boneindices2.data(), boneindices2.size(), sizeof(boneindices2[0]) }); + } + if (!boneweights2.empty()) + { + boneweights2.resize(boneweights.size()); // ensure same size as first bone stream, we only resized so far on demand + streams.push_back({ boneweights2.data(), boneweights2.size(), sizeof(boneweights2[0]) }); + } + assert(boneindices2.size() == boneweights2.size()); for (MeshComponent::MorphTarget& morph : morphs) { streams.push_back({ morph.vertex_positions.data(), morph.vertex_positions.size(), sizeof(morph.vertex_positions[0]) }); @@ -422,6 +441,16 @@ void ImportModel_FBX(const std::string& filename, wi::scene::Scene& scene) boneweights.resize(num_vertices); meshcomponent.vertex_boneweights.insert(meshcomponent.vertex_boneweights.end(), boneweights.begin(), boneweights.end()); } + if (!boneindices2.empty()) + { + boneindices2.resize(num_vertices); + meshcomponent.vertex_boneindices2.insert(meshcomponent.vertex_boneindices2.end(), boneindices2.begin(), boneindices2.end()); + } + if (!boneweights2.empty()) + { + boneweights2.resize(num_vertices); + meshcomponent.vertex_boneweights2.insert(meshcomponent.vertex_boneweights2.end(), boneweights2.begin(), boneweights2.end()); + } for (size_t i = 0; i < morphs.size(); ++i) { morphs[i].vertex_positions.resize(num_vertices); diff --git a/Editor/ModelImporter_GLTF.cpp b/Editor/ModelImporter_GLTF.cpp index 952ffda2b..9605a9ec6 100644 --- a/Editor/ModelImporter_GLTF.cpp +++ b/Editor/ModelImporter_GLTF.cpp @@ -3203,6 +3203,12 @@ void Import_Extension_VRMC(LoaderState& state) } } + + auto ext_vrmc_springbone_extended_collider = state.gltfModel.extensions.find("VRMC_springBone_extended_collider"); + if (ext_vrmc_springbone_extended_collider != state.gltfModel.extensions.end()) + { + wi::backlog::post("VRMC_springBone_extended_collider extension found in model, but it is not implemented yet in the importer.", wi::backlog::LogLevel::Warning); + } } void VRM_ToonMaterialCustomize(const std::string& name, MaterialComponent& material) diff --git a/Editor/PaintToolWindow.cpp b/Editor/PaintToolWindow.cpp index df6086081..dcc0c1a80 100644 --- a/Editor/PaintToolWindow.cpp +++ b/Editor/PaintToolWindow.cpp @@ -1170,39 +1170,69 @@ void PaintToolWindow::Update(float dt) continue; SoftBodyPhysicsComponent* softbody = scene.softbodies.GetComponent(object.meshID); - if (softbody == nullptr || !softbody->HasVertices()) + if (softbody == nullptr || softbody->physicsobject == nullptr) continue; - size_t j = 0; - for (auto& ind : softbody->physicsToGraphicsVertexMapping) + // Painting: + if (painting) { - XMVECTOR P = softbody->vertex_positions_simulation[ind].LoadPOS(); - - if (painting) + for (size_t j = 0; j < mesh->vertex_positions.size(); ++j) { + XMVECTOR P = SkinVertex(*mesh, *softbody, (uint32_t)j); + const float dist = wi::math::Distance(P, CENTER); if (dist <= pressure_radius) { RecordHistory(object.meshID); softbody->weights[j] = (mode == MODE_SOFTBODY_PINNING ? 0.0f : 1.0f); - softbody->_flags |= SoftBodyPhysicsComponent::FORCE_RESET; + softbody->Reset(); } } + } - wi::renderer::RenderablePoint point; - point.size = 0.01f; - XMStoreFloat3(&point.position, P); - if (softbody->weights[j] == 0) + // Visualizing: + const XMMATRIX W = XMLoadFloat4x4(&softbody->worldMatrix); + uint32_t first_subset = 0; + uint32_t last_subset = 0; + mesh->GetLODSubsetRange(0, first_subset, last_subset); + for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex) + { + const MeshComponent::MeshSubset& subset = mesh->subsets[subsetIndex]; + for (size_t j = 0; j < subset.indexCount; j += 3) { - point.color = XMFLOAT4(1, 0, 0, 1); + const uint32_t i0 = mesh->indices[j + 0]; + const uint32_t i1 = mesh->indices[j + 1]; + const uint32_t i2 = mesh->indices[j + 2]; + const float weight0 = softbody->weights[i0]; + const float weight1 = softbody->weights[i1]; + const float weight2 = softbody->weights[i2]; + XMVECTOR N0 = XMVectorZero(), N1 = XMVectorZero(), N2 = XMVectorZero(); + wi::renderer::RenderableTriangle tri; + XMStoreFloat3(&tri.positionA, SkinVertex(*mesh, *softbody, i0, &N0) + N0 * 0.01f); + XMStoreFloat3(&tri.positionB, SkinVertex(*mesh, *softbody, i1, &N1) + N1 * 0.01f); + XMStoreFloat3(&tri.positionC, SkinVertex(*mesh, *softbody, i2, &N2) + N2 * 0.01f); + if (weight0 == 0) + tri.colorA = XMFLOAT4(1, 1, 0, 1); + else + tri.colorA = XMFLOAT4(1, 1, 1, 1); + if (weight1 == 0) + tri.colorB = XMFLOAT4(1, 1, 0, 1); + else + tri.colorB = XMFLOAT4(1, 1, 1, 1); + if (weight2 == 0) + tri.colorC = XMFLOAT4(1, 1, 0, 1); + else + tri.colorC = XMFLOAT4(1, 1, 1, 1); + if (wireframe) + { + wi::renderer::DrawTriangle(tri, true); + } + if (weight0 == 0 && weight1 == 0 && weight2 == 0) + { + tri.colorA = tri.colorB = tri.colorC = XMFLOAT4(1, 0, 0, 0.8f); + wi::renderer::DrawTriangle(tri); + } } - else - { - point.color = XMFLOAT4(0, 1, 0, 1); - } - wi::renderer::DrawPoint(point); - - j++; } } } @@ -1769,8 +1799,7 @@ void PaintToolWindow::ConsumeHistoryOperation(wi::Archive& archive, bool undo) archive >> archive_softbody.weights; softbody->weights = archive_softbody.weights; - - softbody->_flags |= SoftBodyPhysicsComponent::FORCE_RESET; + softbody->Reset(); } break; case PaintToolWindow::MODE_HAIRPARTICLE_ADD_TRIANGLE: diff --git a/Editor/SoftBodyWindow.cpp b/Editor/SoftBodyWindow.cpp index 941595a45..a14ab6d4d 100644 --- a/Editor/SoftBodyWindow.cpp +++ b/Editor/SoftBodyWindow.cpp @@ -8,7 +8,7 @@ void SoftBodyWindow::Create(EditorComponent* _editor) { editor = _editor; wi::gui::Window::Create(ICON_SOFTBODY " Soft Body Physics", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE); - SetSize(XMFLOAT2(580, 260)); + SetSize(XMFLOAT2(580, 320)); closeButton.SetTooltip("Delete SoftBodyPhysicsComponent"); OnClose([=](wi::gui::EventArgs args) { @@ -19,6 +19,18 @@ void SoftBodyWindow::Create(EditorComponent* _editor) editor->GetCurrentScene().softbodies.Remove(entity); + MeshComponent* mesh = editor->GetCurrentScene().meshes.GetComponent(entity); + if (mesh != nullptr && mesh->armatureID == INVALID_ENTITY) + { + // When removing soft body, and mesh also doesn't have an armature, + // then remove the bone vertex buffers + mesh->vertex_boneindices.clear(); + mesh->vertex_boneweights.clear(); + mesh->vertex_boneindices2.clear(); + mesh->vertex_boneweights2.clear(); + mesh->CreateRenderData(); + } + editor->RecordEntity(archive, entity); editor->componentsWnd.RefreshEntityTree(); @@ -35,8 +47,34 @@ void SoftBodyWindow::Create(EditorComponent* _editor) infoLabel.SetSize(XMFLOAT2(100, 90)); AddWidget(&infoLabel); - detailSlider.Create(10, 100, 1, 90, "Detail: "); - detailSlider.SetTooltip("Set the detail to keep between simulation and graphics mesh. This will recreate the soft body, vertex changes will be lost.\nLower = less detailed, higher = more detailed."); + resetButton.Create("Reset"); + resetButton.SetTooltip("Set the detail to keep between simulation and graphics mesh.\nLower = less detailed, higher = more detailed."); + resetButton.SetSize(XMFLOAT2(wid, hei)); + resetButton.SetPos(XMFLOAT2(x, y)); + resetButton.OnClick([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + SoftBodyPhysicsComponent* physicscomponent = scene.softbodies.GetComponent(x.entity); + if (physicscomponent == nullptr) + { + // Try also getting it through object's mesh: + ObjectComponent* object = scene.objects.GetComponent(x.entity); + if (object != nullptr) + { + physicscomponent = scene.softbodies.GetComponent(object->meshID); + } + } + if (physicscomponent != nullptr) + { + physicscomponent->Reset(); + } + } + }); + AddWidget(&resetButton); + + detailSlider.Create(0.001f, 1, 1, 1000, "LOD Detail: "); + detailSlider.SetTooltip("Set the detail to keep between simulation and graphics mesh.\nLower = less detailed, higher = more detailed."); detailSlider.SetSize(XMFLOAT2(wid, hei)); detailSlider.SetPos(XMFLOAT2(x, y)); detailSlider.OnSlide([&](wi::gui::EventArgs args) { @@ -61,7 +99,7 @@ void SoftBodyWindow::Create(EditorComponent* _editor) }); AddWidget(&detailSlider); - massSlider.Create(0, 10, 1, 100000, "Mass: "); + massSlider.Create(0, 100, 1, 100000, "Mass: "); massSlider.SetTooltip("Set the mass amount for the physics engine."); massSlider.SetSize(XMFLOAT2(wid, hei)); massSlider.SetPos(XMFLOAT2(x, y)); @@ -140,6 +178,33 @@ void SoftBodyWindow::Create(EditorComponent* _editor) }); AddWidget(&restitutionSlider); + pressureSlider.Create(0, 100000, 0, 100000, "Pressure: "); + pressureSlider.SetTooltip("Set the pressure amount for the physics engine."); + pressureSlider.SetSize(XMFLOAT2(wid, hei)); + pressureSlider.SetPos(XMFLOAT2(x, y += step)); + pressureSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + SoftBodyPhysicsComponent* physicscomponent = scene.softbodies.GetComponent(x.entity); + if (physicscomponent == nullptr) + { + // Try also getting it through object's mesh: + ObjectComponent* object = scene.objects.GetComponent(x.entity); + if (object != nullptr) + { + physicscomponent = scene.softbodies.GetComponent(object->meshID); + } + } + if (physicscomponent != nullptr) + { + physicscomponent->pressure = args.fValue; + physicscomponent->physicsobject = {}; + } + } + }); + AddWidget(&pressureSlider); + vertexRadiusSlider.Create(0, 1, 0, 100000, "Vertex Radius: "); vertexRadiusSlider.SetTooltip("Set how much distance vertices should keep from other physics bodies."); vertexRadiusSlider.SetSize(XMFLOAT2(wid, hei)); @@ -214,6 +279,7 @@ void SoftBodyWindow::SetEntity(Entity entity) massSlider.SetValue(physicscomponent->mass); frictionSlider.SetValue(physicscomponent->friction); restitutionSlider.SetValue(physicscomponent->restitution); + pressureSlider.SetValue(physicscomponent->pressure); vertexRadiusSlider.SetValue(physicscomponent->vertex_radius); windCheckbox.SetCheck(physicscomponent->IsWindEnabled()); } @@ -257,10 +323,12 @@ void SoftBodyWindow::ResizeLayout() }; add_fullwidth(infoLabel); + add_fullwidth(resetButton); add(detailSlider); add(massSlider); add(frictionSlider); add(restitutionSlider); + add(pressureSlider); add(vertexRadiusSlider); add_right(windCheckbox); diff --git a/Editor/SoftBodyWindow.h b/Editor/SoftBodyWindow.h index dba11716a..7d8d6a014 100644 --- a/Editor/SoftBodyWindow.h +++ b/Editor/SoftBodyWindow.h @@ -11,9 +11,11 @@ public: void SetEntity(wi::ecs::Entity entity); wi::gui::Label infoLabel; + wi::gui::Button resetButton; wi::gui::Slider detailSlider; wi::gui::Slider massSlider; wi::gui::Slider frictionSlider; + wi::gui::Slider pressureSlider; wi::gui::Slider restitutionSlider; wi::gui::Slider vertexRadiusSlider; wi::gui::CheckBox windCheckbox; diff --git a/WickedEngine/Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp b/WickedEngine/Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp index 413415f9f..9c85a5698 100644 --- a/WickedEngine/Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp +++ b/WickedEngine/Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp @@ -200,7 +200,7 @@ void SoftBodySharedSettings::CreateConstraints(const VertexAttributes *inVertexA temp_edge.mVertex[1] = inVtx2; temp_edge.mCompliance = 0.5f * (inCompliance1 + inCompliance2); temp_edge.mRestLength = (Vec3(mVertices[inVtx2].mPosition) - Vec3(mVertices[inVtx1].mPosition)).Length(); - JPH_ASSERT(temp_edge.mRestLength > 0.0f); + //JPH_ASSERT(temp_edge.mRestLength > 0.0f); mEdgeConstraints.push_back(temp_edge); } }; diff --git a/WickedEngine/Utility/CMakeLists.txt b/WickedEngine/Utility/CMakeLists.txt index 92795b3d2..7fd7a4dbf 100644 --- a/WickedEngine/Utility/CMakeLists.txt +++ b/WickedEngine/Utility/CMakeLists.txt @@ -36,6 +36,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/h264.h ${CMAKE_CURRENT_SOURCE_DIR}/dds.h ${CMAKE_CURRENT_SOURCE_DIR}/lodepng.h + ${CMAKE_CURRENT_SOURCE_DIR}/meshoptimizer/meshoptimizer.h ) install(FILES ${HEADER_FILES} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/WickedEngine/Utility/") @@ -116,6 +117,21 @@ set (SOURCE_FILES samplerBlueNoiseErrorDistribution_128x128_OptimizedFor_2d2d2d2d_1spp.cpp pugixml.cpp lodepng.cpp + meshoptimizer/allocator.cpp + meshoptimizer/clusterizer.cpp + meshoptimizer/indexcodec.cpp + meshoptimizer/indexgenerator.cpp + meshoptimizer/overdrawanalyzer.cpp + meshoptimizer/overdrawoptimizer.cpp + meshoptimizer/simplifier.cpp + meshoptimizer/spatialorder.cpp + meshoptimizer/stripifier.cpp + meshoptimizer/vcacheanalyzer.cpp + meshoptimizer/vcacheoptimizer.cpp + meshoptimizer/vertexcodec.cpp + meshoptimizer/vertexfilter.cpp + meshoptimizer/vfetchanalyzer.cpp + meshoptimizer/vfetchoptimizer.cpp ) if (WIN32) diff --git a/Editor/meshoptimizer/CMakeLists.txt b/WickedEngine/Utility/meshoptimizer/CMakeLists.txt similarity index 100% rename from Editor/meshoptimizer/CMakeLists.txt rename to WickedEngine/Utility/meshoptimizer/CMakeLists.txt diff --git a/Editor/meshoptimizer/allocator.cpp b/WickedEngine/Utility/meshoptimizer/allocator.cpp similarity index 100% rename from Editor/meshoptimizer/allocator.cpp rename to WickedEngine/Utility/meshoptimizer/allocator.cpp diff --git a/Editor/meshoptimizer/clusterizer.cpp b/WickedEngine/Utility/meshoptimizer/clusterizer.cpp similarity index 100% rename from Editor/meshoptimizer/clusterizer.cpp rename to WickedEngine/Utility/meshoptimizer/clusterizer.cpp diff --git a/Editor/meshoptimizer/indexcodec.cpp b/WickedEngine/Utility/meshoptimizer/indexcodec.cpp similarity index 100% rename from Editor/meshoptimizer/indexcodec.cpp rename to WickedEngine/Utility/meshoptimizer/indexcodec.cpp diff --git a/Editor/meshoptimizer/indexgenerator.cpp b/WickedEngine/Utility/meshoptimizer/indexgenerator.cpp similarity index 100% rename from Editor/meshoptimizer/indexgenerator.cpp rename to WickedEngine/Utility/meshoptimizer/indexgenerator.cpp diff --git a/Editor/meshoptimizer/meshoptimizer.h b/WickedEngine/Utility/meshoptimizer/meshoptimizer.h similarity index 100% rename from Editor/meshoptimizer/meshoptimizer.h rename to WickedEngine/Utility/meshoptimizer/meshoptimizer.h diff --git a/Editor/meshoptimizer/overdrawanalyzer.cpp b/WickedEngine/Utility/meshoptimizer/overdrawanalyzer.cpp similarity index 100% rename from Editor/meshoptimizer/overdrawanalyzer.cpp rename to WickedEngine/Utility/meshoptimizer/overdrawanalyzer.cpp diff --git a/Editor/meshoptimizer/overdrawoptimizer.cpp b/WickedEngine/Utility/meshoptimizer/overdrawoptimizer.cpp similarity index 100% rename from Editor/meshoptimizer/overdrawoptimizer.cpp rename to WickedEngine/Utility/meshoptimizer/overdrawoptimizer.cpp diff --git a/Editor/meshoptimizer/quantization.cpp b/WickedEngine/Utility/meshoptimizer/quantization.cpp similarity index 100% rename from Editor/meshoptimizer/quantization.cpp rename to WickedEngine/Utility/meshoptimizer/quantization.cpp diff --git a/Editor/meshoptimizer/simplifier.cpp b/WickedEngine/Utility/meshoptimizer/simplifier.cpp similarity index 100% rename from Editor/meshoptimizer/simplifier.cpp rename to WickedEngine/Utility/meshoptimizer/simplifier.cpp diff --git a/Editor/meshoptimizer/spatialorder.cpp b/WickedEngine/Utility/meshoptimizer/spatialorder.cpp similarity index 100% rename from Editor/meshoptimizer/spatialorder.cpp rename to WickedEngine/Utility/meshoptimizer/spatialorder.cpp diff --git a/Editor/meshoptimizer/stripifier.cpp b/WickedEngine/Utility/meshoptimizer/stripifier.cpp similarity index 100% rename from Editor/meshoptimizer/stripifier.cpp rename to WickedEngine/Utility/meshoptimizer/stripifier.cpp diff --git a/Editor/meshoptimizer/vcacheanalyzer.cpp b/WickedEngine/Utility/meshoptimizer/vcacheanalyzer.cpp similarity index 100% rename from Editor/meshoptimizer/vcacheanalyzer.cpp rename to WickedEngine/Utility/meshoptimizer/vcacheanalyzer.cpp diff --git a/Editor/meshoptimizer/vcacheoptimizer.cpp b/WickedEngine/Utility/meshoptimizer/vcacheoptimizer.cpp similarity index 100% rename from Editor/meshoptimizer/vcacheoptimizer.cpp rename to WickedEngine/Utility/meshoptimizer/vcacheoptimizer.cpp diff --git a/Editor/meshoptimizer/vertexcodec.cpp b/WickedEngine/Utility/meshoptimizer/vertexcodec.cpp similarity index 100% rename from Editor/meshoptimizer/vertexcodec.cpp rename to WickedEngine/Utility/meshoptimizer/vertexcodec.cpp diff --git a/Editor/meshoptimizer/vertexfilter.cpp b/WickedEngine/Utility/meshoptimizer/vertexfilter.cpp similarity index 100% rename from Editor/meshoptimizer/vertexfilter.cpp rename to WickedEngine/Utility/meshoptimizer/vertexfilter.cpp diff --git a/Editor/meshoptimizer/vfetchanalyzer.cpp b/WickedEngine/Utility/meshoptimizer/vfetchanalyzer.cpp similarity index 100% rename from Editor/meshoptimizer/vfetchanalyzer.cpp rename to WickedEngine/Utility/meshoptimizer/vfetchanalyzer.cpp diff --git a/Editor/meshoptimizer/vfetchoptimizer.cpp b/WickedEngine/Utility/meshoptimizer/vfetchoptimizer.cpp similarity index 100% rename from Editor/meshoptimizer/vfetchoptimizer.cpp rename to WickedEngine/Utility/meshoptimizer/vfetchoptimizer.cpp diff --git a/WickedEngine/WickedEngine_SOURCE.vcxitems b/WickedEngine/WickedEngine_SOURCE.vcxitems index f76a12417..f7c89cf8b 100644 --- a/WickedEngine/WickedEngine_SOURCE.vcxitems +++ b/WickedEngine/WickedEngine_SOURCE.vcxitems @@ -309,6 +309,7 @@ + @@ -594,6 +595,22 @@ + + + + + + + + + + + + + + + + @@ -806,6 +823,7 @@ false false + diff --git a/WickedEngine/WickedEngine_SOURCE.vcxitems.filters b/WickedEngine/WickedEngine_SOURCE.vcxitems.filters index bd7e3d385..ec6fef1f9 100644 --- a/WickedEngine/WickedEngine_SOURCE.vcxitems.filters +++ b/WickedEngine/WickedEngine_SOURCE.vcxitems.filters @@ -55,6 +55,9 @@ {ea901057-ebb6-4eed-ac17-c98111e6c4bd} + + {5634b6ae-7f51-48ed-8a33-a5be8a31a56b} + @@ -1374,6 +1377,9 @@ JOLT + + UTILITY\meshoptimizer + @@ -2138,6 +2144,54 @@ ENGINE\Physics + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + + + UTILITY\meshoptimizer + @@ -2206,5 +2260,8 @@ + + UTILITY\meshoptimizer + \ No newline at end of file diff --git a/WickedEngine/shaders/ShaderInterop_Renderer.h b/WickedEngine/shaders/ShaderInterop_Renderer.h index be191cf52..67df286f5 100644 --- a/WickedEngine/shaders/ShaderInterop_Renderer.h +++ b/WickedEngine/shaders/ShaderInterop_Renderer.h @@ -1327,7 +1327,7 @@ struct SkinningPushConstants uint vertexCount; float3 aabb_max; - float padding; + uint influence_div4; }; struct DebugObjectPushConstants diff --git a/WickedEngine/shaders/skinningCS.hlsl b/WickedEngine/shaders/skinningCS.hlsl index cd41fe623..910f5cbd5 100644 --- a/WickedEngine/shaders/skinningCS.hlsl +++ b/WickedEngine/shaders/skinningCS.hlsl @@ -94,48 +94,50 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID) // Skinning: [branch] - if (push.bone_offset != ~0u) + if (push.vb_bon >= 0 && push.bone_offset != ~0u) { - float4 ind = 0; - float4 wei = 0; - [branch] - if (push.vb_bon >= 0) + float4 p = 0; + min16float3 n = 0; + min16float3 t = 0; + for (uint influence = 0; influence < push.influence_div4; ++influence) { - // Manual type-conversion for bone props: - uint4 ind_wei_u = bindless_buffers[push.vb_bon].Load4(vertexID * sizeof(uint4)); - - ind.x = (ind_wei_u.x >> 0) & 0xFFFF; - ind.y = (ind_wei_u.x >> 16) & 0xFFFF; - ind.z = (ind_wei_u.y >> 0) & 0xFFFF; - ind.w = (ind_wei_u.y >> 16) & 0xFFFF; - - wei.x = float((ind_wei_u.z >> 0) & 0xFFFF) / 65535.0f; - wei.y = float((ind_wei_u.z >> 16) & 0xFFFF) / 65535.0f; - wei.z = float((ind_wei_u.w >> 0) & 0xFFFF) / 65535.0f; - wei.w = float((ind_wei_u.w >> 16) & 0xFFFF) / 65535.0f; - } - if (any(wei)) - { - float4 p = 0; - float3 n = 0; - float3 t = 0; - float weisum = 0; - - for (uint i = 0; ((i < 4) && (weisum < 1.0f)); ++i) + min16uint4 ind = 0; + min16float4 wei = 0; + [branch] + if (push.vb_bon >= 0) { - float4x4 m = skinningbuffer.Load(push.bone_offset + ind[i] * sizeof(ShaderTransform)).GetMatrix(); + // Manual type-conversion for bone props: + uint4 ind_wei_u = bindless_buffers[push.vb_bon].Load4((vertexID * push.influence_div4 + influence) * sizeof(uint4)); - p += mul(m, float4(pos.xyz, 1)) * wei[i]; - n += mul((float3x3)m, nor.xyz) * wei[i]; - t += mul((float3x3)m, tan.xyz) * wei[i]; + ind.x = min16uint(ind_wei_u.x & 0xFFFFF); + ind.y = min16uint(ind_wei_u.y & 0xFFFFF); + ind.z = min16uint(ind_wei_u.z & 0xFFFFF); + ind.w = min16uint(ind_wei_u.w & 0xFFFFF); - weisum += wei[i]; + wei.x = min16float(float((ind_wei_u.x >> 20) & 0xFFF) / 4095.0); + wei.y = min16float(float((ind_wei_u.y >> 20) & 0xFFF) / 4095.0); + wei.z = min16float(float((ind_wei_u.z >> 20) & 0xFFF) / 4095.0); + wei.w = min16float(float((ind_wei_u.w >> 20) & 0xFFF) / 4095.0); } + if (any(wei)) + { + min16float weisum = 0; + + for (min16uint i = 0; ((i < 4) && (weisum < 1.0)); ++i) + { + float4x4 m = skinningbuffer.Load(push.bone_offset + ind[i] * sizeof(ShaderTransform)).GetMatrix(); + min16float weight = wei[i]; - pos.xyz = p.xyz; - nor.xyz = normalize(n.xyz); - tan.xyz = normalize(t.xyz); + p += mul(m, float4(pos.xyz, 1)) * weight; + n += min16float3(mul((min16float3x3)m, nor.xyz)) * weight; + t += min16float3(mul((min16float3x3)m, tan.xyz)) * weight; + weisum += weight; + } + } } + pos.xyz = p.xyz; + nor.xyz = normalize(n.xyz); + tan.xyz = normalize(t.xyz); } // Store data: diff --git a/WickedEngine/wiMath.h b/WickedEngine/wiMath.h index 80b3cdad8..b360774de 100644 --- a/WickedEngine/wiMath.h +++ b/WickedEngine/wiMath.h @@ -74,6 +74,18 @@ namespace wi::math XMStoreFloat(&Distance, length); return Distance; } + inline float Dot(const XMFLOAT2& v1, const XMFLOAT2& v2) + { + XMVECTOR vector1 = XMLoadFloat2(&v1); + XMVECTOR vector2 = XMLoadFloat2(&v2); + return XMVectorGetX(XMVector2Dot(vector1, vector2)); + } + inline float Dot(const XMFLOAT3& v1, const XMFLOAT3& v2) + { + XMVECTOR vector1 = XMLoadFloat3(&v1); + XMVECTOR vector2 = XMLoadFloat3(&v2); + return XMVectorGetX(XMVector3Dot(vector1, vector2)); + } inline float Distance(const XMFLOAT2& v1, const XMFLOAT2& v2) { XMVECTOR vector1 = XMLoadFloat2(&v1); diff --git a/WickedEngine/wiPhysics.h b/WickedEngine/wiPhysics.h index 112a4c155..2594a5177 100644 --- a/WickedEngine/wiPhysics.h +++ b/WickedEngine/wiPhysics.h @@ -97,10 +97,6 @@ namespace wi::physics wi::scene::RigidBodyPhysicsComponent& physicscomponent, const XMFLOAT3& torque ); - void ApplyTorqueImpulse( - wi::scene::RigidBodyPhysicsComponent& physicscomponent, - const XMFLOAT3& torque - ); enum class ActivationState { @@ -116,6 +112,11 @@ namespace wi::physics ActivationState state ); + XMFLOAT3 GetSoftBodyNodePosition( + wi::scene::SoftBodyPhysicsComponent& physicscomponent, + uint32_t physicsIndex + ); + struct RayIntersectionResult { wi::ecs::Entity entity = wi::ecs::INVALID_ENTITY; @@ -124,6 +125,7 @@ namespace wi::physics XMFLOAT3 normal = XMFLOAT3(0, 0, 0); wi::ecs::Entity humanoid_ragdoll_entity = wi::ecs::INVALID_ENTITY; wi::scene::HumanoidComponent::HumanoidBone humanoid_bone = wi::scene::HumanoidComponent::HumanoidBone::Count; + int softbody_triangleID = -1; const void* physicsobject = nullptr; constexpr bool IsValid() const { return entity != wi::ecs::INVALID_ENTITY; } }; diff --git a/WickedEngine/wiPhysics_BindLua.cpp b/WickedEngine/wiPhysics_BindLua.cpp index b5ffa8583..5e2bb52d8 100644 --- a/WickedEngine/wiPhysics_BindLua.cpp +++ b/WickedEngine/wiPhysics_BindLua.cpp @@ -27,7 +27,6 @@ namespace wi::lua lunamethod(Physics_BindLua, ApplyImpulse), lunamethod(Physics_BindLua, ApplyImpulseAt), lunamethod(Physics_BindLua, ApplyTorque), - lunamethod(Physics_BindLua, ApplyTorqueImpulse), lunamethod(Physics_BindLua, SetActivationState), lunamethod(Physics_BindLua, Intersects), lunamethod(Physics_BindLua, PickDrag), @@ -374,32 +373,6 @@ namespace wi::lua wi::lua::SError(L, "ApplyTorque(RigidBodyPhysicsComponent component, Vector torque) not enough arguments!"); return 0; } - int Physics_BindLua::ApplyTorqueImpulse(lua_State* L) - { - int argc = wi::lua::SGetArgCount(L); - if (argc > 1) - { - scene::RigidBodyPhysicsComponent_BindLua* component = Luna::lightcheck(L, 1); - if (component == nullptr) - { - wi::lua::SError(L, "ApplyTorqueImpulse(RigidBodyPhysicsComponent component, Vector torque) first argument is not a RigidBodyPhysicsComponent!"); - return 0; - } - Vector_BindLua* vec = Luna::lightcheck(L, 2); - if (vec == nullptr) - { - wi::lua::SError(L, "ApplyTorqueImpulse(RigidBodyPhysicsComponent component, Vector torque) second argument is not a Vector!"); - return 0; - } - wi::physics::ApplyTorqueImpulse( - *component->component, - *(XMFLOAT3*)vec - ); - } - else - wi::lua::SError(L, "ApplyTorqueImpulse(RigidBodyPhysicsComponent component, Vector torque) not enough arguments!"); - return 0; - } int Physics_BindLua::SetActivationState(lua_State* L) { int argc = wi::lua::SGetArgCount(L); diff --git a/WickedEngine/wiPhysics_BindLua.h b/WickedEngine/wiPhysics_BindLua.h index dc6d13c52..b176c6ad5 100644 --- a/WickedEngine/wiPhysics_BindLua.h +++ b/WickedEngine/wiPhysics_BindLua.h @@ -35,7 +35,6 @@ namespace wi::lua int ApplyImpulse(lua_State* L); int ApplyImpulseAt(lua_State* L); int ApplyTorque(lua_State* L); - int ApplyTorqueImpulse(lua_State* L); int SetActivationState(lua_State* L); int Intersects(lua_State* L); diff --git a/WickedEngine/wiPhysics_Jolt.cpp b/WickedEngine/wiPhysics_Jolt.cpp index f52f9f1ad..199cc623b 100644 --- a/WickedEngine/wiPhysics_Jolt.cpp +++ b/WickedEngine/wiPhysics_Jolt.cpp @@ -54,6 +54,18 @@ using namespace wi::scene; namespace wi::physics { + inline XMMATRIX GetOrientation(XMVECTOR P0, XMVECTOR P1, XMVECTOR P2) + { + XMVECTOR T = P2 - P1; + XMVECTOR B = P1 - P0; + XMVECTOR N = XMVector3Cross(B, T); + T = XMVector3Cross(B, N); + T = XMVector3Normalize(T); + B = XMVector3Normalize(B); + N = XMVector3Normalize(N); + return XMMATRIX(T, B, N, XMVectorSetW(P0, 1)); + } + namespace jolt { bool ENABLED = true; @@ -70,6 +82,7 @@ namespace wi::physics const uint cMaxContactConstraints = 65536; const EMotionQuality cMotionQuality = EMotionQuality::LinearCast; + inline Vec3 cast(const Float3& v) { return Vec3(v.x, v.y, v.z); } inline Vec3 cast(const XMFLOAT3& v) { return Vec3(v.x, v.y, v.z); } inline Quat cast(const XMFLOAT4& v) { return Quat(v.x, v.y, v.z, v.w); } inline Mat44 cast(const XMFLOAT4X4& v) @@ -257,13 +270,22 @@ namespace wi::physics Entity entity = INVALID_ENTITY; SoftBodySharedSettings shared_settings; - Array simulation_normals; - Array position_offsets; - wi::vector vertex_tangents_tmp; + wi::vector inverseBindMatrices; + struct Neighbors + { + uint32_t left = 0; + uint32_t right = 0; + constexpr void set(uint32_t l, uint32_t r) + { + left = l; + right = r; + } + }; + wi::vector physicsNeighbors; ~SoftBody() { - if (physics_scene == nullptr) + if (physics_scene == nullptr || bodyID.IsInvalid()) return; BodyInterface& body_interface = ((PhysicsScene*)physics_scene.get())->physics_system.GetBodyInterface(); // locking version because destructor can be called from any thread body_interface.RemoveBody(bodyID); @@ -463,22 +485,49 @@ namespace wi::physics wi::scene::Scene& scene, Entity entity, wi::scene::SoftBodyPhysicsComponent& physicscomponent, - const wi::scene::MeshComponent& mesh + wi::scene::MeshComponent& mesh ) { SoftBody& physicsobject = GetSoftBody(physicscomponent); physicsobject.physics_scene = scene.physics_scene; physicsobject.entity = entity; - physicscomponent.CreateFromMesh(mesh); PhysicsScene& physics_scene = GetPhysicsScene(scene); physicsobject.shared_settings.SetEmbedded(); physicsobject.shared_settings.mVertexRadius = physicscomponent.vertex_radius; + physicscomponent.CreateFromMesh(mesh); + if (physicscomponent.physicsIndices.empty()) + { + wi::backlog::post("AddSoftBody failed: physics faces are empty, this means generating physics mesh has failed, try to change settings.", wi::backlog::LogLevel::Error); + return; + } + const size_t vertexCount = physicscomponent.physicsToGraphicsVertexMapping.size(); + + physicsobject.physicsNeighbors.resize(vertexCount); + + for (size_t i = 0; i < physicscomponent.physicsIndices.size(); i += 3) + { + const uint32_t physicsInd0 = physicscomponent.physicsIndices[i + 0]; + const uint32_t physicsInd1 = physicscomponent.physicsIndices[i + 1]; + const uint32_t physicsInd2 = physicscomponent.physicsIndices[i + 2]; + + SoftBodySharedSettings::Face& face = physicsobject.shared_settings.mFaces.emplace_back(); + face.mVertex[0] = physicsInd0; + face.mVertex[2] = physicsInd1; + face.mVertex[1] = physicsInd2; + + physicsobject.physicsNeighbors[physicsInd0].set(physicsInd2, physicsInd1); + physicsobject.physicsNeighbors[physicsInd1].set(physicsInd0, physicsInd2); + physicsobject.physicsNeighbors[physicsInd2].set(physicsInd1, physicsInd0); + } + const XMMATRIX worldMatrix = XMLoadFloat4x4(&physicscomponent.worldMatrix); - const size_t vertexCount = physicscomponent.physicsToGraphicsVertexMapping.size(); physicsobject.shared_settings.mVertices.resize(vertexCount); + physicsobject.inverseBindMatrices.resize(vertexCount); + physicscomponent.boneData.resize(vertexCount); + const float distributed_mass = physicscomponent.mass / vertexCount; for (size_t i = 0; i < vertexCount; ++i) { uint32_t graphicsInd = physicscomponent.physicsToGraphicsVertexMapping[i]; @@ -489,24 +538,16 @@ namespace wi::physics XMStoreFloat3(&position, P); physicsobject.shared_settings.mVertices[i].mPosition = Float3(position.x, position.y, position.z); - float weight = physicscomponent.weights[i]; - physicsobject.shared_settings.mVertices[i].mInvMass = weight == 0 ? 0 : 1.0f / weight; - } + float weight = physicscomponent.weights[graphicsInd] * distributed_mass; + physicsobject.shared_settings.mVertices[i].mInvMass = weight == 0 ? 0 : 1.0f / (1.0f + weight); - uint32_t first_subset = 0; - uint32_t last_subset = 0; - mesh.GetLODSubsetRange(0, first_subset, last_subset); - for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex) - { - const MeshComponent::MeshSubset& subset = mesh.subsets[subsetIndex]; - const uint32_t* indices = mesh.indices.data() + subset.indexOffset; - for (uint32_t i = 0; i < subset.indexCount; i += 3) - { - SoftBodySharedSettings::Face& face = physicsobject.shared_settings.mFaces.emplace_back(); - face.mVertex[0] = physicscomponent.graphicsToPhysicsVertexMapping[indices[i + 0]]; - face.mVertex[2] = physicscomponent.graphicsToPhysicsVertexMapping[indices[i + 1]]; - face.mVertex[1] = physicscomponent.graphicsToPhysicsVertexMapping[indices[i + 2]]; - } + // The soft body node will have a bind matrix similar to an armature bone: + XMVECTOR P0 = XMLoadFloat3(&mesh.vertex_positions[graphicsInd]); + XMVECTOR P1 = XMLoadFloat3(&mesh.vertex_positions[physicscomponent.physicsToGraphicsVertexMapping[physicsobject.physicsNeighbors[i].left]]); + XMVECTOR P2 = XMLoadFloat3(&mesh.vertex_positions[physicscomponent.physicsToGraphicsVertexMapping[physicsobject.physicsNeighbors[i].right]]); + XMMATRIX B = GetOrientation(P0, P1, P2); + B = XMMatrixInverse(nullptr, B); + XMStoreFloat4x4(&physicsobject.inverseBindMatrices[i], B); } SoftBodySharedSettings::VertexAttributes vertexAttributes = { 1.0e-5f, 1.0e-5f, 1.0e-5f }; @@ -517,6 +558,7 @@ namespace wi::physics settings.mNumIterations = (uint32)softbodyIterationCount; settings.mFriction = physicscomponent.friction; settings.mRestitution = physicscomponent.restitution; + settings.mPressure = physicscomponent.pressure; settings.mUpdatePosition = false; settings.mAllowSleeping = !physicscomponent.IsDisableDeactivation(); settings.mUserData = (uint64_t)&physicsobject; @@ -530,23 +572,6 @@ namespace wi::physics wi::backlog::post("AddSoftBody failed: body couldn't be created! This could mean that there are too many physics objects.", wi::backlog::LogLevel::Error); return; } - - physicsobject.simulation_normals.resize(physicsobject.shared_settings.mVertices.size()); - - physicsobject.vertex_tangents_tmp.resize(mesh.vertex_tangents.size()); - physicsobject.position_offsets.resize(mesh.vertex_positions.size()); - for (size_t i = 0; i < mesh.vertex_positions.size(); ++i) - { - XMFLOAT3 position = mesh.vertex_positions[i]; - XMVECTOR P = XMLoadFloat3(&position); - P = XMVector3Transform(P, worldMatrix); - XMStoreFloat3(&position, P); - - uint32_t physicsInd = physicscomponent.graphicsToPhysicsVertexMapping[i]; - Float3 physicsPosF3 = physicsobject.shared_settings.mVertices[physicsInd].mPosition; - Vec3 physicsPos(physicsPosF3.x, physicsPosF3.y, physicsPosF3.z); - physicsobject.position_offsets[i] = cast(position) - physicsPos; - } } struct Ragdoll @@ -1257,11 +1282,6 @@ namespace wi::physics const ArmatureComponent* armature = mesh->IsSkinned() ? scene.armatures.GetComponent(mesh->armatureID) : nullptr; mesh->SetDynamic(true); - if (physicscomponent._flags & SoftBodyPhysicsComponent::FORCE_RESET) - { - physicscomponent._flags &= ~SoftBodyPhysicsComponent::FORCE_RESET; - physicscomponent.physicsobject = nullptr; - } if (physicscomponent._flags & SoftBodyPhysicsComponent::SAFE_TO_REGISTER && physicscomponent.physicsobject == nullptr) { AddSoftBody(scene, entity, physicscomponent, *mesh); @@ -1297,13 +1317,13 @@ namespace wi::physics SoftBodyMotionProperties* motion = (SoftBodyMotionProperties*)body.GetMotionProperties(); // System controls zero weight soft body nodes: - for (size_t ind = 0; ind < physicscomponent.weights.size(); ++ind) + for (size_t ind = 0; ind < physicscomponent.physicsToGraphicsVertexMapping.size(); ++ind) { - float weight = physicscomponent.weights[ind]; + uint32_t graphicsInd = physicscomponent.physicsToGraphicsVertexMapping[ind]; + float weight = physicscomponent.weights[graphicsInd]; if (weight == 0) { - uint32_t graphicsInd = physicscomponent.physicsToGraphicsVertexMapping[ind]; XMFLOAT3 position = mesh->vertex_positions[graphicsInd]; XMVECTOR P = armature == nullptr ? XMLoadFloat3(&position) : wi::scene::SkinVertex(*mesh, *armature, graphicsInd); P = XMVector3Transform(P, worldMatrix); @@ -1529,118 +1549,34 @@ namespace wi::physics const SoftBodyMotionProperties* motion = (const SoftBodyMotionProperties*)body.GetMotionProperties(); const Array& soft_vertices = motion->GetVertices(); - const Array& soft_faces = motion->GetFaces(); - // Recompute normals: (Note: normalization will happen on final storage) - for (auto& n : physicsobject.simulation_normals) + // Update bone matrices from physics vertices: + for (size_t i = 0; i < soft_vertices.size(); ++i) { - n = Vec3::sZero(); - } - for (auto& f : soft_faces) - { - Vec3 x1 = soft_vertices[f.mVertex[0]].mPosition; - Vec3 x2 = soft_vertices[f.mVertex[1]].mPosition; - Vec3 x3 = soft_vertices[f.mVertex[2]].mPosition; - Vec3 n = (x2 - x1).Cross(x3 - x1); - physicsobject.simulation_normals[f.mVertex[0]] += n; - physicsobject.simulation_normals[f.mVertex[1]] += n; - physicsobject.simulation_normals[f.mVertex[2]] += n; + XMFLOAT3 p0 = cast(soft_vertices[i].mPosition); + XMFLOAT3 p1 = cast(soft_vertices[physicsobject.physicsNeighbors[i].left].mPosition); + XMFLOAT3 p2 = cast(soft_vertices[physicsobject.physicsNeighbors[i].right].mPosition); + XMVECTOR P0 = XMLoadFloat3(&p0); + XMVECTOR P1 = XMLoadFloat3(&p1); + XMVECTOR P2 = XMLoadFloat3(&p2); + XMMATRIX W = GetOrientation(P0, P1, P2); + XMMATRIX B = XMLoadFloat4x4(&physicsobject.inverseBindMatrices[i]); + XMMATRIX M = B * W; + XMFLOAT4X4 boneData; + XMStoreFloat4x4(&boneData, M); + physicscomponent.boneData[i].Create(boneData); + +#if 0 + scene.locker.lock(); + wi::renderer::DrawAxis(W, 0.05f, false); + scene.locker.unlock(); +#endif + + physicscomponent.aabb._min = wi::math::Min(physicscomponent.aabb._min, p0); + physicscomponent.aabb._max = wi::math::Max(physicscomponent.aabb._max, p0); } - // Soft body simulation nodes will update graphics mesh: - for (size_t ind = 0; ind < mesh->vertex_positions.size(); ++ind) - { - const Vec3& offset = physicsobject.position_offsets[ind]; - uint32_t physicsInd = physicscomponent.graphicsToPhysicsVertexMapping[ind]; - - const XMFLOAT3 position = cast(soft_vertices[physicsInd].mPosition + offset); - const XMFLOAT3 normal = cast(physicsobject.simulation_normals[physicsInd]); - - physicscomponent.vertex_positions_simulation[ind].FromFULL(position); - physicscomponent.vertex_normals_simulation[ind].FromFULL(normal); // normalizes internally - - physicscomponent.aabb._min = wi::math::Min(physicscomponent.aabb._min, position); - physicscomponent.aabb._max = wi::math::Max(physicscomponent.aabb._max, position); - } - - // Update tangent vectors: - if (!mesh->vertex_uvset_0.empty() && !physicscomponent.vertex_normals_simulation.empty()) - { - uint32_t first_subset = 0; - uint32_t last_subset = 0; - mesh->GetLODSubsetRange(0, first_subset, last_subset); - for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex) - { - const MeshComponent::MeshSubset& subset = mesh->subsets[subsetIndex]; - for (size_t i = 0; i < subset.indexCount; i += 3) - { - const uint32_t i0 = mesh->indices[i + 0]; - const uint32_t i1 = mesh->indices[i + 1]; - const uint32_t i2 = mesh->indices[i + 2]; - - const XMFLOAT3 v0 = physicscomponent.vertex_positions_simulation[i0].GetPOS(); - const XMFLOAT3 v1 = physicscomponent.vertex_positions_simulation[i1].GetPOS(); - const XMFLOAT3 v2 = physicscomponent.vertex_positions_simulation[i2].GetPOS(); - - const XMFLOAT2 u0 = mesh->vertex_uvset_0[i0]; - const XMFLOAT2 u1 = mesh->vertex_uvset_0[i1]; - const XMFLOAT2 u2 = mesh->vertex_uvset_0[i2]; - - const XMVECTOR nor0 = physicscomponent.vertex_normals_simulation[i0].LoadNOR(); - const XMVECTOR nor1 = physicscomponent.vertex_normals_simulation[i1].LoadNOR(); - const XMVECTOR nor2 = physicscomponent.vertex_normals_simulation[i2].LoadNOR(); - - const XMVECTOR facenormal = XMVector3Normalize(XMVectorAdd(XMVectorAdd(nor0, nor1), nor2)); - - const float x1 = v1.x - v0.x; - const float x2 = v2.x - v0.x; - const float y1 = v1.y - v0.y; - const float y2 = v2.y - v0.y; - const float z1 = v1.z - v0.z; - const float z2 = v2.z - v0.z; - - const float s1 = u1.x - u0.x; - const float s2 = u2.x - u0.x; - const float t1 = u1.y - u0.y; - const float t2 = u2.y - u0.y; - - const float r = 1.0f / (s1 * t2 - s2 * t1); - const XMVECTOR sdir = XMVectorSet((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, - (t2 * z1 - t1 * z2) * r, 0); - const XMVECTOR tdir = XMVectorSet((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, - (s1 * z2 - s2 * z1) * r, 0); - - XMVECTOR tangent; - tangent = XMVector3Normalize(XMVectorSubtract(sdir, XMVectorMultiply(facenormal, XMVector3Dot(facenormal, sdir)))); - float sign = XMVectorGetX(XMVector3Dot(XMVector3Cross(tangent, facenormal), tdir)) < 0.0f ? -1.0f : 1.0f; - - XMFLOAT3 t; - XMStoreFloat3(&t, tangent); - - physicsobject.vertex_tangents_tmp[i0].x += t.x; - physicsobject.vertex_tangents_tmp[i0].y += t.y; - physicsobject.vertex_tangents_tmp[i0].z += t.z; - physicsobject.vertex_tangents_tmp[i0].w = sign; - - physicsobject.vertex_tangents_tmp[i1].x += t.x; - physicsobject.vertex_tangents_tmp[i1].y += t.y; - physicsobject.vertex_tangents_tmp[i1].z += t.z; - physicsobject.vertex_tangents_tmp[i1].w = sign; - - physicsobject.vertex_tangents_tmp[i2].x += t.x; - physicsobject.vertex_tangents_tmp[i2].y += t.y; - physicsobject.vertex_tangents_tmp[i2].z += t.z; - physicsobject.vertex_tangents_tmp[i2].w = sign; - } - } - - for (size_t i = 0; i < physicscomponent.vertex_tangents_simulation.size(); ++i) - { - physicscomponent.vertex_tangents_simulation[i].FromFULL(physicsobject.vertex_tangents_tmp[i]); - } - } - - mesh->aabb = physicscomponent.aabb; + scene.skinningAllocator.fetch_add(uint32_t(physicscomponent.boneData.size() * sizeof(ShaderTransform))); }); wi::jobsystem::Dispatch(ctx, (uint32_t)scene.humanoids.GetCount(), 1, [&scene, &physics_scene](wi::jobsystem::JobArgs args) { @@ -1709,6 +1645,7 @@ namespace wi::physics settings.mDrawCenterOfMassTransform = false; settings.mDrawShape = true; settings.mDrawSoftBodyVertices = true; + settings.mDrawSoftBodyEdgeConstraints = true; settings.mDrawShapeWireframe = true; settings.mDrawShapeColor = BodyManager::EShapeColor::ShapeTypeColor; physics_scene.physics_system.DrawBodies(settings, &debug_renderer); @@ -1937,13 +1874,6 @@ namespace wi::physics body_interface.AddTorque(physicsobject.bodyID, cast(torque), EActivation::Activate); } } - void ApplyTorqueImpulse( - wi::scene::RigidBodyPhysicsComponent& physicscomponent, - const XMFLOAT3& torque - ) - { - ApplyTorque(physicscomponent, torque); - } void SetActivationState( wi::scene::RigidBodyPhysicsComponent& physicscomponent, @@ -1986,6 +1916,26 @@ namespace wi::physics } } + XMFLOAT3 GetSoftBodyNodePosition( + wi::scene::SoftBodyPhysicsComponent& physicscomponent, + uint32_t physicsIndex + ) + { + SoftBody& physicsobject = GetSoftBody(physicscomponent); + if (physicsobject.bodyID.IsInvalid() || physicsobject.physics_scene == nullptr) + return XMFLOAT3(0, 0, 0); + + const PhysicsScene& physics_scene = *(const PhysicsScene*)physicsobject.physics_scene.get(); + BodyLockRead lock(physics_scene.physics_system.GetBodyLockInterfaceNoLock(), physicsobject.bodyID); + if (!lock.Succeeded()) + return XMFLOAT3(0, 0, 0); + + const Body& body = lock.GetBody(); + const SoftBodyMotionProperties* motion = (const SoftBodyMotionProperties*)body.GetMotionProperties(); + const Array& soft_vertices = motion->GetVertices(); + return cast(soft_vertices[physicsIndex].mPosition); + } + RayIntersectionResult Intersects( const wi::scene::Scene& scene, wi::primitive::Ray ray @@ -2010,7 +1960,7 @@ namespace wi::physics inray.mDirection = inray.mDirection * range; RayCastSettings settings; - settings.mBackFaceMode = EBackFaceMode::IgnoreBackFaces; + settings.mBackFaceMode = EBackFaceMode::CollideWithBackFaces; settings.mTreatConvexAsSolid = false; ClosestHitCollisionCollector collector; @@ -2052,6 +2002,8 @@ namespace wi::physics result.position_local = cast(position_local); result.normal = cast(normal); result.physicsobject = &body; + const SoftBodyShape* shape = (const SoftBodyShape*)body.GetShape(); + result.softbody_triangleID = (int)shape->GetFaceIndex(collector.mHit.mSubShapeID2); } return result; @@ -2064,15 +2016,28 @@ namespace wi::physics float pick_distance = 0; Body* bodyA = nullptr; Body* bodyB = nullptr; + int softBodyVertex = -1; + Vec3 softBodyVertexOffset = Vec3::sZero(); + float prevInvMass = 0; ~PickDragOperation_Jolt() { - if (physics_scene == nullptr) + if (physics_scene == nullptr || bodyB == nullptr) return; PhysicsScene& physics_scene = *((PhysicsScene*)this->physics_scene.get()); - physics_scene.physics_system.RemoveConstraint(constraint); BodyInterface& body_interface = physics_scene.physics_system.GetBodyInterfaceNoLock(); - body_interface.RemoveBody(bodyA->GetID()); - body_interface.DestroyBody(bodyA->GetID()); + if (bodyA != nullptr) + { + // Rigid body constraint removal + physics_scene.physics_system.RemoveConstraint(constraint); + body_interface.RemoveBody(bodyA->GetID()); + body_interface.DestroyBody(bodyA->GetID()); + } + if (bodyB->IsSoftBody() && softBodyVertex >= 0) + { + SoftBodyMotionProperties* motion = (SoftBodyMotionProperties*)bodyB->GetMotionProperties(); + SoftBodyMotionProperties::Vertex& node = motion->GetVertex((uint)softBodyVertex); + node.mInvMass = prevInvMass; + } } }; void PickDrag( @@ -2092,8 +2057,19 @@ namespace wi::physics PickDragOperation_Jolt* internal_state = (PickDragOperation_Jolt*)op.internal_state.get(); const float dist = internal_state->pick_distance; Vec3 pos = Vec3(ray.origin.x + ray.direction.x * dist, ray.origin.y + ray.direction.y * dist, ray.origin.z + ray.direction.z * dist); - //body_interface.SetPosition(internal_state->bodyA->GetID(), pos, EActivation::Activate); - body_interface.MoveKinematic(internal_state->bodyA->GetID(), pos, Quat::sIdentity(), physics_scene.GetKinematicDT(scene.dt)); + if (internal_state->softBodyVertex >= 0) + { + // Soft body vertex: + SoftBodyMotionProperties* motion = (SoftBodyMotionProperties*)internal_state->bodyB->GetMotionProperties(); + SoftBodyMotionProperties::Vertex& node = motion->GetVertex((uint)internal_state->softBodyVertex); + node.mPosition = pos + internal_state->softBodyVertexOffset; + } + else + { + // Rigid body constraint: + //body_interface.SetPosition(internal_state->bodyA->GetID(), pos, EActivation::Activate); + body_interface.MoveKinematic(internal_state->bodyA->GetID(), pos, Quat::sIdentity(), physics_scene.GetKinematicDT(scene.dt)); + } } else { @@ -2102,31 +2078,41 @@ namespace wi::physics if (!result.IsValid()) return; Body* body = (Body*)result.physicsobject; - if (!body->IsRigidBody()) - return; auto internal_state = std::make_shared(); internal_state->physics_scene = scene.physics_scene; internal_state->pick_distance = wi::math::Distance(ray.origin, result.position); internal_state->bodyB = body; - Vec3 pos = cast(result.position); + if (body->IsRigidBody()) + { + Vec3 pos = cast(result.position); - internal_state->bodyA = body_interface.CreateBody(BodyCreationSettings(new SphereShape(0.01f), pos, Quat::sIdentity(), EMotionType::Kinematic, Layers::MOVING)); - body_interface.AddBody(internal_state->bodyA->GetID(), EActivation::Activate); + internal_state->bodyA = body_interface.CreateBody(BodyCreationSettings(new SphereShape(0.01f), pos, Quat::sIdentity(), EMotionType::Kinematic, Layers::MOVING)); + body_interface.AddBody(internal_state->bodyA->GetID(), EActivation::Activate); #if 0 - DistanceConstraintSettings settings; - settings.SetEmbedded(); - settings.mPoint1 = settings.mPoint2 = pos; + DistanceConstraintSettings settings; + settings.SetEmbedded(); + settings.mPoint1 = settings.mPoint2 = pos; #else - FixedConstraintSettings settings; - settings.SetEmbedded(); - settings.mAutoDetectPoint = true; + FixedConstraintSettings settings; + settings.SetEmbedded(); + settings.mAutoDetectPoint = true; #endif - internal_state->constraint = settings.Create(*internal_state->bodyA, *internal_state->bodyB); - physics_scene.physics_system.AddConstraint(internal_state->constraint); + internal_state->constraint = settings.Create(*internal_state->bodyA, *internal_state->bodyB); + physics_scene.physics_system.AddConstraint(internal_state->constraint); + } + else if (body->IsSoftBody()) + { + SoftBodyMotionProperties* motion = (SoftBodyMotionProperties*)body->GetMotionProperties(); + internal_state->softBodyVertex = (int)motion->GetFace((uint)result.softbody_triangleID).mVertex[0]; + SoftBodyVertex& vertex = motion->GetVertex((uint)internal_state->softBodyVertex); + internal_state->prevInvMass = vertex.mInvMass; + vertex.mInvMass = 0; + internal_state->softBodyVertexOffset = vertex.mPosition - cast(result.position); + } op.internal_state = internal_state; } diff --git a/WickedEngine/wiRenderPath2D.cpp b/WickedEngine/wiRenderPath2D.cpp index 160c00d27..331a205df 100644 --- a/WickedEngine/wiRenderPath2D.cpp +++ b/WickedEngine/wiRenderPath2D.cpp @@ -218,7 +218,7 @@ namespace wi RenderPassImage::RenderTarget( &rtFinal_MSAA, RenderPassImage::LoadOp::CLEAR, - RenderPassImage::StoreOp::DONTCARE, + RenderPassImage::StoreOp::STORE, ResourceState::RENDERTARGET, ResourceState::RENDERTARGET ), @@ -252,7 +252,7 @@ namespace wi RenderPassImage::RenderTarget( &rtFinal_MSAA, RenderPassImage::LoadOp::CLEAR, - RenderPassImage::StoreOp::DONTCARE, + RenderPassImage::StoreOp::STORE, ResourceState::RENDERTARGET, ResourceState::RENDERTARGET ), diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index d67124d0c..322590b9e 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -2854,7 +2854,7 @@ void RenderMeshes( const PipelineState* pso = nullptr; const PipelineState* pso_backside = nullptr; // only when separate backside rendering is required (transparent doublesided) { - if (IsWireRender()) + if (IsWireRender() && renderPass != RENDERPASS_ENVMAPCAPTURE) { switch (renderPass) { @@ -4333,40 +4333,6 @@ void UpdateRenderData( barrier_stack.push_back(GPUBarrier::Buffer(&vis.scene->voxelgrid_gpu, ResourceState::COPY_DST, ResourceState::SHADER_RESOURCE)); } - // Soft body updates: - for (size_t i = 0; i < vis.scene->softbodies.GetCount(); ++i) - { - Entity entity = vis.scene->softbodies.GetEntity(i); - const SoftBodyPhysicsComponent& softbody = vis.scene->softbodies[i]; - - const MeshComponent* mesh = vis.scene->meshes.GetComponent(entity); - if (mesh != nullptr && mesh->streamoutBuffer.IsValid() && softbody.HasVertices()) - { - GraphicsDevice::GPUAllocation allocation = device->AllocateGPU(mesh->so_pos.size + mesh->so_nor.size + mesh->so_tan.size, cmd); - uint8_t* dst = (uint8_t*)allocation.data; - uint64_t offset = allocation.offset; - std::memcpy(dst, softbody.vertex_positions_simulation.data(), mesh->so_pos.size); - device->CopyBuffer(&mesh->streamoutBuffer, mesh->so_pos.offset, &allocation.buffer, offset, mesh->so_pos.size, cmd); - dst += mesh->so_pos.size; - offset += mesh->so_pos.size; - if (!softbody.vertex_normals_simulation.empty()) - { - std::memcpy(dst, softbody.vertex_normals_simulation.data(), mesh->so_nor.size); - device->CopyBuffer(&mesh->streamoutBuffer, mesh->so_nor.offset, &allocation.buffer, offset, mesh->so_nor.size, cmd); - dst += mesh->so_nor.size; - offset += mesh->so_nor.size; - } - if (!softbody.vertex_tangents_simulation.empty()) - { - std::memcpy(dst, softbody.vertex_tangents_simulation.data(), mesh->so_tan.size); - device->CopyBuffer(&mesh->streamoutBuffer, mesh->so_tan.offset, &allocation.buffer, offset, mesh->so_tan.size, cmd); - dst += mesh->so_tan.size; - offset += mesh->so_tan.size; - } - barrier_stack.push_back(GPUBarrier::Buffer(&mesh->streamoutBuffer, ResourceState::COPY_DST, ResourceState::SHADER_RESOURCE)); - } - } - barrier_stack.push_back(GPUBarrier::Image(&textures[TEXTYPE_3D_WIND], textures[TEXTYPE_3D_WIND].desc.layout, ResourceState::UNORDERED_ACCESS)); barrier_stack.push_back(GPUBarrier::Image(&textures[TEXTYPE_3D_WIND_PREV], textures[TEXTYPE_3D_WIND_PREV].desc.layout, ResourceState::UNORDERED_ACCESS)); @@ -4424,18 +4390,10 @@ void UpdateRenderData( const MeshComponent& mesh = vis.scene->meshes[i]; if ( - (mesh.IsSkinned() || !mesh.morph_targets.empty()) && // Note: even if all morphs are inactive, the skinning must be done + (mesh.IsSkinned() || !mesh.morph_targets.empty() || mesh.vb_bon.IsValid()) && // Note: even if all morphs are inactive, the skinning must be done mesh.streamoutBuffer.IsValid() ) { - const SoftBodyPhysicsComponent* softbody = vis.scene->softbodies.GetComponent(entity); - if (softbody != nullptr && softbody->physicsobject != nullptr) - { - // If soft body simulation is active, don't perform skinning. - // (Soft body animated vertices are skinned in simulation phase by physics system) - continue; - } - SkinningPushConstants push; push.vb_pos_wind = mesh.vb_pos_wind.descriptor_srv; push.vb_nor = mesh.vb_nor.descriptor_srv; @@ -4451,7 +4409,15 @@ void UpdateRenderData( } else { - push.bone_offset = ~0u; + const SoftBodyPhysicsComponent* softbody = vis.scene->softbodies.GetComponent(entity); + if (softbody != nullptr) + { + push.bone_offset = softbody->gpuBoneOffset; + } + else + { + push.bone_offset = ~0u; + } } push.vb_bon = mesh.vb_bon.descriptor_srv; if (mesh.active_morph_count > 0) @@ -4477,6 +4443,7 @@ void UpdateRenderData( push.aabb_max = {}; } push.vertexCount = (uint)mesh.vertex_positions.size(); + push.influence_div4 = mesh.GetBoneInfluenceDiv4(); device->PushConstants(&push, sizeof(push), cmd); device->Dispatch(((uint32_t)mesh.vertex_positions.size() + 63) / 64, 1, 1, cmd); @@ -17292,6 +17259,34 @@ void DrawLine(const RenderableLine2D& line) { renderableLines2D.push_back(line); } +void DrawAxis(const XMMATRIX& matrix, float size, bool depth) +{ + RenderableLine line; + + // X + line.start = XMFLOAT3(0, 0, 0); + line.end = XMFLOAT3(size, 0, 0); + line.color_start = line.color_end = XMFLOAT4(1, 0.2f, 0.2f, 1); + XMStoreFloat3(&line.start, XMVector3Transform(XMLoadFloat3(&line.start), matrix)); + XMStoreFloat3(&line.end, XMVector3Transform(XMLoadFloat3(&line.end), matrix)); + DrawLine(line, depth); + + // Y + line.start = XMFLOAT3(0, 0, 0); + line.end = XMFLOAT3(0, size, 0); + line.color_start = line.color_end = XMFLOAT4(0.2f, 1, 0.2f, 1); + XMStoreFloat3(&line.start, XMVector3Transform(XMLoadFloat3(&line.start), matrix)); + XMStoreFloat3(&line.end, XMVector3Transform(XMLoadFloat3(&line.end), matrix)); + DrawLine(line, depth); + + // Z + line.start = XMFLOAT3(0, 0, 0); + line.end = XMFLOAT3(0, 0, size); + line.color_start = line.color_end = XMFLOAT4(0.2f, 0.2f, 1, 1); + XMStoreFloat3(&line.start, XMVector3Transform(XMLoadFloat3(&line.start), matrix)); + XMStoreFloat3(&line.end, XMVector3Transform(XMLoadFloat3(&line.end), matrix)); + DrawLine(line, depth); +} void DrawPoint(const RenderablePoint& point, bool depth) { if(depth) diff --git a/WickedEngine/wiRenderer.h b/WickedEngine/wiRenderer.h index 51561c0ba..348aa5a2a 100644 --- a/WickedEngine/wiRenderer.h +++ b/WickedEngine/wiRenderer.h @@ -1120,6 +1120,8 @@ namespace wi::renderer // Add 2D line to render in the next frame. It will be rendered in DrawDebugWorld() in screen space void DrawLine(const RenderableLine2D& line); + void DrawAxis(const XMMATRIX& matrix, float size, bool depth = false); + struct RenderablePoint { XMFLOAT3 position = XMFLOAT3(0, 0, 0); diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index 5643b87e7..f7941c7d9 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -3586,6 +3586,13 @@ namespace wi::scene armature.aabb = AABB(_min, _max); }); + wi::jobsystem::Dispatch(ctx, (uint32_t)softbodies.GetCount(), 1, [&](wi::jobsystem::JobArgs args) { + SoftBodyPhysicsComponent& softbody = softbodies[args.jobIndex]; + const uint32_t dataSize = uint32_t(softbody.boneData.size() * sizeof(ShaderTransform)); + softbody.gpuBoneOffset = skinningAllocator.fetch_add(dataSize); + ShaderTransform* gpu_dst = (ShaderTransform*)((uint8_t*)skinningDataMapped + softbody.gpuBoneOffset); + std::memcpy(gpu_dst, softbody.boneData.data(), dataSize); + }); } void Scene::RunMeshUpdateSystem(wi::jobsystem::context& ctx) { @@ -3594,15 +3601,6 @@ namespace wi::scene Entity entity = meshes.GetEntity(args.jobIndex); MeshComponent& mesh = meshes[args.jobIndex]; - if (!mesh.streamoutBuffer.IsValid()) - { - const SoftBodyPhysicsComponent* softbody = softbodies.GetComponent(entity); - if (softbody != nullptr && wi::physics::IsEnabled()) - { - mesh.CreateStreamoutRenderData(); - } - } - if (mesh.so_pos.IsValid() && mesh.so_pre.IsValid()) { std::swap(mesh.so_pos, mesh.so_pre); @@ -4069,7 +4067,7 @@ namespace wi::scene } SoftBodyPhysicsComponent* softbody = softbodies.GetComponent(object.meshID); - if (softbody != nullptr && mesh.streamoutBuffer.IsValid()) + if (softbody != nullptr) { if (wi::physics::IsEnabled()) { @@ -4078,18 +4076,16 @@ namespace wi::scene // soft body manipulated with the object matrix softbody->worldMatrix = transform.world; - - if (softbody->graphicsToPhysicsVertexMapping.empty()) - { - softbody->CreateFromMesh(mesh); - } } - // simulation aabb will be used for soft bodies - aabb = softbody->aabb; + if (softbody->physicsobject != nullptr) + { + // simulation aabb will be used for soft bodies + aabb = softbody->aabb; - // soft bodies have no transform, their vertices are simulated in world space - W = XMMatrixIdentity(); + // soft bodies have no transform, their vertices are simulated in world space + W = XMMatrixIdentity(); + } } object.center = aabb.getCenter(); @@ -5118,28 +5114,23 @@ namespace wi::scene XMVECTOR p0; XMVECTOR p1; XMVECTOR p2; - - const bool softbody_active = softbody != nullptr && softbody->HasVertices(); - if (softbody_active) + if (softbody != nullptr && !softbody->boneData.empty()) { - p0 = softbody->vertex_positions_simulation[i0].LoadPOS(); - p1 = softbody->vertex_positions_simulation[i1].LoadPOS(); - p2 = softbody->vertex_positions_simulation[i2].LoadPOS(); + p0 = SkinVertex(*mesh, *softbody, i0); + p1 = SkinVertex(*mesh, *softbody, i1); + p2 = SkinVertex(*mesh, *softbody, i2); + } + else if (armature != nullptr && !armature->boneData.empty()) + { + p0 = SkinVertex(*mesh, *armature, i0); + p1 = SkinVertex(*mesh, *armature, i1); + p2 = SkinVertex(*mesh, *armature, i2); } else { - if (armature == nullptr || armature->boneData.empty()) - { - p0 = XMLoadFloat3(&mesh->vertex_positions[i0]); - p1 = XMLoadFloat3(&mesh->vertex_positions[i1]); - p2 = XMLoadFloat3(&mesh->vertex_positions[i2]); - } - else - { - p0 = SkinVertex(*mesh, *armature, i0); - p1 = SkinVertex(*mesh, *armature, i1); - p2 = SkinVertex(*mesh, *armature, i2); - } + p0 = XMLoadFloat3(&mesh->vertex_positions[i0]); + p1 = XMLoadFloat3(&mesh->vertex_positions[i1]); + p2 = XMLoadFloat3(&mesh->vertex_positions[i2]); } float distance; @@ -5337,28 +5328,23 @@ namespace wi::scene XMVECTOR p0; XMVECTOR p1; XMVECTOR p2; - - const bool softbody_active = softbody != nullptr && softbody->HasVertices(); - if (softbody_active) + if (softbody != nullptr && !softbody->boneData.empty()) { - p0 = softbody->vertex_positions_simulation[i0].LoadPOS(); - p1 = softbody->vertex_positions_simulation[i1].LoadPOS(); - p2 = softbody->vertex_positions_simulation[i2].LoadPOS(); + p0 = SkinVertex(*mesh, *softbody, i0); + p1 = SkinVertex(*mesh, *softbody, i1); + p2 = SkinVertex(*mesh, *softbody, i2); + } + else if (armature != nullptr && !armature->boneData.empty()) + { + p0 = SkinVertex(*mesh, *armature, i0); + p1 = SkinVertex(*mesh, *armature, i1); + p2 = SkinVertex(*mesh, *armature, i2); } else { - if (armature == nullptr || armature->boneData.empty()) - { - p0 = XMLoadFloat3(&mesh->vertex_positions[i0]); - p1 = XMLoadFloat3(&mesh->vertex_positions[i1]); - p2 = XMLoadFloat3(&mesh->vertex_positions[i2]); - } - else - { - p0 = SkinVertex(*mesh, *armature, i0); - p1 = SkinVertex(*mesh, *armature, i1); - p2 = SkinVertex(*mesh, *armature, i2); - } + p0 = XMLoadFloat3(&mesh->vertex_positions[i0]); + p1 = XMLoadFloat3(&mesh->vertex_positions[i1]); + p2 = XMLoadFloat3(&mesh->vertex_positions[i2]); } float distance; @@ -5510,34 +5496,31 @@ namespace wi::scene XMVECTOR p0; XMVECTOR p1; XMVECTOR p2; - - const bool softbody_active = softbody != nullptr && softbody->HasVertices(); - if (softbody_active) + if (softbody != nullptr && !softbody->boneData.empty()) { - p0 = softbody->vertex_positions_simulation[i0].LoadPOS(); - p1 = softbody->vertex_positions_simulation[i1].LoadPOS(); - p2 = softbody->vertex_positions_simulation[i2].LoadPOS(); + p0 = SkinVertex(*mesh, *softbody, i0); + p1 = SkinVertex(*mesh, *softbody, i1); + p2 = SkinVertex(*mesh, *softbody, i2); + } + else if (armature != nullptr && !armature->boneData.empty()) + { + p0 = SkinVertex(*mesh, *armature, i0); + p1 = SkinVertex(*mesh, *armature, i1); + p2 = SkinVertex(*mesh, *armature, i2); + p0 = XMVector3Transform(p0, objectMat); + p1 = XMVector3Transform(p1, objectMat); + p2 = XMVector3Transform(p2, objectMat); } else { - if (armature == nullptr || armature->boneData.empty()) - { - p0 = XMLoadFloat3(&mesh->vertex_positions[i0]); - p1 = XMLoadFloat3(&mesh->vertex_positions[i1]); - p2 = XMLoadFloat3(&mesh->vertex_positions[i2]); - } - else - { - p0 = SkinVertex(*mesh, *armature, i0); - p1 = SkinVertex(*mesh, *armature, i1); - p2 = SkinVertex(*mesh, *armature, i2); - } + p0 = XMLoadFloat3(&mesh->vertex_positions[i0]); + p1 = XMLoadFloat3(&mesh->vertex_positions[i1]); + p2 = XMLoadFloat3(&mesh->vertex_positions[i2]); + p0 = XMVector3Transform(p0, objectMat); + p1 = XMVector3Transform(p1, objectMat); + p2 = XMVector3Transform(p2, objectMat); } - p0 = XMVector3Transform(p0, objectMat); - p1 = XMVector3Transform(p1, objectMat); - p2 = XMVector3Transform(p2, objectMat); - XMFLOAT3 min, max; XMStoreFloat3(&min, XMVectorMin(p0, XMVectorMin(p1, p2))); XMStoreFloat3(&max, XMVectorMax(p0, XMVectorMax(p1, p2))); @@ -5795,34 +5778,31 @@ namespace wi::scene XMVECTOR p0; XMVECTOR p1; XMVECTOR p2; - - const bool softbody_active = softbody != nullptr && softbody->HasVertices(); - if (softbody_active) + if (softbody != nullptr && !softbody->boneData.empty()) { - p0 = softbody->vertex_positions_simulation[i0].LoadPOS(); - p1 = softbody->vertex_positions_simulation[i1].LoadPOS(); - p2 = softbody->vertex_positions_simulation[i2].LoadPOS(); + p0 = SkinVertex(*mesh, *softbody, i0); + p1 = SkinVertex(*mesh, *softbody, i1); + p2 = SkinVertex(*mesh, *softbody, i2); + } + else if (armature != nullptr && !armature->boneData.empty()) + { + p0 = SkinVertex(*mesh, *armature, i0); + p1 = SkinVertex(*mesh, *armature, i1); + p2 = SkinVertex(*mesh, *armature, i2); + p0 = XMVector3Transform(p0, objectMat); + p1 = XMVector3Transform(p1, objectMat); + p2 = XMVector3Transform(p2, objectMat); } else { - if (armature == nullptr || armature->boneData.empty()) - { - p0 = XMLoadFloat3(&mesh->vertex_positions[i0]); - p1 = XMLoadFloat3(&mesh->vertex_positions[i1]); - p2 = XMLoadFloat3(&mesh->vertex_positions[i2]); - } - else - { - p0 = SkinVertex(*mesh, *armature, i0); - p1 = SkinVertex(*mesh, *armature, i1); - p2 = SkinVertex(*mesh, *armature, i2); - } + p0 = XMLoadFloat3(&mesh->vertex_positions[i0]); + p1 = XMLoadFloat3(&mesh->vertex_positions[i1]); + p2 = XMLoadFloat3(&mesh->vertex_positions[i2]); + p0 = XMVector3Transform(p0, objectMat); + p1 = XMVector3Transform(p1, objectMat); + p2 = XMVector3Transform(p2, objectMat); } - p0 = XMVector3Transform(p0, objectMat); - p1 = XMVector3Transform(p1, objectMat); - p2 = XMVector3Transform(p2, objectMat); - XMFLOAT3 min, max; XMStoreFloat3(&min, XMVectorMin(p0, XMVectorMin(p1, p2))); XMStoreFloat3(&max, XMVectorMax(p0, XMVectorMax(p1, p2))); @@ -6135,29 +6115,26 @@ namespace wi::scene XMVECTOR p0; XMVECTOR p1; XMVECTOR p2; - - const bool softbody_active = softbody != nullptr && softbody->HasVertices(); - if (softbody_active) + if (softbody != nullptr && !softbody->boneData.empty()) { - p0 = softbody->vertex_positions_simulation[i0].LoadPOS(); - p1 = softbody->vertex_positions_simulation[i1].LoadPOS(); - p2 = softbody->vertex_positions_simulation[i2].LoadPOS(); + p0 = SkinVertex(*mesh, *softbody, i0); + p1 = SkinVertex(*mesh, *softbody, i1); + p2 = SkinVertex(*mesh, *softbody, i2); + } + else if (armature != nullptr && !armature->boneData.empty()) + { + p0 = SkinVertex(*mesh, *armature, i0); + p1 = SkinVertex(*mesh, *armature, i1); + p2 = SkinVertex(*mesh, *armature, i2); + p0 = XMVector3Transform(p0, objectMat); + p1 = XMVector3Transform(p1, objectMat); + p2 = XMVector3Transform(p2, objectMat); } else { - if (armature == nullptr || armature->boneData.empty()) - { - p0 = XMLoadFloat3(&mesh->vertex_positions[i0]); - p1 = XMLoadFloat3(&mesh->vertex_positions[i1]); - p2 = XMLoadFloat3(&mesh->vertex_positions[i2]); - } - else - { - p0 = SkinVertex(*mesh, *armature, i0); - p1 = SkinVertex(*mesh, *armature, i1); - p2 = SkinVertex(*mesh, *armature, i2); - } - + p0 = XMLoadFloat3(&mesh->vertex_positions[i0]); + p1 = XMLoadFloat3(&mesh->vertex_positions[i1]); + p2 = XMLoadFloat3(&mesh->vertex_positions[i2]); p0 = XMVector3Transform(p0, objectMat); p1 = XMVector3Transform(p1, objectMat); p2 = XMVector3Transform(p2, objectMat); @@ -6249,37 +6226,34 @@ namespace wi::scene const SoftBodyPhysicsComponent* softbody = softbodies.GetComponent(object->meshID); const ArmatureComponent* armature = mesh->IsSkinned() ? armatures.GetComponent(mesh->armatureID) : nullptr; + XMVECTOR P; XMVECTOR p0; XMVECTOR p1; XMVECTOR p2; - - const bool softbody_active = softbody != nullptr && softbody->HasVertices(); - if (softbody_active) + if (softbody != nullptr && !softbody->boneData.empty()) { - p0 = softbody->vertex_positions_simulation[vertexID0].LoadPOS(); - p1 = softbody->vertex_positions_simulation[vertexID1].LoadPOS(); - p2 = softbody->vertex_positions_simulation[vertexID2].LoadPOS(); + p0 = SkinVertex(*mesh, *softbody, vertexID0); + p1 = SkinVertex(*mesh, *softbody, vertexID1); + p2 = SkinVertex(*mesh, *softbody, vertexID2); + } + else if (armature != nullptr && !armature->boneData.empty()) + { + p0 = SkinVertex(*mesh, *armature, vertexID0); + p1 = SkinVertex(*mesh, *armature, vertexID1); + p2 = SkinVertex(*mesh, *armature, vertexID2); + + P = XMVectorBaryCentric(p0, p1, p2, bary.x, bary.y); + const size_t objectIndex = objects.GetIndex(objectEntity); + const XMMATRIX objectMat = XMLoadFloat4x4(&matrix_objects[objectIndex]); + P = XMVector3Transform(P, objectMat); } else { - if (armature == nullptr || armature->boneData.empty()) - { - p0 = XMLoadFloat3(&mesh->vertex_positions[vertexID0]); - p1 = XMLoadFloat3(&mesh->vertex_positions[vertexID1]); - p2 = XMLoadFloat3(&mesh->vertex_positions[vertexID2]); - } - else - { - p0 = SkinVertex(*mesh, *armature, vertexID0); - p1 = SkinVertex(*mesh, *armature, vertexID1); - p2 = SkinVertex(*mesh, *armature, vertexID2); - } - } + p0 = XMLoadFloat3(&mesh->vertex_positions[vertexID0]); + p1 = XMLoadFloat3(&mesh->vertex_positions[vertexID1]); + p2 = XMLoadFloat3(&mesh->vertex_positions[vertexID2]); - XMVECTOR P = XMVectorBaryCentric(p0, p1, p2, bary.x, bary.y); - - if (!softbody_active) - { + P = XMVectorBaryCentric(p0, p1, p2, bary.x, bary.y); const size_t objectIndex = objects.GetIndex(objectEntity); const XMMATRIX objectMat = XMLoadFloat4x4(&matrix_objects[objectIndex]); P = XMVector3Transform(P, objectMat); @@ -6290,6 +6264,46 @@ namespace wi::scene return result; } + void Scene::ResetPose(wi::ecs::Entity entity) + { + // All child armatures will be also calling ResetPose, in case you give a parent entity of them, for convenience: + for (size_t i = 0; i < armatures.GetCount(); ++i) + { + Entity armatureEntity = armatures.GetEntity(i); + if (Entity_IsDescendant(armatureEntity, entity)) + { + ResetPose(armatureEntity); + } + } + + const ArmatureComponent* armature = armatures.GetComponent(entity); + if (armature == nullptr) + return; + + XMMATRIX W = XMMatrixIdentity(); + const TransformComponent* armature_transform = transforms.GetComponent(entity); + if (armature_transform != nullptr) + { + W = XMLoadFloat4x4(&armature_transform->world); + } + + for (size_t i = 0; i < armature->boneCollection.size(); ++i) + { + Entity bone = armature->boneCollection[i]; + TransformComponent* transform = transforms.GetComponent(bone); + if (transform != nullptr) + { + transform->ClearTransform(); + transform->MatrixTransform(XMMatrixInverse(nullptr, XMLoadFloat4x4(&armature->inverseBindMatrices[i])) * W); + transform->UpdateTransform(); + const HierarchyComponent* hier = hierarchy.GetComponent(bone); + if (hier != nullptr && hier->parentID != INVALID_ENTITY) + { + Component_Attach(bone, hier->parentID, false); + } + } + } + } void Scene::PutWaterRipple(const std::string& image, const XMFLOAT3& pos) { @@ -6323,44 +6337,64 @@ namespace wi::scene waterRipples.push_back(img); } - XMVECTOR SkinVertex(const MeshComponent& mesh, const ArmatureComponent& armature, uint32_t index, XMVECTOR* N) + XMVECTOR SkinVertex(const MeshComponent& mesh, const wi::vector& boneData, uint32_t index, XMVECTOR* N) { XMVECTOR P = XMLoadFloat3(&mesh.vertex_positions[index]); - const XMUINT4& ind = mesh.vertex_boneindices[index]; - const XMFLOAT4& wei = mesh.vertex_boneweights[index]; - const XMFLOAT4X4 mat[] = { - armature.boneData[ind.x].GetMatrix(), - armature.boneData[ind.y].GetMatrix(), - armature.boneData[ind.z].GetMatrix(), - armature.boneData[ind.w].GetMatrix(), - }; - const XMMATRIX M[] = { - XMMatrixTranspose(XMLoadFloat4x4(&mat[0])), - XMMatrixTranspose(XMLoadFloat4x4(&mat[1])), - XMMatrixTranspose(XMLoadFloat4x4(&mat[2])), - XMMatrixTranspose(XMLoadFloat4x4(&mat[3])), - }; + const uint32_t influence_div4 = mesh.GetBoneInfluenceDiv4(); - XMVECTOR skinned; - skinned = XMVector3Transform(P, M[0]) * wei.x; - skinned += XMVector3Transform(P, M[1]) * wei.y; - skinned += XMVector3Transform(P, M[2]) * wei.z; - skinned += XMVector3Transform(P, M[3]) * wei.w; - P = skinned; + XMVECTOR skinnedP = XMVectorZero(); + XMVECTOR skinnedN = XMVectorZero(); + for (uint32_t influence = 0; influence < influence_div4; ++influence) + { + const XMUINT4& ind = influence == 0 ? mesh.vertex_boneindices[index] : mesh.vertex_boneindices2[index]; + const XMFLOAT4& wei = influence == 0 ? mesh.vertex_boneweights[index]: mesh.vertex_boneweights2[index]; + + const XMFLOAT4X4 mat[] = { + boneData[ind.x].GetMatrix(), + boneData[ind.y].GetMatrix(), + boneData[ind.z].GetMatrix(), + boneData[ind.w].GetMatrix(), + }; + const XMMATRIX M[] = { + XMMatrixTranspose(XMLoadFloat4x4(&mat[0])), + XMMatrixTranspose(XMLoadFloat4x4(&mat[1])), + XMMatrixTranspose(XMLoadFloat4x4(&mat[2])), + XMMatrixTranspose(XMLoadFloat4x4(&mat[3])), + }; + + skinnedP += XMVector3Transform(P, M[0]) * wei.x; + skinnedP += XMVector3Transform(P, M[1]) * wei.y; + skinnedP += XMVector3Transform(P, M[2]) * wei.z; + skinnedP += XMVector3Transform(P, M[3]) * wei.w; + + if (N != nullptr) + { + *N = XMLoadFloat3(&mesh.vertex_normals[index]); + skinnedN += XMVector3TransformNormal(*N, M[0]) * wei.x; + skinnedN += XMVector3TransformNormal(*N, M[1]) * wei.y; + skinnedN += XMVector3TransformNormal(*N, M[2]) * wei.z; + skinnedN += XMVector3TransformNormal(*N, M[3]) * wei.w; + } + } + + P = skinnedP; if (N != nullptr) { - *N = XMLoadFloat3(&mesh.vertex_normals[index]); - skinned = XMVector3TransformNormal(*N, M[0]) * wei.x; - skinned += XMVector3TransformNormal(*N, M[1]) * wei.y; - skinned += XMVector3TransformNormal(*N, M[2]) * wei.z; - skinned += XMVector3TransformNormal(*N, M[3]) * wei.w; - *N = XMVector3Normalize(skinned); + *N = XMVector3Normalize(skinnedN); } return P; } + XMVECTOR SkinVertex(const MeshComponent& mesh, const ArmatureComponent& armature, uint32_t index, XMVECTOR* N) + { + return SkinVertex(mesh, armature.boneData, index, N); + } + XMVECTOR SkinVertex(const MeshComponent& mesh, const SoftBodyPhysicsComponent& softbody, uint32_t index, XMVECTOR* N) + { + return SkinVertex(mesh, softbody.boneData, index, N); + } diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index 12c48ae6d..b2c79b7a3 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -33,11 +33,11 @@ namespace wi::scene 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", 4); // version = 4 - wi::ecs::ComponentManager& meshes = componentLibrary.Register("wi::scene::Scene::meshes", 2); // version = 2 + wi::ecs::ComponentManager& meshes = componentLibrary.Register("wi::scene::Scene::meshes", 3); // version = 3 wi::ecs::ComponentManager& impostors = componentLibrary.Register("wi::scene::Scene::impostors"); wi::ecs::ComponentManager& objects = componentLibrary.Register("wi::scene::Scene::objects", 3); // version = 3 wi::ecs::ComponentManager& rigidbodies = componentLibrary.Register("wi::scene::Scene::rigidbodies", 3); // version = 3 - wi::ecs::ComponentManager& softbodies = componentLibrary.Register("wi::scene::Scene::softbodies", 1); // version = 1 + 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", 2); // version = 2 wi::ecs::ComponentManager& cameras = componentLibrary.Register("wi::scene::Scene::cameras"); @@ -539,11 +539,21 @@ namespace wi::scene // 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 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 diff --git a/WickedEngine/wiScene_BindLua.cpp b/WickedEngine/wiScene_BindLua.cpp index 58eb02811..9d851696b 100644 --- a/WickedEngine/wiScene_BindLua.cpp +++ b/WickedEngine/wiScene_BindLua.cpp @@ -667,6 +667,7 @@ Luna::FunctionType Scene_BindLua::methods[] = { lunamethod(Scene_BindLua, GetWeather), lunamethod(Scene_BindLua, SetWeather), lunamethod(Scene_BindLua, RetargetAnimation), + lunamethod(Scene_BindLua, ResetPose), lunamethod(Scene_BindLua, VoxelizeObject), lunamethod(Scene_BindLua, VoxelizeScene), { NULL, NULL } @@ -3147,6 +3148,20 @@ int Scene_BindLua::RetargetAnimation(lua_State* L) } return 0; } +int Scene_BindLua::ResetPose(lua_State* L) +{ + int argc = wi::lua::SGetArgCount(L); + if (argc > 0) + { + Entity entity = (Entity)wi::lua::SGetLongLong(L, 1); + scene->ResetPose(entity); + } + else + { + wi::lua::SError(L, "ResetPose(Entity entity) not enough arguments!"); + } + return 0; +} int Scene_BindLua::VoxelizeObject(lua_State* L) { diff --git a/WickedEngine/wiScene_BindLua.h b/WickedEngine/wiScene_BindLua.h index 76b87de6e..17c82bd6c 100644 --- a/WickedEngine/wiScene_BindLua.h +++ b/WickedEngine/wiScene_BindLua.h @@ -195,6 +195,7 @@ namespace wi::lua::scene int SetWeather(lua_State* L); int RetargetAnimation(lua_State* L); + int ResetPose(lua_State* L); int VoxelizeObject(lua_State* L); int VoxelizeScene(lua_State* L); diff --git a/WickedEngine/wiScene_Components.cpp b/WickedEngine/wiScene_Components.cpp index 83dabf8cf..5049e1471 100644 --- a/WickedEngine/wiScene_Components.cpp +++ b/WickedEngine/wiScene_Components.cpp @@ -13,6 +13,7 @@ #include "wiLua.h" #include "Utility/mikktspace.h" +#include "Utility/meshoptimizer/meshoptimizer.h" #if __has_include("OpenImageDenoise/oidn.hpp") #include "OpenImageDenoise/oidn.hpp" @@ -832,7 +833,8 @@ namespace wi::scene AlignTo(uv_count * sizeof(Vertex_UVS), alignment) + AlignTo(vertex_atlas.size() * sizeof(Vertex_TEX), alignment) + AlignTo(vertex_colors.size() * sizeof(Vertex_COL), alignment) + - AlignTo(vertex_boneindices.size() * sizeof(Vertex_BON), alignment) + AlignTo(vertex_boneindices.size() * sizeof(Vertex_BON), alignment) + + AlignTo(vertex_boneindices2.size() * sizeof(Vertex_BON), alignment) ; constexpr Format morph_format = Format::R16G16B16A16_FLOAT; @@ -1025,29 +1027,69 @@ namespace wi::scene } } - // skinning buffers: + // bone reference buffers (skinning, soft body): if (!vertex_boneindices.empty()) { vb_bon.offset = buffer_offset; - vb_bon.size = vertex_boneindices.size() * sizeof(Vertex_BON); + const size_t influence_div4 = GetBoneInfluenceDiv4(); + vb_bon.size = (vertex_boneindices.size() + vertex_boneindices2.size()) * sizeof(Vertex_BON); Vertex_BON* vertices = (Vertex_BON*)(buffer_data + buffer_offset); buffer_offset += AlignTo(vb_bon.size, alignment); - assert(vertex_boneindices.size() == vertex_boneweights.size()); + assert(vertex_boneindices.size() == vertex_boneweights.size()); // must have same number of indices as weights + assert(vertex_boneindices2.empty() || vertex_boneindices2.size() == vertex_boneindices.size()); // if second influence stream exists, it must be as large as the first + assert(vertex_boneindices2.size() == vertex_boneweights2.size()); // must have same number of indices as weights for (size_t i = 0; i < vertex_boneindices.size(); ++i) { - XMFLOAT4& wei = vertex_boneweights[i]; - // normalize bone weights - float len = wei.x + wei.y + wei.z + wei.w; - if (len > 0) + // Normalize weights: + // Note: if multiple influence streams are present, + // we have to normalize them together, not separately + float weights[8] = {}; + weights[0] = vertex_boneweights[i].x; + weights[1] = vertex_boneweights[i].y; + weights[2] = vertex_boneweights[i].z; + weights[3] = vertex_boneweights[i].w; + if (influence_div4 > 1) { - wei.x /= len; - wei.y /= len; - wei.z /= len; - wei.w /= len; + weights[4] = vertex_boneweights2[i].x; + weights[5] = vertex_boneweights2[i].y; + weights[6] = vertex_boneweights2[i].z; + weights[7] = vertex_boneweights2[i].w; } + float sum = 0; + for (auto& weight : weights) + { + sum += weight; + } + if (sum > 0) + { + const float norm = 1.0f / sum; + for (auto& weight : weights) + { + weight *= norm; + } + } + // Store back normalized weights: + vertex_boneweights[i].x = weights[0]; + vertex_boneweights[i].y = weights[1]; + vertex_boneweights[i].z = weights[2]; + vertex_boneweights[i].w = weights[3]; + if (influence_div4 > 1) + { + vertex_boneweights2[i].x = weights[4]; + vertex_boneweights2[i].y = weights[5]; + vertex_boneweights2[i].z = weights[6]; + vertex_boneweights2[i].w = weights[7]; + } + Vertex_BON vert; - vert.FromFULL(vertex_boneindices[i], wei); - std::memcpy(vertices + i, &vert, sizeof(vert)); + vert.FromFULL(vertex_boneindices[i], vertex_boneweights[i]); + std::memcpy(vertices + (i * influence_div4 + 0), &vert, sizeof(vert)); + + if (influence_div4 > 1) + { + vert.FromFULL(vertex_boneindices2[i], vertex_boneweights2[i]); + std::memcpy(vertices + (i * influence_div4 + 1), &vert, sizeof(vert)); + } } } @@ -1980,54 +2022,220 @@ namespace wi::scene return PathDataType::Event; } - void SoftBodyPhysicsComponent::CreateFromMesh(const MeshComponent& mesh) + void SoftBodyPhysicsComponent::CreateFromMesh(MeshComponent& mesh) { - vertex_positions_simulation.resize(mesh.vertex_positions.size()); - vertex_normals_simulation.resize(mesh.vertex_normals.size()); - vertex_tangents_simulation.resize(mesh.vertex_tangents.size()); - - XMFLOAT3 _min = XMFLOAT3(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); - XMFLOAT3 _max = XMFLOAT3(std::numeric_limits::lowest(), std::numeric_limits::lowest(), std::numeric_limits::lowest()); - XMMATRIX W = XMLoadFloat4x4(&worldMatrix); - for (size_t i = 0; i < mesh.vertex_positions.size(); ++i) + if (weights.size() != mesh.vertex_positions.size()) { - XMFLOAT3 pos = mesh.vertex_positions[i]; - XMStoreFloat3(&pos, XMVector3Transform(XMLoadFloat3(&pos), W)); - vertex_positions_simulation[i].FromFULL(pos); - _min = wi::math::Min(_min, pos); - _max = wi::math::Max(_max, pos); + weights.resize(mesh.vertex_positions.size()); + std::fill(weights.begin(), weights.end(), 1.0f); } - aabb = AABB(_min, _max); - - if (physicsToGraphicsVertexMapping.empty()) + if (physicsIndices.empty()) { - // Create a mapping that maps unique vertex positions to all vertex indices that share that. Unique vertex positions will make up the physics mesh: - wi::unordered_map uniquePositions; - graphicsToPhysicsVertexMapping.resize(mesh.vertex_positions.size()); - physicsToGraphicsVertexMapping.clear(); - weights.clear(); - - for (size_t i = 0; i < mesh.vertex_positions.size(); ++i) + bool pinning_required = false; + wi::vector source; + uint32_t first_subset = 0; + uint32_t last_subset = 0; + mesh.GetLODSubsetRange(0, first_subset, last_subset); + for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex) { - const XMFLOAT3& position = mesh.vertex_positions[i]; - - size_t hashes[] = { - std::hash{}(int(position.x * detail)), - std::hash{}(int(position.y * detail)), - std::hash{}(int(position.z * detail)), - }; - size_t vertexHash = (((hashes[0] ^ (hashes[1] << 1) >> 1) ^ (hashes[2] << 1)) >> 1); - - if (uniquePositions.count(vertexHash) == 0) + const MeshComponent::MeshSubset& subset = mesh.subsets[subsetIndex]; + const uint32_t* indices = mesh.indices.data() + subset.indexOffset; + for (uint32_t i = 0; i < subset.indexCount; ++i) { - uniquePositions[vertexHash] = (uint32_t)physicsToGraphicsVertexMapping.size(); - physicsToGraphicsVertexMapping.push_back((uint32_t)i); + source.push_back(indices[i]); + pinning_required |= weights[indices[i]] == 0; } - graphicsToPhysicsVertexMapping[i] = uniquePositions[vertexHash]; + } + physicsIndices.resize(source.size()); + + if (pinning_required) + { + // If there is pinning, we need to use precise LOD to retain difference between pinned and soft vertices: + wi::vector vertices(mesh.vertex_positions.size()); + for (size_t i = 0; i < mesh.vertex_positions.size(); ++i) + { + vertices[i].x = mesh.vertex_positions[i].x; + vertices[i].y = mesh.vertex_positions[i].y; + vertices[i].z = mesh.vertex_positions[i].z; + vertices[i].w = weights[i] == 0 ? 1.0f : 0.0f; + } + + // Generate shadow indices for position+weight-only stream: + wi::vector shadow_indices(source.size()); + meshopt_generateShadowIndexBuffer( + shadow_indices.data(), source.data(), source.size(), + vertices.data(), vertices.size(), sizeof(XMFLOAT4), sizeof(XMFLOAT4) + ); + + size_t result = 0; + size_t target_index_count = size_t(shadow_indices.size() * saturate(detail)) / 3 * 3; + float target_error = 1 - saturate(detail); + int tries = 0; + while (result == 0 && tries < 100) + { + result = meshopt_simplify( + &physicsIndices[0], + &shadow_indices[0], + shadow_indices.size(), + (const float*)&mesh.vertex_positions[0], + mesh.vertex_positions.size(), + sizeof(XMFLOAT3), + target_index_count, + target_error + ); + target_error *= 0.5f; + } + assert(result > 0); + physicsIndices.resize(result); + } + else + { + // Sloppy LOD can be used if no pinning is required: + size_t result = 0; + size_t target_index_count = 0; + float target_error = sqr(1 - saturate(detail)); + int tries = 0; + while (result == 0 && tries < 100) + { + result = meshopt_simplifySloppy( + &physicsIndices[0], + &source[0], + source.size(), + (const float*)&mesh.vertex_positions[0], + mesh.vertex_positions.size(), + sizeof(XMFLOAT3), + target_index_count, + target_error + ); + target_error *= 0.5f; + } + assert(result > 0); + physicsIndices.resize(result); } - weights.resize(physicsToGraphicsVertexMapping.size()); - std::fill(weights.begin(), weights.end(), 1.0f); + physicsIndices.shrink_to_fit(); + + // Remap physics indices to point to physics indices: + physicsToGraphicsVertexMapping.clear(); + wi::unordered_map physicsVertices; + for (size_t i = 0; i < physicsIndices.size(); ++i) + { + const uint32_t graphicsInd = physicsIndices[i]; + if (physicsVertices.count(graphicsInd) == 0) + { + physicsVertices[graphicsInd] = physicsToGraphicsVertexMapping.size(); + physicsToGraphicsVertexMapping.push_back(graphicsInd); + } + physicsIndices[i] = (uint32_t)physicsVertices[graphicsInd]; + } + physicsToGraphicsVertexMapping.shrink_to_fit(); + + // BoneQueue is used for assigning the highest weighted fixed number of bones (soft body nodes) to a graphics vertex + static constexpr int influence = 8; + struct BoneQueue + { + struct Bone + { + uint32_t index = 0; + float weight = 0; + constexpr bool operator<(const Bone& other) const { return weight < other.weight; } + constexpr bool operator>(const Bone& other) const { return weight > other.weight; } + }; + Bone bones[influence]; + constexpr void add(uint32_t index, float weight) + { + int mini = 0; + for (int i = 1; i < arraysize(bones); ++i) + { + if (bones[i].weight < bones[mini].weight) + { + mini = i; + } + } + if (weight > bones[mini].weight) + { + bones[mini].weight = weight; + bones[mini].index = index; + } + } + void finalize() + { + std::sort(bones, bones + arraysize(bones), std::greater()); + // Note: normalization of bone weights will be done in MeshComponent::CreateRenderData() + } + constexpr XMUINT4 get_indices() const + { + return XMUINT4( + influence < 1 ? 0 : bones[0].index, + influence < 2 ? 0 : bones[1].index, + influence < 3 ? 0 : bones[2].index, + influence < 4 ? 0 : bones[3].index + ); + } + constexpr XMUINT4 get_indices2() const + { + return XMUINT4( + influence < 5 ? 0 : bones[4].index, + influence < 6 ? 0 : bones[5].index, + influence < 7 ? 0 : bones[6].index, + influence < 8 ? 0 : bones[7].index + ); + } + constexpr XMFLOAT4 get_weights() const + { + return XMFLOAT4( + influence < 1 ? 0 : bones[0].weight, + influence < 2 ? 0 : bones[1].weight, + influence < 3 ? 0 : bones[2].weight, + influence < 4 ? 0 : bones[3].weight + ); + } + constexpr XMFLOAT4 get_weights2() const + { + return XMFLOAT4( + influence < 5 ? 0 : bones[4].weight, + influence < 6 ? 0 : bones[5].weight, + influence < 7 ? 0 : bones[6].weight, + influence < 8 ? 0 : bones[7].weight + ); + } + }; + + // Create skinning bone vertex data: + mesh.vertex_boneindices.resize(mesh.vertex_positions.size()); + mesh.vertex_boneweights.resize(mesh.vertex_positions.size()); + if (influence > 4) + { + mesh.vertex_boneindices2.resize(mesh.vertex_positions.size()); + mesh.vertex_boneweights2.resize(mesh.vertex_positions.size()); + } + wi::jobsystem::context ctx; + wi::jobsystem::Dispatch(ctx, (uint32_t)mesh.vertex_positions.size(), 64, [&](wi::jobsystem::JobArgs args) { + const XMFLOAT3 position = mesh.vertex_positions[args.jobIndex]; + + BoneQueue bones; + for (size_t physicsInd = 0; physicsInd < physicsToGraphicsVertexMapping.size(); ++physicsInd) + { + const uint32_t graphicsInd = physicsToGraphicsVertexMapping[physicsInd]; + const XMFLOAT3 position2 = mesh.vertex_positions[graphicsInd]; + const float dist = wi::math::DistanceSquared(position, position2); + // Note: 0.01 correction is carefully tweaked so that cloth_test and sponza curtains look good + // (larger values blow up the curtains, lower values make the shading of the cloth look bad) + const float weight = 1.0f / (0.01f + dist); + bones.add((uint32_t)physicsInd, weight); + } + + bones.finalize(); + mesh.vertex_boneindices[args.jobIndex] = bones.get_indices(); + mesh.vertex_boneweights[args.jobIndex] = bones.get_weights(); + if (influence > 4) + { + mesh.vertex_boneindices2[args.jobIndex] = bones.get_indices2(); + mesh.vertex_boneweights2[args.jobIndex] = bones.get_weights2(); + } + }); + wi::jobsystem::Wait(ctx); + mesh.CreateRenderData(); } } diff --git a/WickedEngine/wiScene_Components.h b/WickedEngine/wiScene_Components.h index e3f8914ec..2c42485c4 100644 --- a/WickedEngine/wiScene_Components.h +++ b/WickedEngine/wiScene_Components.h @@ -366,6 +366,8 @@ namespace wi::scene wi::vector vertex_uvset_1; wi::vector vertex_boneindices; wi::vector vertex_boneweights; + wi::vector vertex_boneindices2; + wi::vector vertex_boneweights2; wi::vector vertex_atlas; wi::vector vertex_colors; wi::vector vertex_windweights; @@ -517,6 +519,20 @@ namespace wi::scene void RecenterToBottom(); wi::primitive::Sphere GetBoundingSphere() const; + uint32_t GetBoneInfluenceDiv4() const + { + uint32_t influence_div4 = 0; + if (!vertex_boneindices.empty()) + { + influence_div4++; + } + if (!vertex_boneindices2.empty()) + { + influence_div4++; + } + return influence_div4; + } + void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); struct Vertex_POS10 @@ -661,39 +677,30 @@ namespace wi::scene }; struct Vertex_BON { - uint16_t ind0 = 0; - uint16_t ind1 = 0; - uint16_t ind2 = 0; - uint16_t ind3 = 0; - - uint16_t wei0 = 0; - uint16_t wei1 = 1; - uint16_t wei2 = 2; - uint16_t wei3 = 3; + XMUINT4 packed = XMUINT4(0, 0, 0, 0); constexpr void FromFULL(const XMUINT4& boneIndices, const XMFLOAT4& boneWeights) { - ind0 = uint16_t(boneIndices.x); - ind1 = uint16_t(boneIndices.y); - ind2 = uint16_t(boneIndices.z); - ind3 = uint16_t(boneIndices.w); - - wei0 = uint16_t(boneWeights.x * 65535.0f); - wei1 = uint16_t(boneWeights.y * 65535.0f); - wei2 = uint16_t(boneWeights.z * 65535.0f); - wei3 = uint16_t(boneWeights.w * 65535.0f); + // Note: + // - Indices are packed at 20 bits which allow indexing >1 million bones per mesh + // - Weights are packed at 12 bits which allow 4096 distinct values, this was tweaked to + // retain good precision with a high bone count stanford bunny soft body simulation where regular 8 bit weights was not enough + packed.x = (boneIndices.x & 0xFFFFF) | ((uint32_t(boneWeights.x * 4095) & 0xFFF) << 20u); + packed.y = (boneIndices.y & 0xFFFFF) | ((uint32_t(boneWeights.y * 4095) & 0xFFF) << 20u); + packed.z = (boneIndices.z & 0xFFFFF) | ((uint32_t(boneWeights.z * 4095) & 0xFFF) << 20u); + packed.w = (boneIndices.w & 0xFFFFF) | ((uint32_t(boneWeights.w * 4095) & 0xFFF) << 20u); } constexpr XMUINT4 GetInd_FULL() const { - return XMUINT4(ind0, ind1, ind2, ind3); + return XMUINT4(packed.x & 0xFFFFF, packed.y & 0xFFFFF, packed.z & 0xFFFFF, packed.w & 0xFFFFF); } constexpr XMFLOAT4 GetWei_FULL() const { return XMFLOAT4( - float(wei0) / 65535.0f, - float(wei1) / 65535.0f, - float(wei2) / 65535.0f, - float(wei3) / 65535.0f + float(packed.x >> 20u) / 4095.0f, + float(packed.y >> 20u) / 4095.0f, + float(packed.z >> 20u) / 4095.0f, + float(packed.w >> 20u) / 4095.0f ); } }; @@ -964,7 +971,7 @@ namespace wi::scene EMPTY = 0, SAFE_TO_REGISTER = 1 << 0, DISABLE_DEACTIVATION = 1 << 1, - FORCE_RESET = 1 << 2, + _DEPRECATED_FORCE_RESET = 1 << 2, WIND = 1 << 3, }; uint32_t _flags = DISABLE_DEACTIVATION; @@ -972,41 +979,41 @@ namespace wi::scene float mass = 1.0f; float friction = 0.5f; float restitution = 0.0f; + float pressure = 0.0f; float vertex_radius = 0.2f; // how much distance vertices keep from other physics bodies - float detail = 100; // precision to keep within a unit + float detail = 1; // LOD target detail [0,1] + wi::vector physicsIndices; // physics vertex connectivity wi::vector physicsToGraphicsVertexMapping; // maps graphics vertex index to physics vertex index of the same position - wi::vector graphicsToPhysicsVertexMapping; // maps a physics vertex index to first graphics vertex index of the same position wi::vector weights; // weight per physics vertex controlling the mass. (0: disable weight (no physics, only animation), 1: default weight) // Non-serialized attributes: std::shared_ptr physicsobject = nullptr; // You can set to null to recreate the physics object the next time phsyics system will be running. XMFLOAT4X4 worldMatrix = wi::math::IDENTITY_MATRIX; - wi::vector vertex_positions_simulation; // graphics vertices after simulation (world space) - wi::vector vertex_normals_simulation; - wi::vector vertex_tangents_simulation; + uint32_t gpuBoneOffset = 0; + wi::vector boneData; // simulated soft body nodes as bone matrices that can be fed into skinning wi::primitive::AABB aabb; inline void SetDisableDeactivation(bool value) { if (value) { _flags |= DISABLE_DEACTIVATION; } else { _flags &= ~DISABLE_DEACTIVATION; } } - inline void SetWindEnabled(bool value) { if (value) { _flags |= WIND; } else { _flags &= ~DISABLE_DEACTIVATION; } } + inline void SetWindEnabled(bool value) { if (value) { _flags |= WIND; } else { _flags &= ~WIND; } } inline bool IsDisableDeactivation() const { return _flags & DISABLE_DEACTIVATION; } inline bool IsWindEnabled() const { return _flags & WIND; } - void SetDetail(float value) + void Reset() { - detail = value; + physicsIndices.clear(); physicsToGraphicsVertexMapping.clear(); - physicsToGraphicsVertexMapping.shrink_to_fit(); physicsobject = {}; } - inline bool HasVertices() const + void SetDetail(float loddetail) { - return !vertex_positions_simulation.empty(); + detail = loddetail; + Reset(); } // Create physics represenation of graphics mesh - void CreateFromMesh(const MeshComponent& mesh); + void CreateFromMesh(MeshComponent& mesh); void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); }; diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp index 9d980a98f..089f43229 100644 --- a/WickedEngine/wiScene_Serializers.cpp +++ b/WickedEngine/wiScene_Serializers.cpp @@ -472,6 +472,12 @@ namespace wi::scene archive >> subsets_per_lod; } + if (seri.GetVersion() >= 3) + { + archive >> vertex_boneindices2; + archive >> vertex_boneweights2; + } + wi::jobsystem::Execute(seri.ctx, [&](wi::jobsystem::JobArgs args) { CreateRenderData(); @@ -554,6 +560,12 @@ namespace wi::scene archive << subsets_per_lod; } + if (seri.GetVersion() >= 3) + { + archive << vertex_boneindices2; + archive << vertex_boneweights2; + } + } } void ImpostorComponent::Serialize(wi::Archive& archive, EntitySerializer& seri) @@ -747,6 +759,8 @@ namespace wi::scene } void SoftBodyPhysicsComponent::Serialize(wi::Archive& archive, EntitySerializer& seri) { + wi::vector graphicsToPhysicsVertexMapping; + if (archive.IsReadMode()) { archive >> _flags; @@ -772,7 +786,7 @@ namespace wi::scene friction = 0.5f; } - if (seri.version >= 1) + if (seri.GetVersion() >= 1) { archive >> vertex_radius; archive >> detail; @@ -782,6 +796,27 @@ namespace wi::scene SetWindEnabled(true); } + if (seri.GetVersion() >= 2) + { + archive >> pressure; + } + else + { + // Convert weights from per-physics to per-graphics vertex: + // Per graphics vertex is better, because regenerating soft body with different detail will keep the pinning + wi::vector weights2(graphicsToPhysicsVertexMapping.size()); + for (size_t i = 0; i < weights2.size(); ++i) + { + weights2[i] = weights[graphicsToPhysicsVertexMapping[i]]; + } + std::swap(weights, weights2); + } + + if (seri.GetVersion() >= 3) + { + archive >> physicsIndices; + } + _flags &= ~SAFE_TO_REGISTER; } else @@ -798,11 +833,20 @@ namespace wi::scene archive << restitution; } - if (seri.version >= 1) + if (seri.GetVersion() >= 1) { archive << vertex_radius; archive << detail; } + if (seri.GetVersion() >= 2) + { + archive << pressure; + } + + if (seri.GetVersion() >= 3) + { + archive << physicsIndices; + } } } void ArmatureComponent::Serialize(wi::Archive& archive, EntitySerializer& seri) diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index aab69ed10..9878d96ea 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 497; + const int revision = 498; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision); diff --git a/features.txt b/features.txt index 2dd4f62bd..826021e69 100644 --- a/features.txt +++ b/features.txt @@ -39,7 +39,7 @@ Directional lights + cascaded shadow maps Spotlights + shadow maps Point lights + shadow cubemaps Soft shadows (PCF) -BULLET Physics: rigid body, soft body +Physics: rigid body, soft body, ragdoll 3D Audio (Xaudio2) Input: keyboard, mouse, controller (rawinput, xinput), touch Controller feedback (vibration, LED)