Jump to content

  • Log In with Google      Sign In   
  • Create Account

Journal of Aardvajk

Shadows

Posted by , 30 April 2014 - - - - - - · 868 views

Decided to celebrate getting the character controller stable by having a big break from physics for a while and get some graphical stuff working. Getting the model and animations integrated was a quick job as I'd written and polished my code for this stuff in the 2D game (animation controller, blending etc) so decided to implement shadows.

Attached Image

[EDIT] Thanks to Ashaman, now have PCF edges to the shadows as well.

Attached Image

I'm staying away from stencil shadows after my last attempt and am just doing standard shadow mapping here, using an orthographic projection for the depth rendering as I want sort of cartoony, always from above shadows for this game. It actually works suprisingly well. I do nine samples in the pixel shader and average the result to get the depth at each pixel. Produces very nice results if the depth texture is 2048 x 2048, and passable if it is 512 x 512.

Had to rewrite a bunch of stuff in the Scene and SceneItem classes to allow for rendering different passes, at the moment just so I can render the depth pass, then the main pass, but this will come in handly later on I expect for other fancy effects. You can also now easily create an object that casts no shadow, just by setting its RenderPass::Depth method to RenderMethod::Null and so on.

Had to abandon shadows being casted onto the player character as looked a right mess. One thing about orthographic, from above shadows is you can't have them cast on vertical walls either as it looks wrong, so I pass the normal of the pixel into the shadow pixel shader, and only shadow it if its dot with 0, 1, 0 is above a certain threshold.

Since journals are always better with images, here is my depth texture for you to enjoy.

Attached Image

Thanks for reading.


Victory (rather than death, jolly good)

Posted by , 27 April 2014 - - - - - - · 764 views

Victory (rather than death, jolly good) Flying along now the fiddly bits are sorted. Implemented crouching, with the obligatory only stand up if there is room behaviour in the character controller. All seems to work very well so far.

Been messing about today as well with the systems I have to make debugging shapes for rendering. I had a bunch of functions that put vertices into a vertex buffer passed as a method, but problem was until these functions completed, you don't know how big the buffers need to be, which is fine when adding them to the single (large) debug vertex buffer for compositing a debug view of the scene, but not a lot of good when trying to create a persistent buffer containing, say, a capsule at startup.

I made a VertexStream abstract class and changed all methods that were taking a VertexBuffer to take this instead of the reference to a buffer, then implemented two child classes of VertexStream, one taking a vertex buffer and the other taking one of my DataOutMemoryStream classes, which is based on a std::stringstream.

So now I can still use these methods to push vertices directly into a locked, dynamic vertex buffer, but can also get them to fill a stringstream which can then be fed as the initial input to a VertexBuffer as a separate step.

Long and short of it means I can now create a debug mesh for the character to plug into my Scene system, which stores SceneItems who have a reference to a mesh. I added a setScale method to the SceneItem so I could show the capsule standing and ducking.

I have moved all the character controller stuff into a class called Kcc (kinematic character controller) which an Entity can now own if it wants CC behaviour. So the Pc (player character) class is looking like this now:
Pc::Pc(Camera &camera, Physics &physics, Scene &scene, RenderResourceId mesh) : camera(camera), kcc(0.25f, 1, 0.75f, Vec3(0, 0, 6), physics)
{
    sceneItem = scene.add(new SceneItem(mesh));
}

void Pc::update(Physics &physics, float delta)
{
    physics.setHighlight(0, 0);

    kcc.store();

    kcc.crouch(getAsyncKeyState('X'), physics);

    kcc.move(getVelocity(camera) * 4, physics, delta);

    if(kcc.grounded())
    {
        physics.setHighlight(kcc.floor().body, kcc.floor().feature);
    }

    setDebugScreenMessage(kcc.grounded() ? "Grounded" : "Airborne");
}

void Pc::prepareScene(float blend)
{
    sceneItem->setScale(Vec3(1, kcc.crouched() ? 0.75f : 1, 1));
    sceneItem->setPosition(kcc.body().position(blend));
}
The physics.setHighlight() stuff is just to show the current floor for debugging, hence the hot pink floor in the image.

Nice clean usage, and the Kcc is easily reusable for non-player characters if we ever get that far.

So I suppose the next step is to get a rigged, animated mesh in place instead of the capsule, and start to look at all the fun that is inverse kinematics and getting feet placed properly on uneven terrain. Done a fair bit of research into this but not actually implemented anything so should be yet another fun adventure that makes me want to kill myself.

Since victory is preferrable to death, been a good day.

[EDIT] Getting things in place for a broadphase sweep...

Attached Image


CharacterController II, this time its personal...

Posted by , 26 April 2014 - - - - - - · 735 views

CharacterController II, this time its personal... Scrap the last entry with that stupid hack for preventing slope climbing. Turns out there is a proper geometric solution that was easier than I could have ever expected.

When the GJK returns a separation vector, you can dot this (normalized) with the world up vector to produce a value that you can use as the slope clamp. For example, less than 0.8f sets a slope limit of about 20 degrees. Could probably work out the exact relationship if I wanted to make the max slope variable based on an angle, but not for now.

Once we know we are being separated out from a slope that is too steep, first we need to get the vector that points down the face of the slope:
Vec3 transformVelocity(const Vec3 &v, const Vec3 &n)
{
    if(fabs(dotVectors(n, Vec3(0, 1, 0)) - 1) < 0.0001f)
    {
        return v;
    }

    Vec3 r = normalizeVector(reflect(v, n));
    Vec3 p = perpendicular(r, n);

    return normalizeVector(p) * vectorLength(v);
}

