Sorry for the vague title as I can't think of any way to phrase it any better but here is my situation.
I am currently writing a 3D rigid body physics simulator. For this here is my brief architecture:
There are broadly three structs for better organization: World struct which hold the world properties like gravity, etc etc but more importantly a list of bodies. Body struct which defines a single body it hold the body properties like mass, current state. And for simpler organization I also have State struct. Since the state of the body is defined by its 3D position, Orientation, linear velocity and angular velocity. This is the reason I decided to create a struct called State and each body thus have an instance of a State struct.
Due to this architecture the world instance will have a list of body struct. As shown below:
What I am asking is that is it possible to create a property within world which will store the references to the states of all the bodies within the world struct? Or am I better of looping through the list of bodies when I need it?
The reason I am asking this is because for a new physics solver I am writing I need all the states in a single linear algebra equation. This way I can use linear algebra to solve it. I am still figuring out the implementation on paper first for this so no code yet.
Just my question is should I have another property within the World struct which holds the reference to all the states of the bodies the world instance currently holds. Is this a viable architecture? Or am I overcomplicating things? This is my first rust project of this scale so I don't have much experience. I believe I do have fundamental understanding of Ownership, Burrowing and lifetimes (I hope).
it is possible in rust, but it would be hard to use and may cause unexpected compilation errors that are subtle to understand.
it's better to split the storage of data and the "view" of the data into separate types, so you don't have to deal with self-referential data structure.
example:
struct World {
bodies: Vec<Body>,
}
struct StatesView<'b> {
states: Vec<&'b State>,
}
fn create_states_view(world: &World) -> StatesView<'_> {
let states = world.bodies.iter().map(|body| &body.state).collect();
StatesView { states }
}
Thank you. That could work as I just need a good way to access the information without repeatedly looping through the bodies list each time, also I just need to read the data. Updating the data is done completely separately. But can you please tell me what <'b> part denote here? that is is states property is a list of State reference? Is that correct?
What this tells us is that the returned value borrows from the &World given to the function. Therefore, the returned StatesView may not outlive whatever borrow the caller has on the World.
For example:
fn example() {
let world = World { bodies: vec![] };
let view = create_states_view(&world);
// This is fine, because the World still exists and we have access to it.
for _ in view.states.iter() {}
// Since World is not Copy, this destroys the value in World.
drop(world);
// This is now forbidden, and causes a compile-time error.
for _ in view.states.iter() {}
}
for large dataset, you might want to consider using SoA data structures, which are commonly used in games because they are more efficient to ulitize the CPU cache.
alternatively, you can use a full "entity component system" (ECS for short), which is the core part of many game engines.
I can't help thinking that if the physics solver is the main issue, which it likely is for performance reasons you should arrange the data in a way that it requires for it's input to be most efficient. In whatever kind of arrays it likes that you can loan to it by mutable reference as it crunches on the numbers. Use indices into arrays it uses rather than references. Rather than having some preconceived notion of interrelated objects that don't fit the work to be done.
Yes I am writing a simulator. Not for a game engine for now but for engineering purposes but I guess the physics part is the same. The library I am writing now goal is only to give tools for simulators to use. Then I will write a simulator which will make use of this core library to create simulation loop.
I have not thought about optimizations at all. Right now my goal is to make the simulator work first. Implement the basic physics solvers, integrators and all of that. I have done this much but only in a most basic sense.
I just looked at entity component system. That is out of my scope I feel like. My program architecture right now is like this. There is a single instance of World struct which will own all of the bodies we create or destroy. Every body is defined will have a state struct. That's kind of it. Any other expansion such as collisions will be implemented in World struct level. The constraint mechanism will also be implemented at world level.
I have bookmarked SoA and ECS regardless for future use. Thanks a lot.
Yes.. the physics solver is very rigid right now and more like a proof of concept that the physics theory I know works on the computer. Right now I am double checking my theory of constraints (joints for two objects). I will look for optimizations after a basic core library is implemented. Which will probably will take more than a month... I have started working last week and have implemented a very basic simulation where a body can move anywhere with no concept of ground, joints, collision...
Right now the program I am writing is structured in a way such that everything is trivial to debug. If I made an error at in physics I can easily get where I have done that. I will use the lifetime method explained earlier for now. Later on I will think of optimizing how the data is stored for better efficiency such that program don't waste cycles going through moving through memory.
Another common trick is to store the index instead of a reference. There are data structures like "slot maps" that add helpers and protection against issues with deleted items. It can help if references are too restrictive.