Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core Substeps #22

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Core Substeps #22

wants to merge 1 commit into from

Conversation

PavelBlend
Copy link
Owner

@OmidGhotbi I removed my old code. I just wrote the code in this PR. But for some reason it fails.

@OmidGhotbi
Copy link

OmidGhotbi commented May 12, 2024

ok is saw the Simulation.c code that you changed, one thing I noticed is that you added a for loop to calculate substeps in C code then in Python code, you did not change the substeps behavior so the frame in Python is not the same frame in C code, in your case every single subframe in the blender will be split to the number of substeps again. for example if you have 16 substeps in blender it will be multiplied in C code, unless I missed out the python modification this behavior will cause miscalculation.
plus, double down the performance.

@OmidGhotbi
Copy link

OmidGhotbi commented May 12, 2024

Second, in gravity, calculation you multiply deltatime and gravity and once more you multiply it with velocity, it's basically deltatime**2 . I'm not sure if you should use deltatime like this in this part. i will check it.
I'll get back to you as soon as I reach home and be able to run a few tests.

@OmidGhotbi
Copy link

OmidGhotbi commented May 12, 2024

About the steps if you want to do all steps in C code you need to remove substeps from python and add it to C, (kind of replace it)
in python

frame_float = self.step / mol_substep

        mol_substep = scene.mol.substep

        frame_float = self.step / mol_substep
        frame_current = int(frame_float)
        subframe = frame_float - frame_current

And finally in this line :

scene.frame_set(frame=frame_current, subframe=subframe)

scene.frame_set(frame=frame_current, subframe=subframe)
you get the new substep and apply it as a subframe, if you do that in C you do not need it here,

@PavelBlend
Copy link
Owner Author

@OmidGhotbi I removed the previous code and added a new one. The simulation turns out to be unstable.

@OmidGhotbi
Copy link

OmidGhotbi commented May 12, 2024

Let me check it and i get back to you, just one thing the new code has 3 conflicts with my code do I need to keep them or resolve them by replacing them?
Can I help you with the code, and push the code to the new branch?

@PavelBlend
Copy link
Owner Author

I'm sorry. I just noticed that I didn't add all the code. For some reason I didn't add the python code from the addon. I accidentally deleted it. You won't be able to run my code. I didn't plan to merge this code. I wanted to do a test, and if it succeeds, then rewrite the code in a clean version. One of these days I’ll try to write the code again and if I succeed, I’ll add it here.

@PavelBlend
Copy link
Owner Author

Can you tell me the formula for adding gravity and moving particles? Does moving look like this?

delta_time = 1 / (fps * substeps)
new_pos = old_pos + delta_time * velocity

@OmidGhotbi
Copy link

OmidGhotbi commented May 13, 2024

First of all in ops python line 401 you need this change if you are about to calculate substeps in C.

            self.check_write_uv_cache(context)
            # scene.frame_set(frame=frame_current, subframe=subframe) // Replace 
            scene.frame_set(frame=frame_current)

with this change, you will not get a double substep calculation.

please share the source of Eq that you are using for new_pos = old_pos + delta_time * velocity with me so I can see why they used it.
In general, about delta_time we need to talk a little bit. dt is a variable that we normally use it formulate to simulate the time in actual code is not that relevant in general because the changing time is dt itself so normally we add gravity and velocity etc in every frame or subframe and with changing every frame or subframe the dt is implied by nature of change.
so in physics formula, because we can not change time on paper we add it and inside calculation use it, but when we really have time and frames we do not need it because we already have it.

imagine this simple eq,
x = (x + vel ) * dt
so if you have it on paper you need to add a range for dt so it can show the change of x in time. now imagine the dt is frame change in the blender. you will need to add x to itself instead.
x = x + vel
they will give you the same results one in the paper on in time (that we recreate in frame change in blender) because with adding every frame old x position will be the sum of x plus the amount of velocity etc. i hope I have explained it correctly,
remember in code if you are not using the frame for repeating the code in any part you will be forced to use dt instead again. by the way, to make affect smaller and whole process slower you can multiply by dt so every x+vel get smaller and smaller depends on your dt and substeps and control the speed of movement.

the way you can better understand it is dt is for calculate the frame progress.

float frameTime = (float)(_currentFrameDeltaTimeRemaining + _currentFrameTimeStep);
float frameProgress = 1.0f - frameTime / (float)_currentFrameDeltaTime;

or

    int estimatedNumFrameSubsteps = std::max((int)std::ceil(dt / timeStep), 1);
    timeStep = dt / estimatedNumFrameSubsteps;

