Jump to content

TheComet

Member Since 02 Oct 2013
Offline Last Active Apr 26 2017 06:35 AM

#5339357 New lightweight IK library

Posted by TheComet on 13 April 2017 - 07:24 PM

I wasn't sure where to post this, but some of you may know that I've been working on a lightweight inverse kinematics library implementing the FABRIK algorithm. I'd like some feedback and/or complaints and/or condolences on this.

https://github.com/TheComet93/ik

It's written in C89 and has no dependencies other than the C standard library.

You can find information on how to use it on the wiki page https://github.com/TheComet93/ik/wiki and also in the header files solver.h, node.h and effector.h.




#5335948 How to lose friends and alienate coworkers.

Posted by TheComet on 16 March 2017 - 12:42 AM

One way I shot myself in the foot and wasted days debugging was this:

#if defined(TESTING)
#   define protected public
#   define private public
#endif
Some libraries in the project were compiled with and without TESTING defined. Works on gcc for some reason but causes seemingly random runtime errors on Windows.


#5335561 Source files

Posted by TheComet on 12 March 2017 - 07:01 PM

Have you tried attaching with a debugger? Do you have a stacktrace? Can you post the section of code you think is causing the issue?




#5325565 software and hardware interaction

Posted by TheComet on 01 January 2017 - 11:59 AM

I am at the level of how does a command turn into any sort of electronic signal.


 
It's not easy to answer this question because a "command" is an abstract concept. There's no such thing as a "command" at the hardware level. Nor is there any "stack" or "memory" etc. The deeper down you go the more concrete everything gets. Concepts created through abstractions no longer bear any meaning.
 
