Sory, I have used the wrong reply button. The code is written in c# (mono) using the Tao framework SDL/OpenGL. I will list the relevant code. The main entry is in the class App which basically initializes SDL, sets up GL and runs the loop. State is a pass around class that holds variables required by most other classes. It contains matrices for projection and view. The matrices are Matrix class objects, a 1 dimension float array of 16 values (row major). They are indexed, using property syntax, ax, ay, az, aw for row 1, bx, bey (by reserved), bz, bw, for row 2 x, y, z, w, etc. Note that I have set the transpose flag on Gl.glUniformMatrix4fv calls to GL_TRUE. App.cs:
private void setOpenGL(){
resize(w, h);
Gl.glEnable(Gl.GL_DOUBLEBUFFER);
Gl.glEnable(Gl.GL_DEPTH_TEST);
Gl.glEnable(Gl.GL_CULL_FACE);
Gl.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
Gl.glClearDepth(1.0f);
Gl.glDepthMask(Gl.GL_TRUE);
Gl.glDepthFunc(Gl.GL_EQUAL);
Gl.glCullFace(Gl.GL_BACK);
Gl.glFrontFace(Gl.GL_CCW);
}
private void resize(int width, int height){
if(width==0) width=1;
float ratio=((float)height / (float)width);
float frustScale=(float)(1.0 / Math.Tan((degRad * fov) / 2.0));
float ax=frustScale * ratio;
float bey=frustScale;
float cz=(far + near) / (near - far);
float cw=(2.0f * far * near) / (near - far);
float dz=-1.0f;
state.W=width;
state.H=height;
state.Near=near;
state.Far=far;
state.Fov=fov;
state.setProjection(ax, bey, cz, cw, dz);
Gl.glViewport(0, 0, width, height);
Gl.glDepthRange(near, far);
}
private void tick(){
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
ehub.tick();
Gl.glFlush();
Sdl.SDL_GL_SwapBuffers();
}
eHub is an EventHub object that has the Player and the ShaderHub hung off it. The Player code builds quarternions from events passed it by the EventHub, converts these into the view matrix and stores the view matrix in the State object (every loop). Player.cs:
private State state;
private bool hVirgin=true;
private bool pVirgin=true;
private const float _maxPitch=(float)Math.PI*60/180;
public const float _360=(float)Math.PI*2;
private float head=0.0f;
private float pitch=0.0f;
private float vel=0.0f;
private float strf=0.0f;
private bool ford=false;
private bool back=false;
private bool strafel=false;
private bool strafer=false;
private float x, y, z;
private Q q, qHead, qPitch;
private const float walk = 0.01f, strafe = 0.007f;
public Player(State state){
this.state=state;
y=5f;
x=z=0.0f;
qPitch=new Q(0.0f, 0.0f, 0.0f, 1.0f);
qHead=new Q(0.0f, 0.0f, 0.0f, 1.0f);
}
public void setHead(float f){
if(hVirgin){ hVirgin=false; return;}//throw first event away
head+=f;
if(head>_360) head-=_360;
if(head<-_360) head+=_360;
}
public void setPitch(float f){
if(pVirgin){ pVirgin=false; return;}//throw first event away
pitch+=f;
if(pitch>_maxPitch) pitch=_maxPitch;
if(pitch<-_maxPitch) pitch=-_maxPitch;
}
public bool fordP{get{return ford;} set{ford=value;}}
public bool backP{get{return back;} set{back=value;}}
public bool strafelP{get{return strafel;} set{strafel=value;}}
public bool straferP{get{return strafer;} set{strafer=value;}}
public void drawCam(){
if(ford) vel=(float)state.Delay * walk;
if(back) vel=(float)-state.Delay * walk;
if(strafel) strf=(float)-state.Delay * strafe;
if(strafer) strf=(float)state.Delay * strafe;
qPitch.x=(float)Math.Sin(pitch*0.5f);
qPitch.w=(float)Math.Cos(pitch*0.5f);
qHead.y=(float)Math.Sin(head*0.5f);
qHead.w=(float)Math.Cos(head*0.5f);
x+=(1.0f - 2.0f * ( qHead.y * qHead.y + qHead.z * qHead.z ))*strf;//mat[0]
x+=(2.0f * ( qHead.x * qHead.z + qHead.y * qHead.w ))*vel;//mat[8]
z+=(2.0f * (qHead.x * qHead.z - qHead.y * qHead.w))*strf;//mat[2]
z+=(1.0f - 2.0f * ( qHead.x * qHead.x + qHead.y * qHead.y ))*vel;//mat[10]
multQ(ref q, qPitch, qHead);
setMat(q);
strf=0.0f;
vel=0.0f;
}
private void setMat(Q q){
//xbase
state.vax=1.0f - 2.0f * (q.y * q.y + q.z * q.z);
state.vbx=2.0f * (q.x * q.y + q.z * q.w);
state.vcx=2.0f * (q.x * q.z - q.y * q.w);
//ybase
state.vay=2.0f * (q.x * q.y - q.z * q.w);
state.vby=1.0f - 2.0f * (q.x * q.x + q.z * q.z);
state.vcz=2.0f * (q.z * q.y + q.x * q.w);
//zbase
state.vaz=2.0f * (q.x * q.z + q.y * q.w);
state.vbz=2.0f * (q.y * q.z - q.x * q.w);
state.vcz=1.0f - 2.0f * (q.x * q.x + q.y * q.y);
//translation
state.vaw=state.vax * -x + state.vay * -y + state.vaz * z;
state.vbw=state.vbx * -x + state.vby * -y + state.vbz * z;
state.vcw=state.vcx * -x + state.vcy * -y + state.vcz * z;
}
private void multQ(ref Q q, Q a, Q b){
q.w = a.w*b.w - a.x*b.x - a.y*b.y - a.z*b.z;
q.x = a.w*b.x + a.x*b.w + a.y*b.z - a.z*b.y;
q.y = a.w*b.y - a.x*b.z + a.y*b.w + a.z*b.x;
q.z = a.w*b.z + a.x*b.y - a.y*b.x + a.z*b.w;
}
public void draw(){
//called from the EventHub per loop
drawCam();
}
The ShaderHub, hung off the EventHub along with the Player, will have shaders for handling deferred rendering/shading, and shaders for handling drawing to the g buffers. At this iteration it sets up the shader for drawing to gl_Fragment, and then initializes the loading of the room from an xml file. The room is a Quad object constructed from xml data. The xml file contains a list of further objects inside the Quad… pillars eg. Rather than using a scene graph I am playing with the idea of using a linked list of Quads… the current Quad is the one the Player is in, and the head of a linked list of quads the player can see. The projection matrix is attached to shader in the constructor. The view matrix is attached in the draw method once the shader is used. ShaderHub.cs
public ShaderHub(State state){
this.state=state;
qVault=new QuadVault(state);
tVault=new TexVault(state);
mVault=new ModelVault();
shad=Bin._shad;
if(shad<1) Console.WriteLine("Error: GL did not assign shader id");
vshad=Bin.createShader(state.Path + "Game\\assets\\shaders\\main.vert", Bin._vertShad);//0 flags vert shader
if(vshad<1){Console.WriteLine("GL failed to give vert shader id");}
fshad=Bin.createShader(state.Path + "Game\\assets\\shaders\\main.frag", Bin._fragShad);
if(fshad<1){Console.WriteLine("GL failed to give frag shader id");}
bool flag=Bin.setShader(shad, vshad, fshad);
if(!flag){Console.WriteLine("Error linking shader.");}
modelLoc=Gl.glGetUniformLocation(shad, "modelMat");
if(modelLoc == -1){Console.WriteLine("model matrix location not got");}
state.ModelLoc=modelLoc;
projLoc=Gl.glGetUniformLocation(shad, "projMat");
if(projLoc == -1){Console.WriteLine("matrix projMat location not got");}
viewLoc=Gl.glGetUniformLocation(shad, "viewMat");
if(viewLoc == -1){Console.WriteLine("matrix viewMat location not got");}
Gl.glUseProgram(shad);
Gl.glUniformMatrix4fv(projLoc, 1, Gl.GL_TRUE, state.ProjMat);
Gl.glUseProgram(0);
tileLoc=Gl.glGetUniformLocation(Bin._shad, "colMap");
if(tileLoc==-1) Console.WriteLine("Error: sampler tileLoc not got");
state.TileLoc=this.tileLoc;
//tVault is texture vault. mVault is modelVault. qVault is quad vault
current=QuadReader.readQuad(state, tVault);
current.setModels(tVault, mVault);
qVault.addQuad(current);
}
public void draw(){
Gl.glUseProgram(shad);
Gl.glUniformMatrix4fv(viewLoc, 1, Gl.GL_TRUE, state.ViewMat);
current.draw();//draw the current Quad
Gl.glUseProgram(0);
}
The Quad object draws itself as below. North, east, etc refer to walls; whether the room has a wall. modelMat is the matrix containing that defines the world space orientation for this quad. In this instance it is an identity matrix so I have not shown it. The Mat suffix is used to get the float array of the matrix. Quad.cs:
public void setModels(TexVault t, ModelVault m){
for(int i=0; i<smodels.Length; i++){
ModelReader.readModel(ref smodels[i], t, m, state);
smodels[i].init();
}
}
public void draw(){
counter=0;
Gl.glActiveTexture(Gl.GL_TEXTURE0);
Gl.glUniformMatrix4fv(state.ModelLoc, 1, Gl.GL_TRUE, modelMat.Mat);
Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, vbo);
Gl.glEnableVertexAttribArray(0);
Gl.glEnableVertexAttribArray(1);
Gl.glEnableVertexAttribArray(2);
Gl.glVertexAttribPointer(0, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, voff);
Gl.glVertexAttribPointer(1, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, noff);
Gl.glVertexAttribPointer(2, 2, Gl.GL_FLOAT, Gl.GL_FALSE, 0, coff);
//the material's emission vector ports tiling info to alter coords in the vert shader
coordFlags[2]=fts; coordFlags[3]=ftt;
Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_EMISSION, coordFlags);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, ftid);
Gl.glUniform1i(state.TileLoc, 0);
Gl.glDrawArrays(Gl.GL_QUADS, counter, 4);
if(north){
counter+=4;
coordFlags[2]=nts; coordFlags[3]=ntt;
Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_EMISSION, coordFlags);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, ntid);
Gl.glUniform1i(tileLoc, 0);
Gl.glDrawArrays(Gl.GL_QUADS, counter, 4);
}
if(east){
counter+=4;
coordFlags[2]=ets; coordFlags[3]=ett;
Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_EMISSION, coordFlags);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, etid);
Gl.glUniform1i(tileLoc, 0);
Gl.glDrawArrays(Gl.GL_QUADS, counter, 4);
}
if(south){
counter+=4;
coordFlags[2]=sts; coordFlags[3]=stt;
Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_EMISSION, coordFlags);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, etid);
Gl.glUniform1i(tileLoc, 0);
Gl.glDrawArrays(Gl.GL_QUADS, counter, 4);
}
if(west){
counter+=4;
coordFlags[2]=wts; coordFlags[3]=wtt;
Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_EMISSION, coordFlags);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, wtid);
Gl.glUniform1i(tileLoc, 0);
Gl.glDrawArrays(Gl.GL_QUADS, counter, 4);
}
Gl.glBindTexture(Gl.GL_TEXTURE_2D, 0);
Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, vbo);
smodels[0].draw();
}
smodels is an array of StaticModel objects (pillars) that was loaded when when the Quad was loaded. If this line is commented out everything works as expected. The model matrix for the pillar positions the pillar in world space independent from the quad. StaticModel.cs:
public void init(){
//the offsets are read from the relevant xml file and inserted into an identity matrix
modelMat.aw+=xoff;
modelMat.bw+=yoff;
modelMat.cw+=zoff;
}
public void draw(){
Gl.glActiveTexture(Gl.GL_TEXTURE0);
Gl.glUniform1i(state.TileLoc, 0);
Gl.glUniformMatrix4fv(state.ModelLoc, 1, Gl.GL_TRUE, modelMat.Mat);
Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, vbo);
Gl.glEnableVertexAttribArray(0);
Gl.glEnableVertexAttribArray(1);
Gl.glEnableVertexAttribArray(2);
Gl.glVertexAttribPointer(0, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, voff);
Gl.glVertexAttribPointer(1, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, noff);
Gl.glVertexAttribPointer(2, 2, Gl.GL_FLOAT, Gl.GL_FALSE, 0, coff);
coordFlags[2]=ts; coordFlags[3]=tt;
Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_EMISSION, coordFlags);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, tid);
Gl.glDrawArrays(Gl.GL_QUADS, 0, vcount);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, 0);
Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, 0);
}
For some reason the pillar moves with the player. The State class holds the view and the projection matrices. Probably silly of me but I decided to place a separate float array in the state class to handle the view matrix because it gets updated every loop via from the Player class. State.cs:
private string path, level="Game\\assets\\levels\\", quadKey="hellsKitchen\\subway.xml";
private long delay;
private Matrix projMat;
private int w, h;
private float near, far, fov;
private int tileLoc, modelLoc, viewLoc;
private float[] view=new float[]{ 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
public State(){
path=Environment.CurrentDirectory;
string[] dirs=path.Split('\\');
path="";
for(int i=0;i<(dirs.Length-3);i++){
path+=dirs[i];
path+="\\";
}
projMat=new Matrix();
}
public void setProjection(float ax, float bey, float cz, float cw, float dz){
projMat.ax=ax;
projMat.bey=bey;
projMat.cz=cz;
projMat.cw=cw;
projMat.dz=dz;
}
//properties used to set the view matrix
public float vax{get{return view[0];}set{view[0]=value;}}
public float vay{get{return view[1];}set{view[1]=value;}}
public float vaz{get{return view[2];}set{view[2]=value;}}
public float vaw{get{return view[3];}set{view[3]=value;}}
public float vbx{get{return view[4];}set{view[4]=value;}}
public float vby{get{return view[5];}set{view[5]=value;}}
public float vbz{get{return view[6];}set{view[6]=value;}}
public float vbw{get{return view[7];}set{view[7]=value;}}
public float vcx{get{return view[8];}set{view[8]=value;}}
public float vcy{get{return view[9];}set{view[9]=value;}}
public float vcz{get{return view[10];}set{view[10]=value;}}
public float vcw{get{return view[11];}set{view[11]=value;}}
public float vdx{get{return view[12];}set{view[12]=value;}}
public float vdy{get{return view[13];}set{view[13]=value;}}
public float vdz{get{return view[14];}set{view[14]=value;}}
public float vdw{get{return view[15];}set{view[15]=value;}}
public float[] ViewMat{get{return view;}set{view=value;}}
public int ModelLoc{get{return modelLoc;}set{modelLoc=value;}}
public int ViewLoc{get{return viewLoc;}set{viewLoc=value;}}
public int TileLoc{get{return tileLoc;}set{tileLoc=value;}}
The Matrix class is pretty simple. Matrix.cs:
using System;
namespace Game
{
public class Matrix{
private float[] mat=new float[]{ 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
public Matrix(){}
public float[] Mat{get{return mat;}set{mat=value;}}
public float ax{get{return mat[0];}set{mat[0]=value;}}
public float ay{get{return mat[1];}set{mat[1]=value;}}
public float az{get{return mat[2];}set{mat[2]=value;}}
public float aw{get{return mat[3];}set{mat[3]=value;}}
public float bx{get{return mat[4];}set{mat[4]=value;}}
public float bey{get{return mat[5];}set{mat[5]=value;}}
public float bz{get{return mat[6];}set{mat[6]=value;}}
public float bw{get{return mat[7];}set{mat[7]=value;}}
public float cx{get{return mat[8];}set{mat[8]=value;}}
public float cy{get{return mat[9];}set{mat[9]=value;}}
public float cz{get{return mat[10];}set{mat[10]=value;}}
public float cw{get{return mat[11];}set{mat[11]=value;}}
public float dx{get{return mat[12];}set{mat[12]=value;}}
public float dy{get{return mat[13];}set{mat[13]=value;}}
public float dz{get{return mat[14];}set{mat[14]=value;}}
public float dw{get{return mat[15];}set{mat[15]=value;}}
}
}
I have altered the vert shader as Dan pointed out. Still the same problem however. main.vert
#version 330
layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 norm;
layout(location = 2) in vec2 coord;
uniform mat4 projMat;
uniform mat4 viewMat;
uniform mat4 modelMat;
out vec2 coords;
out vec4 position;
void main(){
coords=coord.st * vec2(gl_FrontMaterial.emission.z, gl_FrontMaterial.emission.w);
vec4 worldPos=modelMat * vec4(pos, 1.0);
vec4 eyePos=viewMat * worldPos;
gl_Position=projMat * eyePos;
}