godot/modules/godot_physics_3d/godot_step_3d.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

419 lines
14 KiB
C++
Raw Normal View History

2014-02-10 01:10:30 +00:00
/**************************************************************************/
/* godot_step_3d.cpp */
2014-02-10 01:10:30 +00:00
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "godot_step_3d.h"
#include "godot_joint_3d.h"
2014-02-10 01:10:30 +00:00
#include "core/object/worker_thread_pool.h"
#include "core/os/os.h"
#define BODY_ISLAND_COUNT_RESERVE 128
#define BODY_ISLAND_SIZE_RESERVE 512
#define ISLAND_COUNT_RESERVE 128
#define ISLAND_SIZE_RESERVE 512
#define CONSTRAINT_COUNT_RESERVE 1024
void GodotStep3D::_populate_island(GodotBody3D *p_body, LocalVector<GodotBody3D *> &p_body_island, LocalVector<GodotConstraint3D *> &p_constraint_island) {
2014-02-10 01:10:30 +00:00
p_body->set_island_step(_step);
if (p_body->get_mode() > PhysicsServer3D::BODY_MODE_KINEMATIC) {
// Only rigid bodies are tested for activation.
p_body_island.push_back(p_body);
}
for (const KeyValue<GodotConstraint3D *, int> &E : p_body->get_constraint_map()) {
2022-04-05 10:40:26 +00:00
GodotConstraint3D *constraint = const_cast<GodotConstraint3D *>(E.key);
if (constraint->get_island_step() == _step) {
continue; // Already processed.
}
constraint->set_island_step(_step);
p_constraint_island.push_back(constraint);
all_constraints.push_back(constraint);
2014-02-10 01:10:30 +00:00
// Find connected rigid bodies.
for (int i = 0; i < constraint->get_body_count(); i++) {
2021-08-09 20:13:42 +00:00
if (i == E.value) {
2014-02-10 01:10:30 +00:00
continue;
}
GodotBody3D *other_body = constraint->get_body_ptr()[i];
if (other_body->get_island_step() == _step) {
continue; // Already processed.
}
if (other_body->get_mode() == PhysicsServer3D::BODY_MODE_STATIC) {
continue; // Static bodies don't connect islands.
}
_populate_island(other_body, p_body_island, p_constraint_island);
}
// Find connected soft bodies.
for (int i = 0; i < constraint->get_soft_body_count(); i++) {
GodotSoftBody3D *soft_body = constraint->get_soft_body_ptr(i);
if (soft_body->get_island_step() == _step) {
continue; // Already processed.
}
_populate_island_soft_body(soft_body, p_body_island, p_constraint_island);
2014-02-10 01:10:30 +00:00
}
}
}
void GodotStep3D::_populate_island_soft_body(GodotSoftBody3D *p_soft_body, LocalVector<GodotBody3D *> &p_body_island, LocalVector<GodotConstraint3D *> &p_constraint_island) {
p_soft_body->set_island_step(_step);
for (const GodotConstraint3D *E : p_soft_body->get_constraints()) {
GodotConstraint3D *constraint = const_cast<GodotConstraint3D *>(E);
if (constraint->get_island_step() == _step) {
continue; // Already processed.
}
constraint->set_island_step(_step);
p_constraint_island.push_back(constraint);
all_constraints.push_back(constraint);
// Find connected rigid bodies.
for (int i = 0; i < constraint->get_body_count(); i++) {
GodotBody3D *body = constraint->get_body_ptr()[i];
if (body->get_island_step() == _step) {
continue; // Already processed.
}
if (body->get_mode() == PhysicsServer3D::BODY_MODE_STATIC) {
continue; // Static bodies don't connect islands.
}
_populate_island(body, p_body_island, p_constraint_island);
}
}
}
void GodotStep3D::_setup_constraint(uint32_t p_constraint_index, void *p_userdata) {
GodotConstraint3D *constraint = all_constraints[p_constraint_index];
constraint->setup(delta);
}
void GodotStep3D::_pre_solve_island(LocalVector<GodotConstraint3D *> &p_constraint_island) const {
uint32_t constraint_count = p_constraint_island.size();
uint32_t valid_constraint_count = 0;
for (uint32_t constraint_index = 0; constraint_index < constraint_count; ++constraint_index) {
GodotConstraint3D *constraint = p_constraint_island[constraint_index];
if (p_constraint_island[constraint_index]->pre_solve(delta)) {
// Keep this constraint for solving.
p_constraint_island[valid_constraint_count++] = constraint;
}
2014-02-10 01:10:30 +00:00
}
p_constraint_island.resize(valid_constraint_count);
2014-02-10 01:10:30 +00:00
}
void GodotStep3D::_solve_island(uint32_t p_island_index, void *p_userdata) {
LocalVector<GodotConstraint3D *> &constraint_island = constraint_islands[p_island_index];
int current_priority = 1;
Huge Amount of BugFix -=-=-=-=-=-=-=-=-=-=- -Fixes to Collada Exporter (avoid crash situtions) -Fixed to Collada Importer (Fixed Animation Optimizer Bugs) -Fixes to RigidBody/RigidBody2D body_enter/body_exit, was buggy -Fixed ability for RigidBody/RigidBody2D to get contacts reported and bodyin/out in Kinematic mode. -Added proper trigger support for 3D Physics shapes -Changed proper value for Z-Offset in OmniLight -Fixed spot attenuation bug in SpotLight -Fixed some 3D and 2D spatial soudn bugs related to distance attenuation. -Fixed bugs in EventPlayer (channels were muted by default) -Fix in ButtonGroup (get nodes in group are now returned in order) -Fixed Linear->SRGB Conversion, previous algo sucked, new algo works OK -Changed SRGB->Linear conversion to use hardware if supported, improves texture quality a lot -Fixed options for Y-Fov and X-Fov in camera, should be more intuitive. -Fixed bugs related to viewports and transparency Huge Amount of New Stuff: -=-=-=-=-=-=-=-==-=-=-=- -Ability to manually advance an AnimationPlayer that is inactive (with advance() function) -More work in WinRT platform -Added XY normalmap support, imports on this format by default. Reduces normlmap size and enables much nice compression using LATC -Added Anisotropic filter support to textures, can be specified on import -Added support for Non-Square, Isometric and Hexagonal tilemaps in TileMap. -Added Isometric Dungeon demo. -Added simple hexagonal map demo. -Added Truck-Town demo. Shows how most types of joints and vehicles are used. Please somebody make a nicer town, this one is too hardcore. -Added an Object-Picking API to both RigidBody and Area! (and relevant demo)
2014-10-03 03:10:51 +00:00
uint32_t constraint_count = constraint_island.size();
while (constraint_count > 0) {
for (int i = 0; i < iterations; i++) {
// Go through all iterations.
for (uint32_t constraint_index = 0; constraint_index < constraint_count; ++constraint_index) {
constraint_island[constraint_index]->solve(delta);
Huge Amount of BugFix -=-=-=-=-=-=-=-=-=-=- -Fixes to Collada Exporter (avoid crash situtions) -Fixed to Collada Importer (Fixed Animation Optimizer Bugs) -Fixes to RigidBody/RigidBody2D body_enter/body_exit, was buggy -Fixed ability for RigidBody/RigidBody2D to get contacts reported and bodyin/out in Kinematic mode. -Added proper trigger support for 3D Physics shapes -Changed proper value for Z-Offset in OmniLight -Fixed spot attenuation bug in SpotLight -Fixed some 3D and 2D spatial soudn bugs related to distance attenuation. -Fixed bugs in EventPlayer (channels were muted by default) -Fix in ButtonGroup (get nodes in group are now returned in order) -Fixed Linear->SRGB Conversion, previous algo sucked, new algo works OK -Changed SRGB->Linear conversion to use hardware if supported, improves texture quality a lot -Fixed options for Y-Fov and X-Fov in camera, should be more intuitive. -Fixed bugs related to viewports and transparency Huge Amount of New Stuff: -=-=-=-=-=-=-=-==-=-=-=- -Ability to manually advance an AnimationPlayer that is inactive (with advance() function) -More work in WinRT platform -Added XY normalmap support, imports on this format by default. Reduces normlmap size and enables much nice compression using LATC -Added Anisotropic filter support to textures, can be specified on import -Added support for Non-Square, Isometric and Hexagonal tilemaps in TileMap. -Added Isometric Dungeon demo. -Added simple hexagonal map demo. -Added Truck-Town demo. Shows how most types of joints and vehicles are used. Please somebody make a nicer town, this one is too hardcore. -Added an Object-Picking API to both RigidBody and Area! (and relevant demo)
2014-10-03 03:10:51 +00:00
}
}
// Check priority to keep only higher priority constraints.
uint32_t priority_constraint_count = 0;
++current_priority;
for (uint32_t constraint_index = 0; constraint_index < constraint_count; ++constraint_index) {
GodotConstraint3D *constraint = constraint_island[constraint_index];
if (constraint->get_priority() >= current_priority) {
// Keep this constraint for the next iteration.
constraint_island[priority_constraint_count++] = constraint;
Huge Amount of BugFix -=-=-=-=-=-=-=-=-=-=- -Fixes to Collada Exporter (avoid crash situtions) -Fixed to Collada Importer (Fixed Animation Optimizer Bugs) -Fixes to RigidBody/RigidBody2D body_enter/body_exit, was buggy -Fixed ability for RigidBody/RigidBody2D to get contacts reported and bodyin/out in Kinematic mode. -Added proper trigger support for 3D Physics shapes -Changed proper value for Z-Offset in OmniLight -Fixed spot attenuation bug in SpotLight -Fixed some 3D and 2D spatial soudn bugs related to distance attenuation. -Fixed bugs in EventPlayer (channels were muted by default) -Fix in ButtonGroup (get nodes in group are now returned in order) -Fixed Linear->SRGB Conversion, previous algo sucked, new algo works OK -Changed SRGB->Linear conversion to use hardware if supported, improves texture quality a lot -Fixed options for Y-Fov and X-Fov in camera, should be more intuitive. -Fixed bugs related to viewports and transparency Huge Amount of New Stuff: -=-=-=-=-=-=-=-==-=-=-=- -Ability to manually advance an AnimationPlayer that is inactive (with advance() function) -More work in WinRT platform -Added XY normalmap support, imports on this format by default. Reduces normlmap size and enables much nice compression using LATC -Added Anisotropic filter support to textures, can be specified on import -Added support for Non-Square, Isometric and Hexagonal tilemaps in TileMap. -Added Isometric Dungeon demo. -Added simple hexagonal map demo. -Added Truck-Town demo. Shows how most types of joints and vehicles are used. Please somebody make a nicer town, this one is too hardcore. -Added an Object-Picking API to both RigidBody and Area! (and relevant demo)
2014-10-03 03:10:51 +00:00
}
2014-02-10 01:10:30 +00:00
}
constraint_count = priority_constraint_count;
2014-02-10 01:10:30 +00:00
}
}
void GodotStep3D::_check_suspend(const LocalVector<GodotBody3D *> &p_body_island) const {
2014-02-10 01:10:30 +00:00
bool can_sleep = true;
uint32_t body_count = p_body_island.size();
for (uint32_t body_index = 0; body_index < body_count; ++body_index) {
GodotBody3D *body = p_body_island[body_index];
if (!body->sleep_test(delta)) {
2014-02-10 01:10:30 +00:00
can_sleep = false;
}
2014-02-10 01:10:30 +00:00
}
// Put all to sleep or wake up everyone.
for (uint32_t body_index = 0; body_index < body_count; ++body_index) {
GodotBody3D *body = p_body_island[body_index];
2014-02-10 01:10:30 +00:00
bool active = body->is_active();
2014-02-10 01:10:30 +00:00
if (active == can_sleep) {
body->set_active(!can_sleep);
}
2014-02-10 01:10:30 +00:00
}
}
void GodotStep3D::step(GodotSpace3D *p_space, real_t p_delta) {
2014-02-10 01:10:30 +00:00
p_space->lock(); // can't access space during this
p_space->setup(); //update inertias, etc
p_space->set_last_step(p_delta);
iterations = p_space->get_solver_iterations();
delta = p_delta;
const SelfList<GodotBody3D>::List *body_list = &p_space->get_active_body_list();
2014-02-10 01:10:30 +00:00
const SelfList<GodotSoftBody3D>::List *soft_body_list = &p_space->get_active_soft_body_list();
2014-02-10 01:10:30 +00:00
/* INTEGRATE FORCES */
uint64_t profile_begtime = OS::get_singleton()->get_ticks_usec();
uint64_t profile_endtime = 0;
2014-02-10 01:10:30 +00:00
int active_count = 0;
const SelfList<GodotBody3D> *b = body_list->first();
2014-02-10 01:10:30 +00:00
while (b) {
b->self()->integrate_forces(p_delta);
b = b->next();
active_count++;
}
/* UPDATE SOFT BODY MOTION */
const SelfList<GodotSoftBody3D> *sb = soft_body_list->first();
while (sb) {
sb->self()->predict_motion(p_delta);
sb = sb->next();
active_count++;
}
p_space->set_active_objects(active_count);
2014-02-10 01:10:30 +00:00
// Update the broadphase to register collision pairs.
p_space->update();
{ //profile
profile_endtime = OS::get_singleton()->get_ticks_usec();
p_space->set_elapsed_time(GodotSpace3D::ELAPSED_TIME_INTEGRATE_FORCES, profile_endtime - profile_begtime);
profile_begtime = profile_endtime;
}
/* GENERATE CONSTRAINT ISLANDS FOR MOVING AREAS */
uint32_t island_count = 0;
const SelfList<GodotArea3D>::List &aml = p_space->get_moved_area_list();
while (aml.first()) {
for (GodotConstraint3D *E : aml.first()->self()->get_constraints()) {
GodotConstraint3D *constraint = E;
if (constraint->get_island_step() == _step) {
continue;
}
constraint->set_island_step(_step);
// Each constraint can be on a separate island for areas as there's no solving phase.
++island_count;
if (constraint_islands.size() < island_count) {
constraint_islands.resize(island_count);
}
LocalVector<GodotConstraint3D *> &constraint_island = constraint_islands[island_count - 1];
constraint_island.clear();
all_constraints.push_back(constraint);
constraint_island.push_back(constraint);
}
p_space->area_remove_from_moved_list((SelfList<GodotArea3D> *)aml.first()); //faster to remove here
}
/* GENERATE CONSTRAINT ISLANDS FOR ACTIVE RIGID BODIES */
2014-02-10 01:10:30 +00:00
b = body_list->first();
uint32_t body_island_count = 0;
2014-02-10 01:10:30 +00:00
while (b) {
GodotBody3D *body = b->self();
2014-02-10 01:10:30 +00:00
if (body->get_island_step() != _step) {
++body_island_count;
if (body_islands.size() < body_island_count) {
body_islands.resize(body_island_count);
}
LocalVector<GodotBody3D *> &body_island = body_islands[body_island_count - 1];
body_island.clear();
body_island.reserve(BODY_ISLAND_SIZE_RESERVE);
2014-02-10 01:10:30 +00:00
++island_count;
if (constraint_islands.size() < island_count) {
constraint_islands.resize(island_count);
}
LocalVector<GodotConstraint3D *> &constraint_island = constraint_islands[island_count - 1];
constraint_island.clear();
constraint_island.reserve(ISLAND_SIZE_RESERVE);
2014-02-10 01:10:30 +00:00
_populate_island(body, body_island, constraint_island);
if (body_island.is_empty()) {
--body_island_count;
}
if (constraint_island.is_empty()) {
--island_count;
2014-02-10 01:10:30 +00:00
}
}
b = b->next();
}
/* GENERATE CONSTRAINT ISLANDS FOR ACTIVE SOFT BODIES */
sb = soft_body_list->first();
while (sb) {
GodotSoftBody3D *soft_body = sb->self();
2014-02-10 01:10:30 +00:00
if (soft_body->get_island_step() != _step) {
++body_island_count;
if (body_islands.size() < body_island_count) {
body_islands.resize(body_island_count);
}
LocalVector<GodotBody3D *> &body_island = body_islands[body_island_count - 1];
body_island.clear();
body_island.reserve(BODY_ISLAND_SIZE_RESERVE);
++island_count;
if (constraint_islands.size() < island_count) {
constraint_islands.resize(island_count);
}
LocalVector<GodotConstraint3D *> &constraint_island = constraint_islands[island_count - 1];
constraint_island.clear();
constraint_island.reserve(ISLAND_SIZE_RESERVE);
2014-02-10 01:10:30 +00:00
_populate_island_soft_body(soft_body, body_island, constraint_island);
if (body_island.is_empty()) {
--body_island_count;
}
if (constraint_island.is_empty()) {
--island_count;
}
}
sb = sb->next();
}
p_space->set_island_count((int)island_count);
{ //profile
profile_endtime = OS::get_singleton()->get_ticks_usec();
p_space->set_elapsed_time(GodotSpace3D::ELAPSED_TIME_GENERATE_ISLANDS, profile_endtime - profile_begtime);
profile_begtime = profile_endtime;
}
/* SETUP CONSTRAINTS / PROCESS COLLISIONS */
2014-02-10 01:10:30 +00:00
uint32_t total_constraint_count = all_constraints.size();
WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &GodotStep3D::_setup_constraint, nullptr, total_constraint_count, -1, true, SNAME("Physics3DConstraintSetup"));
WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
2014-02-10 01:10:30 +00:00
{ //profile
profile_endtime = OS::get_singleton()->get_ticks_usec();
p_space->set_elapsed_time(GodotSpace3D::ELAPSED_TIME_SETUP_CONSTRAINTS, profile_endtime - profile_begtime);
profile_begtime = profile_endtime;
}
/* PRE-SOLVE CONSTRAINT ISLANDS */
2014-02-10 01:10:30 +00:00
// WARNING: This doesn't run on threads, because it involves thread-unsafe processing.
for (uint32_t island_index = 0; island_index < island_count; ++island_index) {
_pre_solve_island(constraint_islands[island_index]);
}
/* SOLVE CONSTRAINT ISLANDS */
// WARNING: `_solve_island` modifies the constraint islands for optimization purpose,
// their content is not reliable after these calls and shouldn't be used anymore.
group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &GodotStep3D::_solve_island, nullptr, island_count, -1, true, SNAME("Physics3DConstraintSolveIslands"));
WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
2014-02-10 01:10:30 +00:00
{ //profile
profile_endtime = OS::get_singleton()->get_ticks_usec();
p_space->set_elapsed_time(GodotSpace3D::ELAPSED_TIME_SOLVE_CONSTRAINTS, profile_endtime - profile_begtime);
profile_begtime = profile_endtime;
}
2014-02-10 01:10:30 +00:00
/* INTEGRATE VELOCITIES */
b = body_list->first();
while (b) {
const SelfList<GodotBody3D> *n = b->next();
2014-02-10 01:10:30 +00:00
b->self()->integrate_velocities(p_delta);
b = n;
2014-02-10 01:10:30 +00:00
}
/* SLEEP / WAKE UP ISLANDS */
for (uint32_t island_index = 0; island_index < body_island_count; ++island_index) {
_check_suspend(body_islands[island_index]);
2014-02-10 01:10:30 +00:00
}
/* UPDATE SOFT BODY CONSTRAINTS */
sb = soft_body_list->first();
while (sb) {
sb->self()->solve_constraints(p_delta);
sb = sb->next();
}
{ //profile
profile_endtime = OS::get_singleton()->get_ticks_usec();
p_space->set_elapsed_time(GodotSpace3D::ELAPSED_TIME_INTEGRATE_VELOCITIES, profile_endtime - profile_begtime);
profile_begtime = profile_endtime;
}
all_constraints.clear();
2014-02-10 01:10:30 +00:00
p_space->unlock();
_step++;
}
GodotStep3D::GodotStep3D() {
body_islands.reserve(BODY_ISLAND_COUNT_RESERVE);
constraint_islands.reserve(ISLAND_COUNT_RESERVE);
all_constraints.reserve(CONSTRAINT_COUNT_RESERVE);
}
GodotStep3D::~GodotStep3D() {
2014-02-10 01:10:30 +00:00
}