Welcome, Guest. Please login or register. Did you miss your activation email?

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - Lee R

Pages: 1 [2] 3 4 ... 6
16
SFML projects / Re: Feather Kit - A C++ Game Framework
« on: February 10, 2014, 04:26:55 am »
Okay so this is a bit of a late reply, but hey :P

Quote from: therocode
Quote from: Lee R
Okay, I think I understand your use case better now. You actively want the ability to load meaningless junk. That is, user defined attributes for which the compiled executable has no specialized code. Then, while waiting for a programmer to implement their logic, a level designer could get on with placing objects containing said attributes. If that's the case, then a fully typed entity system could still work. You'd simply define a 'EditorAttribute' which would suck up all unknown attributes. The editor system would look for object with said EditorAttribute and display it's contents as if they were first class attributes. If, on the other hand, the idea is to have the logic be implemented in script files, while the above idea would still work (e.g. through a ScriptVars arrtibute), you would probably be better off back with the old string based system (depending on the amount of data that gets stuffed into the ScriptVars attributes).

[...]

Yes, that is right. Such use cases is what I have in mind. I am not entiry sure what you mean with a 'EditorAttribute' which would suck up all unknown attributes [...]

It's seems like the use case you have in mind is actually quite a bit simpler than that. You just want the ability to load values for known attributes from a JSON file. That's trivial to achieve even with a fully type based system and it takes no more effort than for the new system you've described (i.e. no need for an 'EditorAttribute' or the like). Because this:

Quote from: therocode
The entity manager has such a function:
eManager.registerAttribute<int>("Health");

Provides the entity manager with both the static type of the attribute, and its string identifier. This is exactly the same information that a type based system would need in order for it to satisfy the use case. The code in my earlier post shows what that could look like:
http://en.sfml-dev.org/forums/index.php?topic=14193.msg100008#msg100008

Quote from: therocode
You then create entities like so:
eManager.registerAttribute<int>("Health");
eManager.registerAttribute<float>("Weight");

WeakEntityPtr entity1 = eManager.createEntity({"Health", "Weight"});
WeakEntityPtr entity2 = eManager.createEntity({"Health"});
WeakEntityPtr entity3 = eManager.createEntity({"Weight"});

The parameter is an std::vector<std::string>. [...]

Perhaps std::set<std::string> would makes more sense here?

Quote from: therocode
Example on how to register a default-setter for a glm::vec2 for position:

entityFactory.registerDefaultSetter("position",
[] (const std::string attribute&, const std::vector<std::string>& arguments, fea::WeakEntityPtr entity) {
entity.lock()->setAttribute<glm::vec2>(attribute, glm::vec2(std::stof(arguments[0]), std::stof(arguments[1])));
});

This could just be a matter of taste, but I feel like it would become quite tedious having to provide the 'default-setter' function for the same type, whenever that type is registered in the entity factory as an attribute (e.g. both position and velocity being vec2, and having to provide same setter function for both, individually). That being in addition to having to register both with the entity manager. It would be nice if, for example, the 'glm::vec2' type is registered along with its 'default-setter', not as an attribute, but as a primitive which can then be mapped to a name to form a new attribute.

I realise that it's probably not entirely obvious what I mean, so I went ahead and wrote a reference implementation to demonstrate the idea (which you're free to ignore ;)):

http://codepad.org/kBtU0puv

As an example of usage:

int main()
{
    fea::EntityManager manager;
    fea::EntityFactory producer(manager);

    producer.addPrimitive("vec2", [](const fea::Params& params) {
        return glm::vec2{ std::stof(params[0]), std::stof(params[1]) };
    });

    producer.map("position", "vec2");
    producer.map("velocity", "vec2");

    fea::Template moveable = {
        { "position", { "0.0, 0.0" } },
        { "velocity", { "0.0, 0.0" } }
    };

    producer.addTemplate("moveable", moveable);
    auto entity = producer.instantiate("moveable").lock();

    glm::vec2 position = entity.getAttribute<glm::vec2>("position");
    glm::vec2 velocity = entity.getAttribute<glm::vec2>("velocity");

    return 0;
}
 

There's a few things you might find interesting about this:

- Note that the user no longer explicitly registers anything with the entity manager itself. The entity factory handles all that under the covers (i.e. there is no registration code missing here, this is a complete example).

