aboutsummaryrefslogtreecommitdiff
path: root/smelt/glfw_m
diff options
context:
space:
mode:
Diffstat (limited to 'smelt/glfw_m')
l---------smelt/glfw_m/CxImage1
-rw-r--r--smelt/glfw_m/gfx_glfw.cpp1045
-rw-r--r--smelt/glfw_m/inp_glfw.cpp204
-rw-r--r--smelt/glfw_m/makefile27
-rw-r--r--smelt/glfw_m/sfx_dumb.cpp63
-rw-r--r--smelt/glfw_m/sfx_oal.cpp368
-rw-r--r--smelt/glfw_m/smelt_config.hpp30
-rw-r--r--smelt/glfw_m/smelt_internal.hpp249
-rw-r--r--smelt/glfw_m/smmath_priv.hpp165
-rw-r--r--smelt/glfw_m/sys_glfw.cpp334
10 files changed, 2486 insertions, 0 deletions
diff --git a/smelt/glfw_m/CxImage b/smelt/glfw_m/CxImage
new file mode 120000
index 0000000..07300ae
--- /dev/null
+++ b/smelt/glfw_m/CxImage
@@ -0,0 +1 @@
+../glfw/CxImage \ No newline at end of file
diff --git a/smelt/glfw_m/gfx_glfw.cpp b/smelt/glfw_m/gfx_glfw.cpp
new file mode 100644
index 0000000..ebd24bf
--- /dev/null
+++ b/smelt/glfw_m/gfx_glfw.cpp
@@ -0,0 +1,1045 @@
+// -*- C++ -*-
+/*
+ * Simple MultimEdia LiTerator(SMELT)
+ * by Chris Xiong 2015
+ * GFX implementation based on GLFW/OpenGL 3.2+
+ *
+ * WARNING: This library is in development and interfaces would be very
+ * unstable.
+ *
+ */
+#include "smelt_internal.hpp"
+#include "CxImage/ximage.h"
+#include "smmath_priv.hpp"
+#define dbg printf("%d: 0x%X\n",__LINE__,glGetError())
+static const char* GFX_GLFW_SRCFN="smelt/glfw_m/gfx_glfw.cpp";
+static const char* fixedfunc_pipeline_vsh=
+ "#version 330 core\n"
+ "layout (location=0) in vec3 vp;"
+ "layout (location=1) in vec4 vc;"
+ "layout (location=2) in vec2 vtc;"
+ "out vec4 fc;"
+ "out vec2 ftc;"
+ "uniform mat4 mmodv;"
+ "uniform mat4 mproj;"
+ "void main(){"
+ "gl_Position=mproj*mmodv*vec4(vp,1.0f);"
+ "ftc=vec2(vtc.x,1.0f-vtc.y);"//do flipping at a lower level
+ "fc=vc;"
+ "}"
+;
+static const char* fixedfunc_pipeline_fsh=
+ "#version 330 core\n"
+ "in vec4 fc;"
+ "in vec2 ftc;"
+ "out vec4 color;"
+ "uniform sampler2D tex;"
+ "void main(){"
+ "color=fc*texture(tex,ftc);"
+ "if(color.a<1./256.)discard;"//alpha testing
+ "}"
+;
+struct glTexture
+{
+ GLuint name,rw,rh,dw,dh;
+ const char *fn;
+ DWORD *px,*locpx;
+ bool isTarget,lost,roloc;
+ GLint locx,locy,locw,loch;
+};
+bool SMELT_IMPL::smRenderBegin2D(bool ztest,SMTRG trg)
+{
+ TRenderTargetList *targ=(TRenderTargetList*)trg;
+ if(vertexArray)
+ {smLog("%s:" SLINE ": Last frame not closed.\n",GFX_GLFW_SRCFN);return false;}
+ glBindFramebuffer(GL_FRAMEBUFFER,(targ)?targ->frame:0);
+ glDepthFunc(GL_GEQUAL);
+ ztest?glEnable(GL_DEPTH_TEST):glDisable(GL_DEPTH_TEST);
+ zbufenabled=ztest;
+ if(targ)
+ {
+ glScissor(0,0,targ->w,targ->h);
+ glViewport(0,0,targ->w,targ->h);
+ configProjectionMatrix2D(targ->w,targ->h);
+ }
+ else
+ {
+ glScissor(0,0,scrw,scrh);
+ glViewport(0,0,scrw,scrh);
+ configProjectionMatrix2D(scrw,scrh);
+ }
+ sm2DCamera5f3v(NULL,NULL,NULL);
+ curTarget=targ;tdmode=0;
+ vertexArray=vertexBuf;
+ return true;
+}
+bool SMELT_IMPL::smRenderBegin3D(float fov,bool ztest,SMTRG trg)
+{
+ TRenderTargetList *targ=(TRenderTargetList*)trg;
+ if(vertexArray)
+ {smLog("%s:" SLINE ": Last frame not closed.\n",GFX_GLFW_SRCFN);return false;}
+ glBindFramebuffer(GL_FRAMEBUFFER,(targ)?targ->frame:0);
+ glDepthFunc(GL_LESS);
+ ztest?glEnable(GL_DEPTH_TEST):glDisable(GL_DEPTH_TEST);
+ zbufenabled=ztest;
+ if(targ)
+ {
+ glScissor(0,0,targ->w,targ->h);
+ glViewport(0,0,targ->w,targ->h);
+ configProjectionMatrix3D(targ->w,targ->h,fov);
+ }
+ else
+ {
+ glScissor(0,0,scrw,scrh);
+ glViewport(0,0,scrw,scrh);
+ configProjectionMatrix3D(scrw,scrh,fov);
+ }
+ sm3DCamera6f2v(NULL,NULL);
+ curTarget=targ;tdmode=1;
+ vertexArray=vertexBuf;
+ return true;
+}
+bool SMELT_IMPL::smRenderEnd()
+{
+ batchOGL(true);
+ if(curTarget&&curTarget->ms)
+ {
+ glTexture *pTex=(glTexture*)curTarget->tex;
+ if(pTex&&pTex->lost)
+ configTexture(pTex,pTex->rw,pTex->rh,pTex->px);
+ int w=curTarget->w,h=curTarget->h;
+ glFinish();
+ DWORD *px=new DWORD[w*h];
+ glBindFramebuffer(GL_READ_FRAMEBUFFER,curTarget->frame);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER,curTarget->sframe);
+ glBlitFramebuffer(0,0,w,h,0,0,w,h,GL_COLOR_BUFFER_BIT,GL_NEAREST);
+ glBindFramebuffer(GL_FRAMEBUFFER,curTarget->sframe);
+ glReadPixels(0,0,w,h,GL_RGBA,GL_UNSIGNED_BYTE,px);
+ glBindTexture(GL_TEXTURE_2D,pTex->name);
+ glTexSubImage2D(GL_TEXTURE_2D,0,0,0,w,h,GL_RGBA,GL_UNSIGNED_BYTE,px);
+ glBindTexture(GL_TEXTURE_2D,primTex?(((glTexture*)primTex)->name):0);
+ delete[] px;
+ }
+ if(!curTarget)glfwSwapBuffers((GLFWwindow*)hwnd);
+ return true;
+}
+void SMELT_IMPL::smClrscr(DWORD color,bool clearcol,bool cleardep)
+{
+ GLfloat a=(GLfloat)(GETA(color))/255.f;
+ GLfloat r=(GLfloat)(GETR(color))/255.f;
+ GLfloat g=(GLfloat)(GETG(color))/255.f;
+ GLfloat b=(GLfloat)(GETB(color))/255.f;
+ if(clearcol)
+ glClearColor(r,g,b,a);
+ if(tdmode)glClearDepth(1);
+ else if(zbufenabled)glClearDepth(0);
+ glClear((clearcol?GL_COLOR_BUFFER_BIT:0)|(((zbufenabled||tdmode)&&cleardep)?GL_DEPTH_BUFFER_BIT:0));
+}
+void SMELT_IMPL::sm3DCamera6f2v(float *pos,float *rot)
+{
+ batchOGL();
+ smMatrix Mmodv;
+ Mmodv.loadIdentity();
+ if(pos&&rot)
+ {
+ Mmodv.rotate(smMath::deg2rad(-rot[0]),1,0,0);
+ Mmodv.rotate(smMath::deg2rad(-rot[1]),0,1,0);
+ Mmodv.rotate(smMath::deg2rad(-rot[2]),0,0,1);
+ Mmodv.translate(-pos[0],-pos[1],-pos[2]);
+ }
+ memcpy(mmodv,Mmodv.m,sizeof(mmodv));
+ glUniformMatrix4fv(loc_mmodv,1,0,mmodv);
+}
+void SMELT_IMPL::smMultViewMatrix(float *mat)
+{
+ smMatrix Mmodv(mmodv);
+ Mmodv=Mmodv*smMatrix(mat);
+ memcpy(mmodv,Mmodv.m,sizeof(mmodv));
+}
+void SMELT_IMPL::sm2DCamera5f3v(float *pos,float *dpos,float *rot)
+{
+ batchOGL();
+ smMatrix Mmodv;
+ Mmodv.loadIdentity();
+ if(pos&&dpos&&rot)
+ {
+ Mmodv.translate(-pos[0],-pos[1],.0f);
+ Mmodv.rotate(*rot,.0f,.0f,1.0f);
+ Mmodv.translate(pos[0]+dpos[0],pos[1]+dpos[1],.0f);
+ }
+ memcpy(mmodv,Mmodv.m,sizeof(mmodv));
+ glUniformMatrix4fv(loc_mmodv,1,0,mmodv);
+}
+void SMELT_IMPL::smRenderLinefd(float x1,float y1,float z1,float x2,float y2,float z2,DWORD color)
+{
+ if(vertexArray)
+ {
+ if(primType!=PRIM_LINES||primcnt>=VERTEX_BUFFER_SIZE/PRIM_LINES||primTex||primBlend!=BLEND_ALPHABLEND)
+ {
+ batchOGL();
+ primType=PRIM_LINES;
+ if(primBlend!=BLEND_ALPHAADD)setBlend(BLEND_ALPHAADD);
+ bindTexture(NULL);
+ }
+ int i=(primcnt++)*PRIM_LINES;
+ vertexArray[i].x=x1,vertexArray[i+1].x=x2;
+ vertexArray[i].y=y1,vertexArray[i+1].y=y2;
+ vertexArray[i].x=z1,vertexArray[i+1].x=z2;
+ vertexArray[i].col=vertexArray[i+1].col=color;
+ vertexArray[i].tx=vertexArray[i+1].tx=.0;
+ vertexArray[i].ty=vertexArray[i+1].ty=.0;
+ }
+}
+void SMELT_IMPL::smRenderLinefvd(float *p1,float *p2,DWORD color)
+{
+ if(vertexArray&&p1&&p2)
+ {
+ if(primType!=PRIM_LINES||primcnt>=VERTEX_BUFFER_SIZE/PRIM_LINES||primTex||primBlend!=BLEND_ALPHABLEND)
+ {
+ batchOGL();
+ primType=PRIM_LINES;
+ if(primBlend!=BLEND_ALPHAADD)setBlend(BLEND_ALPHAADD);
+ bindTexture(NULL);
+ }
+ int i=(primcnt++)*PRIM_LINES;
+ vertexArray[i].x=p1[0],vertexArray[i+1].x=p2[0];
+ vertexArray[i].y=p1[1],vertexArray[i+1].y=p2[1];
+ vertexArray[i].x=p2[2],vertexArray[i+1].x=p2[2];
+ vertexArray[i].col=vertexArray[i+1].col=color;
+ vertexArray[i].tx=vertexArray[i+1].tx=.0;
+ vertexArray[i].ty=vertexArray[i+1].ty=.0;
+ }
+}
+void SMELT_IMPL::smRenderTriangle(smTriangle *t)
+{
+ if(vertexArray)
+ {
+ if(primType!=PRIM_TRIANGLES||primcnt>=VERTEX_BUFFER_SIZE/PRIM_TRIANGLES||
+ primTex!=t->tex||primBlend!=t->blend)
+ {
+ batchOGL();
+ primType=PRIM_TRIANGLES;
+ if(primBlend!=t->blend)setBlend(t->blend);
+ bindTexture((glTexture*)t->tex);
+ }
+ memcpy(&vertexArray[(primcnt++)*PRIM_TRIANGLES],t->v,sizeof(smVertex)*PRIM_TRIANGLES);
+ }
+}
+void SMELT_IMPL::smRenderQuad(smQuad *q)
+{
+ if(vertexArray)
+ {
+ if(primType!=PRIM_QUADS||primcnt>=VERTEX_BUFFER_SIZE/PRIM_QUADS||
+ primTex!=q->tex||primBlend!=q->blend)
+ {
+ batchOGL();
+ primType=PRIM_QUADS;
+ if(primBlend!=q->blend)setBlend(q->blend);
+ bindTexture((glTexture*)q->tex);
+ }
+ memcpy(&vertexArray[(primcnt++)*PRIM_QUADS],q->v,sizeof(smVertex)*PRIM_QUADS);
+ }
+}
+smVertex* SMELT_IMPL::smGetVertArray()
+{
+ if(vertexArray)
+ {
+ batchOGL();
+ return vertexArray;
+ }
+ else return NULL;
+}
+void SMELT_IMPL::smDrawVertArray(int prim,SMTEX texture,int blend,int _primcnt)
+{
+ primType=prim;
+ bindTexture((glTexture*)texture);
+ if(primBlend!=blend)setBlend(blend);
+ primcnt=primcnt;
+}
+void SMELT_IMPL::smDrawCustomIndexedVertices(smVertex* vb,WORD* ib,int vbc,int ibc,int blend,SMTEX texture)
+{
+ if(vertexArray)
+ {
+ batchOGL();
+
+ float twm=1.,thm=1.;
+ if(texture)
+ {
+ glTexture *ptex=(glTexture*)texture;
+ if(ptex->dw&&ptex->dh)
+ {
+ twm=(ptex->rw)/(float)(ptex->dw);
+ thm=(ptex->rh)/(float)(ptex->dh);
+ }
+ }
+ if(!tdmode)
+ {
+ float h=curTarget?curTarget->h:scrh;
+ for(int i=0;i<vbc;++i)
+ {
+ vertexArray[i].y=h-vertexArray[i].y;
+ vertexArray[i].z=-vertexArray[i].z;
+ }
+ }
+ for(int i=0;i<vbc;++i)
+ {
+ vb[i].tx*=twm;
+ vb[i].ty=(1.-vb[i].ty)*thm;
+ DWORD color=vb[i].col;
+ BYTE *col=(BYTE*)&vb[i].col;
+ BYTE a=((color>>24)&0xFF);
+ BYTE r=((color>>16)&0xFF);
+ BYTE g=((color>> 8)&0xFF);
+ BYTE b=((color>> 0)&0xFF);
+ col[0]=r;col[1]=g;
+ col[2]=b;col[3]=a;
+ }
+ if(texture!=primTex)bindTexture((glTexture*)texture);
+ if(blend!=primBlend)setBlend(blend);
+ glBindVertexArray(VertexArrayObject);
+ glBindBuffer(GL_ARRAY_BUFFER,VertexBufferObject);
+ glBufferData(GL_ARRAY_BUFFER,sizeof(smVertex)*vbc,vb,GL_STATIC_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,IndexBufferObject);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(GLushort)*ibc,ib,GL_STATIC_DRAW);
+ glDrawElements(GL_TRIANGLES,ibc,GL_UNSIGNED_SHORT,0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,IndexBufferObject);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(GLushort)*((VERTEX_BUFFER_SIZE*6)/4),indexBuf,GL_STATIC_DRAW);
+ glBindVertexArray(0);
+ if(texture!=primTex)bindTexture((glTexture*)primTex);
+
+ }
+}
+SMTRG SMELT_IMPL::smTargetCreate(int w,int h,int ms)
+{
+ bool ok=false;
+ TRenderTargetList *pTarget=new TRenderTargetList;
+ memset(pTarget,0,sizeof(TRenderTargetList));
+ pTarget->tex=buildTexture(w,h,NULL);
+ glTexture *gltex=(glTexture*)pTarget->tex;
+ gltex->isTarget=true;gltex->lost=false;
+ configTexture(gltex,w,h,NULL,false);
+ pTarget->w=w;pTarget->h=h;
+ ok=buildTarget(pTarget,gltex->name,w,h,ms);
+ if(!ok)
+ {
+ smLog("%s:" SLINE ": Failed to create render target.\n",GFX_GLFW_SRCFN);
+ smTextureFree(pTarget->tex);
+ delete pTarget;
+ return 0;
+ }
+ pTarget->next=targets;
+ targets=pTarget;
+ return (SMTRG)pTarget;
+}
+SMTEX SMELT_IMPL::smTargetTexture(SMTRG targ)
+{
+ TRenderTargetList *pTarg=(TRenderTargetList*)targ;
+ return targ?pTarg->tex:0;
+}
+void SMELT_IMPL::smTargetFree(SMTRG targ)
+{
+ TRenderTargetList *pTarget=targets,*pLastTarg=NULL;
+ while(pTarget)
+ {
+ if((TRenderTargetList*)targ==pTarget)
+ {
+ if(pLastTarg)pLastTarg->next=pTarget->next;
+ else targets=pTarget->next;
+ if(curTarget==(TRenderTargetList*)targ)
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT,0);
+ if(pTarget->depth)
+ glDeleteRenderbuffers(1,&pTarget->depth);
+ glDeleteFramebuffers(1,&pTarget->frame);
+ if(pTarget->ms)
+ {
+ glDeleteRenderbuffers(1,&pTarget->colorms);
+ glDeleteRenderbuffers(1,&pTarget->scolor);
+ glDeleteRenderbuffers(1,&pTarget->sdepth);
+ glDeleteFramebuffers(1,&pTarget->sframe);
+ }
+ if(curTarget==(TRenderTargetList*)targ)curTarget=0;
+ smTextureFree(pTarget->tex);
+ delete pTarget;
+ return;
+ }
+ pLastTarg=pTarget;
+ pTarget=pTarget->next;
+ }
+}
+SMTEX SMELT_IMPL::smTextureCreate(int w,int h)
+{
+ DWORD *px=new DWORD[w*h];
+ memset(px,0,sizeof(DWORD)*w*h);
+ SMTEX ret=buildTexture(w,h,px);
+ if(ret)
+ {
+ TTextureList *tex=new TTextureList;
+ tex->tex=ret;
+ tex->w=w;
+ tex->h=h;
+ tex->next=textures;
+ textures=tex;
+ }
+ return ret;
+}
+SMTEX SMELT_IMPL::smTextureLoad(const char *path)
+{
+ FILE *pFile;DWORD size,rsize;char *buff;
+ SMTEX ret=0;
+ pFile=fopen(path,"rb");
+ if(!pFile)return 0;
+ fseek(pFile,0,SEEK_END);size=ftell(pFile);rewind(pFile);
+ buff=(char*)malloc(sizeof(char)*size);
+ if(!buff)return 0;
+ rsize=fread(buff,1,size,pFile);
+ if(rsize!=size)return 0;
+ ret=smTextureLoadFromMemory(buff,size);if(!ret)return 0;
+ glTexture *t=(glTexture*)ret;
+ configTexture(t,t->rw,t->rh,t->px);
+ delete[] t->px;t->px=NULL;
+ t->fn=strcpy(new char[strlen(path)+1],path);
+ free(buff);
+ fclose(pFile);
+ return ret;
+}
+SMTEX SMELT_IMPL::smTextureLoadFromMemory(const char *ptr,DWORD size)
+{
+ int w=0,h=0;SMTEX ret=0;
+ DWORD *px=decodeImage((BYTE*)ptr,NULL,size,w,h);
+ if(px)ret=buildTexture(w,h,px);
+ if(ret)
+ {
+ TTextureList *tex=new TTextureList;
+ tex->tex=ret;tex->w=w;tex->h=h;
+ tex->next=textures;textures=tex;
+ }else smLog("%s:" SLINE ": Unsupported texture format.\n",GFX_GLFW_SRCFN);
+ return ret;
+}
+void SMELT_IMPL::smTextureFree(SMTEX tex)
+{
+ if(!pOpenGLDevice)return;
+ TTextureList *ctex=textures,*lasttex=NULL;
+ while(ctex)
+ {
+ if(ctex->tex==tex)
+ {
+ if(lasttex)lasttex->next=ctex->next;
+ else textures=ctex->next;
+ delete ctex;
+ break;
+ }
+ lasttex=ctex;
+ ctex=ctex->next;
+ }
+ if(tex)
+ {
+ glTexture *ptex=(glTexture*)tex;
+ delete[] ptex->fn;
+ delete[] ptex->locpx;
+ delete[] ptex->px;
+ glDeleteTextures(1,&ptex->name);
+ delete ptex;
+ }
+}
+void SMELT_IMPL::smTextureOpt(int potopt,int filter)
+{
+ batchOGL();
+ if(potopt==TPOT_NONPOT)
+ {
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_R,GL_CLAMP_TO_EDGE);
+ }
+ if(potopt==TPOT_POT)
+ {
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_R,GL_REPEAT);
+ }
+ filtermode=filter;
+ if(filter==TFLT_NEAREST)
+ {
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
+ }
+ if(filter==TFLT_LINEAR)
+ {
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+ }
+}
+int SMELT_IMPL::smTextureGetWidth(SMTEX tex,bool original)
+{
+ if(original)
+ {
+ TTextureList *ctex=textures;
+ while(ctex){if(ctex->tex==tex)return ctex->w;ctex=ctex->next;}
+ }
+ else return ((glTexture*)tex)->rw;
+ return 0;
+}
+int SMELT_IMPL::smTextureGetHeight(SMTEX tex,bool original)
+{
+ if(original)
+ {
+ TTextureList *ctex=textures;
+ while(ctex){if(ctex->tex==tex)return ctex->h;ctex=ctex->next;}
+ }
+ else return ((glTexture*)tex)->rh;
+ return 0;
+}
+DWORD* SMELT_IMPL::smTextureLock(SMTEX tex,int l,int t,int w,int h,bool ro)
+{
+ glTexture *ptex=(glTexture*)tex;
+ if(ptex->locpx)return NULL;
+ bool fromfile=(ptex->px==NULL&&ptex->fn);
+ if(fromfile)
+ {
+ FILE *pFile;DWORD size,rsize;char *buff;
+ pFile=fopen(ptex->fn,"rb");
+ if(!pFile)return 0;
+ fseek(pFile,0,SEEK_END);size=ftell(pFile);rewind(pFile);
+ buff=(char*)malloc(sizeof(char)*size);
+ if(!buff)return 0;
+ rsize=fread(buff,1,size,pFile);
+ if(rsize!=size)return 0;
+ int _w,_h;
+ ptex->px=decodeImage((BYTE*)buff,ptex->fn,size,_w,_h);
+ if(_w!=(int)ptex->rw||_h!=(int)ptex->rh)
+ {delete[] ptex->px;ptex->px=NULL;}
+ free(buff);
+ fclose(pFile);
+ if(ptex->px&&!ro){delete[] ptex->fn;ptex->fn=NULL;}
+ }
+ if(!ptex->px&&!ptex->isTarget)return 0;
+ if(!w)w=ptex->rw;if(!h)h=ptex->rh;
+ //asserts...
+ ptex->roloc=ro;ptex->locx=l;ptex->locy=t;ptex->locw=w;ptex->loch=h;
+ ptex->locpx=new DWORD[w*h];
+ if(!ptex->isTarget)
+ {
+ DWORD *dst=ptex->locpx,*src=ptex->px+((t*ptex->rw)+l);
+ for(int i=0;i<h;++i)//TODO: flip it...
+ {
+ memcpy(dst,src,w*sizeof(DWORD));
+ dst+=w;src+=ptex->rw;
+ }
+ }
+ else return 0;
+ return ptex->locpx;
+}
+void SMELT_IMPL::smTexutreUnlock(SMTEX tex)
+{
+ glTexture *ptex=(glTexture*)tex;
+ if(!ptex->locpx)return;
+ if(!ptex->roloc)
+ {
+ DWORD *src=ptex->locpx+ptex->locw*ptex->loch,
+ *dst=ptex->px+(((ptex->rh-ptex->locy-1)*ptex->rw)+ptex->locx);
+ for(int i=0;i<ptex->loch;++i)
+ {
+ memcpy(dst,src,ptex->locw*sizeof(DWORD));
+ dst-=ptex->rw;src-=ptex->locw;
+ }
+ if(ptex->lost)configTexture(ptex,ptex->rw,ptex->rh,ptex->px);
+ else
+ {
+ glBindTexture(GL_TEXTURE_2D,ptex->name);
+ glTexSubImage2D(GL_TEXTURE_2D,0,ptex->locx,
+ (ptex->rh-ptex->locy)-ptex->loch,ptex->locw,ptex->loch,GL_RGBA,
+ GL_UNSIGNED_BYTE,ptex->locpx);
+ glBindTexture(GL_TEXTURE_2D,primTex?(((glTexture*)primTex)->name):0);
+ }
+ }
+ if(ptex->fn&&ptex->roloc){delete[] ptex->px;ptex->px=NULL;}
+ delete[] ptex->locpx;ptex->locpx=NULL;
+ ptex->roloc=false;
+ ptex->locx=ptex->locy=ptex->locw=ptex->loch=-1;
+}
+
+inline bool ispot(GLuint x){return((x&(x-1))==0);}
+inline GLuint npot(GLuint x)
+{
+ --x;
+ for(unsigned i=1;i<sizeof(GLuint)*8;i<<=1)x|=x>>i;
+ return x+1;
+}
+DWORD* SMELT_IMPL::decodeImage(BYTE *data,const char *fn,DWORD size,int &w,int &h)
+{
+ w=h=0;
+ DWORD *px=NULL;
+ int fnlen=fn?strlen(fn):0;
+ if((fnlen>5)&&(strcasecmp((fn+fnlen)-5,".rgba"))==0)//raw image... pending remove
+ {
+ DWORD *ptr=(DWORD*)data;
+ DWORD _w=ptr[0],_h=ptr[1];
+ if(((_w*_h*4)+8)==size)
+ {
+ w=_w;h=_h;
+ px=new DWORD[w*h];
+ memcpy(px,ptr+2,w*h*4);
+ }
+ return px;
+ }
+ CxImage img;
+ img.Decode(data,size,CXIMAGE_FORMAT_UNKNOWN);
+ if(img.IsValid())
+ {
+ w=img.GetWidth();h=img.GetHeight();
+ px=new DWORD[w*h];
+ BYTE *sptr=(BYTE*)px;
+ bool atunnel=img.AlphaIsValid();
+ for(int i=0;i<h;++i)
+ for(int j=0;j<w;++j)
+ {
+ RGBQUAD rgb=img.GetPixelColor(j,i,true);
+ *(sptr++)=rgb.rgbRed;
+ *(sptr++)=rgb.rgbGreen;
+ *(sptr++)=rgb.rgbBlue;
+ *(sptr++)=atunnel?rgb.rgbReserved:0xFF;
+ }
+ }
+ return px;
+}
+void SMELT_IMPL::bindTexture(glTexture *t)
+{
+ if(t&&t->lost)
+ configTexture(t,t->rw,t->rh,t->px);
+ if(((SMTEX)t)!=primTex)
+ {
+ glBindTexture(GL_TEXTURE_2D,t?t->name:((glTexture*)emptyTex)->name);
+ glUniform1i(loc_tex,0);
+ primTex=(SMTEX)t;
+ }
+}
+bool SMELT_IMPL::buildTarget(TRenderTargetList *pTarget,GLuint texid,int w,int h,int ms=0)
+{
+ bool ok=true;
+ if(ms)
+ {
+ glGenFramebuffers(1,&pTarget->sframe);
+ glBindFramebuffer(GL_FRAMEBUFFER,pTarget->sframe);
+ glGenRenderbuffers(1,&pTarget->scolor);
+ glBindRenderbuffer(GL_RENDERBUFFER,pTarget->scolor);
+ glRenderbufferStorage(GL_RENDERBUFFER,GL_RGBA,w,h);
+ glGenRenderbuffers(1,&pTarget->sdepth);
+ glBindRenderbuffer(GL_RENDERBUFFER,pTarget->sdepth);
+ glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT24,w,h);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_RENDERBUFFER,pTarget->scolor);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,pTarget->sdepth);
+
+ glGenFramebuffers(1,&pTarget->frame);
+ glBindFramebuffer(GL_FRAMEBUFFER,pTarget->frame);
+ glGenRenderbuffers(1,&pTarget->colorms);
+ glBindRenderbuffer(GL_RENDERBUFFER,pTarget->colorms);
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER,ms,GL_RGBA,w,h);
+ glGenRenderbuffers(1,&pTarget->depth);
+ glBindRenderbuffer(GL_RENDERBUFFER,pTarget->depth);
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER,ms,GL_DEPTH_COMPONENT24,w,h);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_RENDERBUFFER,pTarget->colorms);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,pTarget->depth);
+ GLenum rc=glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if((rc==GL_FRAMEBUFFER_COMPLETE)&&(glGetError()==GL_NO_ERROR))
+ {
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ ok=true;pTarget->ms=ms;
+ }
+ else
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER,0);
+ glDeleteRenderbuffers(1,&pTarget->colorms);
+ glDeleteRenderbuffers(1,&pTarget->depth);
+ glDeleteFramebuffers(1,&pTarget->frame);
+ glDeleteFramebuffers(1,&pTarget->sframe);
+ ok=false;ms=0;
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER,curTarget?curTarget->frame:0);
+ }
+ if(!ms)
+ {
+ glGenFramebuffers(1,&pTarget->frame);
+ glGenRenderbuffers(1,&pTarget->depth);
+ glBindFramebuffer(GL_FRAMEBUFFER,pTarget->frame);
+ glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,texid,0);
+ glBindRenderbuffer(GL_RENDERBUFFER,pTarget->depth);
+ glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT24,w,h);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,pTarget->depth);
+ GLenum rc=glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if((rc==GL_FRAMEBUFFER_COMPLETE)&&(glGetError()==GL_NO_ERROR))
+ {
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ ok=true;pTarget->ms=0;
+ }
+ else
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER,0);
+ glDeleteRenderbuffers(1,&pTarget->depth);
+ glDeleteFramebuffers(1,&pTarget->frame);
+ ok=false;
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER,curTarget?curTarget->frame:0);
+ }
+ return ok;
+}
+void SMELT_IMPL::configTexture(glTexture *t,int w,int h,DWORD *px,bool compressed)
+{
+ GLuint tex=0;
+ glGenTextures(1,&tex);
+ t->lost=false;t->name=tex;t->rw=w;t->rh=h;t->px=px;
+ t->dw=t->dh=0;
+ bool fromfile=(!px&&(t->fn));
+ if(fromfile)
+ {
+ FILE *pFile;DWORD size,rsize;char *buff;
+ pFile=fopen(t->fn,"rb");
+ if(!pFile)return;
+ fseek(pFile,0,SEEK_END);size=ftell(pFile);rewind(pFile);
+ buff=(char*)malloc(sizeof(char)*size);
+ if(!buff)return;
+ rsize=fread(buff,1,size,pFile);
+ if(rsize!=size)return;
+ int _w,_h;
+ px=decodeImage((BYTE*)buff,t->fn,size,_w,_h);
+ if(_w!=w||_h!=h){delete[] px;px=NULL;}
+ free(buff);
+ fclose(pFile);
+ }
+ glBindTexture(GL_TEXTURE_2D,tex);
+ glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_LOD,0.0f);
+ glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAX_LOD,0.0f);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_BASE_LEVEL,0);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_LEVEL,0);
+ compressed=false;//compression is unusable at this moment
+ const GLenum fmt=compressed?/*GL_COMPRESSED_RGBA_S3TC_DXT5_EXT*/GL_COMPRESSED_RGBA_ARB:GL_RGBA8;
+ glTexImage2D(GL_TEXTURE_2D,0,fmt,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,px);
+ glBindTexture(GL_TEXTURE_2D,primTex?(((glTexture*)primTex)->name):0);
+ if(fromfile)delete[] px;
+}
+SMTEX SMELT_IMPL::buildTexture(int w,int h,DWORD *px)
+{
+ glTexture *ret=new glTexture;
+ memset(ret,0,sizeof(glTexture));
+ ret->lost=true;
+ ret->rw=w;ret->rh=h;ret->px=px;
+ return (SMTEX)ret;
+}
+void SMELT_IMPL::configProjectionMatrix2D(int w,int h)
+{
+ memset(mproj,0,sizeof(mproj));
+ mproj[0]=2./w;
+ mproj[5]=2./h;
+ mproj[10]=-2.;
+ mproj[15]=1.;
+ mproj[12]=mproj[13]=mproj[14]=-1.;
+ glUniformMatrix4fv(loc_mproj,1,0,mproj);
+}
+void SMELT_IMPL::configProjectionMatrix3D(int w,int h,float fov)
+{
+ memset(mproj,0,sizeof(mproj));
+ float f=1./tanf(M_PI*fov/360.);
+ float ar=(float)w/(float)h;
+ float Near=0.1,Far=1000.;
+ mproj[0]=f/ar;mproj[5]=f;
+ mproj[10]=(Far+Near)/(Near-Far);mproj[11]=-1.0f;
+ mproj[14]=(2*Far*Near)/(Near-Far);
+ glUniformMatrix4fv(loc_mproj,1,0,mproj);
+}
+void SMELT_IMPL::batchOGL(bool endScene)
+{
+ if(vertexArray&&primcnt)
+ {
+ float twm=1.,thm=1.;
+ if(primTex)
+ {
+ glTexture *ptex=(glTexture*)primTex;
+ if(ptex->dw&&ptex->dh)
+ {
+ twm=(ptex->rw)/(float)(ptex->dw);
+ thm=(ptex->rh)/(float)(ptex->dh);
+ }
+ }
+ if(!tdmode)
+ {
+ float h=curTarget?curTarget->h:scrh;
+ for(int i=0;i<primcnt*primType;++i)
+ {
+ vertexArray[i].y=h-vertexArray[i].y;
+ vertexArray[i].z=-vertexArray[i].z;
+ }
+ }
+ for(int i=0;i<primcnt*primType;++i)
+ {
+ vertexArray[i].tx*=twm;
+ vertexArray[i].ty*=thm;
+ DWORD color=vertexArray[i].col;
+ BYTE *col=(BYTE*)&vertexArray[i].col;
+ BYTE a=((color>>24)&0xFF);
+ BYTE r=((color>>16)&0xFF);
+ BYTE g=((color>> 8)&0xFF);
+ BYTE b=((color>> 0)&0xFF);
+ col[0]=r;col[1]=g;
+ col[2]=b;col[3]=a;
+ }
+ glBindVertexArray(VertexArrayObject);
+ glBindBuffer(GL_ARRAY_BUFFER,VertexBufferObject);
+ glBufferData(GL_ARRAY_BUFFER,sizeof(smVertex)*primcnt*primType,vertexBuf,GL_STATIC_DRAW);
+ switch(primType)
+ {
+ case PRIM_LINES:
+ glDrawArrays(GL_LINES,0,2*primcnt);
+ break;
+ case PRIM_TRIANGLES:
+ glDrawArrays(GL_TRIANGLES,0,3*primcnt);
+ break;
+ case PRIM_QUADS:
+ glDrawElements(GL_TRIANGLES,6*primcnt,GL_UNSIGNED_SHORT,0);
+#if 0
+ for (int i=0;i<primcnt*6;i+=3)
+ {
+ printf("idxbuf:%d\n",indexBuf[i]);
+ printf("QUAD'S TRIANGLE:\n");
+#define printVertex(a) printf("(%.2f,%.2f,%.2f),0x%X,(%.2f,%.2f)\n",a.x,a.y,a.z,a.col,a.tx,a.ty);
+ printf("#%d: ",indexBuf[i+0]);printVertex(vertexBuf[indexBuf[i+0]]);
+ printf("#%d: ",indexBuf[i+1]);printVertex(vertexBuf[indexBuf[i+1]]);
+ printf("#%d: ",indexBuf[i+2]);printVertex(vertexBuf[indexBuf[i+2]]);
+#undef printVertex
+ }
+#endif
+ break;
+ }
+ glBindVertexArray(0);
+ primcnt=0;
+ }
+ if(vertexArray)vertexArray=endScene?0:vertexBuf;
+}
+void SMELT_IMPL::setBlend(int blend)
+{
+ if((blend&BLEND_ALPHABLEND)!=(primBlend&BLEND_ALPHABLEND))
+ {
+ if(blend&BLEND_ALPHABLEND)glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+ else glBlendFunc(blend&BLEND_COLORINV?GL_ONE_MINUS_DST_COLOR:GL_SRC_ALPHA,blend&BLEND_COLORINV?GL_ZERO:GL_ONE);
+ }
+ if((blend&BLEND_ZWRITE)!=(primBlend&BLEND_ZWRITE))
+ {
+ if(blend&BLEND_ZWRITE||tdmode)glDepthMask(GL_TRUE);
+ else glDepthMask(GL_FALSE);
+ }
+ if((blend&BLEND_COLORADD)!=(primBlend&BLEND_COLORADD))
+ {
+ if(blend&BLEND_COLORADD)glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_ADD);
+ else glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
+ }
+ if((blend&BLEND_COLORINV)!=(primBlend&BLEND_COLORINV))
+ {
+ if(blend&BLEND_COLORINV)glBlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ZERO);
+ else glBlendFunc(GL_SRC_ALPHA,blend&BLEND_ALPHABLEND?GL_ONE_MINUS_SRC_ALPHA:GL_ONE);
+ }
+ primBlend=blend;
+}
+void SMELT_IMPL::unloadGLEntryPoints()
+{
+}
+bool SMELT_IMPL::checkGLExtension(const char *extlist,const char *ext)
+{
+ return true;/*shit*
+ const char *ptr=strstr(extlist,ext);
+ if(ptr==NULL)return false;
+ const char endchar=ptr[strlen(ext)];
+ if((endchar=='\0')||(endchar==' '))return true;
+ return false;*/
+}
+bool SMELT_IMPL::loadGLEntryPoints()
+{
+ smLog("%s:" SLINE ": Initializing with OpenGL core profile...\n",GFX_GLFW_SRCFN);
+ pOpenGLDevice->have_base_opengl=true;
+ /*
+ * All OpenGL features utilized by SMELT are in the core profile
+ * for OpenGL >= 3.2. So this function is essentially useless.
+ */
+ glewExperimental=true;
+ GLenum glewret=glewInit();
+ if(glewret)
+ {
+ smLog("%s:" SLINE ": glewInit() failed with error %s\n",GFX_GLFW_SRCFN,glewGetErrorString(glewret));
+ pOpenGLDevice->have_base_opengl=false;
+ return false;
+ }
+ if (!pOpenGLDevice->have_base_opengl)
+ {
+ unloadGLEntryPoints();
+ return false;
+ }
+ smLog("%s:" SLINE ": GL_RENDERER: %s\n",GFX_GLFW_SRCFN,(const char *)glGetString(GL_RENDERER));
+ smLog("%s:" SLINE ": GL_VENDOR: %s\n",GFX_GLFW_SRCFN,(const char *)glGetString(GL_VENDOR));
+ smLog("%s:" SLINE ": GL_VERSION: %s\n",GFX_GLFW_SRCFN,(const char *)glGetString(GL_VERSION));
+ const char *verstr=(const char*)glGetString(GL_VERSION);
+ int maj=0,min=0;
+ sscanf(verstr,"%d.%d",&maj,&min);
+ if((maj<3)||((maj==3)&&(min<2)))
+ {
+ smLog("%s:" SLINE ": OpenGL implementation must be at least version 3.2.\n",GFX_GLFW_SRCFN);
+ unloadGLEntryPoints();
+ return false;
+ }
+ return true;
+}
+bool SMELT_IMPL::initOGL()
+{
+ primTex=0;emptyTex=0;
+ if(pOpenGLDevice){smLog("%s:" SLINE ": Multiple initialization!\n",GFX_GLFW_SRCFN);return false;}
+ pOpenGLDevice=new TOpenGLDevice;
+ if(!loadGLEntryPoints())return false;
+ smLog("%s:" SLINE ": Mode: %d x %d\n",GFX_GLFW_SRCFN,scrw,scrh);
+ vertexArray=NULL;textures=NULL;
+ ShaderProgram=vertshader=fragshader=0;
+ VertexBufferObject=VertexArrayObject=IndexBufferObject=0;
+ if(!confOGL())return false;
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ if(!curTarget)glfwSwapBuffers((GLFWwindow*)hwnd);
+ return true;
+}
+void SMELT_IMPL::finiOGL()
+{
+ while(textures)smTextureFree(textures->tex);
+ while(targets)smTargetFree((SMTRG)targets);
+ textures=NULL;targets=NULL;vertexArray=NULL;
+ delete[] vertexBuf;vertexBuf=NULL;
+ delete[] indexBuf;indexBuf=NULL;
+ if(pOpenGLDevice)
+ {
+ if(1)
+ {
+ if(VertexBufferObject!=0)
+ {
+ glBindVertexArray(VertexArrayObject);
+ glBindBuffer(GL_ARRAY_BUFFER,0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
+ glDeleteBuffers(1,&VertexBufferObject);
+ glDeleteBuffers(1,&IndexBufferObject);
+ glBindVertexArray(0);
+ glDeleteVertexArrays(1,&VertexArrayObject);
+ VertexArrayObject=0;
+ VertexBufferObject=0;
+ IndexBufferObject=0;
+ }
+ }
+ glDeleteProgram(ShaderProgram);
+ delete pOpenGLDevice;
+ pOpenGLDevice=NULL;
+ }
+}
+bool SMELT_IMPL::restOGL()
+{
+ if(!pOpenGLDevice)return false;
+ delete[] vertexBuf;vertexBuf=NULL;
+ delete[] indexBuf;indexBuf=NULL;
+ unloadGLEntryPoints();
+ if(!loadGLEntryPoints())return false;
+ if(!confOGL())return false;
+ //if()return();//TODO: rest func
+ return true;
+}
+bool SMELT_IMPL::confOGL()
+{
+ bindTexture(NULL);
+ DWORD ones=~0U;
+ if(!emptyTex)
+ {emptyTex=buildTexture(1,1,&ones);}
+ configTexture((glTexture*)emptyTex,1,1,&ones,false);
+ for(TTextureList *i=textures;i;i=i->next)
+ {
+ glTexture *t=(glTexture*)i->tex;
+ if(!t)continue;t->lost=true;t->name=0;
+ }
+ TRenderTargetList *target=targets;
+ while(target)
+ {
+ glTexture *tex=(glTexture*)target->tex;
+ bindTexture(tex);bindTexture(NULL);
+ buildTarget(target,tex?tex->name:0,target->w,target->h);
+ target=target->next;
+ }
+ int compret=0;
+ ShaderProgram=glCreateProgram();
+ vertshader=glCreateShader(GL_VERTEX_SHADER);
+ fragshader=glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(vertshader,1,&fixedfunc_pipeline_vsh,NULL);
+ glShaderSource(fragshader,1,&fixedfunc_pipeline_fsh,NULL);
+ glCompileShader(vertshader);
+ glGetShaderiv(vertshader,GL_COMPILE_STATUS,&compret);
+ //char log[1024];
+ //glGetShaderInfoLog(vertshader,1024,NULL,log);
+ //puts(log);
+ if(!compret)
+ smLog("%s:" SLINE ": Warning: Your shitty vertex shader failed to compile!\n",GFX_GLFW_SRCFN);
+ glCompileShader(fragshader);
+ glGetShaderiv(fragshader,GL_COMPILE_STATUS,&compret);
+ //glGetShaderInfoLog(fragshader,1024,NULL,log);
+ //puts(log);
+ if(!compret)
+ smLog("%s:" SLINE ": Warning: Your shitty fragment shader failed to compile!\n",GFX_GLFW_SRCFN);
+ glAttachShader(ShaderProgram,vertshader);
+ glAttachShader(ShaderProgram,fragshader);
+ glLinkProgram(ShaderProgram);
+ //glGetProgramInfoLog(ShaderProgram,1024,NULL,log);
+ //puts(log);
+ glGetProgramiv(ShaderProgram,GL_LINK_STATUS,&compret);
+ if(!compret)
+ smLog("%s:" SLINE ": Warning: Default shader linkage failure!\n",GFX_GLFW_SRCFN);
+ glDeleteShader(vertshader);
+ glDeleteShader(fragshader);
+ glUseProgram(ShaderProgram);
+ loc_tex=glGetUniformLocation(ShaderProgram,"tex");
+ loc_mmodv=glGetUniformLocation(ShaderProgram,"mmodv");
+ loc_mproj=glGetUniformLocation(ShaderProgram,"mproj");
+ vertexBuf=new smVertex[VERTEX_BUFFER_SIZE];
+ indexBuf=new GLushort[VERTEX_BUFFER_SIZE*6/4];
+ GLushort* indices=indexBuf;
+ for(int i=0,n=0;i<VERTEX_BUFFER_SIZE/4;++i)
+ {
+ *indices++=n;*indices++=n+1;
+ *indices++=n+2;*indices++=n+2;
+ *indices++=n+3;*indices++=n;
+ n+=4;
+ }
+ glGenBuffers(1,&VertexBufferObject);
+ glGenVertexArrays(1,&VertexArrayObject);
+ glGenBuffers(1,&IndexBufferObject);
+ glBindVertexArray(VertexArrayObject);
+ glBindBuffer(GL_ARRAY_BUFFER,VertexBufferObject);
+ glBufferData(GL_ARRAY_BUFFER,sizeof(smVertex)*VERTEX_BUFFER_SIZE,vertexBuf,GL_STATIC_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,IndexBufferObject);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(GLushort)*((VERTEX_BUFFER_SIZE*6)/4),indexBuf,GL_STATIC_DRAW);
+ glVertexAttribPointer(0,3,GL_FLOAT,0,sizeof(smVertex),(void*)offsetof(smVertex,x));//vp
+ glVertexAttribPointer(1,4,GL_UNSIGNED_BYTE,1,sizeof(smVertex),(void*)offsetof(smVertex,col));//vc
+ glVertexAttribPointer(2,2,GL_FLOAT,0,sizeof(smVertex),(void*)offsetof(smVertex,tx));//vtc
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+ glEnableVertexAttribArray(2);
+ glBindVertexArray(0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT,1);
+ glPixelStorei(GL_PACK_ALIGNMENT,1);
+ glEnable(GL_SCISSOR_TEST);
+ glDisable(GL_CULL_FACE);
+ glActiveTexture(GL_TEXTURE0);
+ glDepthFunc(GL_GEQUAL);
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+ filtermode=TFLT_LINEAR;
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+ //GL_REPEAT doesn't work with non-pot textures...
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_R,GL_CLAMP_TO_EDGE);
+ primcnt=0;primType=PRIM_QUADS;primBlend=BLEND_ALPHABLEND;primTex=0;
+ glScissor(0,0,scrw,scrh);
+ glViewport(0,0,scrw,scrh);
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ configProjectionMatrix2D(scrw,scrh);
+ smMatrix Mmodv;Mmodv.loadIdentity();
+ memcpy(mmodv,Mmodv.m,sizeof(mmodv));
+ glUniformMatrix4fv(loc_mmodv,1,0,mmodv);
+ return true;
+}
diff --git a/smelt/glfw_m/inp_glfw.cpp b/smelt/glfw_m/inp_glfw.cpp
new file mode 100644
index 0000000..4bbd43e
--- /dev/null
+++ b/smelt/glfw_m/inp_glfw.cpp
@@ -0,0 +1,204 @@
+// -*- C++ -*-
+/*
+ * Simple MultimEdia LiTerator(SMELT)
+ * by Chris Xiong 2015
+ * Input manager implementation
+ *
+ * WARNING: This library is in development and interfaces would be very
+ * unstable.
+ *
+ */
+#include "smelt_internal.hpp"
+//static const char* INP_GLFW_SRCFN="smelt/glfw/inp_glfw.cpp";
+bool SMELT_IMPL::smGetInpEvent(smInpEvent *e)
+{
+ TInputEventList *eptr;
+ if(inpQueue)
+ {
+ eptr=inpQueue;memcpy(e,&eptr->e,sizeof(smInpEvent));
+ inpQueue=inpQueue->next;delete eptr;return true;
+ }
+ return false;
+}
+void SMELT_IMPL::smGetMouse2f(float *x,float *y){*x=posx,*y=posy;}
+void SMELT_IMPL::smSetMouse2f(float x,float y){glfwSetInputMode((GLFWwindow*)hwnd,x,y);}
+void SMELT_IMPL::smSetMouseGrab(bool enabled){glfwSetInputMode((GLFWwindow*)hwnd,GLFW_CURSOR,enabled?GLFW_CURSOR_DISABLED:GLFW_CURSOR_NORMAL);}
+int SMELT_IMPL::smGetWheel(){return posz;}
+bool SMELT_IMPL::smIsMouseOver(){return mouseOver;}
+int SMELT_IMPL::smGetKey(){return lkey;}
+int SMELT_IMPL::smGetKeyState(int key)
+{
+ if(!(keyz[key]&4)&&keylst[key])return SMKST_RELEASE;
+ if(!(keyz[key]&4))return SMKST_NONE;
+ if((keyz[key]&4)&&!keylst[key])return SMKST_HIT;
+ return SMKST_KEEP;
+}
+
+void SMELT_IMPL::buildEvent(int type,int k,int scan,int flags,int x,int y)
+{
+ TInputEventList *last,*eptr=new TInputEventList;
+ eptr->e.type=type;eptr->e.chcode=0;
+ int ptx=x,pty=y;
+ if(type==INPUT_KEYDOWN)
+ {
+ k=GLFWKeyToSMKey(k);
+ if((k<0)||(k>(int)(sizeof(keyz)/sizeof(keyz[0]))))return;
+ keyz[k]|=4;if(!(flags&SMINP_REPEAT))keyz[k]|=1;
+ eptr->e.chcode=(k>=32&&k<=127)?k:0;
+ }
+ if(type==INPUT_KEYUP)
+ {
+ k=GLFWKeyToSMKey(k);
+ if((k<0)||(k>(int)(sizeof(keyz)/sizeof(keyz[0]))))return;
+ keyz[k]&=~4;keyz[k]|=2;
+ eptr->e.chcode=(k>=32&&k<=127)?k:0;
+ }
+ if(type==INPUT_MOUSEWHEEL)
+ {eptr->e.sccode=0;eptr->e.wheel=k;}
+ else{eptr->e.sccode=k;eptr->e.wheel=0;}
+ if(type==INPUT_MBUTTONDOWN){keyz[k]|=1|4;captured=true;}
+ if(type==INPUT_MBUTTONUP){keyz[k]|=2;keyz[k]&=~4;ptx=(int)posx;pty=(int)posy;captured=false;}
+ if(keymods&GLFW_MOD_SHIFT)flags|=SMINP_SHIFT;
+ if(keymods&GLFW_MOD_CONTROL)flags|=SMINP_CTRL;
+ if(keymods&GLFW_MOD_ALT)flags|=SMINP_ALT;
+ //if(keymods&KMOD_CAPS)flags|=SMINP_CAPSLOCK;
+ //if(keymods&KMOD_MODE)flags|=SMINP_SCROLLLOCK;
+ //if(keymods&KMOD_NUM)flags|=SMINP_NUMLOCK;
+ eptr->e.flag=flags;
+ if(ptx==-1){eptr->e.x=posx;eptr->e.y=posy;}
+ else
+ {
+ ptx<0?ptx=0:0;pty<0?pty=0:0;
+ ptx>=scrw?ptx=scrw-1:0;pty>=scrh?pty=scrh-1:0;
+ eptr->e.x=(float)ptx;eptr->e.y=(float)pty;
+ }
+ eptr->next=0;
+ if(!inpQueue)inpQueue=eptr;
+ else
+ {
+ last=inpQueue;
+ while(last->next)last=last->next;
+ last->next=eptr;
+ }
+ if(eptr->e.type==INPUT_KEYDOWN||eptr->e.type==INPUT_MBUTTONDOWN)
+ lkey=eptr->e.sccode;
+ if(eptr->e.type==INPUT_MOUSEMOVE)posx=eptr->e.x,posy=eptr->e.y;
+ if(eptr->e.type==INPUT_MOUSEWHEEL)posz=eptr->e.wheel;
+}
+void SMELT_IMPL::initInput(){posx=posy=.0;memset(keyz,0,sizeof(keyz));}
+void SMELT_IMPL::clearQueue()
+{
+ TInputEventList *nxt,*eptr=inpQueue;
+ for(unsigned i=0;i<sizeof(keyz)/sizeof(keyz[0]);++i)keyz[i]&=~3;
+ while(eptr){nxt=eptr->next;delete eptr;eptr=nxt;}
+ inpQueue=NULL;posz=0;lkey=0;
+}
+int SMELT_IMPL::GLFWKeyToSMKey(int glfwkey)
+{
+ switch (glfwkey)
+ {
+ case GLFW_KEY_ESCAPE: return SMK_ESCAPE;
+ case GLFW_KEY_BACKSPACE: return SMK_BACKSPACE;
+ case GLFW_KEY_TAB: return SMK_TAB;
+ case GLFW_KEY_ENTER: return SMK_ENTER;
+ case GLFW_KEY_SPACE: return SMK_SPACE;
+ case GLFW_KEY_LEFT_SHIFT: return SMK_SHIFT;
+ case GLFW_KEY_RIGHT_SHIFT: return SMK_SHIFT;
+ case GLFW_KEY_LEFT_CONTROL: return SMK_CTRL;
+ case GLFW_KEY_RIGHT_CONTROL: return SMK_CTRL;
+ case GLFW_KEY_LEFT_ALT: return SMK_ALT;
+ case GLFW_KEY_RIGHT_ALT: return SMK_ALT;
+ case GLFW_KEY_LEFT_SUPER: return SMK_LWIN;
+ case GLFW_KEY_RIGHT_SUPER: return SMK_RWIN;
+ case GLFW_KEY_PAUSE: return SMK_PAUSE;
+ case GLFW_KEY_CAPS_LOCK: return SMK_CAPSLOCK;
+ case GLFW_KEY_NUM_LOCK: return SMK_NUMLOCK;
+ case GLFW_KEY_SCROLL_LOCK: return SMK_SCROLLLOCK;
+ case GLFW_KEY_PAGE_UP: return SMK_PGUP;
+ case GLFW_KEY_PAGE_DOWN: return SMK_PGDN;
+ case GLFW_KEY_HOME: return SMK_HOME;
+ case GLFW_KEY_END: return SMK_END;
+ case GLFW_KEY_INSERT: return SMK_INSERT;
+ case GLFW_KEY_DELETE: return SMK_DELETE;
+ case GLFW_KEY_LEFT: return SMK_LEFT;
+ case GLFW_KEY_UP: return SMK_UP;
+ case GLFW_KEY_RIGHT: return SMK_RIGHT;
+ case GLFW_KEY_DOWN: return SMK_DOWN;
+ case GLFW_KEY_0: return SMK_0;
+ case GLFW_KEY_1: return SMK_1;
+ case GLFW_KEY_2: return SMK_2;
+ case GLFW_KEY_3: return SMK_3;
+ case GLFW_KEY_4: return SMK_4;
+ case GLFW_KEY_5: return SMK_5;
+ case GLFW_KEY_6: return SMK_6;
+ case GLFW_KEY_7: return SMK_7;
+ case GLFW_KEY_8: return SMK_8;
+ case GLFW_KEY_9: return SMK_9;
+ case GLFW_KEY_A: return SMK_A;
+ case GLFW_KEY_B: return SMK_B;
+ case GLFW_KEY_C: return SMK_C;
+ case GLFW_KEY_D: return SMK_D;
+ case GLFW_KEY_E: return SMK_E;
+ case GLFW_KEY_F: return SMK_F;
+ case GLFW_KEY_G: return SMK_G;
+ case GLFW_KEY_H: return SMK_H;
+ case GLFW_KEY_I: return SMK_I;
+ case GLFW_KEY_J: return SMK_J;
+ case GLFW_KEY_K: return SMK_K;
+ case GLFW_KEY_L: return SMK_L;
+ case GLFW_KEY_M: return SMK_M;
+ case GLFW_KEY_N: return SMK_N;
+ case GLFW_KEY_O: return SMK_O;
+ case GLFW_KEY_P: return SMK_P;
+ case GLFW_KEY_Q: return SMK_Q;
+ case GLFW_KEY_R: return SMK_R;
+ case GLFW_KEY_S: return SMK_S;
+ case GLFW_KEY_T: return SMK_T;
+ case GLFW_KEY_U: return SMK_U;
+ case GLFW_KEY_V: return SMK_V;
+ case GLFW_KEY_W: return SMK_W;
+ case GLFW_KEY_X: return SMK_X;
+ case GLFW_KEY_Y: return SMK_Y;
+ case GLFW_KEY_Z: return SMK_Z;
+ case GLFW_KEY_MINUS: return SMK_MINUS;
+ case GLFW_KEY_EQUAL: return SMK_EQUALS;
+ case GLFW_KEY_BACKSLASH: return SMK_BACKSLASH;
+ case GLFW_KEY_LEFT_BRACKET: return SMK_LBRACKET;
+ case GLFW_KEY_RIGHT_BRACKET: return SMK_RBRACKET;
+ case GLFW_KEY_SEMICOLON: return SMK_SEMICOLON;
+ case GLFW_KEY_APOSTROPHE: return SMK_APOSTROPHE;
+ case GLFW_KEY_COMMA: return SMK_COMMA;
+ case GLFW_KEY_PERIOD: return SMK_PERIOD;
+ case GLFW_KEY_SLASH: return SMK_SLASH;
+ case GLFW_KEY_KP_0: return SMK_NUMPAD0;
+ case GLFW_KEY_KP_1: return SMK_NUMPAD1;
+ case GLFW_KEY_KP_2: return SMK_NUMPAD2;
+ case GLFW_KEY_KP_3: return SMK_NUMPAD3;
+ case GLFW_KEY_KP_4: return SMK_NUMPAD4;
+ case GLFW_KEY_KP_5: return SMK_NUMPAD5;
+ case GLFW_KEY_KP_6: return SMK_NUMPAD6;
+ case GLFW_KEY_KP_7: return SMK_NUMPAD7;
+ case GLFW_KEY_KP_8: return SMK_NUMPAD8;
+ case GLFW_KEY_KP_9: return SMK_NUMPAD9;
+ case GLFW_KEY_KP_MULTIPLY: return SMK_MULTIPLY;
+ case GLFW_KEY_KP_DIVIDE: return SMK_DIVIDE;
+ case GLFW_KEY_KP_ADD: return SMK_ADD;
+ case GLFW_KEY_KP_SUBTRACT: return SMK_SUBTRACT;
+ case GLFW_KEY_KP_DECIMAL: return SMK_DECIMAL;
+ case GLFW_KEY_KP_ENTER:return SMK_ENTER;
+ case GLFW_KEY_F1: return SMK_F1;
+ case GLFW_KEY_F2: return SMK_F2;
+ case GLFW_KEY_F3: return SMK_F3;
+ case GLFW_KEY_F4: return SMK_F4;
+ case GLFW_KEY_F5: return SMK_F5;
+ case GLFW_KEY_F6: return SMK_F6;
+ case GLFW_KEY_F7: return SMK_F7;
+ case GLFW_KEY_F8: return SMK_F8;
+ case GLFW_KEY_F9: return SMK_F9;
+ case GLFW_KEY_F10: return SMK_F10;
+ case GLFW_KEY_F11: return SMK_F11;
+ case GLFW_KEY_F12: return SMK_F12;
+ default: return -1;
+ }
+ return -1;
+}
diff --git a/smelt/glfw_m/makefile b/smelt/glfw_m/makefile
new file mode 100644
index 0000000..d487454
--- /dev/null
+++ b/smelt/glfw_m/makefile
@@ -0,0 +1,27 @@
+CC= g++
+CXXFLAGS= -c -g -O2 -std=c++11 -Wall -I../../include -D_LINUX -fPIC
+TARGET= libsmelt.a
+
+all: objects-normal archive-normal
+
+dumb: CXXFLAGS += -DENABLE_DUMB
+dumb: TARGET= libsmelt-dumb.a
+dumb: all
+
+objects-normal: gfx inp sfx sys
+
+gfx:
+ $(CC) gfx_glfw.cpp $(CXXFLAGS)
+sfx:
+ $(CC) sfx_oal.cpp $(CXXFLAGS)
+inp:
+ $(CC) inp_glfw.cpp $(CXXFLAGS)
+sys:
+ $(CC) sys_glfw.cpp $(CXXFLAGS)
+archive-normal:
+ $(AR) rcs $(TARGET) gfx_glfw.o sfx_oal.o inp_glfw.o sys_glfw.o
+
+clean:
+ rm -f *.o
+clean-all: clean
+ rm *.a
diff --git a/smelt/glfw_m/sfx_dumb.cpp b/smelt/glfw_m/sfx_dumb.cpp
new file mode 100644
index 0000000..4464da1
--- /dev/null
+++ b/smelt/glfw_m/sfx_dumb.cpp
@@ -0,0 +1,63 @@
+// -*- C++ -*-
+/*
+ * Simple MultimEdia LiTerator(SMELT)
+ * by Chris Xiong 2015
+ * SFX dumb implementation
+ * This dumb implementation has everything stubbed, useful if you don't
+ * use the audio routines here.
+ *
+ * WARNING: This file is not intended to be used on its own!
+ *
+ */
+
+SMSFX SMELT_IMPL::smSFXLoad(const char *path)
+{return 0;}
+SMSFX SMELT_IMPL::smSFXLoadFromMemory(const char *ptr,DWORD size)
+{return 0;}
+SMCHN SMELT_IMPL::smSFXPlay(SMSFX fx,int vol,int pan,float pitch,bool loop)
+{return 0;}
+float SMELT_IMPL::smSFXGetLengthf(SMSFX fx)
+{return 0.0;}
+DWORD SMELT_IMPL::smSFXGetLengthd(SMSFX fx)
+{return -1;}
+void SMELT_IMPL::smSFXSetLoopPoint(SMSFX fx,DWORD l,DWORD r)
+{}
+void SMELT_IMPL::smSFXFree(SMSFX fx)
+{}
+void SMELT_IMPL::smChannelVol(SMCHN chn,int vol)
+{}
+void SMELT_IMPL::smChannelPan(SMCHN chn,int pan)
+{}
+void SMELT_IMPL::smChannelPitch(SMCHN chn,float pitch)
+{}
+void SMELT_IMPL::smChannelPause(SMCHN chn)
+{}
+void SMELT_IMPL::smChannelResume(SMCHN chn)
+{}
+void SMELT_IMPL::smChannelStop(SMCHN chn)
+{}
+void SMELT_IMPL::smChannelPauseAll()
+{}
+void SMELT_IMPL::smChannelResumeAll()
+{}
+void SMELT_IMPL::smChannelStopAll()
+{}
+bool SMELT_IMPL::smChannelIsPlaying(SMCHN chn)
+{return false;}
+float SMELT_IMPL::smChannelGetPosf(SMCHN chn)
+{return -1.;}
+void SMELT_IMPL::smChannelSetPosf(SMCHN chn,float pos)
+{}
+int SMELT_IMPL::smChannelGetPosd(SMCHN chn)
+{return -1;}
+void SMELT_IMPL::smChannelSetPosd(SMCHN chn,int pos)
+{}
+
+bool SMELT_IMPL::initOAL()
+{
+ smLog("%s:" SLINE ": I'm dumb!\n",SFX_OAL_SRCFN);
+ return true;
+}
+void SMELT_IMPL::finiOAL()
+{
+}
diff --git a/smelt/glfw_m/sfx_oal.cpp b/smelt/glfw_m/sfx_oal.cpp
new file mode 100644
index 0000000..5b8d135
--- /dev/null
+++ b/smelt/glfw_m/sfx_oal.cpp
@@ -0,0 +1,368 @@
+// -*- C++ -*-
+/*
+ * Simple MultimEdia LiTerator(SMELT)
+ * by Chris Xiong 2015
+ * GFX implementation based on OpenAL
+ *
+ * WARNING: This library is in development and interfaces would be very
+ * unstable.
+ *
+ */
+#include "smelt_internal.hpp"
+
+static const char* SFX_OAL_SRCFN="smelt/glfw/sfx_oal.cpp";
+#ifndef ENABLE_DUMB
+#ifdef ENABLE_OGG_SUPPORT
+struct oggdata{const BYTE *data;DWORD size,pos;};
+static void* readVorbis(const BYTE *data,const DWORD size, ALsizei *decomp_size,ALenum *fmt,ALsizei *freq);
+#endif
+static void* readRiffWv(const BYTE *data,const DWORD size, ALsizei *decomp_size,ALenum *fmt,ALsizei *freq);
+SMSFX SMELT_IMPL::smSFXLoad(const char *path)
+{
+ FILE *pFile;DWORD size,rsize; char *buff;
+ pFile=fopen(path,"rb");
+ if(!pFile)return 0;
+ fseek(pFile,0,SEEK_END);size=ftell(pFile);rewind(pFile);
+ buff=(char*)malloc(sizeof(char)*size);
+ if(!buff)return 0;
+ rsize=fread(buff,1,size,pFile);
+ if(rsize!=size)return 0;
+ SMSFX ret=smSFXLoadFromMemory(buff,size);
+ free(buff);
+ fclose(pFile);
+ return ret;
+}
+SMSFX SMELT_IMPL::smSFXLoadFromMemory(const char *ptr,DWORD size)
+{
+ if(pOpenALDevice&&!mute)
+ {
+#ifdef ENABLE_OGG_SUPPORT
+ bool isOgg=size>4&&ptr[0]=='O'&&ptr[1]=='g'&&ptr[2]=='g'&&ptr[3]=='S';
+#endif
+ void *decompdata=NULL,*decomp=NULL;
+ ALsizei decompsize=0,freq=0;
+ ALenum fmt=AL_FORMAT_STEREO16;
+ decompdata=readRiffWv((const BYTE*)ptr,size,&decompsize,&fmt,&freq);
+#ifdef ENABLE_OGG_SUPPORT
+ if(!decompdata)
+ {
+ if(!isOgg)return 0;
+ else decompdata=readVorbis((const BYTE*)ptr,size,&decompsize,&fmt,&freq);
+ }
+#endif
+ if(!decompdata)return 0;
+ decomp=decompdata;
+ ALuint buff=0;alGenBuffers(1,&buff);
+ alBufferData(buff,fmt,decomp,decompsize,freq);
+ free(decompdata);return(SMSFX)buff;
+ }
+ return 0;
+}
+SMCHN SMELT_IMPL::smSFXPlay(SMSFX fx,int vol,int pan,float pitch,bool loop)
+{
+ if(pOpenALDevice)
+ {
+ ALuint src=getSource();
+ if(src)
+ {
+ if(vol<0)vol=0;if(vol>100)vol=100;
+ if(pan<-100)pan=-100;if(pan>100)pan=100;
+ alSourceStop(src);
+ alSourcei(src,AL_BUFFER,(ALint)fx);
+ alSourcef(src,AL_GAIN,((ALfloat)vol)/100.);
+ alSourcef(src,AL_PITCH,pitch);
+ alSource3f(src,AL_POSITION,((ALfloat)pan)/100.,.0,.0);
+ alSourcei(src,AL_LOOPING,loop?AL_TRUE:AL_FALSE);
+ alSourcePlay(src);
+ }
+ return src;
+ }
+ return 0;
+}
+float SMELT_IMPL::smSFXGetLengthf(SMSFX fx)
+{
+ ALuint buff=(ALuint)fx;
+ ALint freq;
+ DWORD lend=smSFXGetLengthd(fx);
+ alGetBufferi(buff,AL_FREQUENCY,&freq);
+ float ret=lend/(float)freq;
+ return ret;
+}
+DWORD SMELT_IMPL::smSFXGetLengthd(SMSFX fx)
+{
+ if(pOpenALDevice)
+ {
+ ALint size_b,chnc,bit;
+ ALuint buff=(ALuint)fx;
+ alGetBufferi(buff,AL_SIZE,&size_b);
+ alGetBufferi(buff,AL_CHANNELS,&chnc);
+ alGetBufferi(buff,AL_BITS,&bit);
+ DWORD ret=size_b*8/(chnc*bit);
+ return ret;
+ }
+ return -1;
+}
+void SMELT_IMPL::smSFXSetLoopPoint(SMSFX fx,DWORD l,DWORD r)
+{
+ if(pOpenALDevice)
+ {
+ ALint pt[2];pt[0]=l;pt[1]=r;
+ alBufferiv((ALuint)fx,AL_LOOP_POINTS_SOFT,pt);
+ }
+}
+void SMELT_IMPL::smSFXFree(SMSFX fx)
+{
+ if(pOpenALDevice)
+ {
+ ALuint buff=(ALuint)fx;
+ alDeleteBuffers(1,&buff);
+ }
+}
+void SMELT_IMPL::smChannelVol(SMCHN chn,int vol)
+{
+ if(pOpenALDevice)
+ {
+ if(vol<0)vol=0;if(vol>100)vol=100;
+ alSourcef((ALuint)chn,AL_GAIN,((ALfloat)vol)/100.);
+ }
+}
+void SMELT_IMPL::smChannelPan(SMCHN chn,int pan)
+{
+ if(pOpenALDevice)
+ {
+ if(pan<-100)pan=-100;if(pan>100)pan=100;
+ alSource3f((ALuint)chn,AL_POSITION,((ALfloat)pan)/100.,.0,.0);
+ }
+}
+void SMELT_IMPL::smChannelPitch(SMCHN chn,float pitch)
+{
+ if(pOpenALDevice)alSourcef((ALuint)chn,AL_PITCH,pitch);
+}
+void SMELT_IMPL::smChannelPause(SMCHN chn)
+{
+ if(pOpenALDevice)alSourcePause((ALuint)chn);
+}
+void SMELT_IMPL::smChannelResume(SMCHN chn)
+{
+ if(pOpenALDevice)alSourcePlay((ALuint)chn);
+}
+void SMELT_IMPL::smChannelStop(SMCHN chn)
+{
+ if(pOpenALDevice)alSourceStop((ALuint)chn);
+}
+void SMELT_IMPL::smChannelPauseAll()
+{
+ if(pOpenALDevice)
+ {
+ ALCcontext *ctx=alcGetCurrentContext();
+ alcSuspendContext(ctx);
+ }
+}
+void SMELT_IMPL::smChannelResumeAll()
+{
+ if(pOpenALDevice)
+ {
+ ALCcontext *ctx=alcGetCurrentContext();
+ alcProcessContext(ctx);
+ }
+}
+void SMELT_IMPL::smChannelStopAll()
+{
+ if(pOpenALDevice)
+ {
+ for(int i=0;i<scnt;++i)alSourceStop(src[i]);
+ }
+}
+bool SMELT_IMPL::smChannelIsPlaying(SMCHN chn)
+{
+ if(pOpenALDevice)
+ {
+ ALint state=AL_STOPPED;
+ alGetSourceiv((ALuint)chn,AL_SOURCE_STATE,&state);
+ return state==AL_PLAYING;
+ }
+ return false;
+}
+float SMELT_IMPL::smChannelGetPosf(SMCHN chn)
+{
+ if(pOpenALDevice)
+ {
+ ALfloat ret;
+ alGetSourcef((ALuint)chn,AL_SEC_OFFSET,&ret);
+ return (float)ret;
+ }
+ return -1.;
+}
+void SMELT_IMPL::smChannelSetPosf(SMCHN chn,float pos)
+{
+ if(pOpenALDevice)alSourcef((ALuint)chn,AL_SEC_OFFSET,(ALfloat)pos);
+}
+int SMELT_IMPL::smChannelGetPosd(SMCHN chn)
+{
+ if(pOpenALDevice)
+ {
+ ALint ret;
+ alGetSourcei((ALuint)chn,AL_SAMPLE_OFFSET,&ret);
+ return (int)ret;
+ }
+ return -1;
+}
+void SMELT_IMPL::smChannelSetPosd(SMCHN chn,int pos)
+{
+ if(pOpenALDevice)alSourcei((ALuint)chn,AL_SAMPLE_OFFSET,(ALint)pos);
+}
+
+#ifdef ENABLE_OGG_SUPPORT
+static size_t oggRead(void *ptr,size_t size,size_t nmemb,void *ds)
+{
+ oggdata *data=(oggdata*)ds;
+ const DWORD avail=data->size-data->pos;
+ size_t want=nmemb*size;
+ if(want>avail)want=avail-(avail%size);
+ if(want>0)
+ {
+ memcpy(ptr,data->data+data->pos,want);
+ data->pos+=want;
+ }
+ return want/size;
+}
+static int oggSeek(void *ds,ogg_int64_t offset,int whence)
+{
+ oggdata *data=(oggdata*)ds;
+ ogg_int64_t pos=0;
+ switch(whence)
+ {
+ case SEEK_SET:pos=offset;break;
+ case SEEK_CUR:pos=((ogg_int64_t)data->pos)+offset;break;
+ case SEEK_END:pos=((ogg_int64_t)data->size)+offset;break;
+ default: return -1;
+ }
+ if((pos<0)||(pos>((ogg_int64_t)data->size)))return -1;
+ data->pos=(DWORD)pos;
+ return 0;
+}
+static long oggLocate(void *ds)
+{
+ oggdata* data=(oggdata*)ds;
+ return (long)data->pos;
+}
+static int oggClose(void *ds){return 0;}
+
+static ov_callbacks oggrt={oggRead,oggSeek,oggClose,oggLocate};
+static void* readVorbis(const BYTE *data,const DWORD size, ALsizei *decomp_size,ALenum *fmt,ALsizei *freq)
+{
+ oggdata adata={data,size,0};
+ OggVorbis_File vf;
+ memset(&vf,0,sizeof(vf));
+ if(ov_open_callbacks(&adata,&vf,NULL,0,oggrt)==0)
+ {
+ int bs=0;
+ vorbis_info *info=ov_info(&vf,-1);
+ *decomp_size=0;
+ *fmt=(info->channels==1)?AL_FORMAT_MONO16:AL_FORMAT_STEREO16;
+ *freq=info->rate;
+ if(!(info->channels==1||info->channels==2)){ov_clear(&vf);return NULL;}
+ char buf[1024*16];long rc=0;size_t allocated=1024*16;
+ BYTE *ret=(ALubyte*)malloc(allocated);
+ while((rc=ov_read(&vf,buf,sizeof(buf),0,2,1,&bs))!=0)
+ {
+ if(rc>0)
+ {
+ *decomp_size+=rc;
+ if((unsigned)*decomp_size>=allocated)
+ {
+ allocated<<=1;
+ ALubyte *tmp=(ALubyte*)realloc(ret,allocated);
+ if(!tmp){free(ret);ret=NULL;break;}
+ ret=tmp;
+ }
+ memcpy(ret+(*decomp_size-rc),buf,rc);
+ }
+ }
+ ov_clear(&vf);
+ return ret;
+ }
+ return NULL;
+}
+#endif
+static void* readRiffWv(const BYTE *data,const DWORD size, ALsizei *decomp_size,ALenum *fmt,ALsizei *freq)
+{
+ if(data[0x0]!='R'||data[0x1]!='I'||data[0x2]!='F'||data[0x3]!='F')return NULL;
+ if(data[0x8]!='W'||data[0x9]!='A'||data[0xA]!='V'||data[0xB]!='E')return NULL;
+ if(data[0xC]!='f'||data[0xD]!='m'||data[0xE]!='t'||data[0xF]!=' ')return NULL;
+ if(data[0x16]==2)
+ {
+ if(data[0x22]==16)*fmt=AL_FORMAT_STEREO16;
+ else if(data[0x22]==8)*fmt=AL_FORMAT_STEREO8;
+ else return NULL;
+ }
+ else if(data[0x16]==1)
+ {
+ if(data[0x22]==16)*fmt=AL_FORMAT_MONO16;
+ else if(data[0x22]==8)*fmt=AL_FORMAT_MONO8;
+ else return NULL;
+ }else return NULL;
+ *decomp_size=(ALsizei)(data[0x28]|(data[0x29]<<8L)|(data[0x2A]<<16L)|(data[0x2B]<<24L));
+ *freq=(ALsizei)(data[0x18]|(data[0x19]<<8L)|(data[0x1A]<<16L)|(data[0x1B]<<24L));
+ ALubyte *ret=(ALubyte*)malloc(*decomp_size);
+ memcpy(ret,data+44,*decomp_size);
+ return ret;
+}
+ALuint SMELT_IMPL::getSource()
+{
+ for(int i=0;i<scnt;++i)
+ {
+ ALint state=AL_PLAYING;
+ alGetSourceiv(src[i],AL_SOURCE_STATE,&state);
+ if(!(state==AL_PLAYING||state==AL_PAUSED))return src[i];
+ }
+ if(scnt>=SRC_MAX)return 0;
+ ALuint ret=0;
+ alGenSources(1,&ret);if(!ret)return 0;
+ src[scnt++]=ret;return ret;
+}
+bool SMELT_IMPL::initOAL()
+{
+ if(pOpenALDevice)return true;
+ scnt=0;memset(src,0,sizeof(src));
+ smLog("%s:" SLINE ": Initializing OpenAL...\n",SFX_OAL_SRCFN);
+ ALCdevice *dev=alcOpenDevice(NULL);
+ if(!dev)
+ {
+ smLog("%s:" SLINE ": alcOpenDevice() failed.\n",SFX_OAL_SRCFN);
+ return mute=true;
+ }
+ ALint caps[]={ALC_FREQUENCY,44100,0};
+ ALCcontext *ctx=alcCreateContext(dev,caps);
+ if(!ctx)
+ {
+ smLog("%s:" SLINE ": alcCreateContext() failed.\n",SFX_OAL_SRCFN);
+ return mute=true;
+ }
+ alcMakeContextCurrent(ctx);alcProcessContext(ctx);
+ smLog("%s:" SLINE ": Done OpenAL initialization\n",SFX_OAL_SRCFN);
+ smLog("%s:" SLINE ": AL_VENDOR: %s\n",SFX_OAL_SRCFN,(char*)alGetString(AL_VENDOR));
+ smLog("%s:" SLINE ": AL_RENDERER: %s\n",SFX_OAL_SRCFN,(char*)alGetString(AL_RENDERER));
+ smLog("%s:" SLINE ": AL_VERSION: %s\n",SFX_OAL_SRCFN,(char*)alGetString(AL_VERSION));
+ const char* ext=(const char*)alGetString(AL_EXTENSIONS);
+ lpp=strstr(ext,"AL_SOFT_loop_points")!=NULL;
+ if(!lpp)smLog("%s:" SLINE ": Warning: loop points not supported. Please recompile with OpenAL Soft.\n",SFX_OAL_SRCFN);
+ pOpenALDevice=(void*)dev;
+ return true;
+}
+void SMELT_IMPL::finiOAL()
+{
+ if(pOpenALDevice)
+ {
+ for(int i=0;i<scnt;++i)alSourceStop(src[i]);
+ alDeleteSources(scnt,src);scnt=0;memset(src,0,sizeof(src));
+ ALCcontext *ctx=alcGetCurrentContext();
+ ALCdevice *dev=alcGetContextsDevice(ctx);
+ alcMakeContextCurrent(NULL);
+ alcSuspendContext(ctx);alcDestroyContext(ctx);
+ alcCloseDevice(dev);pOpenALDevice=NULL;
+ }
+}
+#else
+#include "sfx_dumb.cpp"
+#endif //ifndef ENABLE_DUMB
diff --git a/smelt/glfw_m/smelt_config.hpp b/smelt/glfw_m/smelt_config.hpp
new file mode 100644
index 0000000..d215d36
--- /dev/null
+++ b/smelt/glfw_m/smelt_config.hpp
@@ -0,0 +1,30 @@
+// -*- C++ -*-
+/*
+ * Simple MultimEdia LiTerator(SMELT)
+ * by Chris Xiong 2015
+ * Configuration header for SDL version
+ *
+ * Modify this header according to your own requirements.
+ *
+ */
+
+/*
+ * Maximum audio channels.
+ * (How many audio can be played simultaneously?)
+ */
+#define SRC_MAX 128
+
+/*
+ * Vertex buffer size.
+ */
+#define VERTEX_BUFFER_SIZE 4000
+
+/*
+ * Uncomment this to build a "dumb" version with no audio routines.
+ */
+//#define ENABLE_DUMB
+
+/*
+ * Undefine this to disable ogg support.
+ */
+#define ENABLE_OGG_SUPPORT
diff --git a/smelt/glfw_m/smelt_internal.hpp b/smelt/glfw_m/smelt_internal.hpp
new file mode 100644
index 0000000..2619b12
--- /dev/null
+++ b/smelt/glfw_m/smelt_internal.hpp
@@ -0,0 +1,249 @@
+// -*- C++ -*-
+/*
+ * Simple MultimEdia LiTerator(SMELT)
+ * by Chris Xiong 2015
+ * Internal header for GLFW version
+ *
+ * WARNING: This library is in development and interfaces would be very
+ * unstable.
+ *
+ */
+#ifndef SMELT_INTERNAL_H
+#define SMELT_INTERNAL_H
+
+#include "../../include/smelt.hpp"
+#include "smelt_config.hpp"
+#include <cstdio>
+#include <cstring>
+#include <cstdlib>
+#include <ctime>
+#include <cstdarg>
+#include <dirent.h>
+#include <unistd.h>
+#define GLEW_STATIC
+#include <GL/glew.h>
+#include <GLFW/glfw3.h>
+#ifndef ENABLE_DUMB
+#include <AL/al.h>
+#include <AL/alc.h>
+#include <AL/alext.h>
+#ifdef ENABLE_OGG_SUPPORT
+#include <ogg/ogg.h>
+#include <vorbis/vorbisfile.h>
+#endif
+#endif
+#ifdef WIN32
+#include <windows.h>
+#include <intrin.h>
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
+#define SLINE xstr(__LINE__)
+
+class TOpenGLDevice
+{
+public:
+ bool have_base_opengl;
+};
+
+struct glTexture;
+class TRenderTargetList
+{
+public:
+ int w,h,ms;
+ SMTEX tex;
+ GLuint depth,frame,colorms,sframe,sdepth,scolor;
+ TRenderTargetList *next;
+};
+class TTextureList
+{
+public:
+ SMTEX tex;
+ int w,h;
+ TTextureList *next;
+};
+class TInputEventList
+{
+public:
+ smInpEvent e;
+ TInputEventList *next;
+};
+
+class SMELT_IMPL:public SMELT
+{
+public:
+ virtual void smRelease();
+ virtual bool smInit();
+ virtual void smFinale();
+ virtual void smMainLoop();
+ virtual void smUpdateFunc(smHook func);
+ virtual void smUpdateFunc(smHandler* h);
+ virtual void smUnFocFunc(smHook func);
+ virtual void smUnFocFunc(smHandler* h);
+ virtual void smFocFunc(smHook func);
+ virtual void smFocFunc(smHandler* h);
+ virtual void smQuitFunc(smHook func);
+ virtual void smQuitFunc(smHandler* h);
+ virtual void smWinTitle(const char* title);
+ virtual bool smIsActive();
+ virtual void smNoSuspend(bool para);
+ virtual void smVidMode(int resX,int resY,bool _windowed);
+ virtual void smLogFile(const char* path);
+ virtual void smLog(const char* format,...);
+ virtual void smScreenShot(const char* path);
+
+ virtual void smSetFPS(int fps);
+ virtual float smGetFPS();
+ virtual float smGetDelta();
+ virtual float smGetTime();
+
+ virtual SMSFX smSFXLoad(const char *path);
+ virtual SMSFX smSFXLoadFromMemory(const char *ptr,DWORD size);
+ virtual SMCHN smSFXPlay(SMSFX fx,int vol=100,int pan=0,float pitch=1.,bool loop=0);
+ virtual float smSFXGetLengthf(SMSFX fx);
+ virtual DWORD smSFXGetLengthd(SMSFX fx);
+ virtual void smSFXSetLoopPoint(SMSFX fx,DWORD l,DWORD r);
+ virtual void smSFXFree(SMSFX fx);
+
+ virtual void smChannelVol(SMCHN chn,int vol);
+ virtual void smChannelPan(SMCHN chn,int pan);
+ virtual void smChannelPitch(SMCHN chn,float pitch);
+ virtual void smChannelPause(SMCHN chn);
+ virtual void smChannelResume(SMCHN chn);
+ virtual void smChannelStop(SMCHN chn);
+ virtual void smChannelPauseAll();
+ virtual void smChannelResumeAll();
+ virtual void smChannelStopAll();
+ virtual bool smChannelIsPlaying(SMCHN chn);
+ virtual float smChannelGetPosf(SMCHN chn);
+ virtual void smChannelSetPosf(SMCHN chn,float pos);
+ virtual int smChannelGetPosd(SMCHN chn);
+ virtual void smChannelSetPosd(SMCHN chn,int pos);
+
+ virtual void smGetMouse2f(float *x,float *y);
+ virtual void smSetMouse2f(float x,float y);
+ virtual void smSetMouseGrab(bool enabled);
+ virtual int smGetWheel();
+ virtual bool smIsMouseOver();
+ virtual int smGetKeyState(int key);
+ virtual int smGetKey();
+ virtual bool smGetInpEvent(smInpEvent *e);
+
+ virtual bool smRenderBegin2D(bool ztest=0,SMTRG trg=0);
+ virtual bool smRenderBegin3D(float fov,bool ztest=0,SMTRG trg=0);
+ virtual bool smRenderEnd();
+ virtual void sm3DCamera6f2v(float *pos,float *rot);
+ virtual void sm2DCamera5f3v(float *pos,float *dpos,float *rot);
+ virtual void smMultViewMatrix(float *mat);
+ virtual void smClrscr(DWORD color,bool clearcol=true,bool cleardep=true);
+ virtual void smRenderLinefd(float x1,float y1,float z1,float x2,float y2,float z2,DWORD color);
+ virtual void smRenderLinefvd(float *p1,float *p2,DWORD color);
+ virtual void smRenderTriangle(smTriangle *t);
+ virtual void smRenderQuad(smQuad *q);
+ virtual smVertex* smGetVertArray();
+ virtual void smDrawVertArray(int prim,SMTEX texture,int blend,int _primcnt);
+ virtual void smDrawCustomIndexedVertices(smVertex* vb,WORD* ib,int vbc,int ibc,int blend,SMTEX texture);
+
+ virtual SMTRG smTargetCreate(int w,int h,int ms=0);
+ virtual SMTEX smTargetTexture(SMTRG targ);
+ virtual void smTargetFree(SMTRG targ);
+
+ virtual SMTEX smTextureCreate(int w,int h);
+ virtual SMTEX smTextureLoad(const char *path);
+ virtual SMTEX smTextureLoadFromMemory(const char *ptr,DWORD size);
+ virtual void smTextureFree(SMTEX tex);
+ virtual void smTextureOpt(int potopt=TPOT_NONPOT,int filter=TFLT_LINEAR);
+ virtual int smTextureGetWidth(SMTEX tex,bool original=false);
+ virtual int smTextureGetHeight(SMTEX tex,bool original=false);
+ virtual DWORD* smTextureLock(SMTEX tex,int l,int t,int w,int h,bool ro=true);
+ virtual void smTexutreUnlock(SMTEX tex);
+//internal routines&variables...
+ static SMELT_IMPL* getInterface();
+ void focusChange(bool actif);
+
+ void *hwnd;
+ bool Active;
+ char curError[256];
+
+ bool (*pUpdateFunc)();
+ bool (*pUnFocFunc)();
+ bool (*pFocFunc)();
+ bool (*pQuitFunc)();
+ smHandler *updateHandler,*unFocHandler,*focHandler,*quitHandler;
+ const char *Icon;
+ char winTitle[256];
+ int scrw,scrh;
+ int dispw,disph;
+ bool windowed,vsync;
+ char logFile[256];
+ int limfps;
+ bool hideMouse,noSuspend;
+
+ TOpenGLDevice *pOpenGLDevice;
+ smVertex *vertexArray;
+ smVertex *vertexBuf;
+ GLushort *indexBuf;
+ GLuint IndexBufferObject;
+ GLuint VertexBufferObject;
+ GLuint VertexArrayObject;
+ GLuint ShaderProgram;
+ GLuint fragshader,vertshader;
+ int loc_tex,loc_mmodv,loc_mproj;
+ float mmodv[16],mproj[16];
+ TRenderTargetList *targets;
+ TRenderTargetList *curTarget;
+ TTextureList *textures;
+ bool tdmode;
+ int primcnt,primType,primBlend,filtermode;
+ SMTEX primTex,emptyTex;
+ bool zbufenabled;
+ bool checkGLExtension(const char *extlist,const char *ext);
+ void unloadGLEntryPoints();
+ bool loadGLEntryPoints();
+ bool initOGL();
+ void finiOGL();
+ bool restOGL();
+ bool confOGL();
+ void batchOGL(bool endScene=false);
+ void configTexture(glTexture *t,int w,int h,DWORD *px,bool compress=true);
+ void bindTexture(glTexture *t);
+ bool buildTarget(TRenderTargetList *pTarget,GLuint texid,int w,int h,int ms);
+ SMTEX buildTexture(int w,int h,DWORD *px);
+ void setBlend(int blend);
+ void configProjectionMatrix2D(int w,int h);
+ void configProjectionMatrix3D(int w,int h,float fov);
+ DWORD* decodeImage(BYTE *data,const char *fn,DWORD size,int &w,int &h);
+
+ void *pOpenALDevice;
+ bool mute,lpp;
+ bool initOAL();
+ void finiOAL();
+#ifndef ENABLE_DUMB
+ int scnt;
+ ALuint src[SRC_MAX];
+ ALuint getSource();
+#endif
+
+ int posz,lkey;
+ float posx,posy;
+ int keymods;
+ bool mouseOver,captured;
+ char keyz[256];
+ bool keylst[256];
+ TInputEventList *inpQueue;
+ void initInput();
+ void clearQueue();
+ void buildEvent(int type,int k,int scan,int flags,int x,int y);
+ bool procGLFWEvent();
+ int GLFWKeyToSMKey(int glfwkey);
+
+ float timeDelta,updateFPSDelay,fps,timeS;
+ DWORD fixDelta,t0;
+ int fcnt;
+ DWORD dt;
+private:
+ SMELT_IMPL();
+};
+extern SMELT_IMPL *pSM;
+#endif
diff --git a/smelt/glfw_m/smmath_priv.hpp b/smelt/glfw_m/smmath_priv.hpp
new file mode 100644
index 0000000..90f0e2a
--- /dev/null
+++ b/smelt/glfw_m/smmath_priv.hpp
@@ -0,0 +1,165 @@
+// -*- C++ -*-
+/*
+ * Simple MultimEdia LiTerator(SMELT)
+ * by Chris Xiong 2015
+ * Math header & implementation for the core
+ *
+ * WARNING: This library is in development and interfaces would be very
+ * unstable.
+ *
+ */
+#ifndef SMMATH_H
+#define SMMATH_H
+#include <cmath>
+#include <cstddef>
+#define sqr(x) ((x)*(x))
+#define EPS 1e-6
+#ifndef PI
+#define PI 3.14159265358979323846f
+#endif
+
+class smMath
+{
+public:
+ static double deg2rad(double deg){return deg/180.*PI;}
+ static double rad2deg(double rad){return rad/PI*180.;}
+ static double clamprad(double a){while(a<0)a+=2*PI;while(a>2*PI)a-=2*PI;return a;}
+ static double clampdeg(double a){while(a<0)a+=360.;while(a>360)a-=360.;return a;}
+};
+class smvec2d
+{
+public:
+ float x,y;
+ smvec2d(float _x,float _y){x=_x;y=_y;}
+ smvec2d(){x=y=.0;}
+ float l(){return sqrtf(sqr(x)+sqr(y));}
+ void normalize(){float L=l();if(L<EPS)return;x/=L;y/=L;}
+ smvec2d getNormalized(){float L=l();if(L<EPS)return smvec2d(0,0);return smvec2d(x/L,y/L);}
+ void swap(){float t=x;x=y;y=t;}
+ void rotate(float rad){float tx=x*cosf(rad)+y*sinf(rad),ty=y*cosf(rad)-x*sinf(rad);x=tx,y=ty;}
+ smvec2d getRotate(float rad){float tx=x*cosf(rad)+y*sinf(rad),ty=y*cosf(rad)-x*sinf(rad);return smvec2d(tx,ty);}
+ friend smvec2d operator -(smvec2d a,smvec2d b){return smvec2d(a.x-b.x,a.y-b.y);}
+ friend smvec2d operator +(smvec2d a,smvec2d b){return smvec2d(a.x+b.x,a.y+b.y);}
+ friend float operator |(smvec2d a,smvec2d b){return a.x*b.x+a.y*b.y;}
+ friend float operator *(smvec2d a,smvec2d b){return a.x*b.y-b.x*a.y;}
+ friend smvec2d operator *(float a,smvec2d b){return smvec2d(a*b.x,a*b.y);}
+ friend smvec2d operator *(smvec2d a,float b){return smvec2d(b*a.x,b*a.y);}
+ friend float operator ^(smvec2d a,smvec2d b){return (a|b)/a.l()/b.l();}
+};
+
+class smvec3d
+{
+public:
+ float x,y,z;
+ smvec3d(float _x,float _y,float _z){x=_x;y=_y;z=_z;}
+ smvec3d(smvec2d a,float _z=.0){x=a.x;y=a.y;z=_z;}
+ smvec3d(){x=y=z=.0;}
+ float l(){return sqrtf(sqr(x)+sqr(y)+sqr(z));}
+ void normalize(){float L=l();if(L<EPS)return;x/=L;y/=L;z/=L;}
+ smvec3d getNormalized(){float L=l();if(L<EPS)return smvec3d(0,0,0);return smvec3d(x/L,y/L,z/L);}
+ friend smvec3d operator -(smvec3d a,smvec3d b){return smvec3d(a.x-b.x,a.y-b.y,a.z-b.z);}
+ friend smvec3d operator +(smvec3d a,smvec3d b){return smvec3d(a.x+b.x,a.y+b.y,a.z+b.z);}
+ friend float operator |(smvec3d a,smvec3d b){return a.x*b.x+a.y*b.y+a.z*b.z;}
+ friend smvec3d operator *(smvec3d a,smvec3d b){return smvec3d(a.y*b.z-a.z*b.y,a.z*b.x-a.x*b.z,a.x*b.y-a.y*b.x);}
+ friend smvec3d operator *(float a,smvec3d b){return smvec3d(a*b.x,a*b.y,a*b.z);}
+ friend smvec3d operator *(smvec3d a,float b){return smvec3d(b*a.x,b*a.y,b*a.z);}
+ friend float operator ^(smvec3d a,smvec3d b){return (a|b)/a.l()/b.l();}
+};
+
+class smvec4d
+{
+public:
+ float x,y,z,w;
+ smvec4d(float _x,float _y,float _z,float _w){x=_x;y=_y;z=_z;w=_w;}
+ smvec4d(smvec3d a,float _w=.0){x=a.x;y=a.y;z=a.z;w=_w;}
+ smvec4d(){x=y=z=w=.0;}
+ float l(){return sqrt(sqr(x)+sqr(y)+sqr(z)+sqr(w));}
+ void normalize(){float L=l();if(L<EPS)return;x/=L;y/=L;z/=L;w/=L;}
+ smvec4d getNormalized(){float L=l();if(L<EPS)return smvec4d(0,0,0,0);return smvec4d(x/L,y/L,z/L,w/L);}
+ friend smvec4d operator -(smvec4d a,smvec4d b){return smvec4d(a.x-b.x,a.y-b.y,a.z-b.z,a.w-b.w);}
+ friend smvec4d operator +(smvec4d a,smvec4d b){return smvec4d(a.x+b.x,a.y+b.y,a.z+b.z,a.w+b.w);}
+ friend float operator |(smvec4d a,smvec4d b){return a.x*b.x+a.y*b.y+a.z*b.z+a.w*b.w;}
+ //Note: this doesn't do a real 4D cross product.
+ friend smvec4d operator *(smvec4d a,smvec4d b){return smvec4d(a.y*b.z-a.z*b.y,a.z*b.x-a.x*b.z,a.x*b.y-a.y*b.x,1);}
+ friend smvec4d operator *(float a,smvec4d b){return smvec4d(a*b.x,a*b.y,a*b.z,a*b.w);}
+ friend smvec4d operator *(smvec4d a,float b){return smvec4d(b*a.x,b*a.y,b*a.z,b*a.w);}
+ friend float operator ^(smvec4d a,smvec4d b){return (a|b)/a.l()/b.l();}
+};
+
+class smMatrix
+{
+public:
+ float m[16];
+ /* sf 0 1 2 3
+ * 0 00 04 08 12
+ * 1 01 05 09 13
+ * 2 02 06 10 14
+ * 3 03 07 11 15
+ */
+ smMatrix(){for(int i=0;i<16;++i)m[i]=.0;}
+ smMatrix(const float* _m){memcpy(m,_m,sizeof(m));}
+ smMatrix(const smMatrix &copy){for(int i=0;i<16;++i)m[i]=copy.m[i];}
+ float* operator [](int s){if(s>=0&&s<4)return m+s*4;else return NULL;}
+ void clear(){for(int i=0;i<16;++i)m[i]=.0;}
+ void loadIdentity(){clear();m[0]=m[5]=m[10]=m[15]=1.;}
+ void translate(float x,float y,float z)
+ {
+ smMatrix tmp;tmp.loadIdentity();
+ tmp.m[12]=x;tmp.m[13]=y;tmp.m[14]=z;
+ *this=*this*tmp;
+ }
+ void rotate(float a,float x,float y,float z)
+ {
+ if(smvec3d(x,y,z).l()<=EPS)return;
+ if(fabsf(smvec3d(x,y,z).l()-1)>EPS)
+ {
+ smvec3d a(x,y,z);a.normalize();
+ x=a.x;y=a.y;z=a.z;
+ }
+ smMatrix tmp;
+ float c=cosf(a),s=sinf(a);
+ tmp.m[ 0]=x*x*(1-c)+c;
+ tmp.m[ 4]=x*y*(1-c)-z*s;
+ tmp.m[ 8]=x*z*(1-c)+y*s;
+ tmp.m[ 1]=y*x*(1-c)+z*s;
+ tmp.m[ 5]=y*y*(1-c)+c;
+ tmp.m[ 9]=y*z*(1-c)-x*s;
+ tmp.m[ 2]=x*z*(1-c)-y*s;
+ tmp.m[ 6]=y*z*(1-c)+x*s;
+ tmp.m[10]=z*z*(1-c)+c;
+ tmp.m[15]=1;
+ *this=*this*tmp;
+ }
+ void lookat(smvec3d eye,smvec3d at,smvec3d up)
+ {
+ smvec3d f=at-eye;f.normalize();up.normalize();
+ smvec3d s=f*up;smvec3d u=s.getNormalized()*f;
+ m[0]= s.x;m[4]= s.y;m[ 8]= s.z;m[12]=0;
+ m[1]= u.x;m[5]= u.y;m[ 9]= u.z;m[13]=0;
+ m[2]=-f.x;m[6]=-f.y;m[10]=-f.z;m[14]=0;
+ m[3]= 0;m[7]= 0;m[11]= 0;m[15]=1;
+ }
+ friend smMatrix operator *(smMatrix a,smMatrix b)
+ {
+ smMatrix ret;
+ for(int i=0;i<4;++i)
+ for(int j=0;j<4;++j)
+ for(int k=0;k<4;++k)
+ ret[j][i]+=a[k][i]*b[j][k];
+ return ret;
+ }
+ friend smvec3d operator *(smMatrix a,smvec3d b)
+ {
+ return smvec3d(a[0][0]*b.x+a[1][0]*b.y+a[2][0]*b.z,
+ a[0][1]*b.x+a[1][1]*b.y+a[2][1]*b.z,
+ a[0][2]*b.x+a[1][2]*b.y+a[2][2]*b.z);
+ }
+ friend smvec4d operator *(smMatrix a,smvec4d b)
+ {
+ return smvec4d(a[0][0]*b.x+a[1][0]*b.y+a[2][0]*b.z+a[3][0]*b.w,
+ a[0][1]*b.x+a[1][1]*b.y+a[2][1]*b.z+a[3][1]*b.w,
+ a[0][2]*b.x+a[1][2]*b.y+a[2][2]*b.z+a[3][2]*b.w,
+ a[0][3]*b.x+a[1][3]*b.y+a[2][3]*b.z+a[3][3]*b.w);
+ }
+};
+#endif
diff --git a/smelt/glfw_m/sys_glfw.cpp b/smelt/glfw_m/sys_glfw.cpp
new file mode 100644
index 0000000..a5829f3
--- /dev/null
+++ b/smelt/glfw_m/sys_glfw.cpp
@@ -0,0 +1,334 @@
+// -*- C++ -*-
+/*
+ * Simple MultimEdia LiTerator(SMELT)
+ * by Chris Xiong 2015
+ * SMELT system implementation
+ *
+ * WARNING: This library is in development and interfaces would be very
+ * unstable.
+ *
+ */
+#include "smelt_internal.hpp"
+#include <thread>
+#include <chrono>
+static const char* SYS_GLFW_SRCFN="smelt/glfw_m/sys_glfw.cpp";
+int refcnt=0;
+SMELT_IMPL *pSM=0;
+char lasterr[1024];
+
+void glfwErrorHandler(int err,const char* desc);
+void glfwFocChangeHandler(GLFWwindow*,int foc);
+void glfwCursorEnterHandler(GLFWwindow*,int ent);
+void glfwKBEventHandler(GLFWwindow* window,int key,int scancode,int action,int mods);
+void glfwMouseButtonHandler(GLFWwindow* w,int btn,int action,int mods);
+void glfwMouseMotionHandler(GLFWwindow*,double x,double y);
+void glfwMouseWheelHandler(GLFWwindow* w,double x,double y);
+
+SMELT* smGetInterface(int apilevel)
+{
+ if(apilevel==SMELT_APILEVEL)
+ return(SMELT*)SMELT_IMPL::getInterface();
+ else return 0;
+}
+
+SMELT_IMPL* SMELT_IMPL::getInterface()
+{
+ if(!pSM)pSM=new SMELT_IMPL();
+ ++refcnt;return pSM;
+}
+
+void SMELT_IMPL::smRelease()
+{
+ --refcnt;
+ if(!refcnt)
+ {
+ if(pSM->hwnd)pSM->smFinale();
+ delete pSM;pSM=0;
+ }
+}
+
+bool SMELT_IMPL::smInit()
+{
+ smLog("%s:" SLINE ": Initalizing SMELT...\n",SYS_GLFW_SRCFN);
+ smLog("%s:" SLINE ": SMELT api version %d\n",SYS_GLFW_SRCFN,SMELT_APILEVEL);
+ time_t t=time(NULL);
+ smLog("%s:" SLINE ": Date %s",SYS_GLFW_SRCFN,asctime(localtime(&t)));
+#ifdef WIN32
+ OSVERSIONINFO os_ver; MEMORYSTATUS mem_st;
+ os_ver.dwOSVersionInfoSize=sizeof(os_ver);
+ GetVersionEx(&os_ver);
+ smLog("%s:" SLINE ": OS: Windows %ld.%ld.%ld\n", SYS_GLFW_SRCFN,os_ver.dwMajorVersion,os_ver.dwMinorVersion,os_ver.dwBuildNumber);
+
+ int CPUInfo[4]={-1};
+ __cpuid(CPUInfo,0x80000000);
+ unsigned int nExIds=CPUInfo[0];
+ char *cpuName,*loced;cpuName=(char*)calloc(0x40,sizeof(char));loced=cpuName;
+ for(unsigned int i=0x80000000;i<=nExIds;++i)
+ {
+ __cpuid(CPUInfo, i);
+ if(i==0x80000002)
+ memcpy(cpuName,CPUInfo,sizeof(CPUInfo));
+ else if(i==0x80000003)
+ memcpy(cpuName+16,CPUInfo,sizeof(CPUInfo));
+ else if(i==0x80000004)
+ memcpy(cpuName+32, CPUInfo, sizeof(CPUInfo));
+ }
+ while(*cpuName==' ')++cpuName;
+ smLog("%s:" SLINE ": CPU: %s\n", SYS_GLFW_SRCFN,cpuName);
+ free(loced);
+
+ GlobalMemoryStatus(&mem_st);
+ smLog("%s:" SLINE ": Memory: %ldK total, %ldK free\n", SYS_GLFW_SRCFN,mem_st.dwTotalPhys/1024L,mem_st.dwAvailPhys/1024L);
+#else
+ system("uname -svm > /tmp/os.out");
+ char osv[100];FILE* a=fopen("/tmp/os.out","r");fgets(osv,100,a);fclose(a);
+ osv[strlen(osv)-1]='\0';
+ smLog("%s:" SLINE ": OS: %s\n",SYS_GLFW_SRCFN,osv);
+ system("rm /tmp/os.out");
+
+ system("cat /proc/cpuinfo | grep name -m 1 > /tmp/cpu.out");
+ a=fopen("/tmp/cpu.out","r");fgets(osv,100,a);fclose(a);
+ osv[strlen(osv)-1]='\0';char *ptr=osv;while(*ptr!=':')++ptr;ptr+=2;
+ smLog("%s:" SLINE ": CPU: %s\n",SYS_GLFW_SRCFN,osv);
+ system("rm /tmp/cpu.out");
+
+ a=fopen("/proc/meminfo","r");
+ unsigned totalm,freem;
+ fscanf(a,"MemTotal: %u kB\n",&totalm);
+ fscanf(a,"MemFree: %u kB\n",&freem);
+ smLog("%s:" SLINE ": RAM: %ukB installed, %ukB free\n",SYS_GLFW_SRCFN,totalm,freem);
+ fclose(a);
+#endif
+ glfwSetErrorCallback(glfwErrorHandler);
+ if(!glfwInit())
+ {
+ smLog("%s:" SLINE ": glfwInit() failed with error %s\n",SYS_GLFW_SRCFN,lasterr);
+ return false;
+ }
+ GLFWmonitor *moninfo=glfwGetPrimaryMonitor();
+ dispw=glfwGetVideoMode(moninfo)->width;
+ disph=glfwGetVideoMode(moninfo)->height;
+ smLog("%s:" SLINE ": Screen: %d x %d\n",SYS_GLFW_SRCFN,dispw,disph);
+ glfwWindowHint(GLFW_RED_BITS,8);
+ glfwWindowHint(GLFW_GREEN_BITS,8);
+ glfwWindowHint(GLFW_BLUE_BITS,8);
+ glfwWindowHint(GLFW_ALPHA_BITS,8);
+ glfwWindowHint(GLFW_DEPTH_BITS,16);
+ glfwWindowHint(GLFW_DOUBLEBUFFER,1);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,3);
+ glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
+ GLFWwindow *screen=glfwCreateWindow(windowed?scrw:dispw,windowed?scrh:disph,winTitle,NULL,NULL);
+ hwnd=(void*)screen;
+ if(!hwnd)
+ {
+ smLog("%s:" SLINE ": glfwCreateWindow() failed with error %s\n",SYS_GLFW_SRCFN,lasterr);
+ glfwTerminate();
+ return false;
+ }
+ glfwMakeContextCurrent(screen);
+ glfwSwapInterval(vsync?1:0);
+ if(!windowed)
+ {
+ mouseOver=true;
+ if(!pSM->Active)pSM->focusChange(true);
+ }
+ if(hideMouse)glfwSetInputMode(screen,GLFW_CURSOR,GLFW_CURSOR_HIDDEN);
+ glfwSetWindowFocusCallback(screen,glfwFocChangeHandler);
+ glfwSetCursorEnterCallback(screen,glfwCursorEnterHandler);
+ glfwSetKeyCallback(screen,glfwKBEventHandler);
+ glfwSetCursorPosCallback(screen,glfwMouseMotionHandler);
+ glfwSetMouseButtonCallback(screen,glfwMouseButtonHandler);
+ glfwSetScrollCallback(screen,glfwMouseWheelHandler);
+ initInput();
+ if(!initOGL()){smFinale();return false;}
+ if(!initOAL()){smFinale();return false;}
+ t0=DWORD(glfwGetTime()*1000.);dt=fcnt=0;fps=.0;
+ updateFPSDelay=.0;
+ return true;
+}
+
+void SMELT_IMPL::smFinale()
+{
+ smLog("%s:" SLINE ": Cleaning up...\n",SYS_GLFW_SRCFN);
+ clearQueue();finiOAL();finiOGL();
+ glfwDestroyWindow((GLFWwindow*)hwnd);glfwTerminate();hwnd=0;
+}
+
+void SMELT_IMPL::smMainLoop()
+{
+ if(!hwnd)return smLog("%s:" SLINE ": Error: SMELT is not initialized.\n",SYS_GLFW_SRCFN);
+ if(!pUpdateFunc&&!updateHandler) return smLog("%s:" SLINE ": UpdateFunc is not defined.\n",SYS_GLFW_SRCFN);
+ Active=true;
+ for(;;)
+ {
+ glfwPollEvents();
+ bool loopcont=true;
+ loopcont=procGLFWEvent();
+ if(!loopcont)break;
+ if(Active||noSuspend)
+ {
+ DWORD ticks;
+ do{ticks=DWORD(glfwGetTime()*1000.);dt=ticks-t0;}while(dt<1);
+ if(dt>=fixDelta)
+ {
+ timeDelta=dt/1000.;
+ if(timeDelta>0.2)timeDelta=fixDelta?fixDelta/1000.:.01;
+ ++fcnt;updateFPSDelay+=timeDelta;t0=ticks;timeS+=timeDelta;
+ if(updateFPSDelay>1){fps=fcnt/updateFPSDelay;updateFPSDelay=.0;fcnt=0;}
+ if(pUpdateFunc){if(pUpdateFunc())break;}
+ if(updateHandler){if(updateHandler->handlerFunc())break;}
+ for(int i=1;i<=255;++i)keylst[i]=((keyz[i]&4)!=0);
+ clearQueue();
+ }
+ else
+ if(fixDelta&&dt+3<fixDelta)std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }else std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+ clearQueue();Active=false;
+}
+void SMELT_IMPL::smUpdateFunc(smHook func){pUpdateFunc=func;}
+void SMELT_IMPL::smUpdateFunc(smHandler* h){updateHandler=h;}
+void SMELT_IMPL::smUnFocFunc(smHook func){pUnFocFunc=func;}
+void SMELT_IMPL::smUnFocFunc(smHandler* h){unFocHandler=h;}
+void SMELT_IMPL::smFocFunc(smHook func){pFocFunc=func;}
+void SMELT_IMPL::smFocFunc(smHandler* h){focHandler=h;}
+void SMELT_IMPL::smQuitFunc(smHook func){pQuitFunc=func;}
+void SMELT_IMPL::smQuitFunc(smHandler* h){quitHandler=h;}
+void SMELT_IMPL::smWinTitle(const char *title){strcpy(winTitle,title);}
+bool SMELT_IMPL::smIsActive(){return Active;}
+void SMELT_IMPL::smNoSuspend(bool para){noSuspend=para;}
+void SMELT_IMPL::smVidMode(int resX,int resY,bool _windowed)
+{
+ if(vertexArray)return;
+ if(!pOpenGLDevice)scrw=resX,scrh=resY,windowed=_windowed;
+ else if(windowed!=_windowed)
+ {
+ windowed=_windowed;
+ glfwSetWindowSize((GLFWwindow*)hwnd,windowed?scrw:dispw,windowed?scrh:disph);
+ restOGL();
+ if(!windowed){mouseOver=true;if(!pSM->Active)pSM->focusChange(true);}
+ }
+}
+void SMELT_IMPL::smLogFile(const char* path)
+{
+ strcpy(logFile,path);
+ FILE *tf=fopen(logFile,"w");
+ if(!tf)*logFile=0;else fclose(tf);
+}
+void SMELT_IMPL::smLog(const char* format,...)
+{
+ va_list ap;
+ va_start(ap,format);vfprintf(stderr,format,ap);va_end(ap);
+ FILE *logf=fopen(logFile,"a");if(!logf)return;
+ va_start(ap,format);vfprintf(logf,format,ap);va_end(ap);
+ fclose(logf);
+}
+void SMELT_IMPL::smScreenShot(const char* path)
+{
+ if(pOpenGLDevice)
+ {
+ //glFinish();
+ //glReadPixels(0,0,screen->w,screen->h,GL_RGB,GL_UNSIGNED_BYTE,surface->pixels);
+ }
+}
+void SMELT_IMPL::smSetFPS(int fps)
+{
+ vsync=(fps==FPS_VSYNC);
+ if(pOpenGLDevice)glfwSwapInterval(vsync?1:0);
+ if(fps>0)fixDelta=1000./fps;else fixDelta=0;
+}
+float SMELT_IMPL::smGetFPS(){return fps;}
+float SMELT_IMPL::smGetDelta(){return timeDelta;}
+float SMELT_IMPL::smGetTime(){return timeS;}
+
+SMELT_IMPL::SMELT_IMPL()
+{
+ hwnd=NULL;Active=false;memset(curError,0,sizeof(curError));
+ pUpdateFunc=pUnFocFunc=pFocFunc=pQuitFunc=NULL;
+ updateHandler=unFocHandler=focHandler=quitHandler=NULL;
+ Icon=NULL;strcpy(winTitle,"untitled");scrw=dispw=800;scrh=disph=600;
+ windowed=vsync=false;memset(logFile,0,sizeof(logFile));
+ limfps=0;hideMouse=true;noSuspend=false;
+ pOpenGLDevice=NULL;targets=NULL;curTarget=NULL;
+ textures=NULL;vertexArray=NULL;vertexBuf=NULL;indexBuf=NULL;primTex=0;
+ pOpenALDevice=NULL;mute=false;inpQueue=NULL;posz=0;posx=posy=.0;
+ mouseOver=true;captured=false;keymods=0;zbufenabled=false;
+ timeDelta=updateFPSDelay=fps=.0;fcnt=dt=fixDelta=0;
+}
+void SMELT_IMPL::focusChange(bool actif)
+{
+ Active=actif;
+ if(actif){pFocFunc?pFocFunc():0;focHandler?focHandler->handlerFunc():0;}
+ else {pUnFocFunc?pUnFocFunc():0;unFocHandler?unFocHandler->handlerFunc():0;};
+}
+bool SMELT_IMPL::procGLFWEvent()
+{
+ if(glfwWindowShouldClose((GLFWwindow*)hwnd))
+ {
+ bool accepted=true;
+ if(pSM->pQuitFunc&&pSM->pQuitFunc())accepted=false;
+ if(pSM->quitHandler&&quitHandler->handlerFunc())accepted=false;
+ if(accepted)return false;
+ }
+ return true;
+}
+void glfwErrorHandler(int err,const char* desc)
+{
+ strcpy(lasterr,desc);
+}
+void glfwFocChangeHandler(GLFWwindow*,int foc)
+{
+ pSM->focusChange(foc);
+}
+void glfwCursorEnterHandler(GLFWwindow*,int ent)
+{
+ pSM->mouseOver=ent;
+}
+void glfwKBEventHandler(GLFWwindow* window,int key,int scancode,int action,int mods)
+{
+ if(action==GLFW_RELEASE)
+ {
+ pSM->keymods=mods;
+ pSM->buildEvent(INPUT_KEYUP,key,0,0,-1,-1);
+ }
+ else
+ {
+ pSM->keymods=mods;
+ if((pSM->keymods&GLFW_MOD_ALT)&&((key==GLFW_KEY_ENTER)||(key==GLFW_KEY_KP_ENTER)))
+ pSM->smVidMode(pSM->scrw,pSM->scrh,!pSM->windowed);
+ pSM->buildEvent(INPUT_KEYDOWN,key,0,0,-1,-1);
+ }
+}
+void glfwMouseButtonHandler(GLFWwindow* w,int btn,int action,int mods)
+{
+ double x,y;glfwGetCursorPos(w,&x,&y);
+ if(action==GLFW_PRESS)
+ {
+ if(btn==GLFW_MOUSE_BUTTON_LEFT)
+ pSM->buildEvent(INPUT_MBUTTONDOWN,SMK_LBUTTON,0,0,x,y);
+ if(btn==GLFW_MOUSE_BUTTON_RIGHT)
+ pSM->buildEvent(INPUT_MBUTTONDOWN,SMK_RBUTTON,0,0,x,y);
+ if(btn==GLFW_MOUSE_BUTTON_MIDDLE)
+ pSM->buildEvent(INPUT_MBUTTONDOWN,SMK_MBUTTON,0,0,x,y);
+ }
+ else
+ {
+ if(btn==GLFW_MOUSE_BUTTON_LEFT)
+ pSM->buildEvent(INPUT_MBUTTONUP,SMK_LBUTTON,0,0,x,y);
+ if(btn==GLFW_MOUSE_BUTTON_RIGHT)
+ pSM->buildEvent(INPUT_MBUTTONUP,SMK_RBUTTON,0,0,x,y);
+ if(btn==GLFW_MOUSE_BUTTON_MIDDLE)
+ pSM->buildEvent(INPUT_MBUTTONUP,SMK_MBUTTON,0,0,x,y);
+ }
+}
+void glfwMouseMotionHandler(GLFWwindow*,double x,double y)
+{
+ pSM->buildEvent(INPUT_MOUSEMOVE,0,0,0,x,y);
+}
+void glfwMouseWheelHandler(GLFWwindow* w,double x,double y)
+{
+ double cx,cy;glfwGetCursorPos(w,&cx,&cy);
+ if(y>0)pSM->buildEvent(INPUT_MOUSEWHEEL,1,0,0,cx,cy);
+ if(y<0)pSM->buildEvent(INPUT_MOUSEWHEEL,-1,0,0,cx,cy);
+}