I'm writing my own version of gluLookAt. I believe I have the correct implementation however, often the matrix that gets returned is full of nan's.
Here is my function:
mat4 look_at(vec4 eye, vec4 at, vec4 up)
{
vec4 vpn = vec4_sub(eye, at);
vec4 n = vec4_norm(vpn);
vec4 u = vec4_norm(vec4_cross(up, n));
vec4 v = vec4_norm(vec4_cross(n, u));
vec4 p = { eye.x, eye.y, eye.z, 1 };
mat4 m;
m.x.x = u.x; m.y.x = u.y; m.z.x = u.z; m.w.x = u.w;
m.x.y = v.x; m.y.y = v.y; m.z.y = v.z; m.w.y = v.w;
m.x.z = n.x; m.y.z = n.y; m.z.z = n.z; m.w.z = n.w;
m.x.w = p.x; m.y.w = p.y; m.z.w = p.z; m.w.w = p.w;
mat4 m_t = mat4_trans(m);
mat4 m_t_inv= mat4_inv(m_t);
return m_t_inv;
}
I am currently trying to look at the top of a cube I made. The cube is 1x1x1 and is centered at the origin. I am setting the model_view like so:
vec4 e = {0, 1, 0, 1};
vec4 a = {0, 0, 0, 0};
vec4 u = {0, 0, 1, 0};
model_view = look_at(e, a, u);
I believe I have the parameters correct. I want to look down at the origin from y=1.
Does the issue appear to be in my function? or have I misunderstood the parameters?
The fourth component of the axis vectors u, v and p has to be zero. In your case the fourth component of vpn is not zero, because eye is {0, 1, 0, 1}.
I recommend doing the computation of u, v and p with vec3 rather than vec4. However, you can fix the issue with setting vpn[3] = 0:
vec4 vpn = vec4_sub(eye, at);
vpn[3] = 0
I found this copy on github to link to but I am using the one downloaded from sourceforge.
My question about the way they design their matrix operations seems really strange to me.
For example, if I create a 4×4 matrix and set it's scale. Then I would like to rotate that previous matrix using the vectormath matrix library seems to reset the matrix back to an identity matrix then applies the rotation which doesn't make any sense why this would happen.
Take a look at this function to make a rotation
static inline void vmathM4MakeRotationY( VmathMatrix4 *result, float radians )
{
float s, c;
s = sinf( radians );
c = cosf( radians );
vmathV4MakeFromElems( &result->col0, c, 0.0f, -s, 0.0f );
vmathV4MakeYAxis( &result->col1 );
vmathV4MakeFromElems( &result->col2, s, 0.0f, c, 0.0f );
vmathV4MakeWAxis( &result->col3 );
}
Does this library expect you to keep multiple matrices around use one to apply rotations then multiply?
edit
this is some previous matrix math code that I used to rotate a matrix, it looks like this.
mat4_s mat4_rotateX(mat4_s* out, float angle, mat4_s* inMat)
{
float s = sinf(angle),
c = cosf(angle),
a10 = inMat->m[4],
a11 = inMat->m[5],
a12 = inMat->m[6],
a13 = inMat->m[7],
a20 = inMat->m[8],
a21 = inMat->m[9],
a22 = inMat->m[10],
a23 = inMat->m[11];
if (!out->m) {
for(size_t i = 0; i < 16; i++)
{
out->m[i] = inMat->m[i];
}
} else if (inMat->m != out->m) { // If the source and destination differ, copy the unchanged rows
out->m[0] = inMat->m[0];
out->m[1] = inMat->m[1];
out->m[2] = inMat->m[2];
out->m[3] = inMat->m[3];
out->m[12] = inMat->m[12];
out->m[13] = inMat->m[13];
out->m[14] = inMat->m[14];
out->m[15] = inMat->m[15];
}
out->m[4] = a10 * c + a20 * s;
out->m[5] = a11 * c + a21 * s;
out->m[6] = a12 * c + a22 * s;
out->m[7] = a13 * c + a23 * s;
out->m[8] = a10 * -s + a20 * c;
out->m[9] = a11 * -s + a21 * c;
out->m[10] = a12 * -s + a22 * c;
out->m[11] = a13 * -s + a23 * c;
return *out;
}
this is the process that I have to take to get vectormath to do the same thing.
mat4* mat4_rotate_y(mat4* out, const float angle){
mat4 m;
mat4_identity_v(&m);
vmathM4MakeRotationY(m, angle);
mat4_multi(out, out, m);
return out;
}
the multiplication code is fairly standard but the vmathM4MakeRotationY looks like this:
static inline void vmathM4MakeRotationZ( VmathMatrix4 *result, float radians )
{
float s, c;
s = sinf( radians );
c = cosf( radians );
vmathV4MakeFromElems( &result->col0, c, s, 0.0f, 0.0f );
vmathV4MakeFromElems( &result->col1, -s, c, 0.0f, 0.0f );
vmathV4MakeZAxis( &result->col2 );
vmathV4MakeWAxis( &result->col3 );
}
just for completeness the vmathV4Make_Axis looks like this:
static inline void vmathV4MakeZAxis(VmathVector4 *result) {
vmathV4MakeFromElems(result, 0.0f, 0.0f, 1.0f, 0.0f);
}
vmathV4MakeFromElms looks like this:
static inline void vmathV4MakeFromElems(VmathVector4 *result, float _x,
float _y, float _z, float _w) {
result->x = _x;
result->y = _y;
result->z = _z;
result->w = _w;
}
This function seems to do "Initialize the matrix to be a rotation transform" instead of what you are expecting, "Add a rotation transform to the current transform".
Like you say, you can work around by storing the rotation in a separate temporary matrix and then multiplying:
VmathMatrix4 temp;
vmathM4MakeRotationY(&temp, 1.23);
vmathM4Mul(&mytransform, &mytransform, &temp);
That is pretty much what a hypothetical vmathM4ApplyRotationY() would have to do anyway.
I try to switch my openGL code from using display list to using VBO.
In the code, I tried to draw a green-grid floor, which is composed by green lines in parallel with x-axis and green lines in parallel with z-axis.
But when the program runs, floor doesn't display. And I can't find where goes wrong.
Here is my code initialize VBO of floor:
//**********************************
//defined in head of my source code
struct BufferVBO1
{
GLfloat x;
GLfloat y;
GLfloat z;
GLfloat r;
GLfloat g;
GLfloat b;
};
struct IdVBO
{
GLuint id;
size_t bufsiz;
};
IdVBO vboGround;
//**************************************
glGenBuffers(1, &vboGround.id);
int groundSiz1 = ( (floorEdge_x_max-floorEdge_x_min)/(5*LENGTH_UNIT)+1 )*2 ;
int groundSiz2 = ( (floorEdge_z_max-floorEdge_z_min)/(5*LENGTH_UNIT)+1 )*2 ;
vboGround.bufsiz = groundSiz1+groundSiz2 ;
BufferVBO1 *groundBuf = new BufferVBO1 [vboGround.bufsiz];
for(int i=0, idx=0; idx<groundSiz1; ++i)
{
groundBuf[idx].x = floorEdge_x_min+i*5*LENGTH_UNIT;
groundBuf[idx].y = 0;
groundBuf[idx].z = floorEdge_z_min;
groundBuf[idx].r = 75/255.0;
groundBuf[idx].g = 1.0;
groundBuf[idx].b = 63/255.0;
++idx;
groundBuf[idx].x = floorEdge_x_min+i*5*LENGTH_UNIT;
groundBuf[idx].y = 0;
groundBuf[idx].z = floorEdge_z_max;
groundBuf[idx].r = 75/255.0;
groundBuf[idx].g = 1.0;
groundBuf[idx].b = 63/255.0;
++idx;
}
for(int i=0, idx=groundSiz1; idx<vboGround.bufsiz; ++i)
{
groundBuf[idx].x = floorEdge_x_min;
groundBuf[idx].y = 0;
groundBuf[idx].z = floorEdge_z_min+i*5*LENGTH_UNIT;
groundBuf[idx].r = 75/255.0;
groundBuf[idx].g = 1.0;
groundBuf[idx].b = 63/255.0;
++idx;
groundBuf[idx].x = floorEdge_x_max;
groundBuf[idx].y = 0;
groundBuf[idx].z = floorEdge_z_min+i*5*LENGTH_UNIT;
groundBuf[idx].r = 75/255.0;
groundBuf[idx].g = 1.0;
groundBuf[idx].b = 63/255.0;
++idx;
}
glBindBuffer(GL_ARRAY_BUFFER, vboGround.id);
glBufferData(GL_ARRAY_BUFFER, sizeof(BufferVBO1)*vboGround.bufsiz, groundBuf, GL_STATIC_DRAW);
delete [] groundBuf ;
glBindBuffer(GL_ARRAY_BUFFER, 0);
Here is the code to display:
glDisable(GL_LIGHTING);
glBindBuffer(GL_ARRAY_BUFFER, vboGround.id);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(BufferVBO1), (void*)0);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer( 3, GL_FLOAT, sizeof(BufferVBO1), (void*)(sizeof(GLfloat)*3) );
for(int i=0; i<vboGround.bufsiz; i+=2)
glDrawArrays(GL_LINE, i, 2);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnable(GL_LIGHTING);
I've tried to draw a simple GL_QUADS floor using VBO, and it works! But when I tried to draw green-grid floor, it doesn't display.
Tell me where is the problem.
The first argument to glDrawArrays() here is invalid:
glDrawArrays(GL_LINE, i, 2);
GL_LINE is not a valid primitive type. You need to use GL_LINES:
glDrawArrays(GL_LINES, i, 2);
GL_LINE may look very similar, but it's one of the possible arguments for glPolygonMode().
You should always call glGetError() when your OpenGL call does not behave as expected. In this case, it should have returned GL_INVALID_ENUM.
BTW, you can draw all the lines in a single glDrawArrays() call, instead of using a loop:
glDrawArrays(GL_LINES, 0, vboGround.bufsize);
Here's my current C code that does what I'd like to do, but it relies on glBlendEquation(GL_MAX) which is unavailable in WebGL. What I want is to render a wiggly fuzzy line. I could use a Gaussian blur but it would have to have a VERY large radius (16 pixels) and I expect it would be REALLY slow.
Note I've removed some gl state management code and a couple other things fore clarity but the code should work as is.
Existing C code:
static const char *pnt_vtx_shader =
"#version 110\n"
"varying vec2 uv;\n"
"void main() {\n"
" uv = (gl_MultiTexCoord0.st - 1.0f);\n"
" gl_Position = gl_Vertex;\n"
"}";
static const char *pnt_shader_src =
"#version 110\n"
"varying vec2 uv;\n"
"void main() {\n"
" gl_FragColor = vec4(exp(-4.5f*0.5f*log2(dot(uv,uv)+1.0f)));\n"
"}";
GLuint shader_prog ;
int samp;
float pw, ph;
float sco_verts[128*8*4];
int sco_ind[128*3*6];
void init(int width, int height, int num_samp)
{
pw = 0.5f*fmaxf(1.0f/24, 8.0f/width), ph = 0.5f*fmaxf(1.0f/24, 8.0f/height);
samp = num_samp;
// helper function, compiles and links the shader, prints out any errors
shader_prog = compile_program(pnt_vtx_shader, pnt_shader_src);
for(int i=0; i<samp; i++) {
sco_verts[(i*8+0)*4+0] = 0; sco_verts[(i*8+0)*4+1] = 2;
sco_verts[(i*8+1)*4+0] = 0; sco_verts[(i*8+1)*4+1] = 0;
sco_verts[(i*8+2)*4+0] = 1; sco_verts[(i*8+2)*4+1] = 2;
sco_verts[(i*8+3)*4+0] = 1; sco_verts[(i*8+3)*4+1] = 0;
sco_verts[(i*8+4)*4+0] = 1; sco_verts[(i*8+4)*4+1] = 2;
sco_verts[(i*8+5)*4+0] = 1; sco_verts[(i*8+5)*4+1] = 0;
sco_verts[(i*8+6)*4+0] = 2; sco_verts[(i*8+6)*4+1] = 2;
sco_verts[(i*8+7)*4+0] = 2; sco_verts[(i*8+7)*4+1] = 0;
}
for(int i=0; i<samp; i++) {
sco_ind[(i*6+0)*3+0] = i*8+0; sco_ind[(i*6+0)*3+1] = i*8+1; sco_ind[(i*6+0)*3+2] = i*8+3;
sco_ind[(i*6+1)*3+0] = i*8+0; sco_ind[(i*6+1)*3+1] = i*8+3; sco_ind[(i*6+1)*3+2] = i*8+2;
sco_ind[(i*6+2)*3+0] = i*8+2; sco_ind[(i*6+2)*3+1] = i*8+4; sco_ind[(i*6+2)*3+2] = i*8+5;
sco_ind[(i*6+3)*3+0] = i*8+2; sco_ind[(i*6+3)*3+1] = i*8+5; sco_ind[(i*6+3)*3+2] = i*8+3;
sco_ind[(i*6+4)*3+0] = i*8+4; sco_ind[(i*6+4)*3+1] = i*8+6; sco_ind[(i*6+4)*3+2] = i*8+7;
sco_ind[(i*6+5)*3+0] = i*8+4; sco_ind[(i*6+5)*3+1] = i*8+7; sco_ind[(i*6+5)*3+2] = i*8+5;
}
}
// getsamp does some averaging over samples
static float getsamp(const float *data, int len, int i, int w) {
float sum = 0, err = 0;
int l = IMAX(i-w, 0);
int u = IMIN(i+w, len);
for(int i = l; i < u; i++)
sum+= data[i];
return sum / (2*w);
}
// R holds a rotation matrix... it's the transpose of what you would give GL though
// because of reasons :P (I wrote code that did all the stuff from this program in
// software first and the GL version shares a bunch of code with that one)
// data is audio samples, [-1, 1], the length of the array is in len
void render_scope(float R[3][3], const float *data, int len)
{
// do the rotate/project ourselves because the GL matrix won't do the right
// thing if we just send it our verticies, we want wour tris to always be
// parrallel to the view plane, because we're actually drawing a fuzzy line
// not a 3D object
// also it makes it easier to match the software implementation
float px, py;
{
float s = getsamp(data, len, 0, len/96);
s=copysignf(log2f(fabsf(s)*3+1)/2, s);
float xt = -0.5f, yt = 0.2f*s, zt = 0.0f;
float x = R[0][0]*xt + R[1][0]*yt + R[2][0]*zt;
float y = R[0][1]*xt + R[1][1]*yt + R[2][1]*zt;
float z = R[0][2]*xt + R[1][2]*yt + R[2][2]*zt;
const float zvd = 1/(z+2);
px=x*zvd*4/3; py=y*zvd*4/3;
}
for(int i=0; i<samp; i++) {
float s = getsamp(data, len, (i+1)*len/(samp), len/96);
s=copysignf(log2f(fabsf(s)*3+1)/2, s);
float xt = (i+1 - (samp)/2.0f)*(1.0f/(samp)), yt = 0.2f*s, zt = 0.0f;
float x = R[0][0]*xt + R[1][0]*yt + R[2][0]*zt;
float y = R[0][1]*xt + R[1][1]*yt + R[2][1]*zt;
float z = R[0][2]*xt + R[1][2]*yt + R[2][2]*zt;
const float zvd = 1/(z+2);
x=x*zvd*4/3; y=y*zvd*4/3;
const float dx=x-px, dy=y-py;
const float d = 1/hypotf(dx, dy);
const float tx=dx*d*pw, ty=dy*d*pw;
const float nx=-dy*d*pw, ny=dx*d*ph;
sco_verts[(i*8+0)*4+2] = px-nx-tx; sco_verts[(i*8+0)*4+3] = py-ny-ty;
sco_verts[(i*8+1)*4+2] = px+nx-tx; sco_verts[(i*8+1)*4+3] = py+ny-ty;
sco_verts[(i*8+2)*4+2] = px-nx ; sco_verts[(i*8+2)*4+3] = py-ny;
sco_verts[(i*8+3)*4+2] = px+nx ; sco_verts[(i*8+3)*4+3] = py+ny;
sco_verts[(i*8+4)*4+2] = x-nx ; sco_verts[(i*8+4)*4+3] = y-ny;
sco_verts[(i*8+5)*4+2] = x+nx ; sco_verts[(i*8+5)*4+3] = y+ny;
sco_verts[(i*8+6)*4+2] = x-nx+tx; sco_verts[(i*8+6)*4+3] = y-ny+ty;
sco_verts[(i*8+7)*4+2] = x+nx+tx; sco_verts[(i*8+7)*4+3] = y+ny+ty;
px=x,py=y;
}
glEnable(GL_BLEND);
glBlendEquation(GL_MAX);
glUseProgram(shader_prog);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof(float)*4, sco_verts);
glVertexPointer(2, GL_FLOAT, sizeof(float)*4, sco_verts + 2);
glDrawElements(GL_TRIANGLES, samp*3*6, GL_UNSIGNED_INT, sco_ind);
}
Here's a screenshot from a test app, I'm not sure the line width is right in this screen shot... but meh it gives the idea, also I'd be using way more points so the lines would be smoother.
I have the part of the following C code that uses data from a file names WMM.COF and uses the data stored in the file to compute the magnetic field of the earth. The program works perfectly except I can't have the program access the external file; I want to have all of the data already stored in the program. I tried using a structure array to replicate the data and then put the array into a string but this causes an error in the program and doesn't produce the correct results. Here is the code of the program that I'm trying to modify.
static void E0000(int IENTRY, int *maxdeg, double alt, double glat, double glon, double time, double *dec, double *dip, double *ti, double *gv)
{
static int maxord,i,icomp,n,m,j,D1,D2,D3,D4;
static double c[13][13],cd[13][13],tc[13][13],dp[13][13],snorm[169],
sp[13],cp[13],fn[13],fm[13],pp[13],k[13][13],pi,dtr,a,b,re,
a2,b2,c2,a4,b4,c4,epoch,gnm,hnm,dgnm,dhnm,flnmj,otime,oalt,
olat,olon,dt,rlon,rlat,srlon,srlat,crlon,crlat,srlat2,
crlat2,q,q1,q2,ct,st,r2,r,d,ca,sa,aor,ar,br,bt,bp,bpp,
par,temp1,temp2,parp,bx,by,bz,bh;
static char model[20], c_str[81], c_new[5];
static double *p = snorm;
char answer;
FILE *wmmdat;
wmmdat = fopen("WMM.COF","r");
/* INITIALIZE CONSTANTS */
maxord = *maxdeg;
sp[0] = 0.0;
cp[0] = *p = pp[0] = 1.0;
dp[0][0] = 0.0;
a = 6378.137;
b = 6356.7523142;
re = 6371.2;
a2 = a*a;
b2 = b*b;
c2 = a2-b2;
a4 = a2*a2;
b4 = b2*b2;
c4 = a4 - b4;
/* READ WORLD MAGNETIC MODEL SPHERICAL HARMONIC COEFFICIENTS */
c[0][0] = 0.0;
cd[0][0] = 0.0;
fgets(c_str, 80, wmmdat);
S3:
if (fgets(c_str, 80, wmmdat) == NULL) goto S4;
/* CHECK FOR LAST LINE IN FILE */
for (i=0; i<4 && (c_str[i] != '\0'); i++)
{
c_new[i] = c_str[i];
c_new[i+1] = '\0';
}
icomp = strcmp("9999", c_new);
if (icomp == 0) goto S4;
/* END OF FILE NOT ENCOUNTERED, GET VALUES */
sscanf(c_str,"%d%d%lf%lf%lf%lf",&n,&m,&gnm,&hnm,&dgnm,&dhnm);
if (n > maxord) goto S4;
if (m > n || m < 0.0)
{
fprintf(stderr, "Corrupt record in model file WMM.COF\n");
exit(1);
}
if (m <= n)
{
c[m][n] = gnm;
cd[m][n] = dgnm;
if (m != 0)
{
c[n][m-1] = hnm;
cd[n][m-1] = dhnm;
}
}
goto S3;
/* CONVERT SCHMIDT NORMALIZED GAUSS COEFFICIENTS TO UNNORMALIZED */
S4:
*snorm = 1.0;
fm[0] = 0.0;
for (n=1; n<=maxord; n++)
{
*(snorm+n) = *(snorm+n-1)*(double)(2*n-1)/(double)n;
j = 2;
for (m=0,D1=1,D2=(n-m+D1)/D1; D2>0; D2--,m+=D1)
{
k[m][n] = (double)(((n-1)*(n-1))-(m*m))/(double)((2*n-1)*(2*n-3));
if (m > 0)
{
flnmj = (double)((n-m+1)*j)/(double)(n+m);
*(snorm+n+m*13) = *(snorm+n+(m-1)*13)*sqrt(flnmj);
j = 1;
c[n][m-1] = *(snorm+n+m*13)*c[n][m-1];
cd[n][m-1] = *(snorm+n+m*13)*cd[n][m-1];
}
c[m][n] = *(snorm+n+m*13)*c[m][n];
cd[m][n] = *(snorm+n+m*13)*cd[m][n];
}
fn[n] = (double)(n+1);
fm[n] = (double)n;
}
k[1][1] = 0.0;
otime = oalt = olat = olon = -1000.0;
fclose(wmmdat);
return;
The code that I came up with to include the data in the program is as follows:
struct wmm
{
int alpha;
int beta;
float gamma;
float delta;
float epsilon;
float zeta;
}book[90]= {{1, 0, -29496.6, 0.0, 11.6, 0.0},
{1, 1, -1586.3, 4944.4, 16.5, -25.9},
{2, 0, -2396.6, 0.0, -12.1, 0.0},
{2, 1, 3026.1, -2707.7, -4.4, -22.5},
{2, 2, 1668.6, -576.1, 1.9, -11.8},
{3, 0, 1340.1, 0.0, 0.4, 0.0},
/* 50+ similar lines of code */
{12, 8, -0.4, 0.1, 0.0, 0.0},
{12, 9, -0.4, 0.3, 0.0, 0.0},
{12, 10, 0.2, -0.9, 0.0, 0.0},
{12, 11, -0.8, -0.2, -0.1, 0.0},
{12, 12, 0.0, 0.9, 0.1, 0.0}};
for (i = 0; i < 90 && offset < buf_size; i++)
{
offset += snprintf(c_str + offset,buf_size - offset, "%d %d %7.1lf %7.1lf %7.1lf %7.1lf \n", book[i].alpha, book[i].beta , book[i].gamma , book[i].delta, book[i].epsilon, book[i].zeta);
}
sscanf(c_str,"%d%d%lf%lf%lf%lf",&n,&m,&gnm,&hnm,&dgnm,&dhnm);
The problem is the snprintf causes the program to freeze and terminate every time it is placed in the program. When the code that I wrote is run on it's own it seems to create c_str properly except when I try to view the variables n,m,gnm,hnm,dgnm, and dhnm only a single value for each is displayed.
I need to continue in an answer due to a lack of space/formatting in a comment.
First of all, you do have 90 entries but you can let the compiler figure out how many entries the book array needs:
struct wmm {
int alpha;
int beta;
float gamma;
float delta;
float epsilon;
float zeta;
} book[] = {
{1, 0, -29496.6, 0.0, 11.6, 0.0},
{1, 1, -1586.3, 4944.4, 16.5, -25.9},
/* ... */
{12, 12, 0.0, 0.9, 0.1, 0.0}
};
And, more importantly, you don't need to put them in a string and pull them back out when you already have them on hand:
for(i = 0; i < sizeof(book)/sizeof(book[0]); ++i) {
n = book[i].alpha;
m = book[i].beta;
gnm = book[i].gamma;
hnm = book[i].delta;
dgnm = book[i].epsilon;
dhnm = book[i].zeta;
/* Do whatever you need to do with the above variables. */
}
This will neatly side step whatever buffer overflow you were causing with your snprintf.
Your c_str is only a char[81] and you're going through your loop 90 times and incrementing your offset into c_str each time; so, you'll run off the end of c_str before long and then you'll tell snprintf to scribble all over unallocated memory. Hence your segfault.