It may be worth looking into automata theory. Computers in their "lowest software form" consist of combinational logic, which is stateless and time independent (it's just boolean functions). In the earlier days, combinational logic was organised together directly in hardware to create an abstract turing machine capable of "executing instructions". In modern CPUs, this became less feasible due to it being inflexible, so a method for dynamically being able to organise boolean circuits using a hardware description language (such as  verilog, VHDL, SpinalHDL or whatever) was devised, making it possible to describe things such as finite state machines or turing machines in software. These descriptions are synthesized and programmed into a hardware device, thus creating something that behaves like a modern CPU. This is what we know as "micro code", which is a stupid name in my opinion, because it creates this false idea that the CPU is "running a program", when in fact the micro code is conceptually operating 3 classes below a turing machine.
 
If you go deeper, you will see that combinational logic can be implemented in many different ways in hardware. It is not restricted to just "2 voltages" nor is it restricted to being a voltage (as previously mentioned). One could for example choose to build an electrical circuit that computes boolean functions using analog current levels. You would then quantize these levels to form the concept of a "digital value". The number of quantization levels can be arbitrary. One might even go further and implement an optical or mechanical solution to computing boolean expressions.
 
But sticking to electrical circuits: These days, combinational logic is largely implemented using CMOS technology; Transistors that turn "on" and "off" (this is an over-simplified way of thinking about it).
 
So to answer your question to some degree: A specific CPU "command" or "instruction" is encoded in memory by a series of transistors that are either turned on or off and are most likely located in one of the chips on one of your RAM sticks. The fetch of these "bits" into the CPU's instruction cache is achieved over a bus (a parallel routing of copper wires) using some standard protocol. The bits are stored inside the CPU in an abstract piece of memory, which doesn't physically exist, but was instantiated via a hardware description language which describes the behaviour of a collection of lookup tables inside the CPU, which in turn were implemented using CMOS technology, thus organising a number of transistors in such a way as to create something that behaves like memory. Depending on what the instruction is, it will cause a series of state transitions to happen on the CPU (the transition functions being described in a hardware description language and also programmed into hardware lookup tables) which will eventually lead to some result data being computed and transferred to some other place in your computer.
 
Going deeper, it is important to realise that an analog circuit is a superset of everything digital, meaning that any digital circuit is by definition analog. A circuit that implements the evaluation of a boolean expression exhibits some complicated analog phenomenon (which can be ignored if you aren't directly involved with the fabrication of the circuit). For example, look at this picture (blue line) to see what the voltage and current does when a complementary MOSFET transistor pair switches from "off" to "on". While the circuit will perform as expected most of the time, random events can occasionally happen, causing a bit to be flipped every now and again. This is why almost every transport of data goes through some form of error check (ranging from a simple parity check to CRC, depending on how important the data is). 
 
You can go even deeper and start looking at how the lattice of silicon atoms transport electrons, and how impurities ("doping") of the lattice cause electrons to be under- or oversaturated, and how electrical fields influence how electrons are transported across silicon lattices with different dopings to eventually construct a physical chunk of atoms that behave like a transistor.
 
Indeed, you can go even deeper and touch on quantum mechanics, as has also been previously mentioned in this thread, which introduces even more weird stuff that can interfere with the normal digital operation of the circuit. This is especially relevant when dealing with very tiny transistors. But we've reached a level where going deeper just gets less and less relevant.




#5323827 Possible to overload with same function body?

Posted by TheComet on 16 December 2016 - 06:08 PM

Perhaps optional parameters is the thing you are looking for?
 

int some_function(int input1, int input2=0) {
    for(int I=0; I<arg_count_from_function; i++)
     {
             // do something here with iterating through the args
     }
     return 0;
}



#5320739 graphic Programming using opengl

Posted by TheComet on 24 November 2016 - 07:53 AM

lqLjJun.gif




#5316445 Reloading Python script (C API)

Posted by TheComet on 24 October 2016 - 06:17 AM

Thanks for the suggestions, this is exactly what I ended up doing. In case some unfortunate soul in the future has a similar problem to mine, here is exactly how I solved reloading scripts.

 

A quick disclaimer: This unfortunately only works for the functions you explicitly want to support for reloading. If your script imports other scripts, you're pretty much doomed with this approach. You should instead try to reset the interpreter.

 

I should also note that using a sub-interpreter (Py_NewInterpreter() and PyThreadState_Swap()) for every script you load won't solve this issue. There's a function in the python source code (see import.c) that maintains a global list of imported modules across all instances. There is no way to remove yourself from this list after your module is loaded.

 

In my situation, I wanted to expose two C++ callback functions to python. This is the class definition:

class ScriptInstance : public common::RefCounted
{
public:
    ScriptInstance(const QString& fileName);


    void reload();


    void flush();
    void apply(ubox::Frame* frame);


private:
    QString fileName_;
    previewer::PythonObject<> module_;
    previewer::PythonObject<> flushFunction_;
    previewer::PythonObject<> applyFunction_;
};

Some notes: PythonObject is a wrapper around PyObject*. It steals a reference on construction and calls Py_XDECREF() on destruction. flush() and apply() are the two functions I want to expose to python.

 

The first thing I do (before creating any ScriptInstance instances) is intialise the python interpreter:

bool PythonInterpreter::initialise()
{
    if(Py_IsInitialized())
        return true;

    // Add built-in ubox module before initialisation
    PyImport_AppendInittab("ubox", PyInit_ubox);


    static wchar_t programName[] = L"my app";
    Py_SetProgramName(programName);
    Py_Initialize();
    if(!Py_IsInitialized())
    {
        defaultLogger.logError("Failed to initialise python interpreter");
        return false;
    }


    // TODO this assumes the default location of the python interpreter
    PySys_SetPath(
        L":"
        L":python/lib/python3.5"
        L":python/lib/python3.5/site-packages"
        L":python/lib/python3.5/lib-dynload"
    );


    // Import modules
    PyObject* tmp;
    tmp = PyImport_ImportModule("ubox"); // XXX This returns NULL for some reason? It loads nevertheless...
    tmp = PyImport_ImportModule("numpy");
    tmp = PyImport_ImportModule("scipy");


    return true;
}

Then, when I load a script, I create a new ScriptInstance and pass in the file name. In its constructor I create a new python module and register it under the name of the script (without the .py extension).

ScriptInstance::ScriptInstance(const QString& fileName) :
    fileName_(fileName)
{
    // Need the global python interpreter to exist
    if(PythonInterpreter::initialise() == false)
        return;

    /*
     * Create a python module for this script instance, in which functions and
     * other data can be registered.
     */
    module_ = PyModule_New(fileInfo.fileName().replace(".py", "").toStdString().c_str());
    PyModule_AddStringConstant(module_, "__file__", "");


    reload();
}

 

The reload() function is responsible for loading the file, compiling it, and extracting the relevant functions flush() and apply():

void ScriptInstance::reload()
{
    QFile file(fileName_);
    file.open(QIODevice::ReadOnly);
    QByteArray bytes = file.readAll();
    file.close();

    PythonObject<> compiled = Py_CompileString(bytes.data(), fileName_.toLatin1().data(), Py_file_input);
    // error handling omitted

    // these are all borrowed references
    PyObject* main = PyImport_AddModule("__main__");
    PyObject* globals = PyModule_GetDict(main);
    PyObject* locals = PyModule_GetDict(module_);

    PythonObject<> eval = PyEval_EvalCode(compiled, globals, locals);
    // error handling omitted

    /*
     * Get the two python functions flush() and apply() and make sure they're
     * correct and can be used.
     */
    flushFunction_ = PyObject_GetAttrString(module_, "flush");
    applyFunction_ = PyObject_GetAttrString(module_, "apply");
    // error handling omitted
}

PyImport_AddModule("__main__") will return a borrowed reference to the __main__ module, which in turn contains a dictionary that contains all of the imported modules and functions thus far. This dictionary must be passed to PyEval_EvalCode() when you're evaluating your script, otherwise basic functions such as print() won't be recognised.

 

The locals dictionary is retrieved from the module that was previously created in the constructor and it will hold all of the data that is local to our module (i.e. it will store the flush() and apply() function objects).

 

After evaluating the code with the proper dictionary objects, you will be able to retrieve the function objects from the module with PyObject_GetAttrString(module_, "func").




#5315574 C++ : Extend class in dll

Posted by TheComet on 17 October 2016 - 12:39 PM

Typically this is achieved through some form of dynamic type information, be it using templates + typeid() or some other form of unique type identification

 

The basic idea consists of two classes. A "Manager" and a "Factory". The manager stores a collection of factories and associated type identification, and the factories know how to instantiate the type you need.

 

The following is a complete example using std::type_info. Note that you will need C++11 because of hash_code(). If you must use c++03 then you can replace hash_code() with name() and run that through your own hash function (or change the type in the container from size_t to std::string).

#include <typeinfo>
#include <map>
#include <string>
#include <iostream>


class Factory {
public:
    virtual void* Create() = 0;
};


template <class T>
class FactoryImpl : public Factory
{
    virtual void* Create() override
    {
        return new T();
    }
};


class Manager
{
public:
    template <class T>
    void RegisterObjectFactory()
    {
        factoryMap_[typeid(T).hash_code()] = new FactoryImpl<T>();
    }


    template <class T>
    T* CreateObject() const
    {
        auto factory = factoryMap_.find(typeid(T).hash_code());
        if(factory == factoryMap_.end())
            return NULL;
        return static_cast<T*>(factory->second->Create());
    }


    template <class T>
    void DestroyObject(T* object)
    {
        delete object;
    }


private:
    std::map<size_t, Factory*> factoryMap_;
};


// ----------------------------------------------------------------------------
// Example usage
// ----------------------------------------------------------------------------
class Test
{
public:
    void Greet() { std::cout << "it works!" << std::endl; }
};


int main()
{
    Manager manager;
    manager.RegisterObjectFactory<Test>();
    Test* test = manager.CreateObject<Test>();


    test->Greet();


    manager.DestroyObject(test);
}

One pitfall to look out for with factories is to make sure you delete the object in the same DLL you created it. This is why Manager::DestroyObject() is necessary. If you were to convert it from raw pointers to std::shared_ptr then this would no longer be necessary to do.

 

With this method you would need to pass the manager instance to your plugin DLLs when they start (typically achieved by defining your own start_plugin() function). Then your plugins can call Manager::RegisterObjectFactory<Whatever>() to register the types they require. This will enable the core application to start instantiating types that weren't originally there when compiling.

 

You might consider extending the example to make it possible to pass the actual class name to the manager, and not just the has code. For example:

manager.CreateObject("Test");

instead of

manager.CreateObject<Test>();

For this you would need to store typeid(T).name() in the corresponding factory class.




#5314617 Coding-Style Poll

Posted by TheComet on 10 October 2016 - 11:52 PM

I can live with the prefixes C, I, m_, g_, etc. I can live with different brace styles, I can live with the 80-character-per-line limitation, but the one thing that gets my jimmies rustled is when I encounter the following for literally every function.
 
 
//////////////////////////////////////////////////////////////////////////////
// *-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_*-*_
////
//// NAME OF FUNCTION:
////             SetValue();
////
//// PURPOSE OF FUNCTION:
////             Sets the value.
////
//// INPUT PARAMETERS:
////             value
////                       The value to set.
// _--_--_---_--_--_--_--____-__-___-_----_--_--
//// AUTHOR: Autism McTrashWizard
//// \\\\ ==== //// \\\\ ====//// \\\\ ====//// \\\\ ====//// \\\\ ====//// \\\\ ====//// \\
void SetValue(float value)
{
    g_value = value;
}



#5308086 trek game

Posted by TheComet on 26 August 2016 - 01:44 PM

Just call it Star Shrek and make everyone an ogre




#5304866 Ide For Linux

Posted by TheComet on 09 August 2016 - 05:23 AM

Anyway, let's start with proper shell integration. Say you have table in your source file with names that needs sorting. In vim: Mark first and last line, then ":'a, 'b ! sort" (run 'sort' command, using as input the lines between and including marker a and marker b. Replace input with the output of the sort command).

 

 

Yeah perhaps this should be a separate topic. But to answer your question on how that would work in KDevelop: Highlight the lines, press F7 and type "sort" (or if you're using Vi input mode, highlight lines, enter ":sort").

 

KDevelop uses Kate's text editor, which tries to emulate most of vim's features, so most of the common commands you'd use in vim are available in KDevelop:

https://kate-editor.org/kate-vi-mode/

 

Qt Creator supports all of those.
 

 

Thanks! I didn't know about this.




#5304669 A Lightweight 2D Game Framework Doesn't Seem To Exist

Posted by TheComet on 08 August 2016 - 08:37 AM

Urho3D (despite the name) is a decent 2D game engine/framework. It ships with Box2D physics, it supports animation, shading, etc. and it compiles not only to desktop platforms but also to Android, iOS and the web (using emscripten).

 

As far as "lightweight" goes, it compiles to an approximately ~5MiB library and takes maybe 4-5 minutes to compile on my i5-2440m. So I'd say when compared to something like Ogre3D it is fairly lightweight.




#5304662 Ide For Linux

Posted by TheComet on 08 August 2016 - 08:19 AM

TL;DR I recommend KDevelop if your project is CMake based.

 

To those recommending QtCreator, it's a fairly decent IDE if you're developing Qt applications, but I don't recommend it for anything other than that. One of the things I disliked about it a lot is how it handles file navigation. In KDevelop I can toggle between header/source files with ctrl+shfit+c. I can "alt tab" between the two last files with ctrl+shift. I can press ctrl+alt+o and quick jump to a file (or by using any of those other shortcuts that index function names or class names). These are things QtCreator doesn't offer and it's annoying to have to search for your files in the file tree every time you want to change files.

 

Why KDevelop?

  1. Integrates with Vi (if you want).
  2. Integrates with CMake. This has proven to work remarkably well, even for highly complex CMakeLists.txt.
  3. Integrates with git. A cool example of this is you can press alt+a and it will use "git blame" to show who edited which line of code.
  4. KDevelop has some of the richest syntax highlighting I've ever seen in any IDE. After programming with it for a while you'll start to see subtle things, like the "++" in "myObject++" changing to purple instead of white because myObject has overloaded operator++. There are *loads* of little things that will help you understand code better.
  5. Lots of helpful navigation shortcuts. As mentioned, you can jump between header/source file pairs, you can jump to declarations/definitions, you can jump to files.
  6. Lots of auto-completion. One of the cooler examples is you can write "it = thing.begin()", then hit alt+1 and the IDE will auto-complete "std::vector<foo>::iterator" for you.

The list goes on and on. Definitely give it a shot!

 

Personally, I've been developing on Linux for over 20 years and never felt the need to be limited by an IDE, but 

to each their own I guess.

 

 

I love this. "Limited" by an IDE. I'd be interested to hear what you consider limiting factors of an IDE?




#5295997 Wannabe

Posted by TheComet on 10 June 2016 - 11:29 AM

It's never too late to pursue anything. There are a million ways to get into game programming, you just have to find one that is easy enough for beginners and offers to you the freedom and features you expect to have. Some immediate products that come to mind are: DarkBASIC, Unity, pygame, etc.

 

I personally started making games with DarkBASIC when I was about 14. It's a very simple language to pick up quickly and the results are immediate. It's also a lot of fun to play around in. I wrote games for about 5 years using only that product.




#5295995 Do you keep a collection of handles in your game for your components

Posted by TheComet on 10 June 2016 - 11:21 AM

This is perhaps a shameless plug, but when I first learned about ECS I tried writing my own framework, and I implemented a way for the user to specify execution dependencies:

https://github.com/thecomet93/ontology#execution-order-of-systems

 

In my project, the dependency tree is resolved and flattened into a linear ordered list of systems to execute. Thus, when calling world.update(), I only have to iterate through the list and call system->update(). This sounds like the exact thing you need. It might also be a possibility to flatten the dependency tree into multiple lists of systems, where each list contains systems that can be executed in parallel. Just a thought.