Jump to content

  • Log In with Google      Sign In   
  • Create Account

Aardvajk

Member Since 02 Mar 2006
Offline Last Active Today, 04:04 AM

#5312052 3D World Editor - Suggestions and Ideas

Posted by on 23 September 2016 - 01:25 AM

In my experience, undo/redo needs to be designed in from the very beginning. It is a nightmare to retrofit to an existing project so I'd focus on it as soon as you can.

Then general idea is as explained above. The only way you should be able to modify your state is via Command objects, which store enough information to be able to undo and redo themselves.

I use the C++ const system to enforce my undo/redo system at the code level. I have a WorldState object that just contains bare read-write data for the world state. This is then wrapped in a World object which provides const-only access (read) to the world state. Eveything like views, toolbars etc uses the World interface to read the state.

The World also has beginCommand(Command*) and endCommand(Command*) methods that take classes deriving from Command. When one is passed to beginCommand() it is given a pointer to the WorldState so it can directly modify the values, but must take responsibility to undo itself and redo itself. When endCommand() is called, if the command is valid, it is pushed onto an UndoStack in the World object.

So the only way to modify the WorldState is via Command objects. It is not possible via any other mechanism so you can't accidentally fall outside the system.

It can feel a bit weird at first that every time you want to modify something, you have to create a Command to do it, but for me it is really the only way to ensure the system is comprehensive and you soon get used to it.

Hope this helps.


#5310742 Is this practical for a resource manager?

Posted by on 14 September 2016 - 06:54 AM

Linked lists are generally a hardware unfriendly way to structure a container these days. A vector of pointers to resources is generally more cache friendly and does not require the intrusion of the prev and next pointers into Resource.

Actually the container structure should never require the objects it stores to contain container-specific members. Take a look at std::list<T> for example.


#5310741 C++ Going Beyond Basics

Posted by on 14 September 2016 - 06:45 AM

Clearly defined goals is my input. Decide on something you want to achieve and learn what you need to know to achieve it.

There is way too much "stuff" for learning in the abstract for too long.


#5310321 Is Infinity Good Design?

Posted by on 11 September 2016 - 03:25 AM

From a technical perspective, the fact you think that as the rooms increase the framerate will inevitably drop shows a lack of understanding in the design.

No matter how large the generated content, you only need to process and draw that which you can currently see on screen, so you should be able to have an "infinite" design without this having any technical issues like that.


#5308990 Getting debug information using TextOut()

Posted by on 01 September 2016 - 06:37 AM

The flicker is inevitable if you first draw a background, then draw the text. The only way to be truly flicker free is to draw the background and then the text to an offscreen buffer, then draw the offscreen buffer to the screen in one go.

 

Otherwise there is a brief period where the background has been drawn but the text has not been.

 

When you are using DirectX this will all be handled for you. You'll be by-default drawing to an offscreen buffer which will then be dumped to the screen in one go. You can simulate it using the Win32 API but if you are planning to switch to DirectX at some point anyway, I wouldn't worry.




#5308003 Does anyone have any advice for my unique situation?

Posted by on 26 August 2016 - 04:15 AM

Here's a simple truth for you that you won't want to hear, Kavik Kang:

Ideas are the cheapest commodity in the entire games industry. They are almost worthless. They are the easy bit.

Getting ideas implemented is hard. You seem to me to lack even the interpersonal skills to make this happen, never mind the technical ability.


#5305933 Using a extern variable for a object causes linker errors

Posted by on 15 August 2016 - 04:35 AM

extern is essentially telling the compiler "this is declared elsewhere". You have to then declare the instance somewhere, usually in a cpp file.

Thing.h
extern int foo;

Thing.cpp
int foo = 123;

Anything including Thing.h can now access foo.


#5288404 c++ should nested switch statements be avoided?

Posted by on 24 April 2016 - 01:43 AM

Here's one from the innards of my virtual machine for handling math operations (+, -, * and /) on an exploding number of combinations of different types. I don't think there is anything wrong with this personally, others might disagree.
 
template<class T> bool operation(State &state, const TypedValue &a, const TypedValue &b, TypedValue &c, Om::Value &res)
{
    switch(a.userType())
    {
        case Om::Type::Int:
        {
            switch(b.userType())
            {
                case Om::Type::Int: return op<T, int>(state, Om::Type::Int, a, b, c, res);
                case Om::Type::Float: return op<T, float>(state, Om::Type::Float, a, b, c, res);

                default: break;
            }

            break;
        }

        case Om::Type::Float:
        {
            switch(b.userType())
            {
                case Om::Type::Int: return op<T, float>(state, Om::Type::Float, a, b, c, res);
                case Om::Type::Float: return op<T, float>(state, Om::Type::Float, a, b, c, res);

                default: break;
            }

            break;
        }

        case Om::Type::String:
        {
            if(T::type == Addition && b.realType() == Om::Type::String)
            {
                c = TypedValue(Om::Type::String, state.allocate<StringEntity>(state.entity<StringEntity>(a.toUint()).text + state.entity<StringEntity>(b.toUint()).text));
                return true;
            }

            break;
        }

        default: break;
    }

    res = Om::ValueProxy::makeError(state, error(a.userType(), b.userType()));
    return false;
}
It is templated on structures like Add, Sub etc, so the same method can be used for all math operations via compile-time polymorphism.


#5287083 How beneficial can personal projects be?

Posted by on 15 April 2016 - 01:24 PM

My 2D platform game and level editor got me a job developing business software.

I wouldn't personally hire a programmer who didn't have personal projects. If all you showed me was coursework, I'd assume you lacked the passion we all share here.


#5287082 Is inheritance evil?

Posted by on 15 April 2016 - 01:21 PM

Of course it isn't. It's s tool that can be used or abused.

Even deep inheritance trees can be appropriate in certain domains. Look at something like Qt for example.

There is, though, a huge amount of very bad OOP out there and a great deal of confusion about the is-a and has-a relationships.

But inheritance itself is not the cause of this. Personally I blame Java :).


#5286744 How to handle a collision in a physics library and OOP?

Posted by on 13 April 2016 - 02:12 PM

It's pretty common for a third party physics library to use some kind of void user data pointer to allow you to link back a physics object to your game object and while this is generally a bit icky, it's not really solvable in any other general purpose way without introducing unnecessary overhead.

I tend to wrap the physics library (Bullet in my case) in my own thin wrapper that hides such activity away in a central place and makes it generally more compatible with the rest of my code. I dislike using a third party API directly in the parts of the code that just use the physics.

With the void pointer, I only ever assign one type of pointer to it, so in a way I (the programmer) do "know" the class type, even if the compiler doesn't. Rules like this are hard to treat as set in stone in the real world.

Rather than dynamic_casting to lots of different classes, can't you abstractify the things the physics needs to know about into the base class and just treat all your objects as this base inside the physics code? Seems unnecessary for the physics to need to know the exact class details for every object to me.


#5286320 Access violation reading location 0xDDDDDDDD.

Posted by on 11 April 2016 - 10:58 AM

Remember you are now responsible for deleting the Sprite when you are either removing it from the list or the list is destroyed, or you'll have memory leaks.

Modern smart pointers can automate this sort of thing for you.


#5277251 [BULLET] Various collision problems

Posted by on 21 February 2016 - 03:14 AM

Bullet's vanilla Kinematic Character Controller is far from finished and polished compared to the KCCs that ship with commercial engines. I've had to give up on it and roll my own (using the Bullet GJK and manually resolving collisions against a capsule shape) in my current Bullet project.

I keep promising to submit mine to Bullet but the problem is it is so tied up into my own wrapper system around Bullet it has hard to break it out, and it is only okay for my particular domain (Mario-style 3D platformer) and probably not of any use in any other context.

My advice - learn enough to implement your own KCC, based on Bullet's collision and ray test functionality so you can tweak it to suit your particular requirements. Not easy though - I had already implemented my own (very poor) GJK before I felt comfortable handing that over to Bullet for a stable result.

Some pointers on the way mine works - I have a capsule shape representing the character, and also constantly cast a ray down from its base to measure the distance to the floor, only applying fake gravity if the controller is above a certain threshold above the "floor" and snapping it to a fixed distance above the floor if within the threshold. This avoids the need to use any kind of friction to prevent slope sliding. I use the Bullet broadphase then the GJK pair test to get separation vectors against the world geometry for other collisions. There are some additional complications when penetrating into steep slopes that require a bit of vector math to figure out.

Massive code dump ahoy...
 
#include "Kcc.h"

#include "maths/Vec2.h"
#include "maths/Vec3.h"
#include "maths/Matrix.h"
#include "maths/Quaternion.h"
#include "maths/Ray.h"

#include "physics/Physics.h"
#include "physics/components/Body.h"

#include "debug/DebugRender.h"

namespace
{

float minFloorDistance(float radius, const Vec3 &normal, float margin)
{
    return sphericalDistanceToNormal(radius, normal) + (margin * 2);
}

RayResult findFloor(Physics &physics, float radius, const Vec3 &base)
{
    RayResult r = physics.rayCast(Ray(base, Vec3(0, -1, 0)), 100);

    if(r.valid() && dotVectors(r.normal(), Vec3(0, 1, 0)) >= 0.8f && r.distance() < minFloorDistance(radius, r.normal(), 0.15f))
    {
        return r;
    }

    return RayResult();
}

Vec3 alignToFloor(const Vec3 &velocity, const Vec3 &normal)
{
    return transformNormal(velocity, rotationToQuaternion(Vec3(0, 1, 0), normal));
}

float lockedY(const RayResult &r, float radius, float height)
{
    return r.worldPoint().y + minFloorDistance(radius, r.normal(), 0.05f) + ((height / 2) - radius);
}

bool lockToFloor(Physics &physics, Vec3 &pos, float radius, float height)
{
    Vec3 base(pos.x, pos.y - ((height / 2) - radius), pos.z);
    RayResult r = findFloor(physics, radius, base);

    if(r.valid())
    {
        pos.y = lockedY(r, radius, height);
        return true;
    }

    return false;
}

Vec3 slopeCorrection(const Vec3 &separation)
{
    float d = dotVectors(normalizeVector(separation), Vec3(0, 1, 0));
    if(d >= 0 && d < 0.8f)
    {
        return getDownVector(separation) * vectorLength(separation);
    }

    return Vec3(0, 0, 0);
}

Vec3 separatingVector(Physics &physics, const Shape &shape, const Vec3 &at)
{
    Vec3 result = at;
    Matrix to = translationMatrix(result);

    BroadphaseResult br = physics.broadphaseAabb(shape.aabb(to).expanded(2));

    if(br.valid())
    {
        int it = 0;
        bool loop = true;

        while(it < 5 && loop)
        {
            loop = false;

            for(auto &b: br.bodies)
            {
                ConvexResult r = physics.convexIntersection(shape, to, b->shape(), b->transform());

                if(r.valid())
                {
                    result += r.separatingVector();
                    to = translationMatrix(result);

                    loop = true;
                }
            }

            ++it;
        }
    }

    return result - at;
}

}

Kcc::Kcc(float radius, float height, const Vec3 &position) : pos(position), prev(position), shape(radius, height)
{
}

void Kcc::setPosition(const Vec3 &value)
{
    pos = value;
    prev = pos;
}

void Kcc::move(Physics &physics, MoveFlags flags, const Vec3 &step, float epsilon)
{
    Vec3 tp = pos;
    prev = pos;

    bool tg = gr;

    implementMove(tp, tg, physics, flags, step);

    if(vectorLength(Vec2(tp.x, tp.z) - Vec2(pos.x, pos.z)) >= epsilon)
    {
        pos.x = tp.x;
        pos.z = tp.z;
    }

    pos.y = tp.y;
    gr = tg;
}

bool Kcc::canMove(Physics &physics, MoveFlags flags, const Vec3 &step, float epsilon) const
{
    Vec3 tp = pos;
    bool tg = gr;

    implementMove(tp, tg, physics, flags, step);

    return vectorLength(Vec2(tp.x, tp.z) - Vec2(pos.x, pos.z)) >= epsilon;
}

float Kcc::distanceToFloor(Physics &physics) const
{
    RayResult r = physics.rayCast(Ray(pos + Vec3(0, -shape.height() / 2, 0), Vec3(0, -1, 0)), 100);
    if(r.valid())
    {
        return r.distance() + (shape.radius() - minFloorDistance(shape.radius(), r.normal(), 0.01f));
    }

    return -1.0f;
}

bool Kcc::booleanIntersection(Physics &physics, const Matrix &transform, const BodyFilter &filter) const
{
    return physics.booleanIntersection(shape, transform, filter);
}

void Kcc::implementMove(Vec3 &position, bool &grounded, Physics &physics, MoveFlags flags, const Vec3 &step) const
{
    bool flying = step.y > 0;
    float hh = shape.height() / 2;

    RayResult floor = findFloor(physics, shape.radius(), position + step + Vec3(0, -(hh - shape.radius()), 0));

    Vec3 dv(step.x, 0, step.z);
    Vec3 mv = floor.valid() && !flying ? alignToFloor(dv, floor.normal()) : dv;

    mv.y = step.y;

    Vec3 sep = separatingVector(physics, shape, position + mv);

    if(!flying)
    {
        mv += slopeCorrection(sep);
    }

    position += mv + sep;

    grounded = false;
    if(step.y <= 0)
    {
        grounded = lockToFloor(physics, position, shape.radius(), shape.height());
    }
}



#5277248 Correct way of doing Mouse Picking

Posted by on 21 February 2016 - 03:04 AM

If it helps, this is the code I use to compute a ray for picking (Direct3D9)
 
Ray Ray::compute(const Vec2 &pos, const Vec2 &size, const Matrix &view, const Matrix &proj)
{
    float w = size.x;
    float h = size.y;

    float sx = pos.x;
    float sy = pos.y;

    Vec3 v;
    v.x = (((2.0f * sx) / w ) - 1) / proj._11;
    v.y = -(((2.0f * sy) / h) - 1) / proj._22;
    v.z =  1.0f;

    Matrix m = inverseMatrix(view);

    Ray ray;

    ray.dir.x = v.x*m._11 + v.y*m._21 + v.z*m._31;
    ray.dir.y = v.x*m._12 + v.y*m._22 + v.z*m._32;
    ray.dir.z = v.x*m._13 + v.y*m._23 + v.z*m._33;
    ray.pos.x = m._41;
    ray.pos.y = m._42;
    ray.pos.z = m._43;

    ray.dir = normalizeVector(ray.dir);

    return ray;
}
pos is the mouse position on the screen, size is the width/height of the viewport. The ray returned has pos and dir and is in world frame. Matrix is just a typedef for D3DXMATRIX, Vec2 a typedef for D3DXVECTOR2 and Vec3 is a typedef for D3DXVECTOR3.


#5277009 c++ copy all data from parent class?

Posted by on 19 February 2016 - 01:13 PM

Could the OP give some more detail of what they are trying to achieve here? Something is badly wrong or maybe misunderstood about how C+++ works here I think.




PARTNERS