- The 'default-setter' function has been reduced to a simple parser; it doesn't concern itself with entities or managers or even attributes. It simply parses the arguments and returns an object of the appropriate type (which the entity factory will automatically deduce).

- Entity templates are parsed as soon as they're added to the system and stored as what I call 'proto entities'. This saves the system from having to re-parse (as in your current system) the strings of a template every time one is instantiated. The point here is that one could add sophisticated error handling to the parser function (previously 'default-setter') and not have to worry about any performance overhead since it will only ever been invoked once per template, rather than once per instantiation.

- This system satisfies the use case quoted at the top of this post; One could create entirely new attributes inside JSON files (in addition to simply being able to provide the values of known attributes).
- The code requires no modification to your existing EntityManager, except that it assumes the 'createEntity' function accepts an std::set<std::string>.

17
SFML projects / Re: Feather Kit - A C++ Game Framework
« on: January 28, 2014, 07:47:20 pm »
Quote from: therocode
Having a type based system instead of string based system indeed has its benefits with the compile time processing and type safety, but on the other hand it has its own drawbacks. For once, now when the system uses strings, it is trivial to read entity templates from JSON files where the strings are directly corresponding to an actual attribute. That is useful since content designers can define things purely in data files without fiddling with, and recompiling the code. This can of course be achieved with programmers creating a predefined lookup-table where strings correspond to a type, but it is less neat so it is a trade-off. [...]

I'm not sure I see the problem. In my last post, I did say that the string mapping could be kept around to ease serialization and such. I mean, the string data must be mapped to real C++ types at some point, else it's nothing but meaningless junk taking up space.

Does this address the use case you have in mind (the concept, rather than exact implementation)?
using LoadFunc = std::function<
    void(EntityID, std::string)
>;
std::map<std::string, LoadFunc> m_loaders;

// ...

template<typename T>
void EntityManager::registerAttribute(const std::string& name)
{
    // ...same as before...

    m_loaders[name] = [this](EntityID id, std::string data) {
        std::stringstream ss(std::move(data));
        ss >> getAttribute<T>(id);
    };
}

void EntityManager::loadAttribute(EntityID id, std::string name, std::string data)
{
    m_loaders[name](id, std::move(data));
}
 

EDIT: Okay, I think I understand your use case better now. You actively want the ability to load meaningless junk. That is, user defined attributes for which the compiled executable has no specialized code. Then, while waiting for a programmer to implement their logic, a level designer could get on with placing objects containing said attributes. If that's the case, then a fully typed entity system could still work. You'd simply define a 'EditorAttribute' which would suck up all unknown attributes. The editor system would look for object with said EditorAttribute and display it's contents as if they were first class attributes. If, on the other hand, the idea is to have the logic be implemented in script files, while the above idea would still work (e.g. through a ScriptVars arrtibute), you would probably be better off back with the old string based system (depending on the amount of data that gets stuffed into the ScriptVars attributes).

Quote
Another approach I am investigating to solve the current issue of not bieng able to store types that has custom value semantics is to store the attributes in std::shared_ptr<void> which will be able to delete them properly. Then a simple cast with a template getter function can retrieve the original value. This would get rid of the current issues.

This is essentially what I suggested in my last post. It's pretty much the same idea, whether it's individual attribute objects that are type erased or entire arrays of them.

Quote
Oh btw, there is a built in way of getting an index representing a unique type instead of using a static incrementing counter:
std::type_index index = std::type_index(typeid(AttributeType));
 

The code I presented for mapping a type to an integer has the additional property that for the first type queried, the returned value will be 0; for the second type queried it will be 1, and so on. This makes it suitable for use in as the index into a vector. Resizing a vector to accommodate the returned value of the code you presented could (and likely would) result in gobs of memory being wasted. This besides the fact that I don't think the std::type_index class defines a conversion to integer.

18
SFML projects / Re: Feather Kit - A C++ Game Framework
« on: January 28, 2014, 12:18:46 am »
Quote from: therocode
I look over the old code and see if I can come up with a nice way to fix it. Feel free to post your ideas on how to do this the best way possible. :) Depending on things I actually might remove the aligned entity backend if I can't figure out a way to do what I wanted to do with it in a good way. Is is quite a special case component. Although the idea is nice.

