forked from Mirrors/openclonk
Implement setting shader uniforms from script (#1206)
Uniform variables are read from the "Uniforms" proplist set on Scenario or on individual objects. Proplist keys are uniform names. Values can either be an int or an array of one to four ints in C4Script. In GLSL, the uniforms then need a matching type (int/ivec2/ivec3/ivec4). There is no error reporting; uniforms are only set if both name and type match. The implementation walks the "Uniforms" proplists on each Draw call. We may need to cache the uniform maps if this turns out to be too slow.directional-lights
parent
bfc830a103
commit
6847e50e79
|
@ -386,6 +386,8 @@ void C4Viewport::Execute()
|
|||
C4Surface *target = pWindow ? pWindow->pSurface : FullScreen.pSurface;
|
||||
cgo.Set(target,DrawX,DrawY,float(ViewWdt)/Zoom,float(ViewHgt)/Zoom,GetViewX(),GetViewY(),Zoom);
|
||||
pDraw->PrepareRendering(target);
|
||||
// Load script uniforms from Global.Uniforms
|
||||
auto uniform_pop = pDraw->scriptUniform.Push(::GameScript.ScenPropList.getPropList());
|
||||
// Do not spoil game contents on owner-less viewport
|
||||
bool draw_game = true;
|
||||
if (Player == NO_OWNER)
|
||||
|
|
|
@ -181,6 +181,7 @@ void C4Draw::Default()
|
|||
ZoomX = 0; ZoomY = 0; Zoom = 1;
|
||||
MeshTransform = nullptr;
|
||||
fUsePerspective = false;
|
||||
scriptUniform.Clear();
|
||||
}
|
||||
|
||||
void C4Draw::Clear()
|
||||
|
|
|
@ -97,6 +97,7 @@ public:
|
|||
float gamma[C4MaxGammaRamps][3]; // input gammas
|
||||
float gammaOut[3]; // combined gamma
|
||||
int MaxTexSize;
|
||||
C4ScriptUniform scriptUniform; // uniforms added to all draw calls
|
||||
protected:
|
||||
float fClipX1,fClipY1,fClipX2,fClipY2; // clipper in unzoomed coordinates
|
||||
float fStClipX1,fStClipY1,fStClipX2,fStClipY2; // stored clipper in unzoomed coordinates
|
||||
|
|
|
@ -497,6 +497,8 @@ void CStdGL::SetupMultiBlt(C4ShaderCall& call, const C4BltTransform* pTransform,
|
|||
|
||||
if (pFoW != nullptr && normalTex != 0)
|
||||
call.SetUniformMatrix3x3Transpose(C4SSU_NormalMatrix, StdMeshMatrix::Inverse(StdProjectionMatrix::Upper3x4(modelview)));
|
||||
|
||||
scriptUniform.Apply(call);
|
||||
}
|
||||
|
||||
void CStdGL::PerformMultiPix(C4Surface* sfcTarget, const C4BltVertex* vertices, unsigned int n_vertices, C4ShaderCall* shader_call)
|
||||
|
|
|
@ -956,6 +956,8 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
pDraw->scriptUniform.Apply(call);
|
||||
|
||||
size_t vertex_count = 3 * instance.GetNumFaces();
|
||||
assert (vertex_buffer_offset % sizeof(StdMeshVertex) == 0);
|
||||
size_t base_vertex = vertex_buffer_offset / sizeof(StdMeshVertex);
|
||||
|
|
|
@ -755,3 +755,99 @@ bool C4ScriptShader::Remove(int id)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<C4ScriptUniform::Popper> C4ScriptUniform::Push(C4PropList* proplist)
|
||||
{
|
||||
#ifdef USE_CONSOLE
|
||||
return std::unique_ptr<C4ScriptUniform::Popper>();
|
||||
#else
|
||||
C4Value ulist;
|
||||
if (!proplist->GetProperty(P_Uniforms, &ulist) || ulist.GetType() != C4V_PropList)
|
||||
return std::unique_ptr<C4ScriptUniform::Popper>();
|
||||
|
||||
uniformStack.emplace();
|
||||
auto& uniforms = uniformStack.top();
|
||||
Uniform u;
|
||||
for (const C4Property* prop : *ulist.getPropList())
|
||||
{
|
||||
if (!prop->Key) continue;
|
||||
switch (prop->Value.GetType())
|
||||
{
|
||||
case C4V_Int:
|
||||
u.type = GL_INT;
|
||||
u.intVec[0] = prop->Value._getInt();
|
||||
break;
|
||||
case C4V_Array:
|
||||
{
|
||||
auto array = prop->Value._getArray();
|
||||
switch (array->GetSize())
|
||||
{
|
||||
case 1: u.type = GL_INT; break;
|
||||
case 2: u.type = GL_INT_VEC2; break;
|
||||
case 3: u.type = GL_INT_VEC3; break;
|
||||
case 4: u.type = GL_INT_VEC4; break;
|
||||
default: continue;
|
||||
}
|
||||
for (int32_t i = 0; i < array->GetSize(); i++)
|
||||
{
|
||||
auto& item = array->_GetItem(i);
|
||||
switch (item.GetType())
|
||||
{
|
||||
case C4V_Int:
|
||||
u.intVec[i] = item._getInt();
|
||||
break;
|
||||
default:
|
||||
goto skip;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
// Uniform is now filled properly. Note that array contents are undefined for higher slots
|
||||
// when "type" only requires a smaller array.
|
||||
uniforms.insert({prop->Key->GetCStr(), u});
|
||||
skip:;
|
||||
}
|
||||
// Debug
|
||||
/*
|
||||
for (auto& p : uniforms)
|
||||
{
|
||||
LogF("Uniform %s (type %d) = %d %d %d %d", p.first.c_str(), p.second.type, p.second.intVec[0], p.second.intVec[1], p.second.intVec[2], p.second.intVec[3]);
|
||||
}
|
||||
*/
|
||||
return std::make_unique<C4ScriptUniform::Popper>(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void C4ScriptUniform::Clear()
|
||||
{
|
||||
uniformStack = {};
|
||||
uniformStack.emplace();
|
||||
}
|
||||
|
||||
void C4ScriptUniform::Apply(C4ShaderCall& call)
|
||||
{
|
||||
#ifndef USE_CONSOLE
|
||||
for (auto& p : uniformStack.top())
|
||||
{
|
||||
// The existing SetUniform* methods only work for pre-defined indexed uniforms. The script
|
||||
// uniforms are unknown at shader compile time, so we have to use OpenGL functions directly
|
||||
// here.
|
||||
GLint loc = glGetUniformLocation(call.pShader->hProg, p.first.c_str());
|
||||
// Is this uniform defined in the shader?
|
||||
if (loc == -1) continue;
|
||||
auto& intVec = p.second.intVec;
|
||||
switch (p.second.type)
|
||||
{
|
||||
case GL_INT: glUniform1i(loc, intVec[0]); break;
|
||||
case GL_INT_VEC2: glUniform2i(loc, intVec[0], intVec[1]); break;
|
||||
case GL_INT_VEC3: glUniform3i(loc, intVec[0], intVec[1], intVec[2]); break;
|
||||
case GL_INT_VEC4: glUniform4i(loc, intVec[0], intVec[1], intVec[2], intVec[3]); break;
|
||||
default:
|
||||
assert(false && "unsupported uniform type");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include <GL/glew.h>
|
||||
#endif
|
||||
|
||||
#include <stack>
|
||||
|
||||
// Shader version
|
||||
const int C4Shader_Version = 150; // GLSL 1.50 / OpenGL 3.2
|
||||
|
||||
|
@ -60,6 +62,7 @@ const int C4Shader_Vertex_PositionPos = 80;
|
|||
class C4Shader
|
||||
{
|
||||
friend class C4ShaderCall;
|
||||
friend class C4ScriptUniform;
|
||||
public:
|
||||
C4Shader();
|
||||
~C4Shader();
|
||||
|
@ -182,6 +185,7 @@ public:
|
|||
#ifndef USE_CONSOLE
|
||||
class C4ShaderCall
|
||||
{
|
||||
friend class C4ScriptUniform;
|
||||
public:
|
||||
C4ShaderCall(const C4Shader *pShader)
|
||||
: fStarted(false), pShader(pShader), iUnits(0)
|
||||
|
@ -344,4 +348,42 @@ public: // Interface for script
|
|||
|
||||
extern C4ScriptShader ScriptShader;
|
||||
|
||||
class C4ScriptUniform
|
||||
{
|
||||
friend class C4Shader;
|
||||
|
||||
struct Uniform
|
||||
{
|
||||
#ifndef USE_CONSOLE
|
||||
GLenum type;
|
||||
union
|
||||
{
|
||||
int intVec[4];
|
||||
// TODO: Support for other uniform types.
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
std::stack<std::map<std::string, Uniform>> uniformStack;
|
||||
|
||||
public:
|
||||
class Popper
|
||||
{
|
||||
C4ScriptUniform* p;
|
||||
size_t size;
|
||||
public:
|
||||
Popper(C4ScriptUniform* p) : p(p), size(p->uniformStack.size()) { }
|
||||
~Popper() { assert(size == p->uniformStack.size()); p->uniformStack.pop(); }
|
||||
};
|
||||
|
||||
// Remove all uniforms.
|
||||
void Clear();
|
||||
// Walk the proplist `proplist.Uniforms` and add uniforms. Automatically pops when the return value is destroyed.
|
||||
std::unique_ptr<Popper> Push(C4PropList* proplist);
|
||||
// Apply uniforms to a shader call.
|
||||
void Apply(C4ShaderCall& call);
|
||||
|
||||
C4ScriptUniform() { Clear(); }
|
||||
};
|
||||
|
||||
#endif // INC_C4Shader
|
||||
|
|
|
@ -981,6 +981,8 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion *Ligh
|
|||
ShaderCall.SetUniform1f(C4LRU_AmbientBrightness, Light->getFoW()->Ambient.GetBrightness());
|
||||
}
|
||||
|
||||
pDraw->scriptUniform.Apply(ShaderCall);
|
||||
|
||||
// Start binding textures
|
||||
if(shader->HaveUniform(C4LRU_LandscapeTex))
|
||||
{
|
||||
|
|
|
@ -1808,6 +1808,9 @@ void C4Object::Draw(C4TargetFacet &cgo, int32_t iByPlayer, DrawMode eDrawMode, f
|
|||
// visible?
|
||||
if (!IsVisible(iByPlayer, !!eDrawMode)) return;
|
||||
|
||||
// Set up custom uniforms.
|
||||
auto uniform_popper = pDraw->scriptUniform.Push(this);
|
||||
|
||||
// Line
|
||||
if (Def->Line) { DrawLine(cgo, iByPlayer); return; }
|
||||
|
||||
|
|
|
@ -300,6 +300,7 @@ C4StringTable::C4StringTable()
|
|||
P[P_EditorInitialize] = "EditorInitialize";
|
||||
P[P_EditorPlacementLimit] = "EditorPlacementLimit";
|
||||
P[P_Sorted] = "Sorted";
|
||||
P[P_Uniforms] = "Uniforms";
|
||||
P[DFA_WALK] = "WALK";
|
||||
P[DFA_FLIGHT] = "FLIGHT";
|
||||
P[DFA_KNEEL] = "KNEEL";
|
||||
|
|
|
@ -524,6 +524,7 @@ enum C4PropertyName
|
|||
P_EditorInitialize,
|
||||
P_EditorPlacementLimit,
|
||||
P_Sorted,
|
||||
P_Uniforms,
|
||||
// Default Action Procedures
|
||||
DFA_WALK,
|
||||
DFA_FLIGHT,
|
||||
|
|
Loading…
Reference in New Issue