start a shader effect so it leasts for n time at spawn time

Started by BielBdeLuna, July 13, 2015, 06:50:05 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

BielBdeLuna

I've never done shader stuff on monsters, I'm looking forward to implement some material changes on the materials of the monsters at spawn time

the idea would be something like:

make a lava monster come out of the lava as a hot bright lava element and as time goes it cools down until it becomes a rocky textured monster

I'm looking forward to implement this thing in the GPL ai script,
how might I influence the material in the most generalist way so it can be reused for other stuff different than the one example I exposed above?


currently I have this amongst the init() function code:


//this code is GPLv3
spawn_time = sys.getTime();

no_shader_effect = true;
spawn_shader_time = getFloatKey( "spawn_shader_time" ); // saves a reference of a the end time shader effect
if ( spawn_shader_time > 0.0 ) {
    no_shader_effect = false;
    spawn_shader_time = spawn_time + spawn_shader_time;
}


so I have a start time reference and an end time reference

I guess I could divide that time by the HZ the games ticks (60 fps)

and then normalize this value so I get a percentage between 0.0 and 1.0 with this:


//this code is GPLv3
float minNormalizeMax(float number, float max, float min ) {

    float interval;
    float incorporated;
    float value;

    interval = max - min;
    incorporated = number - min;

    if ( incorporated <= 0 ) {
        value = 0;
    } else {       
        value = ( 1/interval ) * incorporated;
    }
    //sys.print( "value is: " + value + "\n" );
    return value;

}


so I could get what percentage it should variate per tick

and multiply it with the material value so it end ups influencing it somewhat

you guys know a way to implement something like this in the materials?

maybe I would need it's own thread so it doesn't disturb the character inner-working? and once it reached 1.0 (the end of the effect) auto kill that thread?


BielBdeLuna

in case anyone is interested

I created a thread ready effect so you can change a material parm in a controlled fashion, with three parameters that describe the time it takes the effect: a attack time, a hold time, and a release time.


//this code is GPLv3
void t_mat_effect( float attack_time, float hold_time, float release_time, float mat_parm) {
    float time_portion_attack, time_portion_hold, time_portion_release,
          value_portion_attack, value_portion_release,
          mat_parm_value, threadIdNum;
    float current_time = 0.0;

    // that's for protection, let's not fuck it up here with strange negative numbers
    attack_time = absolute( attack_time );
    hold_time = absolute( hold_time );
    release_time = absolute( release_time );

    //this sets the time portions for every part of the effect
    time_portion_attack = attack_time / GAME_FRAMETIME;
    time_portion_hold = hold_time / GAME_FRAMETIME;
    time_portion_release = release_time / GAME_FRAMETIME;

    //this sets the increments per time_frame, from 0.0 to 1.0, and protects agains a divide by zero situation
    //there is no increment in hold because the effect doesn't change in that time.
    value_portion_attack = 0;
    if ( attack_time > 0 ) {
        value_portion_attack = 1 / attack_time;
    }

    value_portion_release = 0;
    if ( attack_time > 0 ) {
        value_portion_release = 1 / release_time;
    }

    //let's define a start point of the mat_parm_value taking into account the availability of three parts of the effect
    if ( attack_time == 0 ) {
        mat_parm_value = 1;
    } else {       
        mat_parm_value = 0;
    }

    // the actual effect in motion
    while ( current_time <= ( attack_time + hold_time + release_time ) ) {
        self.setShaderParm(mat_parm, mat_parm_value); // this changes the material every frame the effect is active

        if ( ( attack_time > 0 ) && ( current_time <= attack_time ) ) {
            mat_parm_value += value_portion_attack;

            if ( mat_parm_value > 1 ) {
                 mat_parm_value = 1; //let's polish the results
            }

            current_time += portion_attack;
        } else if ( ( hold_time > 0 ) && ( current_time > ( attack_time ) ) && ( current_time <= ( attack_time + hold_time ) ) ) {
            mat_parm_value = 1; // the mat_parm_value doesn't change here
            current_time += portion_hold;
        } else if ( release_time > 0 ) {
            mat_parm_value -= value_portion_release;
            current_time += portion_release;
        }

        waitFrame();
    }

    //let's polish the results
    if ( release_time > 0 ) {
        self.setShaderParm(mat_parm, 0);
    }

    //let's kill that thread gracefully and in a way it can be detected as finished from outside the thread
    threadIdNum = mat_effect_control;
    mat_effect_contro = 0;
    sys.terminate( threadIdNum );
}


unfortunately since this is a thread function you can't use it as a generalist function, you have to put in the script object and you need to initiate it with this:


float mat_effect_control;

mat_effect_control = thread t_mat_effect( 0.2, 0.5, 1.6, 8);


so you can check if mat_effect_control is active if it is 0  or not. avoiding the situation where several instances of that effect affecting the same material parm, so this means the thread function needs to reside in the same script object so it feeds with that mat_effect_control variable from the script object itself.

the function is highly flexible as you can set any of the three time variables as 0 leaving the effect on the entity's material half way. and then you could chain several of those effects to control the "flow" of the entity's material effect.

BielBdeLuna

I checked that if you execute a thread and save it's reference to a variable, this process it's only done at the time the thread is created, when the thread is killed the variable doesn't return to 0:


namespace test_script {
   
    float threadIdNum = 78;
   
