mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2025-07-15 12:24:43 +00:00
temp commit - work on shaders
This commit is contained in:
parent
c698b4128a
commit
3485199bec
7 changed files with 75 additions and 192 deletions
|
@ -5742,38 +5742,6 @@ void Avatar::onUpdate(float dt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// setup shader
|
|
||||||
if (core->afterEffectManager)
|
|
||||||
{
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (!_isUnderWater)
|
|
||||||
{
|
|
||||||
core->afterEffectManager->setActiveShader(AS_WASHOUT);
|
|
||||||
//core->afterEffectManager->setActiveShader(AS_NONE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
*/
|
|
||||||
if (dsq->user.video.shader != AS_NONE)
|
|
||||||
{
|
|
||||||
core->afterEffectManager->setActiveShader((ActiveShader)dsq->user.video.shader);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (damageTimer.isActive() && dsq->isShakingCamera())
|
|
||||||
{
|
|
||||||
if (dsq->user.video.blur)
|
|
||||||
core->afterEffectManager->setActiveShader(AS_BLUR);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
core->afterEffectManager->setActiveShader(AS_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity::onUpdate(dt);
|
Entity::onUpdate(dt);
|
||||||
|
|
||||||
if (isEntityDead() && skeletalSprite.getCurrentAnimation()->name != "dead")
|
if (isEntityDead() && skeletalSprite.getCurrentAnimation()->name != "dead")
|
||||||
|
|
|
@ -10719,11 +10719,6 @@ void Game::removeState()
|
||||||
|
|
||||||
elementUpdateList.clear();
|
elementUpdateList.clear();
|
||||||
|
|
||||||
if (core->afterEffectManager)
|
|
||||||
{
|
|
||||||
//core->afterEffectManager->blurShader.setMode(0);
|
|
||||||
core->afterEffectManager->setActiveShader(AS_NONE);
|
|
||||||
}
|
|
||||||
dsq->setCursor(CURSOR_NORMAL);
|
dsq->setCursor(CURSOR_NORMAL);
|
||||||
dsq->darkLayer.toggle(0);
|
dsq->darkLayer.toggle(0);
|
||||||
dsq->shakeCamera(0,0);
|
dsq->shakeCamera(0,0);
|
||||||
|
|
|
@ -3995,6 +3995,8 @@ luaFunc(entity_damage)
|
||||||
d.attacker = lua_isuserdata(L, 2) ? entity(L, 2) : NULL;
|
d.attacker = lua_isuserdata(L, 2) ? entity(L, 2) : NULL;
|
||||||
d.damage = lua_tonumber(L, 3);
|
d.damage = lua_tonumber(L, 3);
|
||||||
d.damageType = (DamageType)lua_tointeger(L, 4);
|
d.damageType = (DamageType)lua_tointeger(L, 4);
|
||||||
|
d.effectTime = lua_tonumber(L, 5);
|
||||||
|
d.useTimer = !getBool(L, 6);
|
||||||
didDamage = e->damage(d);
|
didDamage = e->damage(d);
|
||||||
}
|
}
|
||||||
luaReturnBool(didDamage);
|
luaReturnBool(didDamage);
|
||||||
|
@ -7779,7 +7781,9 @@ luaFunc(createShader)
|
||||||
|
|
||||||
luaFunc(shader_setAsAfterEffect)
|
luaFunc(shader_setAsAfterEffect)
|
||||||
{
|
{
|
||||||
core->afterEffectManager->scriptShader = lua_isuserdata(L, 1) ? getShader(L, 1) : NULL;
|
int idx = lua_tointeger(L, 2);
|
||||||
|
if(idx < core->afterEffectManager->scriptShader.size())
|
||||||
|
core->afterEffectManager->scriptShader[idx] = lua_isuserdata(L, 1) ? getShader(L, 1) : NULL;
|
||||||
luaReturnNil();
|
luaReturnNil();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7805,8 +7809,10 @@ luaFunc(shader_delete)
|
||||||
{
|
{
|
||||||
Shader *sh = getShader(L);
|
Shader *sh = getShader(L);
|
||||||
delete sh;
|
delete sh;
|
||||||
if(core->afterEffectManager->scriptShader == sh)
|
size_t sz = core->afterEffectManager->scriptShader.size();
|
||||||
core->afterEffectManager->scriptShader = NULL;
|
for(size_t i = 0; i < sz; ++i)
|
||||||
|
if(core->afterEffectManager->scriptShader[i] == sh)
|
||||||
|
core->afterEffectManager->scriptShader[i] = NULL;
|
||||||
luaReturnNil();
|
luaReturnNil();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,12 +91,6 @@ void UserSettings::save()
|
||||||
|
|
||||||
TiXmlElement xml_video("Video");
|
TiXmlElement xml_video("Video");
|
||||||
{
|
{
|
||||||
TiXmlElement xml_shader("Shader");
|
|
||||||
{
|
|
||||||
xml_shader.SetAttribute("num", video.shader);
|
|
||||||
}
|
|
||||||
xml_video.InsertEndChild(xml_shader);
|
|
||||||
|
|
||||||
TiXmlElement xml_blur("Blur");
|
TiXmlElement xml_blur("Blur");
|
||||||
{
|
{
|
||||||
xml_blur.SetAttribute("on", video.blur);
|
xml_blur.SetAttribute("on", video.blur);
|
||||||
|
@ -413,8 +407,6 @@ void UserSettings::load(bool doApply, const std::string &overrideFile)
|
||||||
TiXmlElement *xml_video = doc.FirstChildElement("Video");
|
TiXmlElement *xml_video = doc.FirstChildElement("Video");
|
||||||
if (xml_video)
|
if (xml_video)
|
||||||
{
|
{
|
||||||
readInt(xml_video, "Shader", "num", &video.shader);
|
|
||||||
|
|
||||||
readInt(xml_video, "Blur", "on", &video.blur);
|
readInt(xml_video, "Blur", "on", &video.blur);
|
||||||
|
|
||||||
readInt(xml_video, "NoteEffects", "on", &video.noteEffects);
|
readInt(xml_video, "NoteEffects", "on", &video.noteEffects);
|
||||||
|
|
|
@ -100,7 +100,6 @@ public:
|
||||||
numParticles = 2048;
|
numParticles = 2048;
|
||||||
parallaxOn0 = parallaxOn1 = parallaxOn2 = 1;
|
parallaxOn0 = parallaxOn1 = parallaxOn2 = 1;
|
||||||
saveSlotScreens = 1;
|
saveSlotScreens = 1;
|
||||||
shader = 0;
|
|
||||||
blur = 1;
|
blur = 1;
|
||||||
noteEffects = 0;
|
noteEffects = 0;
|
||||||
fpsSmoothing = 30;
|
fpsSmoothing = 30;
|
||||||
|
@ -115,7 +114,6 @@ public:
|
||||||
displaylists = 0;
|
displaylists = 0;
|
||||||
worldMapRevealMethod = 0;
|
worldMapRevealMethod = 0;
|
||||||
}
|
}
|
||||||
int shader;
|
|
||||||
int blur;
|
int blur;
|
||||||
int noteEffects;
|
int noteEffects;
|
||||||
int fpsSmoothing;
|
int fpsSmoothing;
|
||||||
|
|
|
@ -32,10 +32,9 @@ Effect::Effect()
|
||||||
AfterEffectManager::AfterEffectManager(int xDivs, int yDivs)
|
AfterEffectManager::AfterEffectManager(int xDivs, int yDivs)
|
||||||
{
|
{
|
||||||
active = false;
|
active = false;
|
||||||
activeShader = AS_NONE;
|
|
||||||
numEffects = 0;
|
numEffects = 0;
|
||||||
bRenderGridPoints = true;
|
bRenderGridPoints = true;
|
||||||
scriptShader = 0;
|
scriptShader.resize(10, 0);
|
||||||
|
|
||||||
screenWidth = core->getWindowWidth();
|
screenWidth = core->getWindowWidth();
|
||||||
screenHeight = core->getWindowHeight();
|
screenHeight = core->getWindowHeight();
|
||||||
|
@ -45,45 +44,10 @@ AfterEffectManager::AfterEffectManager(int xDivs, int yDivs)
|
||||||
|
|
||||||
drawGrid = 0;
|
drawGrid = 0;
|
||||||
|
|
||||||
#ifdef BBGE_BUILD_OPENGL
|
|
||||||
|
|
||||||
|
|
||||||
this->xDivs = xDivs;
|
this->xDivs = xDivs;
|
||||||
this->yDivs = yDivs;
|
this->yDivs = yDivs;
|
||||||
//cameraPointer = nCameraPointer;
|
|
||||||
|
|
||||||
//Asssuming the resolutions values are > 256 and < 2048
|
reloadDevice();
|
||||||
//Set the texture heights and widths
|
|
||||||
if (core->frameBuffer.isInited())
|
|
||||||
{
|
|
||||||
textureWidth = core->frameBuffer.getWidth();
|
|
||||||
textureHeight = core->frameBuffer.getHeight();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (screenWidth <= 512)
|
|
||||||
textureWidth = 512;
|
|
||||||
else if (screenWidth <= 1024)
|
|
||||||
textureWidth = 1024;
|
|
||||||
else
|
|
||||||
textureWidth = 2048;
|
|
||||||
if (screenHeight <= 512)
|
|
||||||
textureHeight = 512;
|
|
||||||
else if (screenHeight <= 1024)
|
|
||||||
textureHeight = 1024;
|
|
||||||
else
|
|
||||||
textureHeight = 2048;
|
|
||||||
}
|
|
||||||
|
|
||||||
//create our texture
|
|
||||||
glGenTextures(1,&texture);
|
|
||||||
glBindTexture(GL_TEXTURE_2D,texture);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, 3, textureWidth, textureHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
//BuildMip();
|
|
||||||
|
|
||||||
if (xDivs != 0 && yDivs != 0)
|
if (xDivs != 0 && yDivs != 0)
|
||||||
{
|
{
|
||||||
|
@ -99,14 +63,6 @@ AfterEffectManager::AfterEffectManager(int xDivs, int yDivs)
|
||||||
|
|
||||||
void AfterEffectManager::loadShaders()
|
void AfterEffectManager::loadShaders()
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
blurShader.load("data/shaders/stan.vert", "data/shaders/blur.frag");
|
|
||||||
bwShader.load("data/shaders/stan.vert", "data/shaders/bw.frag");
|
|
||||||
washoutShader.load("data/shaders/stan.vert", "data/shaders/washout.frag");
|
|
||||||
//motionBlurShader.load("data/shaders/stan.vert", "data/shaders/hoblur.frag");
|
|
||||||
motionBlurShader.load("data/shaders/stan.vert", "data/shaders/blur.frag");
|
|
||||||
glowShader.load("data/shaders/stan.vert", "data/shaders/glow.frag");
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AfterEffectManager::~AfterEffectManager()
|
AfterEffectManager::~AfterEffectManager()
|
||||||
|
@ -191,29 +147,6 @@ void AfterEffectManager::destroyEffect(int id)
|
||||||
openSpots.push(id);
|
openSpots.push(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AfterEffectManager::capture()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
#ifdef BBGE_BUILD_OPENGL
|
|
||||||
glBindTexture(GL_TEXTURE_2D,texture);
|
|
||||||
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, screenWidth, screenHeight);
|
|
||||||
#endif
|
|
||||||
*/
|
|
||||||
if (core->frameBuffer.isInited())
|
|
||||||
{
|
|
||||||
core->frameBuffer.endCapture();
|
|
||||||
//core->enable2D(core->pixelScale);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#ifdef BBGE_BUILD_OPENGL
|
|
||||||
glBindTexture(GL_TEXTURE_2D,texture);
|
|
||||||
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, screenWidth, screenHeight);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
//glDisable(GL_TEXTURE_2D);
|
|
||||||
|
|
||||||
}
|
|
||||||
void AfterEffectManager::render()
|
void AfterEffectManager::render()
|
||||||
{
|
{
|
||||||
#ifdef BBGE_BUILD_OPENGL
|
#ifdef BBGE_BUILD_OPENGL
|
||||||
|
@ -225,7 +158,7 @@ void AfterEffectManager::render()
|
||||||
glDisable (GL_ALPHA_TEST);
|
glDisable (GL_ALPHA_TEST);
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
|
|
||||||
capture();
|
core->frameBuffer.endCapture();
|
||||||
glTranslatef(core->cameraPos.x, core->cameraPos.y, 0);
|
glTranslatef(core->cameraPos.x, core->cameraPos.y, 0);
|
||||||
glScalef(core->invGlobalScale, core->invGlobalScale,0);
|
glScalef(core->invGlobalScale, core->invGlobalScale,0);
|
||||||
/*
|
/*
|
||||||
|
@ -241,48 +174,33 @@ void AfterEffectManager::render()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void AfterEffectManager::setActiveShader(ActiveShader as)
|
|
||||||
{
|
|
||||||
activeShader = as;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void AfterEffectManager::renderGrid()
|
void AfterEffectManager::renderGrid()
|
||||||
{
|
{
|
||||||
#ifdef BBGE_BUILD_OPENGL
|
#ifdef BBGE_BUILD_OPENGL
|
||||||
//glBindTexture(GL_TEXTURE_2D, texture);
|
|
||||||
|
|
||||||
|
int shadersOn = 0;
|
||||||
|
int firstShader = -1;
|
||||||
|
int lastShader = -1;
|
||||||
|
for (size_t i = 0; i < scriptShader.size(); ++i)
|
||||||
|
{
|
||||||
|
if(scriptShader[i])
|
||||||
|
{
|
||||||
|
++shadersOn;
|
||||||
|
if(firstShader < 0)
|
||||||
|
firstShader = i;
|
||||||
|
lastShader = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (core->frameBuffer.isInited())
|
if (core->frameBuffer.isInited())
|
||||||
core->frameBuffer.bindTexture();
|
core->frameBuffer.bindTexture();
|
||||||
else
|
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//bwShader.bind();
|
|
||||||
Shader *activeShader=0;
|
Shader *activeShader=0;
|
||||||
if (core->frameBuffer.isInited())
|
if (core->frameBuffer.isInited())
|
||||||
{
|
{
|
||||||
switch(this->activeShader)
|
if(scriptShader.size() && scriptShader[0])
|
||||||
{
|
activeShader = scriptShader[0];
|
||||||
case AS_BLUR:
|
|
||||||
activeShader = &blurShader;
|
|
||||||
break;
|
|
||||||
case AS_BW:
|
|
||||||
activeShader = &bwShader;
|
|
||||||
break;
|
|
||||||
case AS_WASHOUT:
|
|
||||||
activeShader = &washoutShader;
|
|
||||||
break;
|
|
||||||
case AS_MOTIONBLUR:
|
|
||||||
activeShader = &motionBlurShader;
|
|
||||||
break;
|
|
||||||
case AS_GLOW:
|
|
||||||
activeShader = &glowShader;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(scriptShader)
|
|
||||||
activeShader = scriptShader;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,6 +277,40 @@ void AfterEffectManager::renderGrid()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (activeShader)
|
||||||
|
activeShader->unbind();
|
||||||
|
|
||||||
|
float width2 = float(vw)/2;
|
||||||
|
float height2 = float(vh)/2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
for(size_t i = 1; i < scriptShader.size(); ++i)
|
||||||
|
{
|
||||||
|
if(scriptShader[i])
|
||||||
|
{
|
||||||
|
if (core->frameBuffer.isInited())
|
||||||
|
core->frameBuffer.startCapture();
|
||||||
|
|
||||||
|
scriptShader[i]->bind();
|
||||||
|
scriptShader[i]->setInt("tex", 0);
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
glTexCoord2d(0.0f, 0.0f);
|
||||||
|
glVertex3f(-width2, height2, 0.0f);
|
||||||
|
glTexCoord2d(percentX, 0.0f);
|
||||||
|
glVertex3f( width2, height2, 0.0f);
|
||||||
|
glTexCoord2d(percentX, percentY);
|
||||||
|
glVertex3f( width2, -height2, 0.0f);
|
||||||
|
glTexCoord2d(0.0f, percentY);
|
||||||
|
glVertex3f(-width2, -height2, 0.0f);
|
||||||
|
glEnd();
|
||||||
|
scriptShader[i]->unbind();
|
||||||
|
|
||||||
|
capture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
// uncomment to render grid points
|
// uncomment to render grid points
|
||||||
/*
|
/*
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
@ -395,9 +347,6 @@ void AfterEffectManager::renderGrid()
|
||||||
RenderObject::lastTextureApplied = 0;
|
RenderObject::lastTextureApplied = 0;
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
if (activeShader)
|
|
||||||
activeShader->unbind();
|
|
||||||
|
|
||||||
//bwShader.unbind();
|
//bwShader.unbind();
|
||||||
//glActiveTextureARB(GL_TEXTURE0_ARB);
|
//glActiveTextureARB(GL_TEXTURE0_ARB);
|
||||||
//glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
//glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||||
|
@ -427,8 +376,7 @@ void AfterEffectManager::renderGridPoints()
|
||||||
|
|
||||||
void AfterEffectManager::unloadDevice()
|
void AfterEffectManager::unloadDevice()
|
||||||
{
|
{
|
||||||
if (texture)
|
backupBuffer.unloadDevice();
|
||||||
glDeleteTextures(1,&texture);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AfterEffectManager::reloadDevice()
|
void AfterEffectManager::reloadDevice()
|
||||||
|
@ -443,22 +391,16 @@ void AfterEffectManager::reloadDevice()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (screenWidth <= 1024)
|
textureWidth = screenWidth;
|
||||||
textureWidth = 1024;
|
sizePowerOf2Texture(textureWidth);
|
||||||
else
|
textureHeight = screenHeight;
|
||||||
textureWidth = 2048;
|
sizePowerOf2Texture(textureHeight);
|
||||||
if (screenHeight <= 1024)
|
|
||||||
textureHeight = 1024;
|
|
||||||
else
|
|
||||||
textureHeight = 2048;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//create our texture
|
if(backupBuffer.isInited())
|
||||||
glGenTextures(1,&texture);
|
backupBuffer.reloadDevice();
|
||||||
glBindTexture(GL_TEXTURE_2D,texture);
|
else
|
||||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
|
backupBuffer.init(-1, -1, true);
|
||||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, 3, textureWidth, textureHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AfterEffectManager::addEffect(Effect *e)
|
void AfterEffectManager::addEffect(Effect *e)
|
||||||
|
|
|
@ -72,16 +72,6 @@ public:
|
||||||
float time;
|
float time;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ActiveShader
|
|
||||||
{
|
|
||||||
AS_NONE = 0,
|
|
||||||
AS_BLUR ,
|
|
||||||
AS_BW ,
|
|
||||||
AS_WASHOUT ,
|
|
||||||
AS_MOTIONBLUR ,
|
|
||||||
AS_GLOW
|
|
||||||
};
|
|
||||||
|
|
||||||
class AfterEffectManager
|
class AfterEffectManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -96,7 +86,6 @@ public:
|
||||||
|
|
||||||
void resetGrid();
|
void resetGrid();
|
||||||
|
|
||||||
void capture();
|
|
||||||
void render();
|
void render();
|
||||||
void renderGrid();
|
void renderGrid();
|
||||||
void renderGridPoints();
|
void renderGridPoints();
|
||||||
|
@ -111,12 +100,6 @@ public:
|
||||||
|
|
||||||
bool active;
|
bool active;
|
||||||
|
|
||||||
void setActiveShader(ActiveShader as);
|
|
||||||
|
|
||||||
#ifdef BBGE_BUILD_OPENGL
|
|
||||||
GLuint texture;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool bRenderGridPoints;
|
bool bRenderGridPoints;
|
||||||
|
|
||||||
int numEffects;
|
int numEffects;
|
||||||
|
@ -124,12 +107,11 @@ public:
|
||||||
int screenWidth, screenHeight;
|
int screenWidth, screenHeight;
|
||||||
int textureWidth, textureHeight;
|
int textureWidth, textureHeight;
|
||||||
|
|
||||||
Shader blurShader, bwShader, washoutShader, motionBlurShader, glowShader;
|
std::vector<Shader*> scriptShader;
|
||||||
Shader *scriptShader;
|
|
||||||
|
|
||||||
Vector ** drawGrid;
|
Vector ** drawGrid;
|
||||||
|
|
||||||
ActiveShader activeShader;
|
FrameBuffer backupBuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue