58 int C4Shader::GetSourceFileId(
const char *file)
 const 
   60     auto it = std::find(SourceFiles.begin(), SourceFiles.end(), file);
 
   61     if (it == SourceFiles.end()) 
return -1;
 
   62     return std::distance(SourceFiles.begin(), it);
 
   74     AddSlice(VertexSlices, iPos, szText, 
nullptr, 0, 0);
 
   79     AddSlice(FragmentSlices, iPos, szText, 
nullptr, 0, 0);
 
   84     AddSlices(VertexSlices, szWhat, szText, szSource, iSourceTime);
 
   89     AddSlices(FragmentSlices, szWhat, szText, szSource, iSourceTime);
 
   94     return LoadSlices(FragmentSlices, pGroups, szFile);
 
   99     return LoadSlices(VertexSlices, pGroups, szFile);
 
  104     assert(!ScriptSlicesLoaded && 
"Can't change shader categories after initialization");
 
  105     Categories = categories;
 
  108 void C4Shader::LoadScriptSlices()
 
  111     for (
auto& 
id : ScriptShaders)
 
  115     ScriptSlicesLoaded = 
true;
 
  118 void C4Shader::LoadScriptSlice(
int id)
 
  132 void C4Shader::AddSlice(ShaderSliceList& slices, 
int iPos, 
const char *szText, 
const char *szSource, 
int line, 
int iSourceTime)
 
  135     Slice.Position = iPos;
 
  136     Slice.Text.Copy(szText);
 
  137     Slice.Source = szSource;
 
  138     Slice.SourceTime = iSourceTime;
 
  139     Slice.SourceLine = line;
 
  140     slices.push_back(Slice);
 
  143 void C4Shader::AddSlices(ShaderSliceList& slices, 
const char *szWhat, 
const char *szText, 
const char *szSource, 
int iSourceTime)
 
  145     if (std::find(SourceFiles.cbegin(), SourceFiles.cend(), szSource) == SourceFiles.cend())
 
  146         SourceFiles.emplace_back(szSource);
 
  148     const char *pStart = szText, *pPos = szText;
 
  151     bool fGotContent = 
false; 
 
  153 #define SKIP_WHITESPACE do { while(isspace(*pPos)) { ++pPos; } } while (0) 
  158         if (*pPos == 
'/' && *(pPos + 1) == 
'/') {
 
  160             while (*pPos && *pPos != 
'\n') pPos++;
 
  163         if (*pPos == 
'/' && *(pPos + 1) == 
'*') {
 
  165             while (*pPos && (*pPos != 
'*' || *(pPos + 1) != 
'/'))
 
  169             if (*pPos) pPos += 2;
 
  180             if (iPosition != -1 && !iDepth) {
 
  186                     AddSlice(slices, iPosition, Str.
getData(), szSource, 
SGetLine(szText, pStart), iSourceTime);
 
  203             if (
SEqual2(pPos+1, 
"slice") && !isalnum(*(pPos+6))) {
 
  204                 const char *pSliceEnd = pPos; pPos += 6;
 
  206                 if(*pPos != 
'(') { pPos++; 
continue; }
 
  210                 iPosition = ParsePosition(szWhat, &pPos);
 
  211                 if (iPosition != -1) {
 
  214                     if(*pPos != 
')') { pPos++; 
continue; }
 
  225                             AddSlice(slices, -1, Str.
getData(), szSource, 
SGetLine(szText, pSliceEnd), iSourceTime);
 
  232                         ShaderLogF(
"  gl: Missing opening brace in %s!", szWhat);
 
  241         if (!isspace(*pPos)) fGotContent = 
true;
 
  249         AddSlice(slices, iPosition, Str.
getData(), szSource, 
SGetLine(szText, pStart), iSourceTime);
 
  251 #undef SKIP_WHITESPACE 
  254 int C4Shader::ParsePosition(
const char *szWhat, 
const char **ppPos)
 
  256     const char *pPos = *ppPos;
 
  257     while (isspace(*pPos)) pPos++;
 
  260     const char *pStart = pPos;
 
  261     while (isalnum(*pPos)) pPos++;
 
  268             iPosition = PosName.Position;
 
  272     if (iPosition == -1) {
 
  278     while (isspace(*pPos)) pPos++;
 
  281         if (!sscanf(pPos+1, 
"%d%n", &iMod, &iModLen)) {
 
  282             ShaderLogF(
"  gl: Invalid slice modifier in %s", szWhat);
 
  290         if (!sscanf(pPos+1, 
"%d%n", &iMod, &iModLen)) {
 
  291             ShaderLogF(
"  gl: Invalid slice modifier in %s", szWhat);
 
  303 bool C4Shader::LoadSlices(ShaderSliceList& slices, 
C4GroupSet *pGroups, 
const char *szFile)
 
  307     if(!pGroup) 
return false;
 
  326     VertexSlices.clear();
 
  327     FragmentSlices.clear();
 
  330     ScriptSlicesLoaded = 
false;
 
  332     ScriptShaders.clear();
 
  340     glDeleteProgram(hProg);
 
  348 bool C4Shader::Init(
const char *szWhat, 
const char **szUniforms, 
const char **szAttributes)
 
  353     if (!ScriptSlicesLoaded)
 
  355         Categories.emplace_back(szWhat);
 
  359     StdStrBuf VertexShader = Build(VertexSlices, 
true),
 
  360         FragmentShader = Build(FragmentSlices, 
true);
 
  365         ShaderLogF(
"******** Vertex shader for %s:", szWhat);
 
  367         ShaderLogF(
"******** Fragment shader for %s:", szWhat);
 
  373     const GLuint hVert = Create(GL_VERTEX_SHADER,
 
  376     const GLuint hFrag = Create(GL_FRAGMENT_SHADER,
 
  382         if (hFrag) glDeleteShader(hFrag);
 
  383         if (hVert) glDeleteShader(hVert);
 
  388     const GLuint hNewProg = glCreateProgram();
 
  390     glAttachShader(hNewProg, hVert);
 
  391     glAttachShader(hNewProg, hFrag);
 
  392     glLinkProgram(hNewProg);
 
  394     glDeleteShader(hFrag);
 
  395     glDeleteShader(hVert);
 
  398     DumpInfoLog(
FormatString(
"%s shader program", szWhat).getData(), hNewProg, 
true);
 
  400     glGetProgramiv(hNewProg, GL_LINK_STATUS, &status);
 
  401     if(status != GL_TRUE) {
 
  402         glDeleteProgram(hNewProg);
 
  403         ShaderLogF(
"  gl: Failed to link %s shader!", szWhat);
 
  406     ShaderLogF(
"  gl: %s shader linked successfully", szWhat);
 
  409     if (hProg != 0) glDeleteProgram(hProg);
 
  413     int iUniformCount = 0;
 
  414     if (szUniforms != 
nullptr)
 
  415         while (szUniforms[iUniformCount])
 
  417     Uniforms.resize(iUniformCount);
 
  419     int iAttributeCount = 0;
 
  420     if (szAttributes != 
nullptr)
 
  421         while (szAttributes[iAttributeCount])
 
  423     Attributes.resize(iAttributeCount);
 
  427     for (
int i = 0; i < iUniformCount; i++) {
 
  428         Uniforms[i].address = glGetUniformLocation(hProg, szUniforms[i]);
 
  429         Uniforms[i].name = szUniforms[i];
 
  430         ShaderLogF(
"Uniform %s = %d", szUniforms[i], Uniforms[i].address);
 
  433     for (
int i = 0; i < iAttributeCount; i++) {
 
  434         Attributes[i].address = glGetAttribLocation(hProg, szAttributes[i]);
 
  435         Attributes[i].name = szAttributes[i];
 
  436         ShaderLogF(
"Attribute %s = %d", szAttributes[i], Attributes[i].address);
 
  451     std::set<int> toAdd, toRemove;
 
  452     std::set_difference(ScriptShaders.begin(), ScriptShaders.end(), next.begin(), next.end(), std::inserter(toRemove, toRemove.end()));
 
  453     std::set_difference(next.begin(), next.end(), ScriptShaders.begin(), ScriptShaders.end(), std::inserter(toAdd, toAdd.end()));
 
  454     ScriptShaders = next;
 
  456     auto removeSlices = [&](ShaderSliceList::iterator& pSlice)
 
  461         ShaderSliceList::iterator pNext;
 
  462         for (; pSlice != FragmentSlices.end(); pSlice = pNext)
 
  464             pNext = pSlice; pNext++;
 
  466                 FragmentSlices.erase(pSlice);
 
  471     std::vector<StdCopyStrBuf> sourcesToUpdate;
 
  472     for (ShaderSliceList::iterator pSlice = FragmentSlices.begin(); pSlice != FragmentSlices.end(); pSlice++)
 
  473         if (pSlice->Source.getLength())
 
  475             if (pSlice->Source.BeginsWith(
"[script "))
 
  479                 sscanf(pSlice->Source.getData(), 
"[script %d", &sid);
 
  480                 if (toRemove.find(sid) != toRemove.end())
 
  481                     removeSlices(pSlice);
 
  484             else if (
FileExists(pSlice->Source.getData()) &&
 
  485                      FileTime(pSlice->Source.getData()) > pSlice->SourceTime)
 
  487                 sourcesToUpdate.push_back(pSlice->Source);
 
  488                 removeSlices(pSlice);
 
  493     if (toAdd.size() == 0 && toRemove.size() == 0 && sourcesToUpdate.size() == 0)
 
  497     for (
auto& Source : sourcesToUpdate)
 
  502         if(!Group.
Open(szParentPath) ||
 
  523     std::vector<const char*> UniformNames(Uniforms.size() + 1);
 
  524     for (std::size_t i = 0; i < Uniforms.size(); ++i)
 
  525         UniformNames[i] = Uniforms[i].name;
 
  526     UniformNames[Uniforms.size()] = 
nullptr;
 
  528     std::vector<const char*> AttributeNames(Attributes.size() + 1);
 
  529     for (std::size_t i = 0; i < Attributes.size(); ++i)
 
  530         AttributeNames[i] = Attributes[i].name;
 
  531     AttributeNames[Attributes.size()] = 
nullptr;
 
  550 StdStrBuf C4Shader::Build(
const ShaderSliceList &Slices, 
bool fDebug)
 
  556     GLint iMaxFrags = 0, iMaxVerts = 0;
 
  557     glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &iMaxFrags);
 
  558     glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &iMaxVerts);
 
  560     int iMaxFrags = INT_MAX, iMaxVerts = INT_MAX;
 
  562     Buf.
Format(
"#version %d\n" 
  563                "#define MAX_FRAGMENT_UNIFORM_COMPONENTS %d\n" 
  564                "#define MAX_VERTEX_UNIFORM_COMPONENTS %d\n",
 
  568     int iPos = -1, iNextPos = -1;
 
  573         if (fDebug && iPos > 0)
 
  575         for (
const auto & Slice : Slices)
 
  577             if (Slice.Position < iPos) 
continue;
 
  578             if (Slice.Position > iPos)
 
  580                 iNextPos = std::min(iNextPos, Slice.Position);
 
  586                 if (Slice.Source.getLength())
 
  589                     Buf.
AppendFormat(
"\t// Slice from %s:\n#line %d %d\n", Slice.Source.getData(), Slice.SourceLine - (
C4Shader_Version < 330), GetSourceFileId(Slice.Source.getData()) + 1);
 
  592                     Buf.
Append(
"\t// Built-in slice:\n#line 1 0\n");
 
  600             Buf.
Append(
"void main() {\n");
 
  608     Buf.
Append(
"// File number to name mapping:\n//\t  0: <built-in shader code>\n");
 
  609     for (
int i = 0; i < SourceFiles.size(); ++i)
 
  610         Buf.
AppendFormat(
"//\t%3d: %s\n", i + 1, SourceFiles[i].c_str());
 
  615 GLuint C4Shader::Create(GLenum iShaderType, 
const char *szWhat, 
const char *szShader)
 
  618     GLuint hShader = glCreateShader(iShaderType);
 
  622     glShaderSource(hShader, 1, &szShader, 
nullptr);
 
  623     glCompileShader(hShader);
 
  626     DumpInfoLog(szWhat, hShader, 
false);
 
  630     glGetShaderiv(hShader, GL_COMPILE_STATUS, &status);
 
  631     if (status == GL_TRUE)
 
  635     glDeleteShader(hShader);
 
  639 void C4Shader::DumpInfoLog(
const char *szWhat, GLuint hShader, 
bool forProgram)
 
  644         glGetProgramiv(hShader, GL_INFO_LOG_LENGTH, &iLength);
 
  646         glGetShaderiv(hShader, GL_INFO_LOG_LENGTH, &iLength);
 
  647     if(iLength <= 1) 
return;
 
  650     std::vector<char> buf(iLength + 1);
 
  651     int iActualLength = 0;
 
  653         glGetProgramInfoLog(hShader, iLength, &iActualLength, &buf[0]);
 
  655         glGetShaderInfoLog(hShader, iLength, &iActualLength, &buf[0]);
 
  656     if(iActualLength > iLength || iActualLength <= 0) 
return;
 
  659     buf[iActualLength] = 
'\0';
 
  685     GLint hTex = GL_TEXTURE0 + iUnits;
 
  686     glActiveTexture(hTex);
 
  694     assert(pShader->hProg != 0); 
 
  698         const_cast<C4Shader *
>(pShader)->Refresh();
 
  701     glUseProgram(pShader->hProg);
 
  723     std::set<int> result;
 
  724     for (
auto& cat : cats)
 
  725         for (
auto& 
id : categories[cat])
 
  736     auto nsource = 
"\n" + source;
 
  737     shaders.emplace(std::make_pair(
id, ShaderInstance{
type, nsource}));
 
  738     categories[shaderName].emplace(
id);
 
  746     if (shaders.erase(
id))
 
  748         for (
auto& kv : categories)
 
  749             if (kv.second.erase(
id))
 
  760     return std::unique_ptr<C4ScriptUniform::Popper>();
 
  764         return std::unique_ptr<C4ScriptUniform::Popper>();
 
  766     uniformStack.emplace();
 
  767     auto& uniforms = uniformStack.top();
 
  771         if (!prop->Key) 
continue;
 
  772         switch (prop->Value.GetType())
 
  776             u.intVec[0] = prop->Value._getInt();
 
  780             auto array = prop->Value._getArray();
 
  781             switch (array->GetSize())
 
  783             case 1: u.type = GL_INT; 
break;
 
  784             case 2: u.type = GL_INT_VEC2; 
break;
 
  785             case 3: u.type = GL_INT_VEC3; 
break;
 
  786             case 4: u.type = GL_INT_VEC4; 
break;
 
  789             for (int32_t i = 0; i < array->GetSize(); i++)
 
  791                 auto& item = array->_GetItem(i);
 
  792                 switch (item.GetType())
 
  795                     u.intVec[i] = item._getInt();
 
  808         uniforms.insert({prop->Key->GetCStr(), u});
 
  818     return std::make_unique<C4ScriptUniform::Popper>(
this);
 
  824     uniformStack = std::stack<UniformMap>();
 
  825     uniformStack.emplace();
 
  831     for (
auto& p : uniformStack.top())
 
  836         GLint loc = glGetUniformLocation(call.pShader->hProg, p.first.c_str());
 
  838         if (loc == -1) 
continue;
 
  839         auto& intVec = p.second.intVec;
 
  840         switch (p.second.type)
 
  842         case GL_INT:      glUniform1iv(loc, 1, intVec); 
break;
 
  843         case GL_INT_VEC2: glUniform2iv(loc, 1, intVec); 
break;
 
  844         case GL_INT_VEC3: glUniform3iv(loc, 1, intVec); 
break;
 
  845         case GL_INT_VEC4: glUniform4iv(loc, 1, intVec); 
break;
 
  847             assert(
false && 
"unsupported uniform type");
 
C4Application Application
bool ShaderLog(const char *szMessage)
bool ShaderLogF(const char *strMessage ...)
const uint32_t C4Shader_RefreshInterval
C4ShaderPosName C4SH_PosNames[]
C4ScriptShader ScriptShader
const int C4Shader_PositionMaterial
const int C4Shader_Vertex_NormalPos
const int C4Shader_PositionTexture
const int C4Shader_Vertex_ColorPos
const int C4Shader_Vertex_TexCoordPos
const int C4Shader_PositionFinish
const int C4Shader_LastPosition
const int C4ShaderCall_MaxUnits
const int C4Shader_PositionNormal
const int C4Shader_Vertex_PositionPos
const int C4Shader_PositionColor
const int C4Shader_PositionCoordinate
const int C4Shader_PositionInit
const int C4Shader_Version
const int C4Shader_PositionLight
bool SEqual2(const char *szStr1, const char *szStr2)
int SGetLine(const char *szText, const char *cpPosition)
bool SEqual(const char *szStr1, const char *szStr2)
StdStrBuf FormatString(const char *szFmt,...)
bool GetParentPath(const char *szFilename, char *szBuffer)
char * GetFilename(char *szPath)
int FileTime(const char *fname)
bool FileExists(const char *szFileName)
const char * AtRelativePath(const char *filename)
C4ConfigGraphics Graphics
StdStrBuf GetFullName() const
bool LoadEntryString(const char *entry_name, StdStrBuf *buffer)
bool Open(const char *group_name, bool do_create=false)
C4Group * FindEntry(const char *szWildcard, int32_t *pPriority=nullptr, int32_t *pID=nullptr)
bool GetProperty(C4PropertyName k, C4Value *pResult) const
int Add(const std::string &shaderName, ShaderType type, const std::string &source)
std::set< int > GetShaderIDs(const std::vector< std::string > &cats)
GLint AllocTexUnit(int iUniform)
void SetUniform1i(int iUniform, int iX) const
bool LoadVertexSlices(C4GroupSet *pGroupSet, const char *szFile)
bool LoadFragmentSlices(C4GroupSet *pGroupSet, const char *szFile)
void AddFragmentSlice(int iPos, const char *szText)
void AddDefine(const char *name)
bool HaveUniform(int iUniform) const
void AddVertexSlice(int iPos, const char *szText)
void AddVertexSlices(const char *szWhat, const char *szText, const char *szSource="", int iFileTime=0)
void SetScriptCategories(const std::vector< std::string > &categories)
bool Init(const char *szWhat, const char **szUniforms, const char **szAttributes)
void AddFragmentSlices(const char *szWhat, const char *szText, const char *szSource="", int iFileTime=0)
static C4TimeMilliseconds Now()
C4PropList * getPropList() const
void ObjectLabel(uint32_t identifier, uint32_t name, int32_t length, const char *label)
void AppendFormat(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
const char * getData() const
void AppendChar(char cChar)
void Append(const char *pnData, size_t iChars)
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O