    void superCoolThread( float idthread ) {
        float i = 0;
        while( i < 15 ) {
            sys.println( "i is: " + i );

            i += 1;
            sys.waitFrame();
        }
        sys.terminate( idthread );
    }

    void execute() { // called by the map
        sys.println( "at the start thread number is: " + threadIdNum );
        threadIdNum = thread superCoolThread( threadIdNum );

        sys.wait(2);

        sys.println( "current thread number is: " + threadIdNum );
    }
}


so this wil leave a threadIdNum variable that is not 0 at the end
it would be cool to have a return value in the thread and a way to kill the thread form within at the same time.


BielBdeLuna

a solution to avoid creating several copies of the same code could relay on encapsulating the generalist code within a single function and let the thread function be specific about the thread things:


float thread_controler_1, thread_controler_2;

void whateverhappensinthread( code variables ) {
    while ( true ) {
        long repeteable generalist code
            and a break;

        sys.waitFrame();
    }
}

void t_threadcontrol_1( code variables ){
    float remember;
   
    whateverhappensinthread( code variables );

    remember = thread_controler_1;
    thread_controler_1 = 0;
    sys.terminate( remember );
}

void t_threadcontrol_2( code variables ){
    float remember;

    whateverhappensinthread( code variables );

    remember = thread_controler_2;
    thread_controler_2 = 0;
    sys.terminate( remember );
}

void execute() { // this should initiate two different threads alternatively and consecutively
    while ( true ) {
        float next_one = 0;
       
        if ( thread_controler_1 == 0 && thread_controler_2 != 0 ) { // both have finished, let's select the next
            if ( next_one  == 0 ) {
                next_one = 1;
                thread_controler_1 thread = thread t_trheadcontrol_1( code variables );
            } else {
                next_one = 0;
                thread_controler_2 thread = thread t_trheadcontrol_2( code variables );
            }
        }
        sys.waitFrame();
    }
}


so the threads could have a common part ( the whateverhappensinthread function ) with the generalist code,
and a specific part ( the thread control ) with the very specific code for that thread.

motorsep

Why not simply use table to animate transition from one material to another ?

BielBdeLuna

for more usability and flexibility. what would you propose?

The Happy Friar

In the def file you can call events during animations.  Could a skin change be called then too?

motorsep

Either setting shader parm or skin switch can be called on animation frame. If there is no even function already (at least setting shaderparm isn't in the even functions), just call a small script that set shader parm, which in return would set material stage where transition happens. And then switch skin to set permanent material. Simple, efficient, quite generic.

BielBdeLuna

tied to the animation, but what about a material change tied to the AI scripting? not so simple then eh?

motorsep

Quote from: BielBdeLuna on July 18, 2015, 06:06:56 PM
tied to the animation, but what about a material change tied to the AI scripting? not so simple then eh?

Same thing really. Set shader parm, switch skin.... unless I am missing the actual practical application.

Can you describe a game scenario where conventional methods won't work?

In your initial example (lava monster cooling down) conventional method would work. In Doom 3, after you kill Cyberdemon, the hole in the ground has similar effect - it closes up and cools down. Pretty similar effect.

BielBdeLuna

#10
imagine a monster that every time he gets angry he burns up in flames, this could last more than one animation state. and the effect would be triggered by the ai script, my idea is to give flexibility to the developer, not a fixed effect, the lava man was just an example.

imagine a monster that changes the material shader when he sees the player, and when he has lost it, the material goes back to the first stage, this is all triggered via ai scripting and doesn't correspond to any animation in particular.

@The Happy Friar: I don't remember correctly but i think you can call functions form the def file in the animations isn't it? aren't the footsteps of the player calling an internal function ( a script function that works in the c++)? you could make a function to change the skin and call it form the animation I guess.

motorsep

Quote from: BielBdeLuna on July 18, 2015, 08:45:04 PM
imagine a monster that every time he gets angry he burns up in flames, this could last more than one animation state. and the effect would be triggered by the ai script, my idea is to give flexibility to the developer, not a fixed effect, the lava man was just an example.

imagine a monster that changes the material shader when he sees the player, and when he has lost it, the material goes back to the first stage, this is all triggered via ai scripting and doesn't correspond to any animation in particular.

The thing is that everything is tied up to animation frames. So when monster sees player, it plays an animation (it doesn't have to, but 99% out of 100 it does). Therefore why not to use existing system?

What conventional way of doing things is incapable of is changing shaderParm on the fly, back and forth and also speeding it up or slowing it down. For example, monster sees you, kicks off shaderParm and until table runs out (or however it's set up), monster will be changing color.

BielBdeLuna

not everything is tied to animation, take a look at combat, there a lot of things happening there that don't get translated to an animation.

you're free to do it the way you please, if you get the flexibility you need go on, nobody is holding you.

motorsep

Quote from: BielBdeLuna on July 19, 2015, 09:40:10 AM
you're free to do it the way you please, if you get the flexibility you need go on, nobody is holding you.

On the contrary, what I am saying is that the current conventional way doesn't allow for rewinding texture transition, or accelerating it, or decelerating it. You set it and it runs it's course per table. Good enough for almost every scenario, but not flexible when it comes to some effects that were not in Doom 3.

Does you script allow for rewinding, accelerating, or decelerating of the effect?

BielBdeLuna

not at the moment, but I can make it to do it. let me think how to do it