Vec3 getDownVector(const Vec3 &n)
{
    return transformVelocity(Vec3(0, -1, 0), normalizeVector(n));
}
Now we have the unit vector pointing down the slope, and we have the separation vector representing the amount we have been pushed back out of the slope, and we know from our dot test above that the slope is too steep to climb.

So all we need to do is project the separation vector onto the down vector, then add the result to the velocity before it is applied to the position:
    Vec3 sep = physics.getSeparationVector(body, pos, vel);

    float d = dotVectors(normalizeVector(sep), Vec3(0, 1, 0));
    if(d < 0.8f)
    {
        Vec3 p = getDownVector(sep);
        vel += p * vectorLength(sep);
    }

    vel += sep;
And the player now stops exactly at the point of contact with the slope, but gravity still pulls you down if you aren't on the floor.

I've set it up so the arrow keys rotate the world around both X and Z axis now so can easily test this against a huge range of different slopes and different configurations and so far seems fine. And doesn't feel like a hack either.

So the whole CC update step has two raycasts and one convex test now. The overall flow looks like this:
  • Get the requested velocity
  • Find the current floor (raycast)
  • If touching the floor, and the floor angle is not a steep slope, transform the velocity by the slope normal and lock the player to the floor
  • If not touching a floor, add gravity to the velocity
  • Get a separation vector if any (GJK)
  • Dot this with (0, 1, 0) and if that is below a threshold, use code above to project the separation vector onto the slope down vector
  • Add separation vector to velocity
  • Do a floor check at the new position (raycast) and lock to floor if within a threshold
  • If the move distance is over a low threshold update the body position
You know, I have totally failed to find a good article on hand-written character controllers and think maybe I should write one for GDNet once I have found all the inevitable problems with my approach. There are loads of resources that work out in theory, but fall to bits when used in a real game environment. The only open source CC I know of is Bullet's which has a host of problems and is not really usable.


Continued adventures in CharacterControlling

Posted by , 25 April 2014 - - - - - - · 660 views

Continued adventures in CharacterControlling Somewhere in a dark corner of Eastern Europe, there must be an asylum for programmers who have dared to try to implement a robust character controller. Listen - you can almost hear their pathetic mewling. Poor, lost souls, doomed forever to crouch in the corner of filthy cells, doing trigonometry on the walls with their own faeces.

I shall soon be checking in, no doubt. Its getting bad. Really bad.

But we are making progress. I have a sort of stable character controller working now. Actually the only major problem left to solve is sliding down slopes while you are pushing into them. I've managed to stop you from climbing up slopes above a certain angle which is a big win.

Its a solution borne more of trial and error than sound maths. I did try to work it out theoretically but nothing seemed to work.

The problem, in a nutshell, is that when you intersect a slope, the minimum translation vector you get back from the collision system (GJK in this case but would be the same with SAT) is pointing up slightly, as the slope normal is doing the same. This means that when you resolve a collision into a sloped surface, the result pushes you up the slope slightly.

I've been playing with various ways to fix this using clever trig but nothing worked out, but I have discovered that, due to a number of odd corner cases, it was necessary to re-lock the character to the floor after all the other complicated stuff going on, at the end of the update, using a ray-cast and the distance from last journal entry to figure out the position on the floor based on the angle of the slope.

Turns out that as long as the threshold for this lock is set slightly higher than I ideally wanted, it actually takes care of the slope climbing problem quite nicely. The GJK separation vector moves you up slightly, but they you get snapped back down again into what looks to be the correct position.

I've set up a couple of inputs to make every shape in the world rotate so I can test a variety of surfaces and it seems to behave correctly. The main omission now is that if you push into a steep slope, you stay put rather than getting dragged down by gravity - I want you to fall down the slope at the same speed as you would without pushing into it.

But progress anyway. Pleased to see that I can rotate the world and it all still seems to work, although I've been testing on a level slightly rotated to the left so long now, the real world looks slightly wonky to me Posted Image

Victory or death!


Further CharacterController developments

Posted by , 19 April 2014 - - - - - - · 545 views

Still driving myself mad with character controllers. Hopefully a bit of progress this morning though.

Part of the issue is locking the character to the floor, so that we only need to apply gravity when he isn't grounded. If we just do this naively, only applying gravity if the distance is greater than a threshold, we can end up with a capsule being grounded at different heights, for example when walking off a very shallow ledge, it can end up floating very slightly.

The problem to solve is that this minimum distance from the centre of the bottom sphere of the capsule to the floor varies depending on the angle of the floor.

So after a bit of high tech work in Paint.NET:

Attached Image

We know the normal of the floor, and the radius of the circle.

(0, 1, 0) dot floorNormal is the cosine of the angle between the world up vector and the floor normal. Some simple logic shows that this must be equal to the cosine of a in the image above.

Bit of trig tells us that if we know the cosine of a and the length of x that y is just x / cos(a).

I like it when you see equations leading to physical geometry - at the point at which cos(a) becomes 0, the normal of the floor is orthogonal to the world up vector, in which case it will never be found by a ray cast down from the sphere - reflecting the fact that the distance is undefined at this point.

Da dah! So now we have the correct grounded distance from the centre of the lower sphere to the floor. So now we have a value to lock to if the distance is within a certain threshold, otherwise apply gravity.

So far so good. This seems to work out well for accurately detecting the floor.

More will follow. Victory or death.






April 2014 »

S M T W T F S
  12345
6789101112
13141516171819
20212223242526
27282930   

Recent Entries

Recent Entries