frames per second | AI off | AI on | ||
Physics off | ~30 | ~15 | ||
Physics on | 12-14 | 4-7 |
Name | Self | Children | Total | |
btGjkPairDetector::getClosestPointsNonVirtual | 37 | 2062 | 2099 | |
btMinkowskiPenetrationDepthSolver::getPenetrationDirections | 48 | 1861 | 1909 | |
btDiscreteDynamicsWorld::debugDrawConstraint | 3 | 1688 | 1691 | |
btCompoundCollisionAlgorithm::CreateFunc::CreateCollisionAlgorithm | 3 | 1000 | 1003 |
i bet hes just running his gui in a thread and loading data in another.
lua5.1.dll!00101212()
[Frames below may be incorrect and/or missing, no symbols loaded for lua5.1.dll]
lua5.1.dll!00113aab()
lua5.1.dll!00114fd1()
lua5.1.dll!001058f2()
lua5.1.dll!0011192d()
msvcr80.dll!63874d83()
Test.exe!_nh_malloc_dbg_impl(unsigned int nSize, int nhFlag, int nBlockUse, const char * szFileName, int nLine, int * errno_tmp) Line 239 + 0x19 bytes C++
Test.exe!_nh_malloc_dbg(unsigned int nSize, int nhFlag, int nBlockUse, const char * szFileName, int nLine) Line 302 + 0x1d bytes C++
Test.exe!malloc(unsigned int nSize) Line 56 + 0x15 bytes C++
Test.exe!operator new(unsigned int size) Line 59 + 0x9 bytes C++
Test.exe!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Copy(unsigned int _Newsize, unsigned int _Oldlen) Line 1932 + 0x15 bytes C++
00fc63e8()
Test.exe!boost::`anonymous namespace'::create_current_thread_tls_key() + 0xb bytes C++
Test.exe!boost::call_once<void (*)(void)>() + 0xe9 bytes C++
Test.exe!boost::detail::thread_data<Test::TestGame::Loader>::run() Line 62 C++
Test.exe!boost::`anonymous namespace'::thread_start_function() + 0x63 bytes C++
Test.exe!_callthreadstartex() Line 314 + 0xf bytes C
Test.exe!_threadstartex(void * ptd) Line 297 C
kernel32.dll!763e3677()
ntdll.dll!770b9f42()
ntdll.dll!770b9f15()
lua5.1.dll!00251212()
[Frames below may be incorrect and/or missing, no symbols loaded for lua5.1.dll]
lua5.1.dll!00263aab()
lua5.1.dll!00264fd1()
lua5.1.dll!002558f2()
lua5.1.dll!0026192d()
msvcr80.dll!74cb4d83()
Test.exe!_threadstartex(void * ptd) Line 292 + 0x5 bytes
kernel32.dll!773b339a()
ntdll.dll!77e59ef2()
ntdll.dll!77e59ec5()
'Test.exe': Loaded 'C:\Programming\C++\MSVCCibraryEngine\Release\Test.exe', Symbols loaded.
'Test.exe': Loaded 'C:\Windows\SysWOW64\ntdll.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\kernel32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\KernelBase.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Program Files\Alwil Software\Avast5\snxhk.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\opengl32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\msvcrt.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\advapi32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\sechost.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\rpcrt4.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\sspicli.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\cryptbase.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\gdi32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\user32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\lpk.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\usp10.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\glu32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\ddraw.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\dciman32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\setupapi.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\cfgmgr32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\oleaut32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\ole32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\devobj.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\dwmapi.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Program Files (x86)\Lua\5.1\lua51.dll', Binary was not built with debug information.
'Test.exe': Loaded 'C:\Program Files (x86)\Lua\5.1\lua5.1.dll', Binary was not built with debug information.
'Test.exe': Loaded 'C:\Windows\winsxs\x86_microsoft.vc80.crt_1fc8b3b9a1e18e3b_8.0.50727.6195_none_d09154e044272b9a\msvcr80.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\ws2_32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\nsi.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\OpenAL32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\winmm.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\imm32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\msctf.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\atioglxx.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\version.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\atiadlxy.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\wintrust.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\crypt32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\msasn1.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\MMDevAPI.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\propsys.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\wdmaud.drv', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\ksuser.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\avrt.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\shlwapi.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\AudioSes.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\msacm32.drv', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\msacm32.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\midimap.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\wrap_oal.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\dsound.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\powrprof.dll', Cannot find or open the PDB file
'Test.exe': Loaded 'C:\Windows\SysWOW64\clbcatq.dll', Cannot find or open the PDB file
The thread 'Win32 Thread' (0x11e4) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0xfb0) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0x768) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0x10dc) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0x12c8) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0xc78) has exited with code 0 (0x0).
'Test.exe': Unloaded 'C:\Windows\SysWOW64\wrap_oal.dll'
'Test.exe': Loaded 'C:\Windows\SysWOW64\wrap_oal.dll', Cannot find or open the PDB file
The thread 'Win32 Thread' (0x12c0) has exited with code 0 (0x0).
WARNING: 0:19: implicit cast from int to float
WARNING: 0:20: implicit cast from int to float
WARNING: 0:21: implicit cast from int to float
WARNING: 0:19: implicit cast from int to float
WARNING: 0:20: implicit cast from int to float
WARNING: 0:21: implicit cast from int to float
WARNING: built-in varying gl_TexCoord [3] defined in Vertex shader and not used in Fragment shader
WARNING: built-in varying gl_TexCoord [4] defined in Vertex shader and not used in Fragment shader
WARNING: 0:19: implicit cast from int to float
WARNING: 0:20: implicit cast from int to float
WARNING: 0:21: implicit cast from int to float
WARNING: built-in varying gl_TexCoord [3] defined in Vertex shader and not used in Fragment shader
WARNING: built-in varying gl_TexCoord [4] defined in Vertex shader and not used in Fragment shader
WARNING: built-in varying gl_TexCoord [0] defined in Vertex shader and not used in Fragment shader
WARNING: built-in varying gl_TexCoord [1] defined in Vertex shader and not used in Fragment shader
WARNING: built-in varying gl_TexCoord [2] defined in Vertex shader and not used in Fragment shader
WARNING: built-in varying gl_TexCoord [3] defined in Vertex shader and not used in Fragment shader
WARNING: built-in varying gl_TexCoord [4] defined in Vertex shader and not used in Fragment shader
WARNING: built-in varying gl_TexCoord [0] defined in Vertex shader and not used in Fragment shader
WARNING: built-in varying gl_TexCoord [1] defined in Vertex shader and not used in Fragment shader
WARNING: built-in varying gl_TexCoord [2] defined in Vertex shader and not used in Fragment shader
WARNING: built-in varying gl_TexCoord [3] defined in Vertex shader and not used in Fragment shader
WARNING: built-in varying gl_TexCoord [4] defined in Vertex shader and not used in Fragment shader
The thread 'Win32 Thread' (0xbdc) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0x1028) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0x1308) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0x1068) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0xfe4) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0x10c0) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0x1344) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0x13e0) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0xca8) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0x1020) has exited with code 0 (0x0).
The program '[5108] Test.exe: Native' has exited with code 0 (0x0).
lua5.1.dll!00101212()
[Frames below may be incorrect and/or missing, no symbols loaded for lua5.1.dll]
lua5.1.dll!00113aab()
lua5.1.dll!00114fd1()
lua5.1.dll!001058f2()
lua5.1.dll!0011192d()
Test.exe!_read_nolock(int fh, void * inputbuf, unsigned int cnt) Line 230 + 0x15 bytes
msvcr80.dll!65424d83()
Test.exe!_threadstartex(void * ptd) Line 292 + 0x5 bytes
kernel32.dll!773b339a()
ntdll.dll!77e59ef2()
ntdll.dll!77e59ec5()
Test # | Condition | Spheres | No constraints | Framerate |
1 | Always true | 2 fps | ||
2 | Always true | Yes | 4 fps | |
3 | Always false | 20 fps | ||
4 | Always false | Yes | 76 fps | |
5 | False if both items are ragdolls | 3 fps | ||
6 | False if both items are ragdolls | Yes | 4 fps | |
7 | False if both items are ragdolls | Yes | Yes | 5 fps |
8 | False if both items are ragdolls | Yes | 3 fps |
The impulse it applies depends on the relative local velocity at the point of contact, and applying the impulse modifies the relative local velocity used when handling subsequent contact points. The result varies depending on which order the contact points are evaluated in, and is wrong either way.
Given a graph with rigid bodies as nodes and contact points between them as edges, I need to find what impulses to apply at each contact point in order for it to behave physically.
Naw man, I know all that stuff already. My DoCollisionResponse function works correctly for a single pair of colliding objects. And I'm not doing perfectly elastic collisions anyway; my game would not be very fun if every floor and wall were covered with repulsion gel. :ick:
Given a graph with rigid bodies as nodes and contact points between them as edges, I need to find what impulses to apply at each contact point in order for it to behave physically.
I've made a few more observations since I originally formulated the problem:
- Done correctly, after all the impulses are applied, no contact point should have an "inward" relative local velocity.
- It seems like this will be an O(n2) algorithm. Probably.
struct ContactPoint
{
struct Part
{
RigidBody* obj;
Vec3 pos, norm; // both are world coords
Part() : obj(NULL), pos(), norm() { }
} a, b;
};
void PhysicsWorld::Imp::DoCollisionResponse(const ContactPoint& cp)
{
RigidBody* ibody = cp.a.obj;
RigidBody* jbody = cp.b.obj;
RigidBody::Imp* iimp = ibody->imp;
RigidBody::Imp* jimp = jbody->imp;
bool j_can_move = jimp->can_move;
float m1 = iimp->mass_info.mass;
float m2 = jimp->mass_info.mass;
if(m1 + m2 > 0)
{
Vec3 i_poi = ibody->GetInvTransform().TransformVec3(cp.a.pos, 1.0f);
Vec3 j_poi = jbody->GetInvTransform().TransformVec3(cp.b.pos, 1.0f);
Vec3 i_v = iimp->GetLocalVelocity(cp.a.pos);
Vec3 j_v = jimp->GetLocalVelocity(cp.b.pos);
Vec3 dv = j_v - i_v;
const Vec3& normal = Vec3::Normalize(cp.a.norm - cp.b.norm);
float nvdot = Vec3::Dot(normal, dv);
if(nvdot < 0.0f)
{
float A, B;
GetUseMass(normal, cp, A, B);
float use_mass = 1.0f / A;
float bounciness = iimp->bounciness * jimp->bounciness;
float impulse_mag = -(1.0f + bounciness) * B * use_mass;
if(impulse_mag < 0)
{
Vec3 impulse = normal * impulse_mag;
if(impulse.ComputeMagnitudeSquared() != 0)
{
ibody->ApplyImpulse(impulse, i_poi);
if(j_can_move)
jbody->ApplyImpulse(-impulse, j_poi);
}
float sfric_coeff = iimp->friction * jimp->friction;
float kfric_coeff = 0.9f * sfric_coeff;
Vec3 t_dv = dv - normal * nvdot;
float t_dv_magsq = t_dv.ComputeMagnitudeSquared();
if(t_dv_magsq > 0.001f) // object is moving; apply kinetic friction
{
float t_dv_mag = sqrtf(t_dv_magsq);
GetUseMass(t_dv / t_dv_mag, cp, A, B);
use_mass = 1.0f / A;
Vec3 fric_impulse = t_dv * min(use_mass, fabs(impulse_mag * kfric_coeff / t_dv_mag));
ibody->ApplyImpulse(fric_impulse, i_poi);
if(j_can_move)
jbody->ApplyImpulse(-fric_impulse, j_poi);
}
else // object isn't moving; apply static friction
{
Vec3 df = jimp->applied_force - iimp->applied_force;
float nfdot = Vec3::Dot(normal, df);
Vec3 t_df = df - normal * nfdot;
float t_df_mag = t_df.ComputeMagnitude();
float fric_i_mag = min(impulse_mag * sfric_coeff, t_df_mag);
if(fric_i_mag > 0)
{
Vec3 fric_impulse = t_df * (-fric_i_mag / t_df_mag);
ibody->ApplyImpulse(fric_impulse, i_poi);
if(j_can_move)
jbody->ApplyImpulse(-fric_impulse, j_poi);
}
}
}
}
}
}
struct Edge
{
ContactPoint* cp;
ContactPoint::Part* self;
ContactPoint::Part* other;
Node* other_node;
};
struct Node
{
RigidBody* body;
vector<Edge> edges;
};
Done correctly, after all the impulses are applied, no contact point should have an "inward" relative local velocity.I mean that if I were to evaluate nvdot (from DoCollisionResponse) again for all of the ContactPoints, it wouldn't be negative for any of them.
for(vector<ContactPoint*>::iterator iter = collision_graph.contact_points.begin(); iter != collision_graph.contact_points.end(); ++iter)
DoCollisionResponse(**iter);
for(int i = 0; i < MAX_SEQUENTIAL_SOLVER_ITERATIONS; ++i)
{
bool any = false;
for(vector<ContactPoint*>::iterator iter = collision_graph.contact_points.begin(); iter != collision_graph.contact_points.end(); ++iter)
if(DoCollisionResponse(**iter))
any = true;
if(!any)
break;
}
Code: [Select]float t_dv_magsq = t_dv.ComputeMagnitudeSquared();
if(t_dv_magsq > 0.001f) // object is moving; apply kinetic friction
{
float t_dv_mag = sqrtf(t_dv_magsq);
// ...
}
else // object isn't moving; apply static friction
{
// ...
}
You not only use t_dv_magsq to determine if it's moving or not, but you also use it to determine the non-negative magnitude. What's wrong with using this?Code: [Select]float t_dv_mag = t_dv.ComputeMagnitude();
if(t_dv_mag != 0.001f) // object is moving; apply kinetic friction
{
// float t_dv_mag = sqrtf(t_dv_magsq);
// ...
}
else // object isn't moving; apply static friction
{
// ...
}
Are you planning on doing some KE calculations further down the road?QuoteQuoteDone correctly, after all the impulses are applied, no contact point should have an "inward" relative local velocity.I mean that if I were to evaluate nvdot (from DoCollisionResponse) again for all of the ContactPoints, it wouldn't be negative for any of them.
void Eggs() { cout << "Eggs" << endl; }
void Ham() { cout << "Ham" << endl; }
void Seuss(bool eggs)
{
for(int i = 0; i < 10000; ++i)
if(eggs)
Eggs();
else
{
Ham();
Ham();
}
}
void Derp() { Seuss(true); }
void Herp() { Seuss(false); }
int main()
{
Derp();
Herp();
return 0;
}
function | self | children | total |
main | 0 | 300 | 301 |
- Derp | 1 | 100 | 101 |
Herp | 1 | 199 | 200 |
Eggs | 99 | 0 | 99 |
Ham | 198 | 0 | 198 |
Derp | 1 | 100 | 101 |
- Seuss | 1 | 99 | 100 |
Herp | 1 | 199 | 200 |
- Seuss | 1 | 198 | 199 |
Seuss | 2 | 297 | 299 |
- Eggs | 99 | 0 | 99 |
- Ham | 198 | 0 | 198 |
function | self | children | total |
Derp | 1 | 100 | 101 |
- Seuss | 1 | 99 | 100 |
- Eggs | 99 | 0 | 99 |
- Ham | 198 | 0 | 198 |
function | self | children | total |
Derp | 1 | 100 | 101 |
- Seuss | 1 | 99 | 100 |
- Eggs | 99 | 0 | 99 |
Throwing neural nets at my program and hoping it makes it work. So far nothing.
Throwing neural nets at my program and hoping it makes it work. So far nothing.
Alright dude, what's going on?
Then again, maybe I could simulate just a little bit, to get a sense of what's moving fastest toward the desired state?
Of course, there's the old fashioned way of doing it: turn off physics on Actor's and have them run through keyframe animations. :nervous:
I kinda want to see a neural net on chip type processor which does the heavy lifting for you.GPGPU can do that, and relatively easily - inputs and outputs are textures and/or meshes, and a shader does the computations.
Integer computations are a pain, but you don't want those for neural nets anyway.
Edit: I just realized something! If I constrain the placed foot to be immobile w.r.t. the ground, I don't have to worry about any net torques or linear acceleration! Unless of course the thing he's standing on isn't immobile, but whatever; this should be good enough. I'm going to try to implement this.
I kinda want to see a neural net on chip type processor which does the heavy lifting for you.GPGPU can do that, and relatively easily - inputs and outputs are textures and/or meshes, and a shader does the computations.
GPUs are massively parallel which suit neural nets very well (unlike some other computations)
Integer computations are a pain, but you don't want those for neural nets anyway.
// enforce joint rotation limits
const float inv_foresight = 360.0f;
const float foresight = 1.0f / inv_foresight;
Vec3 proposed_av = current_av - alpha; // proposed angular velocity
Quaternion proposed_ori = a_to_b * Quaternion::FromPYR(proposed_av.x * foresight, proposed_av.y * foresight, proposed_av.z * foresight); // orientation that would result from that velocity; a_to_b is a quat
Vec3 proposed_pyr = oriented_axes * -proposed_ori.ToPYR(); // rotation in the coordinate system the rotation limits are defined in; oriented_axes is a Mat3
bool any_changes = false;
if(proposed_pyr.x < min_extents.x) { proposed_pyr.x = min_extents.x; any_changes = true; }
else if(proposed_pyr.x > max_extents.x) { proposed_pyr.x = max_extents.x; any_changes = true; }
if(proposed_pyr.y < min_extents.y) { proposed_pyr.y = min_extents.y; any_changes = true; }
else if(proposed_pyr.y > max_extents.y) { proposed_pyr.y = max_extents.y; any_changes = true; }
if(proposed_pyr.z < min_extents.z) { proposed_pyr.z = min_extents.z; any_changes = true; }
else if(proposed_pyr.z > max_extents.z) { proposed_pyr.z = max_extents.z; any_changes = true; }
if(any_changes)
{
// at least one rotation limit was violated, so we must recompute alpha
Vec3 actual_pyr = reverse_oriented_axes * proposed_pyr; // comparable to -proposed_ori.ToPYR(); reverse_oriented_axes is a Mat3
Quaternion actual_ori = Quaternion::FromPYR(-actual_pyr); // comparable to proposed_ori
Vec3 actual_av = (b_to_a * actual_ori).ToPYR() * inv_foresight; // comparable to proposed_av
alpha = current_av - actual_av;
}
Oh, I've already got friction. And hypothetically my code is set up to deal with either static or kinetic friction... but in practice, the branch for static friction never executes. Maybe I'll look into it sometime. :blah:
What I meant is that f you want to crunch a lot of floating-point numbers fast and can do it in parallel, the GPU is usually the best place to do it.Integer computations are a pain, but you don't want those for neural nets anyway.Huh, what?
What I meant is that f you want to crunch a lot of floating-point numbers fast and can do it in parallel, the GPU is usually the best place to do it.Integer computations are a pain, but you don't want those for neural nets anyway.Huh, what?
However, GPUs are optimised for floating-point, so the same is probably not true for integer calcs.
execute at PHYSICS_TICK_FREQUENCY hz:
begin populating a "constraint graph"; a graph where nodes = rigid bodies and edges = physics constraints
do collision detection; contact points get added to constraint graph
add other less transient constraints (e.g. joint & placed foot constraints) to the constraint graph
break the constraint graph into subgraphs (static objects don't merge subgraphs)
for each subgraph:
active_constraints = all the edges in that subgraph
repeat MAX_SEQUENTIAL_SOLVER_ITERATIONS times or until active_constraints is empty:
new_active_constraints = empty
for each constraint in active_constraints:
constraint does its thing
if it applies a nonzero force/torque/impulse/whatever:
add all the other edges from the two nodes this edge connected to new_active_constraints
active_constraints = new_active_constraints
A = the input layer
Do
{
B = the next layer after A in the MLP
For each node J in layer B:
total = 0
For each node K in layer A:
total += weight[J, K] * value[K]
value[J] = ActivationFunction(total)
A = B
} until A is the output layer
So to change the hidden layer weights, we must first change the output layer weights according to the derivative of the activation function, and so this algorithm represents a backpropagation of the activation function.... which suggests that I need to make the changes as soon as I compute them. But in the article on backpropagation (http://en.wikipedia.org/wiki/Backpropagation) there's this bit of pseudocode1 which pretty clearly indicates that I should wait until I've computed all the changes before I apply them. And it doesn't say anything about the computation of delta_wi depending on the values that were computed for delta_wh. :confused:
Initialize the weights in the network (often randomly)
Do
For each example e in the training set
O = neural-net-output(network, e) ; forward pass
T = teacher output for e
Calculate error (T - O) at the output units
Compute delta_wh for all weights from hidden layer to output layer ; backward pass
Compute delta_wi for all weights from input layer to hidden layer ; backward pass continued
Update the weights in the network
Until all examples classified correctly or stopping criterion satisfied
Return the network
Do you mean that maybe this profiler is too dumb to deal with a multi-threaded program, and is seeing stuff happening in multiple threads and recording the data wrong because it doesn't know about threads?
KeyframeAnimation::UpdatePose(TimingInfo)
free(void*)
_CIexp_pentium4()
class CollisionObject
{
protected:
virtual void DoStuff() { Debug("default behavior"); }
};
class RigidBody : public CollisionObject
{
protected:
void DoStuff() { Debug("overridden behavior"); }
};
class CollisionGroup : public CollisionObject
{
protected:
vector<RigidBody*> children;
void DoStuff(); // various attempted implementations below
};
void CollisionGroup::DoStuff()
{
for(vector<RigidBody*>::iterator iter = children.begin(); iter != children.end(); ++iter)
(*iter)->DoStuff();
}
void CollisionGroup::DoStuff()
{
for(vector<RigidBody*>::iterator iter = children.begin(); iter != children.end(); ++iter)
((CollisionObject*)*iter)->DoStuff();
}
void CollisionGroup::DoStuff()
{
for(vector<RigidBody*>::iterator iter = children.begin(); iter != children.end(); ++iter)
((CollisionGroup*)*iter)->DoStuff();
}
given a collection of constraints A // all constraints (or all constraints yet to be added to a batch)
i = 0
while A is not empty
start an empty collection of nodes U // used nodes
start an empty collection of constraints N // constraints which will be processed by some other batch
start an empty collection of constraints B[i] // constraints for this batch
for each constraint C in A
if either of the two nodes of C are in U
add C to N
else
add C to B[i]
add both nodes to U
A = N
increment i
0x00000000 --> 0x00000000
0x00000001 --> 0x3F800000
0x00000002 --> 0x40000000
0x00000003 --> 0x40400000
0x00000004 --> 0x40800000
0x00000005 --> 0x40A00000
0x00000006 --> 0x40C00000
0x00000007 --> 0x40E00000
0x00000008 --> 0x41000000
0x00000009 --> 0x41100000
0x0000000A --> 0x41200000
0x0000000B --> 0x41300000
0x0000000C --> 0x41400000
0x0000000D --> 0x41500000
0x0000000E --> 0x41600000
0x0000000F --> 0x41700000
0x00000010 --> 0x41800000
0x00000011 --> 0x41880000
0x00000012 --> 0x41900000
That... shouldn't be the case. Derived classes are supposed to inherit every method and member, with the exception of the parent class's constructors/deconstructors.Actually, it should be the case that this doesn't compile.
If you have MSVC, try seeing what is accessible by using the autocomplete... basically just do the (*iter) -> and wait for MSVC to pop out a window with all possible options.
The compiler might be getting confused with all the DoStuff() functions. :P
class Foo {
private: //only use protected if it's required to explicitly call this from a base class
virtual void DoStuff()
{
cout<<"foo::DoStuff"<<endl;
}
public:
void DoStuffPolymorphic()
{
DoStuff();
}
};
class Bar : public Foo {
protected:
void DoStuff()
{
cout<<"bar::DoStuff"<<endl;
}
};
class Bar2 : public Foo {
Foo* b;
public:
Bar2() {
b = new Bar();
}
void Blubb() {
b->DoStuffPolymorphic(); //will call Bar:DoStuff
}
};
But... wha :confused: That's even worse, a protected method should not be able to override a private method!public/private/protected is access control. You are not accessing Foo:DoStuff. By declaring it virtual you explicitly allow it to be overwritten. Extending access control to overwriting virtual functions would make private virtual functions simply impossible.
DoStuff is supposed to be protected so that only classes derived from CollisionObject can access it. Making a public DoStuffPolymorphic method wrecks that. And making it protected wouldn't help, because then the derived classes couldn't call it.Uh yeah, that's the part were I didn't read properly.
#include <iostream>
using namespace std;
class Foo {
protected:
virtual void DoStuff()
{
cout<<"foo::DoStuff"<<endl;
}
protected: //note the protected here
void DoStuffPolymorphic(Foo* test)
{
test->DoStuff();
}
};
class Bar : public Foo {
private:
void DoStuff()
{
cout<<"bar::DoStuff"<<endl;
}
};
class Bar2 : public Foo {
Foo* b;
public:
Bar2() {
b = new Bar();
}
void Blubb() {
DoStuffPolymorphic(b); //calls bar::DoStuff()
}
};
int main()
{
Bar2 test;
test.Blubb();
}
class Foo {
virtual void DoStuff() {}
};
class Bar : public Foo {
public:
void DoStuff() {}
};
int main() {
Foo* f = new Bar();
//f->DoStuff(); //error C2248: 'Foo::DoStuff' : cannot access private member declared in class 'Foo'
static_cast<Bar*>(f)->DoStuff(); //ok, Bar::DoStuff is public
}
I'm leaving the deletes out to make the code shorter, btw, I'm not forgetting them. :)
for(unsigned int i = 0; i < iterations; ++i)
for(unsigned int j = 0; j < batches.size(); ++j)
{
// do constraint shader stuff
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, active_vtex);
glTexBufferEXT(GL_TEXTURE_BUFFER, GL_RGBA32F, active_vdata);
glUniform1i(u_velocity_data, 1);
GLDEBUG();
// set up outputs for transform feedback
glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, inactive_vdata, 0, num_rigid_bodies * 4 * sizeof(float));
glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 1, inactive_vdata, num_rigid_bodies * 4 * sizeof(float), num_rigid_bodies * 4 * sizeof(float));
GLDEBUG();
glBeginTransformFeedback(GL_POINTS);
glDrawArrays(GL_POINTS, num_rigid_bodies * j, num_rigid_bodies);
glEndTransformFeedback();
GLDEBUG();
glFlush();
// change which direction the copying is going (back and forth)... can't use one buffer as both input and output or it will be undefined behavior!
swap(active_vdata, inactive_vdata);
swap(active_vtex, inactive_vtex);
}
Another thing that I'm concerned about with this PID thing... maybe some of my issues are arising from the fact that this is a discrete simulation instead of sensors monitoring a continuous real-life phenomenon (even if the sensors were sampling it discretely)... e.g. it may be possible to overshoot the mark in a single tick.
Simulating a single arm is a start, but try a simulation with three legs for a stationary test and four legs for a movement test before going to 6 or more.
...Which is why I suggested that you try simulating in a math program suite before trying as a discrete time system (DTS). A DTS can, and will, become very unstable if the input signal operates at a frequency greater than what it is sampled at. In realistic terms, this means you can't have the bug accelerating too fast between iterations.
...Which is why I suggested that you try simulating in a math program suite before trying as a discrete time system (DTS). A DTS can, and will, become very unstable if the input signal operates at a frequency greater than what it is sampled at. In realistic terms, this means you can't have the bug accelerating too fast between iterations.
RE: "simulating in a math program suite": You mean using an existing simulator? Or just for the analysis? I am somewhat disinclined to use an existing simulator, because its behavior would invariably be different from that of my physics engine, and ultimately it's my physics engine that I need it to work correctly in. Which isn't to say that my physics engine is physically inaccurate (although it may be), just that the specific of how my constraints behave, how the friction works, etc., would likely not be easily and accurately reproducible in another simulator.
RE: "if the input signal operates at a frequency greater than what it is sampled at": About a month ago I changed how constraint motors are controlled so that it runs a callback immediately before every physics tick... so it should have up-to-date info about the state of the physics world, and the values I set for the motor torques will potentially be changed again as soon as the next tick. And the ticks have a fixed timestep.
Ah, so so by "bounciness" you meant coefficient of restitution? That's correct then, sorry.
Ah, so so by "bounciness" you meant coefficient of restitution? That's correct then, sorry.
Not sure what you meant on the friction issue.
Perhaps knocking up something that draws the vectors (velocity, normal, and tangential) onscreen during sim would help you figure out where it's going weird?
I'm pretty sure you have to abandon the idea of any surface being perfectly hard, and deliberately allow an initial interpenetration based on the dynamic load, settling on a final interpenetration based on the static forces after collision energy is dissipated.
- Think "dropping a box on a mattress".
If you calculated the area of the interpenetration and use that as a scaling factor for the restitution force in the collision, would that work?
- The interpenetrating volumes might be more accurate, but harder to work out as you'll have to find the area first.
Really I'm just stuck on getting the rigid body physics working cleanly
If I only measure relative local velocities at a single point, and apply impulses for restitution and friction at that same point, a box will behave identically to a sphere.
UpdateVel all the dynamic collision objects (effectively all rigid bodies); includes something like vel += gravity * timestep;
UpdateVel all the RayColliders
RayTestPrivate all the RayColliders
Do collision detection --> populate a list of ContactPoints
DoUpdateAction all the persistent PhysicsConstraints (e.g. JointConstraint) and collect them in a list of PhysicsConstraints
DoUpdateAction all the ContactPoints and add them to the list as well magic anti-penetration displacement directly modifies pos here
Solve the constraint graph (i.e. repeatedly DoConstraintAction all the PhysicsConstraints in the list we just built) modifies vel to keep objects from going through each other, etc.
UpdatePos all the dynamic collision objects includes something like pos += vel * timestep
UpdatePos all the RayColliders
Vec3 dv = GetRelativeLocalVelocity(); // i.e. the difference between the local velocities of each object at the point
float nvdot = Vec3::Dot(normal, dv);
if nvdot < adhesion_threshold:
Vec3 normal_nvdot = normal * nvdot;
// normal force aka restitution
float use_restitution_coeff = nvdot < bounce_threshold ? restitution_coeff : 1.0f;
Vec3 restitution_impulse = rlv_to_impulse * normal_nvdot * -use_restitution_coeff;
// friction
Vec3 full_friction_impulse = rlv_to_impulse * (normal_nvdot - dv);
float fric_fraction = min(1.0f, fric_coeff * restitution_impulse.ComputeMagnitude() / full_friction_impulse.ComputeMagnitude());
// apply computed impulses
Vec3 apply_impulse = restitution_impulse + full_friction_impulse * fric_fraction;
ApplyImpulse(apply_impulse);
if apply_impulse.ComputeMagnitude() > impulse_threshold:
wake up adjacent constraint edges in the constraint graph
a.vel += impulse / a.mass;
b.vel -= impulse / b.mass;
a.angular_vel += Mat3::Inverse(a.moi) * Vec3::Cross(impulse, pos - a.com);
b.angular_vel -= Mat3::Inverse(b.moi) * Vec3::Cross(impulse, pos - b.com);
no sinking into each otherGenuinely, I'm pretty certain this is where the jitter really comes from, as real objects are neither infinitely hard nor infinitely rigid.
if nvdot < adhesion_threshold
if fabs(nvdot) < adhesion_threshold
This will make sure that the adhesion threshold comparison is always carried out in cases where it needs to be carried out.float use_restitution_coeff = nvdot < bounce_threshold ? restitution_coeff: 1.0f
Vec3 restitution_impulse = rlv_to_impulse * normal_vector * (-1)*use_restitution_coeff)
made me scratch my head for a while since impulse as I know it is force multiplied by time, thus it's unit is [kg m/s]. Okay, after a while I realized what it actually does, so no problem any more. Again, make sure the normal vector is pointing at the direction you want it to be pointing at, since there is no general guarantee that the surface normal is doing that. Ditto for the signs of the elements of the rlv_to_impulse. Additionally, there is a possibility for discontinuous derivatives at this point, since the restitution coefficient is basically calculated inside an if-loop. I have occasionally seen a local optimizer written by a certain person (cough cough) approach a local minimum, and assault a limiting condition that had a very heavy weight, which consequently threw the search out of the immediate area of the minimum since the derivative grew so large on the other side... Lesson learned, if the result doesn't improve, don't try to improve things by always going to max iterations. :Dfloat fric_fraction = min(1.0f, fric_coeff*|restitution_impulse|/|full_friction_impulse|)
at the current state, this should evaluate with positive numbers, so that's clear. How was the comparison actually implemented in the code?float fric_fraction = min(1.0f, fric_coeff * sqrtf(restitution_impulse.ComputeMagnitudeSquared() / full_friction_impulse.ComputeMagnitudeSquared()));
Re: absolute value: nope; a negative nvdot indicates the objects are moving toward each other, and therefore I should do something to keep them from going through each other. The small positive "adhesion threshold" is just a hack I came up with to eliminate jitter. More on that at the end of this post.
The 3x3 grid idea
An idea I've been thinking about doing for a while is to have a 3x3 grid of poses for everything "pelvis" and up, and lerping between them to control the direction the head & gun aim. However, as I just mentioned, IRL your pelvis doesn't stay in one pos/ori when you sweep your aim left/right. Maybe I could do something where it comes up with the pelvis xform first (logic for this will be non-trivial), then transforms the "forward" vector (computed from pitch/yaw values) into the pelvis' local coords and looks up that value in the 3x3 grid? I don't know.
Upper body imposes limits on possible pelvis xform, based on aim direction
For each leg:
Leg imposes limits on possible pelvis xform, based on PlacedFootConstraint/anticipated collision with ground
Select pelvis xform
Look up upper body joint orientations based on pelvis xform and aim direction
For each leg:
Look up leg joint orientations based on pelvis xform and PlacedFootConstraint/anticipated collision with ground
have a "desired velocity"
x,z components are based on forward and sidestep controls
y component is based on terrain slope? and jump control
constrain pelvis pos/ori:
pelvis moves by a velocity somewhere between the desired velocity and the current average velocity of the dood
pelvis yaws a little to help the aim yaw
pelvis moves forward/backward to balance the aim pitch (and maybe pitch the pelvis up/down a little as well?)
for both feet:
if the foot is grounded:
try to solve the knee IK to keep it grounded
if the foot is not grounded, or if the knee IK fails:
find a landing place for the foot
if landing place found:
move foot toward the landing place (but also up)
foot moves at about 2x the dood's average velocity
else if override (e.g. sprint or jump):
move foot forward anyway?
I still suggest weed.
per-foot/leg stuff:
end effector info?
position
required normal vector (or just "down"?) (optional)
out-of-contact timer
cached proposed footfall
bone position (and orientation?)
object to step on?
ETA?
required ground clearance?
virtual methods:
solve leg IK
find suitable footfall
pose ungrounded leg (with or without a planned footfall)
dood stuff:
virtual methods:
compute desired velocity
set root bone xform
new "jump timer" behavior