| | WIP: Controller system | |
| | Author | Message |
---|
Darktib Committer
Messages : 389 Date d'inscription : 2009-07-20 Localisation : A coté de Paris
| Subject: WIP: Controller system Thu Feb 16, 2012 1:44 pm | |
| The last week I have started working on a controller system (which is not related to SPK::System) for SPARK. What is a controller ? Actually, when you create an effect, all parameters are fixed, and if you want to change it dynamically, you must do it yourself. For example, to change the direction of a StraightEmitter at run-time you must add some code in your main loop. A Controller is a class designed to change object parameters at run-time, without needing user code in the loop: a Controller possess special attributes that are called 'controls', that can be connected to other object attributes. When the controller sets the value of a control, every attribute connected to the control is set to the value of the control. And the cool point is that Controllers are also SPKObjects : you can chain controllers! A simple example: let's say we have a StraightEmitter emitting particles from a point, and we want to "rotate" the direction (ie, direction = ( sin(t), 0, cos(t)) ) Currently, to do this, the code is: - Code:
-
// Init // ... SPK::Ref<SPK::StraightEmitter> emitter = ...; // ...
while(gameIsRunning) { emitter.setDirection(SPK::Vector3D(sin(t),0,cos(t))); render(); }
With controllers, it becomes: - Code:
-
// Init // ... SPK::Ref<SPK::StraightEmitter> emitter = ...; SPK::Ref<SinController> sc = ...; // has an unique control, "value" (float) SPK::Ref<CosController> cc = ...; // has an unique control, "value" (float) SPK::Ref<VectorMultiplexer> vm = ...; // has an unique control, "value" (Vector3D) vm.setY(0.0f); sc.connect("value",vm,"x"); cc.connect("value",vm,"z"); vm.connect("value",emitter,"direction"); // ...
while(gameIsRunning) { render(); }
The main advantage here is that controls and connection are serializables, an artist can put some dynamics in the effect without having the programmer to code extra functions. The system is not finished yet, I am still working on it (because of the implications, for example 100% automatic deserialization, etc...). I will post further improvements of this feature in this thread. For the moment, here is a test demo: Link : http://www.mediafire.com/?7njyil0y7ag65m4Code : - Code:
-
// external libs #include <irrlicht.h>
// SPARK lib #include <SPARK.h> #include <SPARK_IRR.h>
// windows only #ifdef _WIN32 #include <windows.h> #endif
float dirX = 0.0f,dirY = 0.0f,dirZ = 0.0f;
using namespace irr; IrrlichtDevice* device = NULL;
// Input Receiver class MyEventReceiver : public IEventReceiver { public: virtual bool OnEvent(const SEvent& event) { if(event.EventType==EET_KEY_INPUT_EVENT) if(event.KeyInput.Key == KEY_ESCAPE && event.KeyInput.PressedDown==false) device->closeDevice(); return false; } };
class SinController : public SPK::Controller { SPK_START_DESCRIPTION(SinController) SPK_CONTROL("value",SPK::SPK_TYPE_FLOAT) SPK_END_DESCRIPTION
public: SinController(float p_=0):time(0),p(p_){}
protected: virtual void innerUpdateControls(float deltaTime) { float period = 2.0f; time = fmod(time + deltaTime,period); float tTime = time / period; float phase = p * 2 * irr::core::PI; float output = sinf(phase + tTime * 2 * irr::core::PI); if(!setControl(0,output)) printf("Failed to set sin control\n"); } float time,p; };
class VectorMultiplexer : public SPK::Controller { SPK_START_DESCRIPTION(VectorMultiplexer) SPK_ATTRIBUTE("x",SPK::SPK_TYPE_FLOAT,setX) SPK_ATTRIBUTE("y",SPK::SPK_TYPE_FLOAT,setY) SPK_ATTRIBUTE("z",SPK::SPK_TYPE_FLOAT,setZ) SPK_CONTROL("value",SPK::SPK_TYPE_VECTOR) SPK_CONTROL("test1",SPK::SPK_TYPE_FLOAT_PAIR) SPK_CONTROL("test2",SPK::SPK_TYPE_INT32_ARRAY) SPK_END_DESCRIPTION
public: VectorMultiplexer():x(0),y(0),z(1) {}
void setX(float v) { x = v; dirX = x; } void setY(float v) { y = v; dirY = y; } void setZ(float v) { z = v; dirZ = z; }
protected: float x,y,z; virtual void innerUpdateControls(float deltaTime) { SPK::Vector3D output(x,y,z); if(!setControl(0,output)) printf("Vector: failed to set control\n"); if(!setControl(1,12.3f,45.6f)) printf("float-pair: failed to set control\n"); if(!setControl(2,std::vector<int>(3,42))) printf("int-array: failed to set control\n"); } };
// Main function int main(int argc, char *argv[]) { //!IRRLICHT video::E_DRIVER_TYPE chosenDriver = video::EDT_OPENGL; #ifdef _WIN32 if(MessageBoxA(0,"Do you want to use DirectX 9 ? (else OpenGL)","SPARK Irrlicht test",MB_YESNO | MB_ICONQUESTION) == IDYES) chosenDriver = video::EDT_DIRECT3D9; #endif
//!IRRLICHT device = createDevice(chosenDriver, core::dimension2d<u32>(800,600), 32, false, false, false, new MyEventReceiver);
video::IVideoDriver* driver = device->getVideoDriver(); scene::ISceneManager* smgr = device->getSceneManager(); gui::IGUIEnvironment* guienv = device->getGUIEnvironment();
device->setWindowCaption(L"SPARK Irrlicht test - Mouse to aim, arrows to move"); device->getCursorControl()->setVisible(false); irr::scene::ICameraSceneNode* cam = smgr->addCameraSceneNodeFPS(smgr->getRootSceneNode(),100.0f,0.0005f); cam->setPosition(irr::core::vector3df(0.0f,0.0f,1.5f)); cam->setTarget(irr::core::vector3df(0.0f,-0.2f,0.0f)); cam->setNearValue(0.05f);
// Inits Particle Engine // Sets the update step SPK::System::setClampStep(true,0.1f); // clamp the step to 100 ms SPK::System::useAdaptiveStep(0.001f,0.01f); // use an adaptive step from 1ms to 10ms (1000fps to 100fps) irr::scene::CSPKParticleSystemNode* system = new irr::scene::CSPKParticleSystemNode(smgr->getRootSceneNode(),smgr); system->drop(); // We let the scene manager take care of the system life time
{ SPK::Ref<SPK::IRR::IRRQuadRenderer> quadRenderer = SPK::IRR::IRRQuadRenderer::create(device); quadRenderer->setBlendMode(SPK::BLEND_MODE_ADD); quadRenderer->enableRenderingOption(SPK::RENDERING_OPTION_DEPTH_WRITE,false); quadRenderer->setTexture(driver->getTexture("res\\flare.bmp")); quadRenderer->setTexturingMode(SPK::TEXTURE_MODE_2D);
SPK::Ref<SPK::Point> emitpt = SPK::Point::create(); SPK::Ref<SPK::StraightEmitter> emitter = SPK::StraightEmitter::create(SPK::Vector3D(1,0,1),emitpt); emitter->setForce(0.4f,0.6f); emitter->setFlow(200);
SPK::Ref<SPK::ColorGraphInterpolator> graphInterpolator = SPK::ColorGraphInterpolator::create(); graphInterpolator->addEntry(0.0f,0xFF000088); graphInterpolator->addEntry(0.5f,0x00FF0088); graphInterpolator->addEntry(1.0f,0x0000FF88);
SPK::Ref<SPK::Group> group = system->createSPKGroup(400); group->setRadius(0.15f); group->setLifeTime(1.0f,2.0f); group->setColorInterpolator(graphInterpolator); group->setParamInterpolator(SPK::PARAM_SCALE,SPK::FloatRandomInterpolator::create(0.8f,1.2f,0.0f,0.0f)); group->setParamInterpolator(SPK::PARAM_ANGLE,SPK::FloatRandomInitializer::create(0.0f,2 * 3.14159f)); group->addEmitter(emitter); group->addModifier(SPK::Gravity::create(SPK::Vector3D(0.0f,-0.5f,0.0f))); group->addModifier(SPK::Friction::create(0.2f)); group->setRenderer(quadRenderer);
SPK::Ref<SinController> scontroller1 = SPK_NEW(SinController); SPK::Ref<SinController> scontroller2 = SPK_NEW(SinController,0.25); SPK::Ref<VectorMultiplexer> vmcontroller1 = SPK_NEW(VectorMultiplexer); SPK::Ref<VectorMultiplexer> vmcontroller2 = SPK_NEW(VectorMultiplexer); scontroller1->connect("value",vmcontroller1,"x"); scontroller1->connect("value",vmcontroller2,"z"); scontroller2->connect("value",vmcontroller1,"z"); scontroller2->connect("value",vmcontroller2,"x"); vmcontroller1->connect("value",emitter,"direction"); vmcontroller2->connect("value",emitpt,"position"); system->addSPKController(scontroller1); system->addSPKController(scontroller2); system->addSPKController(vmcontroller1); system->addSPKController(vmcontroller2);
int startX = 80, startY = 80; float lineLength = 40.0f;
while(device->run()) { //system->setPosition(irr::core::vector3df(-dirX,dirY,-dirZ));
driver->beginScene(true, true, irr::video::SColor(0,0,0,0));
// Renders scene smgr->drawAll();
irr::core::stringw infos; infos+="FPS: "; infos+=driver->getFPS(); infos+=" - Nb Particles: "; infos+=system->getNbParticles(); guienv->getBuiltInFont()->draw(infos.c_str(),irr::core::rect<irr::s32>(0,0,170,20),irr::video::SColor(255,255,255,255));
irr::video::SMaterial mat; mat.Lighting = false; driver->setTransform(irr::video::ETS_WORLD,irr::core::matrix4()); driver->setMaterial(mat); driver->draw2DLine(irr::core::vector2di(startX,startY),irr::core::vector2di((int)(startX + lineLength * dirX),(int)(startY + lineLength * dirZ)));
driver->endScene(); }
SPK_DUMP_MEMORY } device->drop(); SPK_DUMP_MEMORY
return 0; } Note 1: the code is just to show how to get this to work. Obviously, you cannot compile it because the controller system is not on the SVN repository yet Note 2: the point of emission is describing a circle, and the direction of emission too, but in the opposite way, leading to this strange effect. Feel free to comment! | |
| | | Darktib Committer
Messages : 389 Date d'inscription : 2009-07-20 Localisation : A coté de Paris
| Subject: Re: WIP: Controller system Thu Feb 23, 2012 3:42 pm | |
| Some random piece of news about the controller system... About types: this system will introduce SPK::SPKType, which is a (big) enum containing all types of variable that can be controlled in SPARK. A type is a combination of a base type (bool, char, float, int, Ref<SPKObject>, Ref<Emitter>, etc...) and a store method (single, pair, triplet, quadruplet, array). It allows you to precisely define an attribute, and permits the controller system to use your already existing code for setting attributes. For example, I've written a controller which changes the zone of an emitter each x seconds (x is real): - Code:
-
class ZoneChanger : public SPK::Controller { SPK_START_DESCRIPTION(ZoneChanger) SPK_CONTROL("value",SPK::SPK_TYPE_ZONE_REF) SPK_END_DESCRIPTION
public: ZoneChanger(float p=1.0f):time(0.0f),period(p){} void addZone(const SPK::Ref<SPK::Zone>& z) { zones.push_back(z); timePerIndex = period / zones.size(); }
protected: virtual void innerUpdateControls(float deltaTime) { time = fmod(time + deltaTime,period); SPK::Ref<SPK::Zone> output = zones[(unsigned int)(time / timePerIndex)]; if(!setControl(0,output)) printf("Failed to set zone control\n"); } float time,period,timePerIndex; std::vector<SPK::Ref<SPK::Zone> > zones; }; Note: this is a test controller, the final one will use attributes and will be a lot more powerful. Now, let's focus on the description declaration. It is a lot concise now : you no longer have to call SPK_PARENT_ATTRIBUTES or SPK_IMPLEMENT_OBJECT, as it is done automatically. And attributes have a third parameter, the setter of the attribute. It is needed to control the attribute but also to deserialize it. I am on holiday next week, so there won't be any advances on the controller system for a week. For you, time to see the code! You can download a copy of the repository with the controller system here. This is still far from being finished (lacks automatic deserialization, serialization is intentionnally broken, and the code should be cleaner I think). There is a demo : 'demos/ControllerTest'. Be aware you will need Irrlicht 1.8 (svn) for it to compile (there was a typo in Irrlicht 1.7 code that was fixed on Irrlicht SVN) Have fun, and don't forget to post any idea/request/patch/bug here! | |
| | | Darktib Committer
Messages : 389 Date d'inscription : 2009-07-20 Localisation : A coté de Paris
| Subject: Re: WIP: Controller system Sat Mar 17, 2012 2:22 pm | |
| Lately, I've introduced some meta-programming facilities in SPARK. First, back to the types.Types in SPARK are defined as values in the enumeration 'SPK::SPKType'. There are 19 base types: Base type | Corresponding C++ type | SPK_TYPE_BOOL | bool | SPK_TYPE_CHAR | char | SPK_TYPE_INT32 | int | SPK_TYPE_UINT32 | unsigned int | SPK_TYPE_FLOAT | float | SPK_TYPE_VECTOR | Vector3D | SPK_TYPE_COLOR | Color | SPK_TYPE_STRING | std::string | SPK_TYPE_OBJECT_REF | Ref<SPKObject> | SPK_TYPE_ACTION_REF | Ref<Action> | SPK_TYPE_CONTROLLER_REF | Ref<Controller> | SPK_TYPE_EMITTER_REF | Ref<Emitter> | SPK_TYPE_GROUP_REF | Ref<Group> | SPK_TYPE_FLOAT_INTERPOLATOR_REF | Ref<FloatInterpolator> | SPK_TYPE_COLOR_INTERPOLATOR_REF | Ref<ColorInterpolator> | SPK_TYPE_MODIFIER_REF | Ref<Modifier> | SPK_TYPE_RENDERER_REF | Ref<Renderer> | SPK_TYPE_ZONE_REF | Ref<Zone> | SPK_TYPE_ZONED_MODIFIER_REF | Ref<ZonedModifier> |
Then, you have 5 store method:
- single : 1 value of the base type
- pair : 2 values of the base type
- triplet : 3 values of the base type
- quadruplet : 4 values of the base type
- array : n values of the base type in a variable length array (std::vector)
The SPKType value all respect the same (quite intuitive) pattern. Let's take an example: SPK_TYPE_FLOAT_TRIPLETThe blue part is the prefix that all SPARK types share. The red part is the name of the base type. Here it is FLOAT, but it could have been ACTION_REF for example. The green part is the suffix linked to the store method (the 'single' store method does not have a suffix). Here it is triplet, that means a value of type SPK_TYPE_FLOAT_TRIPLET contains 3 floats. Now, why that much types ?With the controllers system, Attributes have an extra field, their setter. The setter prototype is computed using the type of the attribute. For example, SPK::Zone has only one attribute, their position, which type is SPK_TYPE_VECTOR. SPARK expects a setter of the form void (Zone::*)(const SPK::Vector3D&) (the used function is obviously 'setPosition'). It enable attributes to be strongly typed (and you cannot pass an innappropriate setter). Note that numeric types are passed by value and not by reference.Also, the setter for the type SPK_TYPE_*_PAIR (for example) is void (MyClass::*)(U,U) where U is the c++ type corresponding to the SPARK base type Meta-programmingThe meta programming facilities allows user to find, given a SPKType, the C++ corresponding type, the number of parameters for the setter, i parameter type (ie, 'real' type + const ref if not numeric), the base type, etc... All is done by using the helper SPKTypeInfo. Finally you can find the SPKType associated with a C++ type. This feature is necessary to generate Attributes, Controls, etc... with code repetition | |
| | | Darktib Committer
Messages : 389 Date d'inscription : 2009-07-20 Localisation : A coté de Paris
| Subject: Re: WIP: Controller system Thu Apr 26, 2012 9:20 am | |
| About (de)serialization...
I've introduced a little change in the previous work: instead of specifying only the setter(s) for an attribute, getter(s) are now also needed. Therefore, serialization and deserialization are entirely automatic, no code from user is needed. And you can remove the two function innerImport and innerExport.
This kind of feature is really nice because: 1/ SPARK is ensuring the validity of data, users can't add wrong data (bug, etc...) 2/ The (de)serialization processus is now totally handled by SPARK so some optimizations can safely be done. I do not expect a boost in speed, but I expect the process to consume less memory than before.
For the Loader/Saver part, the class IO::Attribute will disappear, as it is no longer needed now.
Note: multi-threaded (de)serialization is not possible. | |
| | | Darktib Committer
Messages : 389 Date d'inscription : 2009-07-20 Localisation : A coté de Paris
| Subject: Re: WIP: Controller system Tue Jul 03, 2012 6:46 am | |
| Despite the absence of messages recently, the project has advanced; a new kind of attributes was created: structured attributes, which consists of an array of structure. Each field of each item in the array can be independently controlled, thus allowing cool effects . It is the only attribute that have this ability. For example, given the structure - Code:
-
struct S { float x; Color y; };
the old method was to create 2 array attributes, one for the x values and one for the y: - Code:
-
attribute 1 = array<float> // for x attribute 2 = array<Color> // for y
Now, you just need one attribute. - Code:
-
attribute = array<S>
There is also a good side-effect: serialization / deserialization now ensure correctness of data: with the old method, 2 arrays are serialized, and the user can change one of the array, so for example one have 10 items and the other have 6 items: full deserialization is impossible. Here is a demo, with the source used to create it (not the cleanest code I've ever wrote...) (this does not include the code of the controller system): http://www.mediafire.com/?kmiq3a059785kq9 | |
| | | Darktib Committer
Messages : 389 Date d'inscription : 2009-07-20 Localisation : A coté de Paris
| Subject: Re: WIP: Controller system Sun Aug 19, 2012 8:39 am | |
| | |
| | | Sponsored content
| Subject: Re: WIP: Controller system | |
| |
| | | | WIP: Controller system | |
|
Similar topics | |
|
| Permissions in this forum: | You cannot reply to topics in this forum
| |
| |
| |