I personally think that it would be a good idea for you to go learn about type erasure, then redesign the whole system.

Also, it would make things a lot simpler if the "EntityBackend" type wasn't runtime configurable. That way, the full static type of both the attributes and their respective containers could be known, potentially allowing the registration/query code to be as straight forward as:

std::map<..., std::shared_ptr<void>> m_attributeArrays;
// ...
template<typename T>
void EntityManager::registerAttribute(const std::string& name)
{
    // 'attributeArrays' is a map containing type erased AttributeArray objects.
    m_attributeArrays[name] = std::make_shared<AttributeArray<T>>(name, this);
}

template<typename T>
T& EntityManager::getAttribute(const std::string& name, const EntityId id)
{
    // cast back to known static type.
    void* vp = m_attributeArrays[[name].get();
    return static_cast<AttributeArray<T>*>(vp)->get(id);
}
 

If you do go for a full redesign, you could consider looking up attributes by type rather than strings. That would allow the attribute arrays to be accessed by an integer index into a vector (which is hella faster than string keys into a map). Like so:

inline unsigned nextTypeID()
{
    static unsigned id = 0;
    return id++;
}

template<typename T>
unsigned TypeID()
{
    static const unsigned id = nextID();
    return id;
}

// ...
std::vector<std::shared_ptr<void>> m_attributeArrays;
// ...

template<typename T>
void EntityManager::registerAttribute(const std::string& name)
{
    // 'attributeArrays' is a vector containing type erased AttributeArray objects.
    const auto index = TypeID<T>();
    if (index >= m_attributeArrays.size()) {
        auto ptr = std::make_shared<AttributeArray<T>>(name, this);
        m_attributeArrays.resize(1 + index);
        m_attributeArrays[index] = ptr;
    }
}

template<typename T>
T& EntityManager::getAttribute(const EntityId id)
{
    // cast back to known static type.
    void* vp = m_attributeArrays[TypeID<T>()].get();
    return static_cast<AttributeArray<T>*>(vp)->get(id);
}
 

You could also keep the old string mapping around as a 'slow path' to ease serialization and such.

Because types will be registered at the granularity of attributes (versus, say, components), there would have to be some mechanism in place to allow multiple attributes of the same type to be registered independently. It could be as simple as matching the actual attribute type with a tag type:

template<typename T, typename Tag>
struct Attribute
{
    using value_type = T;
    using tag_type = Tag;
};

template<typename T>
typename T::value_type EntityManager::getAttribute(const EntityId id);

struct HealthTag {};
using Health = Attribute<int, HealthTag>;

//...

int health = entManager.getAttribute<Health>(id);
 

Hopefully, all that was more helpful than it was confusing.  ;)

19
General / Re: AW: Re: [Solved] Resource Manager Stack Corruption
« on: January 22, 2014, 07:30:53 pm »
Performance is not really an argument when it comes to unique_ptr, because compilers can esentially translate it the same way as new/delete, with the exception to cover all destruction cases. ;)st. The micro gain in performace is nothing, if the application leaks or crashes constantly. :D

I'm no computer scientist, but it seems to me like they may even be more performant in some cases. Consider for example std::map<int, T*> versus std::map<int, std::unique_ptr<T>>. Assume the destruction of the map object takes O(N) time (i.e. there are N nodes to delete). The std::map<int, T*> version imposes and additional, cache miss filled loop to first manually delete the pointed to objects. What was an O(N) time operation has now effectively becomes O(2n) with a bunch of cache misses thrown in for good measure.

20
SFML projects / Re: Feather Kit - A C++ Game Framework
« on: January 22, 2014, 06:31:38 pm »
I'm not sure if you realise it, but, there are serious constraints placed on the set of types which can be used as attributes. This is due to the use of memcpy in both of the 'EntityBackend' implementations. In short, any attribute type must be trivially copyable. I strongly suggest that you either rewrite the code to avoid memcpy, or at the very least statically assert that the attribute type being registered/queried is in fact trivially copyable. It isn't difficult to come up with worst case scenarios here (i.e. this is a real and serious issue).

The implementation of 'AlignedEntityBackend' imposes the further constraint that attribute objects cannot contain pointers to other attribute objects because the pointer value will become invalid if the array containing the pointee is resized.