one point I need to mention last night I rewrote the part you changed and associated Python codes and noticed one thing when the collider objects move fast the calculation will not be correct because C code doesn't know about the small changes that happened in the scene, for example in Trap cup I created in my test scene for TAngra it will start good but because all the movements are happening in two frames and I have over 70 substeps the continuation of the movement doesn't pass to C code and by nature of movement the results altho is good it's not correct. Cause the C code is not aware of the change in 70 substeps frames.
so I can say in the current state of this code you will get performance and in the other hand, you will lose precision.

@OmidGhotbi
Copy link

About the repo i understand it's a test branch I think maybe it is faster if I can modify the code in my forked repo modify this branch and push it so you can see ass well. it's just a suggestion that's all.

@PavelBlend
Copy link
Owner Author

Is it possible to make two types of substeps? One for blender and the other for core. For example, blender will have 16 substeps, and core will have 4 substeps. As a result, each substep from blender will be divided into susbteps from core:

blender_substeps = 16
core_substeps = 4
final_core_substeps = blender_substeps * core_substeps

This will prevent you from losing so much accuracy. The addon will handle collision every 4 steps.

@PavelBlend
Copy link
Owner Author

PavelBlend commented May 13, 2024

please share the source of Eq that

I found it a long time ago. Now I don’t remember where.

I think I need to remove the current code and rewrite it again.

@OmidGhotbi
Copy link

OmidGhotbi commented May 13, 2024

Is it possible to make two types of substeps? One for blender and the other for core. For example, blender will have 16 substeps, and core will have 4 substeps. As a result, each substep from blender will be divided into susbteps from core:

blender_substeps = 16
core_substeps = 4
final_core_substeps = blender_substeps * core_substeps

This will prevent you from losing so much accuracy. The addon will handle collision every 4 steps.

I was thinking about that a few days ago too, something about making 2 or 4 substeps in the blender and others in C code as much as I like it it could scarify the precision but it's more balance, the question will be how much performance we can gain with it. if it's 25% it's not worth it because we can do that just by using vectors instead of arrays in existing code. I'm not sure about that. if you can gain more than 50% of performance okay we can work with it.

@OmidGhotbi
Copy link

you can make 3 different presets:
1 - Full Python substeps for precision = best for accuracy slow movment
2 - Halp precision by a combination of Python + C balance precision = best for normal scenes and medium movement
3 - Full C Low precision = best for slow movement

Another choice is to try to find another type of relative solver or granular solver that is already better.

@OmidGhotbi
Copy link

OmidGhotbi commented May 14, 2024

Based on the movement we have in the current code you can calculate the Velocity like this:
dt = timeStep / numSubsteps
vel = (pos - prevPos) / dt

As you can see the (position + velocity) means new position
newPos = currentPos + vel

if you have gravity you will get the te new position the same way
newPos = currentPos + (gravity * dt)

you can combine all of them in one function or do in separate functions that is preferred.

of course, you need the equation to update particle position and this is a general answer.

Something like that:


while (simulate) {
    for (int i = 0; i < particles.length; i++)
    {
        Vel [ ị ] = Vel [ ị ] + dt * gravity
        Pos [ ị ] = Xpos [ ị ]
        Xpos [ ị ] = Xpos [ ị ] + dt * Vel [ ị ]

       foreach C in constraints
            solve(C, dt)

       for all particles i
            Vel [ ị ] < (Xpos [ ị ] - Pos [ ị ]) / dt
    }
}    

This can change from equation to equation and this is a general approach so you need the math for it or try as many as math to get what you are looking for.
this approach is based on position and can work in many situations.
Remember dt is just here to make change smaller for example if old pos is 0 and the movement is 16 cm for 1 frame and your substeps is 16 that means (Xpos + NewPos) * dt = (0+16) * 1/16 = 16/16 = 1 so for every subframe 1cm of movement. dt is here to reduce the movement base of the number of subframes so movement in 16 sub frames is the same as movement in 1 frame.

@OmidGhotbi
Copy link

i tested the new version and as we talked I made a few changes, first I divided substeps into two parts one subsample from the blender and the second internal iteration directly in C code. In this manner, first, I divided the frames by substeps. Then inside the C code divide every step by iteration number, so if we have 10 substeps from blender and 4 internal iterations it will be 40 subsamples total for every frame.
The result was better depending on how many iterations we had. at least it is more accurate with the same time for simulation or faster with the same substeps.

by the way, I converted the code into C++ as well and I'm trying to use nvc++ to compile it for GPU so it can be faster by a factor of 14 to 20 times of course the limitation of blender itself will be applied.
here is some resource if you need to read more about it.
https://developer.nvidia.com/blog/accelerating-python-on-gpus-with-nvc-and-cython/

let me know about it.

@OmidGhotbi
Copy link

Remember for substeps from blender we need dt but for internal iteration, we do need to apply dt / iteration otherwise it will explode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants