#include <cstdio>
#include <cstdarg>
#include <algorithm>
#include <smcolor.hpp>
#include "extrasmeltutils.hpp"
SMELT* smEntity3DBuffer::sm=nullptr;
SMELT* smParticle::sm=nullptr;
SMELT* smParticleSystem::sm=nullptr;
smVertex makeVertex(float x,float y,float z,DWORD color,float tx,float ty)
{smVertex v;v.x=x;v.y=y;v.z=z;v.col=color;v.tx=tx;v.ty=ty;return v;}
void smEntity3D::addVertices(size_t n,...)
{
va_list vl;va_start(vl,n);
for(int i=0;i<n;++i)
{
smVertex v=va_arg(vl,smVertex);
vertices.push_back(v);
}
va_end(vl);
}
void smEntity3D::addIndices(size_t n,...)
{
va_list vl;va_start(vl,n);
for(int i=0;i<n;++i)
{
int idx=va_arg(vl,int);
indices.push_back((WORD)idx);
}
va_end(vl);
}
smVertex smEntity3D::vertex(size_t idx)const
{
if(idx>0&&idx<vertices.size())return vertices[idx];
return smVertex();
}
WORD smEntity3D::index(size_t idx)const
{
if(idx>0&&idx<indices.size())return indices[idx];
return 0;
}
void smEntity3D::setVertex(size_t idx,smVertex v)
{
if(idx>0&&idx<vertices.size())vertices[idx]=v;
}
void smEntity3D::setIndex(size_t idx,WORD i)
{
if(idx>0&&idx<indices.size())indices[idx]=i;
}
smEntity3D smEntity3D::cube(smvec3d a,smvec3d b,DWORD color,int faces)
{
smEntity3D ret;
ret.addVertices(8,
makeVertex(a.x,a.y,a.z,color,0,0),makeVertex(b.x,a.y,a.z,color,0,0),
makeVertex(b.x,b.y,a.z,color,0,0),makeVertex(a.x,b.y,a.z,color,0,0),
makeVertex(a.x,a.y,b.z,color,0,0),makeVertex(b.x,a.y,b.z,color,0,0),
makeVertex(b.x,b.y,b.z,color,0,0),makeVertex(a.x,b.y,b.z,color,0,0));
if(faces&0x1)//a.z
ret.addIndices(6, 0,1,3, 1,2,3);
if(faces&0x2)//b.z
ret.addIndices(6, 4,5,7, 5,6,7);
if(faces&0x4)//a.x
ret.addIndices(6, 0,3,7, 0,4,7);
if(faces&0x8)//b.x
ret.addIndices(6, 1,2,6, 1,5,6);
if(faces&0x10)//a.y
ret.addIndices(6, 0,1,4, 1,4,5);
if(faces&0x20)//b.y
ret.addIndices(6, 2,3,7, 2,6,7);
return ret;
}
smEntity3DBuffer::smEntity3DBuffer()
{
sm=smGetInterface(SMELT_APILEVEL);
vertices.clear();indices.clear();
}
void smEntity3DBuffer::addTransformedEntity(smEntity3D *entity,smMatrix t,smvec3d p)
{
if(entity->vertices.size()+vertices.size()>4000)drawBatch();
for(unsigned i=0;i<entity->indices.size();++i)
indices.push_back(entity->indices[i]+vertices.size());
for(unsigned i=0;i<entity->vertices.size();++i)
{
smvec3d tp=smvec3d(entity->vertices[i].x,entity->vertices[i].y,entity->vertices[i].z);
tp=t*tp;tp=tp+p;vertices.push_back(makeVertex(tp.x,tp.y,tp.z,entity->vertices[i].col,entity->vertices[i].tx,entity->vertices[i].ty));
}
}
void smEntity3DBuffer::drawBatch()
{
if(!vertices.size())return;
sm->smDrawCustomIndexedVertices(&vertices[0],&indices[0],vertices.size(),indices.size(),BLEND_ALPHABLEND,0);
vertices.clear();indices.clear();
}
smParticle::smParticle()
{sm=smGetInterface(SMELT_APILEVEL);dead=false;clifespan=0;}
smParticle::~smParticle(){sm->smRelease();}
void smParticle::render()
{sm->smRenderQuad(&q);}
void smParticle::update()
{
clifespan+=sm->smGetDelta();if(clifespan>lifespan){dead=true;return;}
vel=vel+accel;pos=pos+vel;rotv=rotv+rota;rot=rot+rotv;
size=clifespan/lifespan*(finalsize-initsize)+initsize;
smColorRGBA fc(finalcolor),ic(initcolor),cc;
cc.a=clifespan/lifespan*(fc.a-ic.a)+ic.a;
cc.r=clifespan/lifespan*(fc.r-ic.r)+ic.r;
cc.g=clifespan/lifespan*(fc.g-ic.g)+ic.g;
cc.b=clifespan/lifespan*(fc.b-ic.b)+ic.b;
color=cc.getHWColor();
for(int i=0;i<4;++i)q.v[i].col=color;
smMatrix m;m.loadIdentity();
if(lookat)m.lookat(pos,lookatpos,smvec3d(0,0,1));else
{m.rotate(rot.x,1,0,0);m.rotate(rot.y,0,1,0);m.rotate(rot.z,0,0,1);}
smvec3d v0=m*smvec3d(-size,-size,0),v1=m*smvec3d(size,-size,0);
smvec3d v2=m*smvec3d(size,size,0),v3=m*smvec3d(-size,size,0);
q.v[0].x=v0.x+pos.x;q.v[0].y=v0.y+pos.y;q.v[0].z=v0.z+pos.z;
q.v[1].x=v1.x+pos.x;q.v[1].y=v1.y+pos.y;q.v[1].z=v1.z+pos.z;
q.v[2].x=v2.x+pos.x;q.v[2].y=v2.y+pos.y;q.v[2].z=v2.z+pos.z;
q.v[3].x=v3.x+pos.x;q.v[3].y=v3.y+pos.y;q.v[3].z=v3.z+pos.z;
}
smParticleSystem::smParticleSystem()
{sm=smGetInterface(SMELT_APILEVEL);particles.clear();posGenerator=nullptr;active=false;}
smParticleSystem::~smParticleSystem()
{for(unsigned i=0;i<particles.size();++i)delete particles[i];particles.clear();}
void smParticleSystem::setParticleSystemInfo(smParticleSystemInfo _psinfo)
{psinfo=_psinfo;}
void smParticleSystem::setPos(smvec3d _pos){pos=_pos;}
void smParticleSystem::setPSEmissionPosGen(smPSEmissionPositionGenerator *_gen)
{posGenerator=_gen;}
void smParticleSystem::setPSLookAt(smvec3d at){lookat=true;lookatpos=at;}
void smParticleSystem::unsetPSLookAt(){lookat=false;}
void smParticleSystem::startPS()
{active=true;nemdelay=0;re.setSeed(time(nullptr));}
void smParticleSystem::stopPS()
{active=false;}
void smParticleSystem::updatePS()
{
cemdelay+=sm->smGetDelta();
if(active&&cemdelay>nemdelay&&(int)particles.size()<psinfo.maxcount)
{
int ec=re.nextInt(psinfo.emissioncount-psinfo.ecvar,psinfo.emissioncount+psinfo.ecvar);
for(int i=0;i<ec;++i)
{
smParticle *p=new smParticle();
p->pos=pos+(posGenerator?posGenerator->genPos():smvec3d(0,0,0));
p->vel=smvec3d(
re.nextDouble(psinfo.vel.x-psinfo.velvar.x,psinfo.vel.x+psinfo.velvar.x),
re.nextDouble(psinfo.vel.y-psinfo.velvar.y,psinfo.vel.y+psinfo.velvar.y),
re.nextDouble(psinfo.vel.z-psinfo.velvar.z,psinfo.vel.z+psinfo.velvar.z));
p->accel=smvec3d(
re.nextDouble(psinfo.acc.x-psinfo.accvar.x,psinfo.acc.x+psinfo.accvar.x),
re.nextDouble(psinfo.acc.y-psinfo.accvar.y,psinfo.acc.y+psinfo.accvar.y),
re.nextDouble(psinfo.acc.z-psinfo.accvar.z,psinfo.acc.z+psinfo.accvar.z));
p->rotv=smvec3d(
re.nextDouble(psinfo.rotv.x-psinfo.rotvvar.x,psinfo.rotv.x+psinfo.rotvvar.x),
re.nextDouble(psinfo.rotv.y-psinfo.rotvvar.y,psinfo.rotv.y+psinfo.rotvvar.y),
re.nextDouble(psinfo.rotv.z-psinfo.rotvvar.z,psinfo.rotv.z+psinfo.rotvvar.z));
p->rota=smvec3d(
re.nextDouble(psinfo.rota.x-psinfo.rotavar.x,psinfo.rota.x+psinfo.rotavar.x),
re.nextDouble(psinfo.rota.y-psinfo.rotavar.y,psinfo.rota.y+psinfo.rotavar.y),
re.nextDouble(psinfo.rota.z-psinfo.rotavar.z,psinfo.rota.z+psinfo.rotavar.z));
p->rot=smvec3d(0,0,0);if(lookat)p->lookat=true,p->lookatpos=lookatpos;else p->lookat=false;
p->lifespan=re.nextDouble(psinfo.lifespan-psinfo.lifespanvar,psinfo.lifespan+psinfo.lifespanvar);
p->initsize=re.nextDouble(psinfo.initsize-psinfo.initsizevar,psinfo.initsize+psinfo.initsizevar);
p->finalsize=re.nextDouble(psinfo.finalsize-psinfo.finalsizevar,psinfo.finalsize+psinfo.finalsizevar);
p->size=p->initsize;
p->initcolor=ARGB(
re.nextInt(GETA(psinfo.initcolor)-GETA(psinfo.initcolorvar),GETA(psinfo.initcolor)+GETA(psinfo.initcolorvar)),
re.nextInt(GETR(psinfo.initcolor)-GETR(psinfo.initcolorvar),GETR(psinfo.initcolor)+GETR(psinfo.initcolorvar)),
re.nextInt(GETG(psinfo.initcolor)-GETG(psinfo.initcolorvar),GETG(psinfo.initcolor)+GETG(psinfo.initcolorvar)),
re.nextInt(GETB(psinfo.initcolor)-GETB(psinfo.initcolorvar),GETB(psinfo.initcolor)+GETB(psinfo.initcolorvar)));
p->finalcolor=ARGB(
re.nextInt(GETA(psinfo.finalcolor)-GETA(psinfo.finalcolorvar),GETA(psinfo.finalcolor)+GETA(psinfo.finalcolorvar)),
re.nextInt(GETR(psinfo.finalcolor)-GETR(psinfo.finalcolorvar),GETR(psinfo.finalcolor)+GETR(psinfo.finalcolorvar)),
re.nextInt(GETG(psinfo.finalcolor)-GETG(psinfo.finalcolorvar),GETG(psinfo.finalcolor)+GETG(psinfo.finalcolorvar)),
re.nextInt(GETB(psinfo.finalcolor)-GETB(psinfo.finalcolorvar),GETB(psinfo.finalcolor)+GETB(psinfo.finalcolorvar)));
p->color=p->initcolor;p->q.tex=psinfo.texture;p->q.blend=psinfo.blend;
p->q.v[0].tx=p->q.v[3].tx=0;p->q.v[0].ty=p->q.v[1].ty=0;
p->q.v[1].tx=p->q.v[2].tx=1;p->q.v[2].ty=p->q.v[3].ty=1;
particles.push_back(p);
}
cemdelay=0;
nemdelay=re.nextDouble(psinfo.emissiondelay-psinfo.edvar,psinfo.emissiondelay+psinfo.edvar);
}
for(unsigned i=0,j;i<particles.size()&&!particles[i]->dead;++i)
{
particles[i]->update();
if(particles[i]->dead)
{
for(j=particles.size()-1;j>i&&particles[j]->dead;--j);
std::swap(particles[i],particles[j]);
}
}
while(!particles.empty()&&particles.back()->dead)
{delete particles.back();particles.back()=nullptr;particles.pop_back();}
}
void smParticleSystem::renderPS()
{for(unsigned i=0;i<particles.size();++i)particles[i]->render();}
smColor::smColor()
{r=g=b=h=s=v=a=0;}
void smColor::update_rgb()
{
auto f=[this](float n){
float k=fmodf(n+6.0f*this->h,6.0f);
return this->v-this->v*this->s*max(0.0f,min(1.0f,min(k,4.0f-k)));
};
r=f(5);
g=f(3);
b=f(1);
}
void smColor::update_hsv()
{
v=max(r,max(g,b));
float vm=min(r,min(g,b));
float chroma=v-vm;
if(v-vm<EPSF)h=0;
else if(v-r<EPSF)h=(0.0f+(g-b)/chroma)/6.0f;
else if(v-g<EPSF)h=(2.0f+(b-r)/chroma)/6.0f;
else if(v-b<EPSF)h=(4.0f+(r-g)/chroma)/6.0f;
if(v<EPSF)s=0;else s=chroma/v;
}
void smColor::clamp(bool hsv)
{
if(hsv)
{
h=min(1.0f,max(0.0f,h));
s=min(1.0f,max(0.0f,s));
v=min(1.0f,max(0.0f,v));
update_rgb();
}
else
{
r=min(1.0f,max(0.0f,r));
g=min(1.0f,max(0.0f,g));
b=min(1.0f,max(0.0f,b));
update_hsv();
}
}
float smColor::alpha()const
{return a;}
float smColor::red()const
{return r;}
float smColor::green()const
{return g;}
float smColor::blue()const
{return b;}
float smColor::hue()const
{return h;}
float smColor::saturation()const
{return s;}
float smColor::hslSaturation()const
{
float l=lightness();
if(fabsf(l)<EPSF||fabsf(l-1)<EPSF)return 0;
return (v-l)/min(l,1-l);
}
float smColor::value()const
{return v;}
float smColor::lightness()const
{return v-v*s/2;}
void smColor::setAlpha(float alpha)
{a=alpha;}
void smColor::setRed(float red)
{
if(fabsf(r-red)>EPSF)
{
r=red;
update_hsv();
}
}
void smColor::setGreen(float green)
{
if(fabsf(g-green)>EPSF)
{
g=green;
update_hsv();
}
}
void smColor::setBlue(float blue)
{
if(fabsf(b-blue)>EPSF)
{
b=blue;
update_hsv();
}
}
void smColor::setHue(float hue)
{
if(fabsf(h-hue)>EPSF)
{
h=hue;
update_rgb();
}
}
void smColor::setSaturation(float saturation)
{
if(fabsf(s-saturation)>EPSF)
{
s=saturation;
update_rgb();
}
}
void smColor::setHSLSaturation(float saturation)
{
float ss=hslSaturation();
float l=lightness();
if(fabsf(ss-saturation)>EPSF)
{
ss=saturation;
v=l+ss*min(l,1-l);
if(v<EPSF)s=0;
else s=2-2*l/v;
update_rgb();
}
}
void smColor::setValue(float value)
{
if(fabsf(v-value)>EPSF)
{
v=value;
update_rgb();
}
}
void smColor::setLightness(float lightness)
{
float ss=hslSaturation();
float l=this->lightness();
if(fabsf(l-lightness)>EPSF)
{
l=lightness;
v=l+ss*min(l,1-l);
if(v<EPSF)s=0;
else s=2-2*l/v;
update_rgb();
}
}
smColor smColor::lighter(int factor)
{
smColor ret(*this);
ret.setValue(v*(factor/100.0f));
ret.clamp(true);
return ret;
}
smColor smColor::darker(int factor)
{
smColor ret(*this);
ret.setValue(factor?(v/(factor/100.0f)):1.);
ret.clamp(true);
return ret;
}
uint32_t smColor::toHWColor()
{
return RGBA(r*255,g*255,b*255,a*255);
}
smColor smColor::fromHWColor(uint32_t color)
{
smColor ret;
ret.r=GETR(color)/255.0f;
ret.g=GETG(color)/255.0f;
ret.b=GETB(color)/255.0f;
ret.a=GETA(color)/255.0f;
ret.update_hsv();
return ret;
}