It should also be documented that conversions are unsafe. That is, the type of the object being retrieved/set must *exactly* match the registered type to avoid undefined behaviour. About the best you could do here is a runtime type check (because of the use of strings to identify attributes).

I have some idea to avoid/mitigate the above situations if you get stumped.

21
General / Re: Simulating Jumping
« on: January 21, 2014, 10:01:10 pm »
@Lee R, when I wrote:
Velocity = Velocity + Gravity * DeltaTime
Position = Position + Velocity * DeltaTime

I meant to say I used Euler Integration. Sorry for the confusion. The code you highlighted was the code implementation.

Erm, yes, I am aware. I appear to be speaking a different language or something, so I'll bow out of this one.

Quote
When I said it didn't work I meant it didn't give the desired effect. The player would go up really high at a fast rate and disappear off the display.
That's because you were reapplying the movement from all previous frames to the current frame (i.e. position += position ...).

22
General / Re: [Solved] Resource Manager Stack Corruption
« on: January 21, 2014, 09:41:25 pm »
VS2012

23
General / Re: Resource Manager Stack Corruption
« on: January 21, 2014, 09:37:50 pm »
I do believe that this is a problem on SFML's side, because this code reproduces the error:
sf::Font* font = new sf::Font;
font->loadFromFile("Fonts/arial.ttf");
delete font;

EDIT: http://en.sfml-dev.org/forums/index.php?topic=10191.5 this guy is in the exact same situation.

I'm unable to reproduce this behaviour. It's hard to imagine how this code could cause a problem if you're linking against the correct runtime libraries in all of your application's modules. Perhaps you could upload the project files of a minimal repro for someone to take a look at. I don't know about everyone else, but my crystal ball is currently in for cleaning ;)

24
General / Re: Simulating Jumping
« on: January 21, 2014, 09:17:15 pm »
Lee R, I'm not sure I understand...

I'm not sure that you need to ;) He translated the Euler method into C++ incorrectly (and, looking at it again, I see he's only applying it when a certain key has been pressed, rather than every frame).

Quote
@Lee R: Ah! I thought your quote was from his code. And after reading your post 3 times I couldn't understand why you were reapeting the quote. :p Sorry!

No problem. I'm guessing that you weren't the only one :s

25
General / Re: Resource Manager Stack Corruption
« on: January 21, 2014, 02:47:55 pm »
Long version: From the defines dotted around, I assume you have explicit template instantiations inside a DLL. When passing STL objects across DLL boundaries (e.g. std::sting as function parameters), both the DLL and .exe must link against the multithreaded DLL version of the CRT to ensure they use the same heap allocator. In addition, the release/debug version must match because STL objects may have a different size/layout in each.

Short version: Forget the DLL. Go header only; it is a template after all.


26
General / Re: Simulating Jumping
« on: January 21, 2014, 10:40:24 am »
This:
Velocity = Velocity + Gravity * DeltaTime
Position = Position + Velocity * DeltaTime

Is not the same as this:
vel += vel + gravity * dt;
position += position + vel * dt;
 

You meant either:
vel = vel + gravity * dt;
position = position + vel * dt;
 

Or:
vel += gravity * dt;
position += vel * dt;
 

27
General / Re: Lighting Polygon Clipping
« on: January 20, 2014, 11:22:07 pm »
I'm running a GTX 660 (low end of the mid range) and don't remember having any issues with the performance of that technique. I didn't envision creating scenes with 20 shadow casting lights in them, though (not every light needs to cast a shadow e.g. emissive lights). Fill rate could also have been an issue, if you weren't using downscaled render textures. I wonder how that Intel card would fair with a raster based approach ;) Overall, the 'lights as geometry' is probably the way to go; I just went with what seemed quickest to implement and found the performance acceptable.

28
General / Re: Lighting Polygon Clipping
« on: January 20, 2014, 10:09:25 pm »
I lost my lighting code in the great reformat of 2013. A sad time indeed. Your intuition looks right to me, though. This is from memory so please excuse any mistakes; in pseudo code, it goes something like:

Ambient:Color        - The colour of "no light".
Geometry:VertexArray - Shadow geometry.
LightTex:Texture     - Light falloff texture.
RT1:RenderTexture    - Holds entire light map.
RT2:RenderTexture    - Holds current light map pass.
Scene                - Anything affected by light.

RT1.clear(Ambient)
foreach light in lights_on_screen()
    Geometry.clear()
    foreach shape in shapes_in_range_of(light)
        Geometry.add(make_shadow(light, shape))
    RT2.clear(Black)
    RT2.draw(LightTex, BlendNone)
    RT2.draw(Geometry, BlendNone)
    RT1.draw(RT2, BlendAdd)
Screen.draw(Scene)
Screen.draw(RT1, BlendMultiply)
 

29
General / Re: Lighting Polygon Clipping
« on: January 20, 2014, 04:12:57 pm »
Algorithm is:
For each light for each convex hull do:
1. find two points that will create lines with biggest angle when a
2. take curve enclosed between these points, including them
3. add 3 points: one taken from line made by light point and last edge point, one taken from line made by light point and avg of hull vertices and one taken from line made by light point and last edge point. Take them from far away the light point so that they lie at least 2* radius of light away from it, to be safe.
4. This polygon is the hard shadow for this hull made by this light. That's it.

I won't pretend to understand exactly what you're describing there, but it sounds overly complicated. To form hard edged shadow geometry, you can project (away form the light source) the end points of each non-facing edge. The quad formed by the original end points and the projected end points is then the shadow geometry.

Here is the most drain-dead-simple form I could come up with to demonstrate. To make the shadow always reach the edge of the screen, you'd simply project the end points further out.
#include <SFML/Graphics.hpp>

sf::VertexArray shadow(const sf::Shape& caster, sf::Vector2f emitter, sf::Color ambient)
{
    sf::VertexArray geometry(sf::Quads);

    // for each edge of 'shape'.
    const unsigned size = caster.getPointCount();
    for (unsigned i = size - 1, j = 0; j < size; i = j++)
    {
        const auto& xf = caster.getTransform();
        // find world space end points of current edge.
        auto a = xf.transformPoint(caster.getPoint(i));
        auto b = xf.transformPoint(caster.getPoint(j));

        auto ab = b - a;
        auto ea = a - emitter;
        // if current egde faces away from emitter.
        if ((ab.x * ea.y - ab.y * ea.x) < 0.0f)
        {
            // make quad from edge end points and projected end points.
            geometry.append(sf::Vertex(a, ambient));
            geometry.append(sf::Vertex(a + ea, ambient));
            geometry.append(sf::Vertex(b + (b - emitter), ambient));
            geometry.append(sf::Vertex(b, ambient));
        }
    }

    return geometry;
}

int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "Shadows");
    window.setFramerateLimit(30);

    // Light blocker.
    sf::RectangleShape shape(sf::Vector2f(100.0f, 100.0f));
    shape.setFillColor(sf::Color::Blue);
    shape.setPosition(250.0f, 250.0f);
    shape.setRotation(60.0f);

    sf::Vector2f emitter;   // Position of light emitter.

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                window.close();
            }
            else if (event.type == sf::Event::MouseMoved)
            {
                // Emitter position tracks mouse pointer.
                emitter = sf::Vector2f(
                    static_cast<float>(event.mouseMove.x),
                    static_cast<float>(event.mouseMove.y)
                );
            }
        }

        window.clear(sf::Color::White);
        // Draw shadows cast by 'shape'.
        window.draw(shadow(shape, emitter, sf::Color::Black));
        // Dwaw actual 'shape'.
        window.draw(shape);
        window.display();
    }

    return 0;
}
 

The real tricky part is forming an image composed of multiple, coloured, shadow casting lights using only the blend modes provided by SFML.

30
SFML projects / Re: [Thor] Vector projection + example
« on: September 01, 2013, 10:30:40 pm »
I added the function thor::projectedVector() which returns the projection of a vector onto an other

You can get rid of the normalization step using the following definition (where a1 is the projection of a onto b):


template <typename T>
sf::Vector2<T> projectedVector(const sf::Vector2<T>& vector, const sf::Vector2<T>& axis)
{
    assert(axis != sf::Vector2<T>());
    return dotProduct(vector, axis) / squaredLength(axis) * axis;
}

Pages: 1 [2] 3 4 ... 6
anything