#include "stdafx.h" #include "RigidBodyWindow.h" using namespace wi::ecs; using namespace wi::scene; void RigidBodyWindow::Create(EditorComponent* _editor) { editor = _editor; wi::gui::Window::Create(ICON_RIGIDBODY " Rigid Body Physics", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE | wi::gui::Window::WindowControls::FIT_ALL_WIDGETS_VERTICAL); SetSize(XMFLOAT2(670, 440)); closeButton.SetTooltip("Delete RigidBodyPhysicsComponent"); OnClose([=](wi::gui::EventArgs args) { wi::Archive& archive = editor->AdvanceHistory(); archive << EditorComponent::HISTORYOP_COMPONENT_DATA; editor->RecordEntity(archive, entity); editor->GetCurrentScene().rigidbodies.Remove(entity); editor->RecordEntity(archive, entity); editor->componentsWnd.RefreshEntityTree(); }); auto forEachSelectedPhysicsComponent = [this](auto /* void(RigidBodyPhysicsComponent*, wi::gui::EventArgs) */ func) { return [this, func](wi::gui::EventArgs args) { wi::scene::Scene& scene = editor->GetCurrentScene(); for (auto& x : editor->translator.selected) { RigidBodyPhysicsComponent* physicscomponent = scene.rigidbodies.GetComponent(x.entity); if (physicscomponent != nullptr) { func(physicscomponent, args); } } }; }; auto forEachSelectedPhysicsComponentWithRefresh = [forEachSelectedPhysicsComponent](auto /* void(RigidBodyPhysicsComponent*, wi::gui::EventArgs) */ func) { return forEachSelectedPhysicsComponent([func](auto physicscomponent, auto args) { func(physicscomponent, args); physicscomponent->SetRefreshParametersNeeded(); }); }; collisionShapeComboBox.Create("Collision Shape: "); collisionShapeComboBox.AddItem("Box", RigidBodyPhysicsComponent::CollisionShape::BOX); collisionShapeComboBox.AddItem("Sphere", RigidBodyPhysicsComponent::CollisionShape::SPHERE); collisionShapeComboBox.AddItem("Capsule", RigidBodyPhysicsComponent::CollisionShape::CAPSULE); collisionShapeComboBox.AddItem("Cylinder", RigidBodyPhysicsComponent::CollisionShape::CYLINDER); collisionShapeComboBox.AddItem("Convex Hull", RigidBodyPhysicsComponent::CollisionShape::CONVEX_HULL); collisionShapeComboBox.AddItem("Triangle Mesh", RigidBodyPhysicsComponent::CollisionShape::TRIANGLE_MESH); collisionShapeComboBox.AddItem("Height Field", RigidBodyPhysicsComponent::CollisionShape::HEIGHTFIELD); collisionShapeComboBox.OnSelect([=](wi::gui::EventArgs args) { forEachSelectedPhysicsComponent([](auto physicscomponent, auto args) { RigidBodyPhysicsComponent::CollisionShape shape = (RigidBodyPhysicsComponent::CollisionShape)args.userdata; if (physicscomponent->shape != shape) { physicscomponent->physicsobject.reset(); physicscomponent->shape = shape; } })(args); RefreshShapeType(); }); collisionShapeComboBox.SetSelected(0); collisionShapeComboBox.SetEnabled(true); collisionShapeComboBox.SetTooltip("Set rigid body collision shape."); AddWidget(&collisionShapeComboBox); XSlider.Create(0, 10, 1, 100000, "XSlider"); XSlider.OnSlide(forEachSelectedPhysicsComponent([](auto physicscomponent, auto args) { switch (physicscomponent->shape) { default: case RigidBodyPhysicsComponent::CollisionShape::BOX: physicscomponent->box.halfextents.x = args.fValue; break; case RigidBodyPhysicsComponent::CollisionShape::SPHERE: physicscomponent->sphere.radius = args.fValue; break; case RigidBodyPhysicsComponent::CollisionShape::CAPSULE: case RigidBodyPhysicsComponent::CollisionShape::CYLINDER: physicscomponent->capsule.height = args.fValue; break; } physicscomponent->physicsobject.reset(); })); ZSlider.SetLocalizationEnabled(false); AddWidget(&XSlider); YSlider.Create(0, 10, 1, 100000, "YSlider"); YSlider.OnSlide(forEachSelectedPhysicsComponent([](auto physicsComponent, auto args) { switch (physicsComponent->shape) { default: case RigidBodyPhysicsComponent::CollisionShape::BOX: physicsComponent->box.halfextents.y = args.fValue; break; case RigidBodyPhysicsComponent::CollisionShape::CAPSULE: case RigidBodyPhysicsComponent::CollisionShape::CYLINDER: physicsComponent->capsule.radius = args.fValue; break; } physicsComponent->physicsobject.reset(); })); ZSlider.SetLocalizationEnabled(false); AddWidget(&YSlider); ZSlider.Create(0, 10, 1, 100000, "ZSlider"); ZSlider.OnSlide(forEachSelectedPhysicsComponent([](auto physicscomponent, auto args) { switch (physicscomponent->shape) { default: case RigidBodyPhysicsComponent::CollisionShape::BOX: case RigidBodyPhysicsComponent::CollisionShape::CYLINDER: physicscomponent->box.halfextents.z = args.fValue; break; } physicscomponent->physicsobject.reset(); })); ZSlider.SetLocalizationEnabled(false); AddWidget(&ZSlider); massSlider.Create(0, 10, 1, 100000, "Mass: "); massSlider.SetTooltip("Set the mass amount for the physics engine."); massSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->mass = args.fValue; })); AddWidget(&massSlider); frictionSlider.Create(0, 1, 0.5f, 100000, "Friction: "); frictionSlider.SetTooltip("Set the friction amount for the physics engine."); frictionSlider.OnSlide(forEachSelectedPhysicsComponent([](auto physicscomponent, auto args) { physicscomponent->friction = args.fValue; })); AddWidget(&frictionSlider); restitutionSlider.Create(0, 1, 0, 100000, "Restitution: "); restitutionSlider.SetTooltip("Set the restitution amount for the physics engine."); restitutionSlider.OnSlide(forEachSelectedPhysicsComponent([](auto physicscomponent, auto args) { physicscomponent->restitution = args.fValue; })); AddWidget(&restitutionSlider); lineardampingSlider.Create(0, 1, 0, 100000, "Linear Damping: "); lineardampingSlider.SetTooltip("Set the linear damping amount for the physics engine."); lineardampingSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->damping_linear = args.fValue; })); AddWidget(&lineardampingSlider); angulardampingSlider.Create(0, 1, 0, 100000, "Angular Damping: "); angulardampingSlider.SetTooltip("Set the angular damping amount for the physics engine."); angulardampingSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->damping_angular = args.fValue; })); AddWidget(&angulardampingSlider); buoyancySlider.Create(0, 2, 0, 1000, "Buoyancy: "); buoyancySlider.SetTooltip("Higher buoyancy will make the bodies float up faster in water."); buoyancySlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->buoyancy = args.fValue; })); AddWidget(&buoyancySlider); physicsMeshLODSlider.Create(0, 6, 0, 6, "Use Mesh LOD: "); physicsMeshLODSlider.SetTooltip("Specify which LOD to use for triangle mesh physics."); physicsMeshLODSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { if (physicscomponent->mesh_lod != uint32_t(args.iValue)) { physicscomponent->physicsobject.reset(); // will be recreated automatically physicscomponent->mesh_lod = uint32_t(args.iValue); } physicscomponent->mesh_lod = uint32_t(args.iValue); })); AddWidget(&physicsMeshLODSlider); kinematicCheckBox.Create("Kinematic: "); kinematicCheckBox.SetTooltip("Toggle kinematic behaviour."); kinematicCheckBox.SetCheck(false); kinematicCheckBox.OnClick(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->SetKinematic(args.bValue); })); AddWidget(&kinematicCheckBox); disabledeactivationCheckBox.Create("Disable Deactivation: "); disabledeactivationCheckBox.SetTooltip("Toggle kinematic behaviour."); disabledeactivationCheckBox.SetCheck(false); disabledeactivationCheckBox.OnClick(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->SetDisableDeactivation(args.bValue); })); AddWidget(&disabledeactivationCheckBox); startDeactivatedCheckBox.Create("Start deactivated: "); startDeactivatedCheckBox.SetTooltip("If enabled, the rigid body will start in a deactivated state.\nEven if the body is dynamic (non-kinematic, mass > 0), it will not fall unless interacted with."); startDeactivatedCheckBox.SetCheck(false); startDeactivatedCheckBox.OnClick(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->SetStartDeactivated(args.bValue); })); AddWidget(&startDeactivatedCheckBox); offsetXSlider.Create(-10, 10, 0, 100000, "Local Offset X: "); offsetXSlider.SetTooltip("Set a local offset relative to the object transform"); offsetXSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->local_offset.x = args.fValue; })); AddWidget(&offsetXSlider); offsetYSlider.Create(-10, 10, 0, 100000, "Local Offset Y: "); offsetYSlider.SetTooltip("Set a local offset relative to the object transform"); offsetYSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->local_offset.y = args.fValue; })); AddWidget(&offsetYSlider); offsetZSlider.Create(-10, 10, 0, 100000, "Local Offset Z: "); offsetZSlider.SetTooltip("Set a local offset relative to the object transform"); offsetZSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->local_offset.z = args.fValue; })); AddWidget(&offsetZSlider); physicsDebugCheckBox.Create(ICON_EYE " Physics visualizer: "); physicsDebugCheckBox.SetTooltip("Visualize the physics world"); physicsDebugCheckBox.OnClick([=](wi::gui::EventArgs args) { wi::physics::SetDebugDrawEnabled(args.bValue); editor->generalWnd.physicsDebugCheckBox.SetCheck(args.bValue); editor->componentsWnd.constraintWnd.physicsDebugCheckBox.SetCheck(args.bValue); }); physicsDebugCheckBox.SetCheck(wi::physics::IsDebugDrawEnabled()); AddWidget(&physicsDebugCheckBox); characterCheckBox.Create(ICON_PHYSICS_CHARACTER " Character physics: "); characterCheckBox.SetTooltip("Enable physics-driven character"); characterCheckBox.OnClick(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->SetCharacterPhysics(args.bValue); })); AddWidget(&characterCheckBox); characterLabel.Create("CharacterLabel"); characterLabel.SetText("Notes about physics-driven character:\n- The capsule shape is recommended for a physics character.\n- The friction and mass of the physics character are taken from the standard rigid body parameters.\n- This is different from CharacterComponent which uses custom character movement logic."); characterLabel.SetFitTextEnabled(true); AddWidget(&characterLabel); characterSlopeSlider.Create(0, 90, 50, 90, "Max slope angle: "); characterSlopeSlider.SetTooltip("Specify the slope angle that the character can stand on."); characterSlopeSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->character.maxSlopeAngle = wi::math::DegreesToRadians(args.fValue); })); AddWidget(&characterSlopeSlider); characterGravitySlider.Create(0, 4, 1, 100, "Gravity factor: "); characterGravitySlider.SetTooltip("Specify the strength of gravity acting on the character."); characterGravitySlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->character.gravityFactor = args.fValue; })); AddWidget(&characterGravitySlider); vehicleLabel.Create("VehicleLabel"); std::string tips; tips += "Vehicle physics tips:\n"; tips += "- The vehicle's base shape can be configured in the standard shape settings above\n"; tips += "- A vehicle is always facing forward in +Z direction by default\n"; tips += "- Check that mass is set higher than the default 1kg, somewhere around ~1000 kg\n"; tips += "- Enable physics visualizer while editing vehicle parameters\n"; tips += "- You can reset vehicles by Ctrl + left click on the physics toggle button\n"; vehicleLabel.SetText(tips); vehicleLabel.SetFitTextEnabled(true); AddWidget(&vehicleLabel); vehicleCombo.Create(ICON_VEHICLE " Vehicle physics: "); vehicleCombo.AddItem("None", (uint64_t)RigidBodyPhysicsComponent::Vehicle::Type::None); vehicleCombo.AddItem("Car", (uint64_t)RigidBodyPhysicsComponent::Vehicle::Type::Car); vehicleCombo.AddItem("Motorcycle", (uint64_t)RigidBodyPhysicsComponent::Vehicle::Type::Motorcycle); vehicleCombo.OnSelect(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.type = (RigidBodyPhysicsComponent::Vehicle::Type)args.userdata; })); AddWidget(&vehicleCombo); vehicleCollisionCombo.Create("Vehicle collision: "); vehicleCollisionCombo.AddItem("Ray", (uint64_t)RigidBodyPhysicsComponent::Vehicle::CollisionMode::Ray); vehicleCollisionCombo.AddItem("Sphere", (uint64_t)RigidBodyPhysicsComponent::Vehicle::CollisionMode::Sphere); vehicleCollisionCombo.AddItem("Cylinder", (uint64_t)RigidBodyPhysicsComponent::Vehicle::CollisionMode::Cylinder); vehicleCollisionCombo.OnSelect(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.collision_mode = (RigidBodyPhysicsComponent::Vehicle::CollisionMode)args.userdata; })); AddWidget(&vehicleCollisionCombo); wheelRadiusSlider.Create(0.001f, 10, 1, 1000, "Wheel radius: "); wheelRadiusSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.wheel_radius = args.fValue; })); AddWidget(&wheelRadiusSlider); wheelWidthSlider.Create(0.001f, 10, 1, 1000, "Wheel width: "); wheelWidthSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.wheel_width = args.fValue; })); AddWidget(&wheelWidthSlider); chassisHalfWidthSlider.Create(0, 10, 1, 1000, "Chassis width: "); chassisHalfWidthSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.chassis_half_width = args.fValue; })); AddWidget(&chassisHalfWidthSlider); chassisHalfHeightSlider.Create(-10, 10, 1, 1000, "Chassis height: "); chassisHalfHeightSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.chassis_half_height = args.fValue; })); AddWidget(&chassisHalfHeightSlider); chassisHalfLengthSlider.Create(0, 10, 1, 1000, "Chassis length: "); chassisHalfLengthSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.chassis_half_length = args.fValue; })); AddWidget(&chassisHalfLengthSlider); frontWheelOffsetSlider.Create(-10, 10, 0, 1000, "Front wheel offset: "); frontWheelOffsetSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.front_wheel_offset = args.fValue; })); AddWidget(&frontWheelOffsetSlider); rearWheelOffsetSlider.Create(-10, 10, 0, 1000, "Rear wheel offset: "); rearWheelOffsetSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.rear_wheel_offset = args.fValue; })); AddWidget(&rearWheelOffsetSlider); maxTorqueSlider.Create(0, 1000, 0, 1000, "Max Torque: "); maxTorqueSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.max_engine_torque = args.fValue; })); AddWidget(&maxTorqueSlider); clutchStrengthSlider.Create(0, 100, 0, 1000, "Clutch Strength: "); clutchStrengthSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.clutch_strength = args.fValue; })); AddWidget(&clutchStrengthSlider); maxRollAngleSlider.Create(0, 180, 0, 180, "Max Roll Angle: "); maxRollAngleSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.max_roll_angle = wi::math::DegreesToRadians(args.fValue); })); AddWidget(&maxRollAngleSlider); maxSteeringAngleSlider.Create(0, 90, 0, 90, "Max Steering Angle: "); maxSteeringAngleSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.max_steering_angle = wi::math::DegreesToRadians(args.fValue); })); AddWidget(&maxSteeringAngleSlider); fSuspensionMinSlider.Create(0, 2, 0, 200, "F. Susp. Min Length: "); fSuspensionMinSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.front_suspension.min_length = args.fValue; })); AddWidget(&fSuspensionMinSlider); fSuspensionMaxSlider.Create(0, 2, 0, 200, "F. Susp. Max Length: "); fSuspensionMaxSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.front_suspension.max_length = args.fValue; physicscomponent->SetRefreshParametersNeeded(); })); AddWidget(&fSuspensionMaxSlider); fSuspensionFrequencySlider.Create(0, 2, 0, 200, "F. Susp. Frequency: "); fSuspensionFrequencySlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.front_suspension.frequency = args.fValue; })); AddWidget(&fSuspensionFrequencySlider); fSuspensionDampingSlider.Create(0, 2, 0, 200, "F. Susp. Damping: "); fSuspensionDampingSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.front_suspension.damping = args.fValue; })); AddWidget(&fSuspensionDampingSlider); rSuspensionMinSlider.Create(0, 2, 0, 200, "R. Susp. Min Length: "); rSuspensionMinSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.rear_suspension.min_length = args.fValue; })); AddWidget(&rSuspensionMinSlider); rSuspensionMaxSlider.Create(0, 2, 0, 200, "R. Susp. Max Length: "); rSuspensionMaxSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.rear_suspension.max_length = args.fValue; })); AddWidget(&rSuspensionMaxSlider); rSuspensionFrequencySlider.Create(0, 2, 0, 200, "R. Susp. Frequency: "); rSuspensionFrequencySlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.rear_suspension.frequency = args.fValue; })); AddWidget(&rSuspensionFrequencySlider); rSuspensionDampingSlider.Create(0, 2, 0, 200, "R. Susp. Damping: "); rSuspensionDampingSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.rear_suspension.damping = args.fValue; })); AddWidget(&rSuspensionDampingSlider); motorcycleFBrakeSuspensionAngleSlider.Create(0, 90, 0, 90, "F. Brake Susp. Angle: "); motorcycleFBrakeSuspensionAngleSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.motorcycle.front_suspension_angle = wi::math::DegreesToRadians(args.fValue); })); AddWidget(&motorcycleFBrakeSuspensionAngleSlider); motorcycleFBrakeTorqueSlider.Create(0, 2000, 0, 2000, "F. Brake Torque: "); motorcycleFBrakeTorqueSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.motorcycle.front_brake_torque = args.fValue; })); AddWidget(&motorcycleFBrakeTorqueSlider); motorcycleRBrakeTorqueSlider.Create(0, 2000, 0, 2000, "R. Brake Torque: "); motorcycleRBrakeTorqueSlider.OnSlide(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.motorcycle.rear_brake_torque = args.fValue; })); AddWidget(&motorcycleRBrakeTorqueSlider); fourwheelCheckbox.Create("4-wheel drive: "); fourwheelCheckbox.OnClick(forEachSelectedPhysicsComponentWithRefresh([](auto physicscomponent, auto args) { physicscomponent->vehicle.car.four_wheel_drive = args.bValue; })); AddWidget(&fourwheelCheckbox); motorleanCheckbox.Create("Motorcycle lean control: "); // Here we don't need to recreate physics!! motorleanCheckbox.OnClick(forEachSelectedPhysicsComponent([](auto physicscomponent, auto args) { physicscomponent->vehicle.motorcycle.lean_control = args.bValue; })); AddWidget(&motorleanCheckbox); driveCheckbox.Create(ICON_VEHICLE " Drive in Editor: "); driveCheckbox.SetTooltip("Override editor controls to drive this vehicle.\nControls: WASD for driving, left-right for camera rotation, Left Shift: brake, Space: handbrake\nController: R2: forward, L2: backward, Square/X: handbrake."); driveCheckbox.OnClick([=](wi::gui::EventArgs args) { if (args.bValue) { driving_entity = entity; } else { driving_entity = INVALID_ENTITY; } }); AddWidget(&driveCheckbox); wheelEntityFrontLeftCombo.Create("Front Left Wheel: "); wheelEntityFrontLeftCombo.SetTooltip("Map an entity transform to this wheel, so the wheel graphics can be animated by physics (optional).\nThe entity name must contain the word wheel to be displayed here."); wheelEntityFrontLeftCombo.OnSelect(forEachSelectedPhysicsComponent([](auto physicscomponent, auto args) { physicscomponent->vehicle.wheel_entity_front_left = args.userdata; })); AddWidget(&wheelEntityFrontLeftCombo); wheelEntityFrontRightCombo.Create("Front Right Wheel: "); wheelEntityFrontRightCombo.SetTooltip("Map an entity transform to this wheel, so the wheel graphics can be animated by physics (optional).\nThe entity name must contain the word wheel to be displayed here."); wheelEntityFrontRightCombo.OnSelect(forEachSelectedPhysicsComponent([](auto physicscomponent, auto args) { physicscomponent->vehicle.wheel_entity_front_right = args.userdata; })); AddWidget(&wheelEntityFrontRightCombo); wheelEntityRearLeftCombo.Create("Rear Left Wheel: "); wheelEntityRearLeftCombo.SetTooltip("Map an entity transform to this wheel, so the wheel graphics can be animated by physics (optional).\nThe entity name must contain the word wheel to be displayed here."); wheelEntityRearLeftCombo.OnSelect(forEachSelectedPhysicsComponent([](auto physicscomponent, auto args) { physicscomponent->vehicle.wheel_entity_rear_left = args.userdata; })); AddWidget(&wheelEntityRearLeftCombo); wheelEntityRearRightCombo.Create("Rear Right Wheel: "); wheelEntityRearRightCombo.SetTooltip("Map an entity transform to this wheel, so the wheel graphics can be animated by physics (optional).\nThe entity name must contain the word wheel to be displayed here."); wheelEntityRearRightCombo.OnSelect(forEachSelectedPhysicsComponent([](auto physicscomponent, auto args) { physicscomponent->vehicle.wheel_entity_rear_right = args.userdata; })); AddWidget(&wheelEntityRearRightCombo); SetMinimized(true); SetVisible(false); SetEntity(INVALID_ENTITY); } void RigidBodyWindow::RefreshShapeType() { Scene& scene = editor->GetCurrentScene(); const RigidBodyPhysicsComponent* physicscomponent = scene.rigidbodies.GetComponent(entity); if (physicscomponent == nullptr) return; switch (physicscomponent->shape) { case RigidBodyPhysicsComponent::CollisionShape::BOX: XSlider.SetVisible(true); YSlider.SetVisible(true); ZSlider.SetVisible(true); XSlider.SetText("Width: "); YSlider.SetText("Height: "); ZSlider.SetText("Depth: "); XSlider.SetValue(physicscomponent->box.halfextents.x); YSlider.SetValue(physicscomponent->box.halfextents.y); ZSlider.SetValue(physicscomponent->box.halfextents.z); break; case RigidBodyPhysicsComponent::CollisionShape::SPHERE: XSlider.SetVisible(true); YSlider.SetVisible(false); ZSlider.SetVisible(false); XSlider.SetText("Radius: "); XSlider.SetValue(physicscomponent->sphere.radius); break; case RigidBodyPhysicsComponent::CollisionShape::CAPSULE: case RigidBodyPhysicsComponent::CollisionShape::CYLINDER: XSlider.SetVisible(true); YSlider.SetVisible(true); ZSlider.SetVisible(false); XSlider.SetText("Height: "); YSlider.SetText("Radius: "); XSlider.SetValue(physicscomponent->capsule.height); YSlider.SetValue(physicscomponent->capsule.radius); break; case RigidBodyPhysicsComponent::CollisionShape::CONVEX_HULL: case RigidBodyPhysicsComponent::CollisionShape::TRIANGLE_MESH: default: XSlider.SetVisible(false); YSlider.SetVisible(false); ZSlider.SetVisible(false); break; } } void RigidBodyWindow::SetEntity(Entity entity) { Scene& scene = editor->GetCurrentScene(); const RigidBodyPhysicsComponent* physicsComponent = scene.rigidbodies.GetComponent(entity); if (physicsComponent != nullptr) { if (this->entity == entity) return; this->entity = entity; massSlider.SetValue(physicsComponent->mass); frictionSlider.SetValue(physicsComponent->friction); restitutionSlider.SetValue(physicsComponent->restitution); lineardampingSlider.SetValue(physicsComponent->damping_linear); angulardampingSlider.SetValue(physicsComponent->damping_angular); buoyancySlider.SetValue(physicsComponent->buoyancy); physicsMeshLODSlider.SetValue(float(physicsComponent->mesh_lod)); offsetXSlider.SetValue(physicsComponent->local_offset.x); offsetYSlider.SetValue(physicsComponent->local_offset.y); offsetZSlider.SetValue(physicsComponent->local_offset.z); kinematicCheckBox.SetCheck(physicsComponent->IsKinematic()); disabledeactivationCheckBox.SetCheck(physicsComponent->IsDisableDeactivation()); startDeactivatedCheckBox.SetCheck(physicsComponent->IsStartDeactivated()); collisionShapeComboBox.SetSelectedByUserdataWithoutCallback((uint64_t)physicsComponent->shape); characterCheckBox.SetCheck(physicsComponent->IsCharacterPhysics()); characterSlopeSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->character.maxSlopeAngle)); characterGravitySlider.SetValue(physicsComponent->character.gravityFactor); vehicleCombo.SetSelectedByUserdataWithoutCallback((uint64_t)physicsComponent->vehicle.type); vehicleCollisionCombo.SetSelectedByUserdataWithoutCallback((uint64_t)physicsComponent->vehicle.collision_mode); wheelRadiusSlider.SetValue(physicsComponent->vehicle.wheel_radius); wheelWidthSlider.SetValue(physicsComponent->vehicle.wheel_width); chassisHalfWidthSlider.SetValue(physicsComponent->vehicle.chassis_half_width); chassisHalfHeightSlider.SetValue(physicsComponent->vehicle.chassis_half_height); chassisHalfLengthSlider.SetValue(physicsComponent->vehicle.chassis_half_length); frontWheelOffsetSlider.SetValue(physicsComponent->vehicle.front_wheel_offset); rearWheelOffsetSlider.SetValue(physicsComponent->vehicle.rear_wheel_offset); maxTorqueSlider.SetValue(physicsComponent->vehicle.max_engine_torque); clutchStrengthSlider.SetValue(physicsComponent->vehicle.clutch_strength); maxRollAngleSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->vehicle.max_roll_angle)); maxSteeringAngleSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->vehicle.max_steering_angle)); fourwheelCheckbox.SetCheck(physicsComponent->vehicle.car.four_wheel_drive); motorleanCheckbox.SetCheck(physicsComponent->vehicle.motorcycle.lean_control); fSuspensionMinSlider.SetValue(physicsComponent->vehicle.front_suspension.min_length); fSuspensionMaxSlider.SetValue(physicsComponent->vehicle.front_suspension.max_length); fSuspensionFrequencySlider.SetValue(physicsComponent->vehicle.front_suspension.frequency); fSuspensionDampingSlider.SetValue(physicsComponent->vehicle.front_suspension.damping); rSuspensionMinSlider.SetValue(physicsComponent->vehicle.rear_suspension.min_length); rSuspensionMaxSlider.SetValue(physicsComponent->vehicle.rear_suspension.max_length); rSuspensionFrequencySlider.SetValue(physicsComponent->vehicle.rear_suspension.frequency); rSuspensionDampingSlider.SetValue(physicsComponent->vehicle.rear_suspension.damping); motorcycleFBrakeSuspensionAngleSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->vehicle.motorcycle.front_suspension_angle)); motorcycleFBrakeTorqueSlider.SetValue(physicsComponent->vehicle.motorcycle.front_brake_torque); motorcycleRBrakeTorqueSlider.SetValue(physicsComponent->vehicle.motorcycle.rear_brake_torque); wheelEntityFrontLeftCombo.ClearItems(); wheelEntityFrontLeftCombo.AddItem("INVALID_ENTITY", (uint64_t)INVALID_ENTITY); wheelEntityFrontRightCombo.ClearItems(); wheelEntityFrontRightCombo.AddItem("INVALID_ENTITY", (uint64_t)INVALID_ENTITY); wheelEntityRearLeftCombo.ClearItems(); wheelEntityRearLeftCombo.AddItem("INVALID_ENTITY", (uint64_t)INVALID_ENTITY); wheelEntityRearRightCombo.ClearItems(); wheelEntityRearRightCombo.AddItem("INVALID_ENTITY", (uint64_t)INVALID_ENTITY); for (size_t i = 0; i < scene.transforms.GetCount(); ++i) { Entity wheelEntity = scene.transforms.GetEntity(i); if (!scene.Entity_IsDescendant(wheelEntity, entity)) continue; const NameComponent* name = scene.names.GetComponent(wheelEntity); if (name != nullptr && wi::helper::toUpper(name->name).find("WHEEL") != std::string::npos) { wheelEntityFrontLeftCombo.AddItem(name->name, (uint64_t)wheelEntity); wheelEntityFrontRightCombo.AddItem(name->name, (uint64_t)wheelEntity); wheelEntityRearLeftCombo.AddItem(name->name, (uint64_t)wheelEntity); wheelEntityRearRightCombo.AddItem(name->name, (uint64_t)wheelEntity); } } wheelEntityFrontLeftCombo.SetSelectedByUserdataWithoutCallback(physicsComponent->vehicle.wheel_entity_front_left); wheelEntityFrontRightCombo.SetSelectedByUserdataWithoutCallback(physicsComponent->vehicle.wheel_entity_front_right); wheelEntityRearLeftCombo.SetSelectedByUserdataWithoutCallback(physicsComponent->vehicle.wheel_entity_rear_left); wheelEntityRearRightCombo.SetSelectedByUserdataWithoutCallback(physicsComponent->vehicle.wheel_entity_rear_right); RefreshShapeType(); } else { this->entity = INVALID_ENTITY; } } void RigidBodyWindow::ResizeLayout() { RefreshShapeType(); wi::gui::Window::ResizeLayout(); layout.margin_left = 145; layout.add(collisionShapeComboBox); layout.add(XSlider); layout.add(YSlider); layout.add(ZSlider); layout.add(massSlider); layout.add(frictionSlider); layout.add(restitutionSlider); layout.add(lineardampingSlider); layout.add(angulardampingSlider); layout.add(buoyancySlider); layout.add(physicsMeshLODSlider); layout.add(offsetXSlider); layout.add(offsetYSlider); layout.add(offsetZSlider); layout.add_right(startDeactivatedCheckBox); layout.add_right(disabledeactivationCheckBox); layout.add_right(kinematicCheckBox); layout.add_right(physicsDebugCheckBox); layout.jump(); layout.add_right(characterCheckBox); if (characterCheckBox.GetCheck()) { characterLabel.SetVisible(true); characterSlopeSlider.SetVisible(true); characterGravitySlider.SetVisible(true); layout.add_fullwidth(characterLabel); layout.add(characterSlopeSlider); layout.add(characterGravitySlider); } else { characterLabel.SetVisible(false); characterSlopeSlider.SetVisible(false); characterGravitySlider.SetVisible(false); } layout.jump(); layout.add(vehicleCombo); if (vehicleCombo.GetSelected() > 0) { vehicleLabel.SetVisible(true); vehicleCollisionCombo.SetVisible(true); wheelRadiusSlider.SetVisible(true); wheelWidthSlider.SetVisible(true); chassisHalfWidthSlider.SetVisible(true); chassisHalfHeightSlider.SetVisible(true); chassisHalfLengthSlider.SetVisible(true); frontWheelOffsetSlider.SetVisible(true); rearWheelOffsetSlider.SetVisible(true); maxTorqueSlider.SetVisible(true); clutchStrengthSlider.SetVisible(true); maxRollAngleSlider.SetVisible(true); maxSteeringAngleSlider.SetVisible(true); driveCheckbox.SetVisible(true); fSuspensionMinSlider.SetVisible(true); fSuspensionMaxSlider.SetVisible(true); fSuspensionFrequencySlider.SetVisible(true); fSuspensionDampingSlider.SetVisible(true); rSuspensionMinSlider.SetVisible(true); rSuspensionMaxSlider.SetVisible(true); rSuspensionFrequencySlider.SetVisible(true); rSuspensionDampingSlider.SetVisible(true); layout.add_fullwidth(vehicleLabel); layout.add_right(driveCheckbox); layout.add(vehicleCollisionCombo); layout.add(wheelRadiusSlider); layout.add(wheelWidthSlider); layout.add(chassisHalfWidthSlider); layout.add(chassisHalfHeightSlider); layout.add(chassisHalfLengthSlider); layout.add(frontWheelOffsetSlider); layout.add(rearWheelOffsetSlider); layout.add(maxTorqueSlider); layout.add(clutchStrengthSlider); layout.add(maxRollAngleSlider); layout.add(maxSteeringAngleSlider); layout.add(fSuspensionMinSlider); layout.add(fSuspensionMaxSlider); layout.add(fSuspensionFrequencySlider); layout.add(fSuspensionDampingSlider); layout.add(rSuspensionMinSlider); layout.add(rSuspensionMaxSlider); layout.add(rSuspensionFrequencySlider); layout.add(rSuspensionDampingSlider); RigidBodyPhysicsComponent::Vehicle::Type type = (RigidBodyPhysicsComponent::Vehicle::Type)vehicleCombo.GetSelectedUserdata(); switch (type) { case wi::scene::RigidBodyPhysicsComponent::Vehicle::Type::Car: motorcycleFBrakeSuspensionAngleSlider.SetVisible(false); motorcycleFBrakeTorqueSlider.SetVisible(false); motorcycleRBrakeTorqueSlider.SetVisible(false); wheelEntityFrontLeftCombo.SetVisible(true); wheelEntityFrontRightCombo.SetVisible(true); wheelEntityRearLeftCombo.SetVisible(true); wheelEntityRearRightCombo.SetVisible(true); layout.add(wheelEntityFrontLeftCombo); layout.add(wheelEntityFrontRightCombo); layout.add(wheelEntityRearLeftCombo); layout.add(wheelEntityRearRightCombo); fourwheelCheckbox.SetVisible(true); motorleanCheckbox.SetVisible(false); layout.add_right(fourwheelCheckbox); break; case wi::scene::RigidBodyPhysicsComponent::Vehicle::Type::Motorcycle: motorcycleFBrakeSuspensionAngleSlider.SetVisible(true); motorcycleFBrakeTorqueSlider.SetVisible(true); motorcycleRBrakeTorqueSlider.SetVisible(true); layout.add(motorcycleFBrakeSuspensionAngleSlider); layout.add(motorcycleFBrakeTorqueSlider); layout.add(motorcycleRBrakeTorqueSlider); wheelEntityFrontLeftCombo.SetVisible(true); wheelEntityFrontRightCombo.SetVisible(false); wheelEntityRearLeftCombo.SetVisible(true); wheelEntityRearRightCombo.SetVisible(false); layout.add(wheelEntityFrontLeftCombo); layout.add(wheelEntityRearLeftCombo); motorleanCheckbox.SetVisible(true); fourwheelCheckbox.SetVisible(false); layout.add_right(motorleanCheckbox); break; default: break; } } else { vehicleLabel.SetVisible(false); vehicleCollisionCombo.SetVisible(false); wheelRadiusSlider.SetVisible(false); wheelWidthSlider.SetVisible(false); chassisHalfWidthSlider.SetVisible(false); chassisHalfHeightSlider.SetVisible(false); chassisHalfLengthSlider.SetVisible(false); frontWheelOffsetSlider.SetVisible(false); rearWheelOffsetSlider.SetVisible(false); maxTorqueSlider.SetVisible(false); clutchStrengthSlider.SetVisible(false); maxRollAngleSlider.SetVisible(false); maxSteeringAngleSlider.SetVisible(false); wheelEntityFrontLeftCombo.SetVisible(false); wheelEntityFrontRightCombo.SetVisible(false); wheelEntityRearLeftCombo.SetVisible(false); wheelEntityRearRightCombo.SetVisible(false); fourwheelCheckbox.SetVisible(false); motorleanCheckbox.SetVisible(false); driveCheckbox.SetVisible(false); fSuspensionMinSlider.SetVisible(false); fSuspensionMaxSlider.SetVisible(false); fSuspensionFrequencySlider.SetVisible(false); fSuspensionDampingSlider.SetVisible(false); rSuspensionMinSlider.SetVisible(false); rSuspensionMaxSlider.SetVisible(false); rSuspensionFrequencySlider.SetVisible(false); rSuspensionDampingSlider.SetVisible(false); motorcycleFBrakeSuspensionAngleSlider.SetVisible(false); motorcycleFBrakeTorqueSlider.SetVisible(false); motorcycleRBrakeTorqueSlider.SetVisible(false); } }