Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Notice the parameter is defined as float, not vec4. You're passing an array of floats into the engine which will be converted to a vec4 object for use in the shader. The values correspond to XYZW or RGBA coordinates depending on if the vector will be used as a direction or a color.

Using Per-Player Global Shader Uniforms

As of preview v.35 [869dfc51] there are 48 new shader uniforms which can be set via NwScript in a per-player manner.

There are sixteen instances of three types of uniform.

Uniform NameType
scriptableInt<n>Signed 32-bit Integer
scriptableFloat<n>Signed 32-bit Single Precision Float
scriptableVec<n>Four-part Signed 32-bit Float Vector

In each variable name <n> is equal to a number 1 to 16 and corresponds to NwScript constants SHADER_UNIFORM_*

To set any of the uniforms in NwScript, use the corresponding function:

  • void SetShaderUniformFloat(object oPlayer, int nShader, float fValue)
  • void SetShaderUniformInt(object oPlayer, int nShader, int nValue)
  • void SetShaderUniformVec(object oPlayer, int nShader, float fX, float fY, float fZ, float fW)

In each function oPlayer is the player who will set their global uniform value.

The value nShader accepts the constants SHADER_UNIFORM_* with indices 1 to 16, which correspond to the values 0 to 15;

The value of fValue in each function must be the correct type, integer or float respectively.

To pass a vector value, fill out ALL parts, even if not needed. For example a vec2 value will only use the fX ad fY, but you will supply 0.0 for fZ and fW as they were not given default values of 0.0.

To capture the data in your custom shader, you will either need to specifically define the names you need, for example "uniform float scriptableFloat1" if you are using just that one global uniform.

Or, you can easily capture all available scriptable uniforms by including the new include file, inc_scriptable. Add this line to the top of your shader under other includes:

Code Block
#include "inc_scriptable"

Note that players reconnecting after already having these uniforms set will need to have them reapplied.

What to do if you run out of global floats

Remember that each vec4 has 4 more floats, so you can pass additional information beyond your float quota via those vectors.

Simply pass each additional float as part of the vec4 structure, and then unpack them in the shader script.

Getting more work out of global ints

An int data structure can hold 32 bits of information. Each bit is the equivalent of a power of 2.

For example bit 0 is 2^0, accepting 1 or 0.

You can therefore store a bitflag system in any int. Just keep in mind that these are signed integers, bit range will be 0 to 30, and the last flag will hold your negative sign.

To pack bitflags from NwScript, set your global integer to a value equal to:

Code Block
int nValue = bitFlag0;
nValue += bitFlag1 * 2;
nValue += bitFlag2 * 2^2;
nValue += bitFlag3 * 2^3;
nValue += bitFlag4 * 2^4;
...
nValue += bitFlag30 * 2^30;

To unpack bitflags in the shader, simply enumerate flags as powers of 2:

Code Block
#define MY_BITFLAG0 0x00000001
#define MY_BITFLAG1 0x00000002
#define MY_BITFLAG2 0x00000004
...
#define MY_BITFLAG30 0x40000000

Each number slot will go 1, 2, 4, 8, 0. At zero, the next slot in front will become 1. Repeat as needed until you get to 40000000 for bit 31.

Because boolean operations are not implemented yet in GLSL, we must now add some special functions to get back our info. You could add these as an include file called "inc_boolean" and then add it to your custom shader using #include "inc_boolean".

Code Block
//get nA mod nB
int modi(int nA, int nB) {
    return nA - nB * (nA / nB);
}

//check nA or nB
int or(int nA, int nB) {
    int result = 0;
    int n = 1;

    for(int i = 0; i < 32; i++) {
        if ((modi(nA, 2) == 1) || (modi(nB, 2) == 1)) {
            result += n;
        }
        nA = nA / 2;
        nB = nB / 2;
        n = n * 2;
        if(!(nA > 0 || nB > 0)) {
            break;
        }
    }
    return result;
}

//check nA and nB
int and(int nA, int nB) {
    int result = 0;
    int n = 1;

    for(int i = 0; i < 32; i++) {
        if ((modi(nA, 2) == 1) && (modi(nB, 2) == 1)) {
            result += n;
        }

        nA = nA / 2;
        nB = nB / 2;
        n = n * 2;

        if(!(nA > 0 && nB > 0)) {
            break;
        }
    }
    return result;
}

//check not nA
int not(int nA) {
    int result = 0;
    int n = 1;
    
    for(int i = 0; i < 32; i++) {
        if (modi(nA, 2) == 0) {
            result += n;    
        }
        nA = nA / 2;
        n = n * 2;
    }
    return result;
}

//check if just one flag is set
bool GetIsFlagSet(int nA, int nB){
	return and(nA,nB)==nB;

}

And then you can ask your single global int questions using:

Code Block
//check if flag 2 is set
if ( GetIsFlagSet(scriptableInt1, MY_BITFLAG2) ){
	//do something here
}

//check if both flag 1 and 2 are set
if ( GetIsFlagSet(scriptableInt1, (MY_BITFLAG1 + MY_BITFLAG2)) ){
	//do something here
}

//check if either flag 1 or flag 2 is set
//check if both flag 1 and 2 are set
if ( GetIsFlagSet(scriptableInt1, MY_BITFLAG1) || GetIsFlagSet(scriptableInt1, MY_BITFLAG2)){
	//do something here
}

Voilà