Simple implementation of 4D cross-section [duplicate] - c++

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
i want to try writing a playground similar to this 4D toys,
so i started learning opengl.
from my current understanding, people use VBOs and uniform transformation matrix for mostly-static objects
(like cubes, skeletal animations etc., which usually just involves transformations)
i also heard that morphing between models also uses VBOs for caching both models, since both of them will be well defined and not so much intermediates.
but in the 4D toys mentioned above, objects are morphing and clipped a lot.
and it is likely that there is no defined models, and many transitions in between.
(it might be a simple square now, and a spiky ball clipped in half later ).
in this case, is updating-vertex-VBO-per-frame or Vertex Arrays(which i saw in another question) a suitable solution?

For starters I would use 4D -> 3D projection instead of cut by hyperplane. The result is not the same but will get you closer to your goal (so you can latter upgrade this to cut). So similarly like in 3D -> 2D conversions used in graphics you got 2 choices one is using perspective projection and second is just ignoring the 4th dimension coordinate while rendering. I will use the latter as it is simpler.
structures
To make this as simple as I can I will use wire-frame instead of BR rendering. So you need to handle 4D mesh (wire-frame). I would use 2 tables:
double pnt[]; // 4D point list (x,y,z,u)
int lin[]; // lines point indexes (i0,i1)
first one stores all the vertexes of your mesh and the second hold index pairs of points connected by lines in wire-frame representation.
transforms
If I would only ignore the 4th coordinate then we would not get the desired functionality. So to make the 4th dimension work we need to add 4D transform to orient our mesh in 4D before rendering. So use homogenous transform matrix and lets call ir rep. In 4D it should be 5x5 orthonormal matrix with 4x4 rotation part rot.
To make this even easier avoid smooth rotations for now (as in 4D that is not as easy) and compute random rotation 4x4 matrix instead. So just set all the cells randomly <-1,+1>. Handle each row as basis vector. To make them orthonormal just make them unit and exploit cross product. For more info see:
How to generate a randomly oriented, high dimension circle in python?
render
simply convert point table by your transform matrix
(x',y',z',u',W) = rep * (x,y,z,u,1)
then take the (x,y,z`) and render ...
Here simple OpenGL/C++ example of 4D hyper cube:
//---------------------------------------------------------------------------
//--- Mesh 4D: ver 0.000 ----------------------------------------------------
//---------------------------------------------------------------------------
#ifndef _mesh4D_h
#define _mesh4D_h
//---------------------------------------------------------------------------
#include <math.h>
#include "nd_math.h"
#include "list.h"
//---------------------------------------------------------------------------
const double pi = M_PI;
const double pi2 =2.0*M_PI;
const double pipol=0.5*M_PI;
const double deg=M_PI/180.0;
const double rad=180.0/M_PI;
//---------------------------------------------------------------------------
class mesh4D
{
public:
matrix<5> rep; // 4D uniform 5x5 transform matrix
List<double> pnt; // 4D point list (x,y,z,u)
List<int> lin; // lines point indexes (i0,i1)
mesh4D() {}
mesh4D(mesh4D& a) { *this=a; }
~mesh4D() {}
mesh4D* operator = (const mesh4D *a) { *this=*a; return this; }
//mesh4D* operator = (const mesh4D &a) { ...copy... return this; }
void set_randomrep(); // random oriented uniform 4D transform matrix with origin (0,0,0,0)
void set_hypercube(double a);
void draw();
};
//---------------------------------------------------------------------------
void mesh4D::set_randomrep()
{
int i,j;
matrix<4> rot;
rep.unit();
rot.rnd();
rot.orthonormal();
for (i=0;i<4;i++)
for (j=0;j<4;j++)
rep[i][j]=rot[i][j];
}
void mesh4D::set_hypercube(double a)
{
rep.unit(); // reset orientation
pnt.num=0; // clear point list
lin.num=0; // clear line list
pnt.add(-a); pnt.add(-a); pnt.add(-a); pnt.add(-a);
pnt.add(+a); pnt.add(-a); pnt.add(-a); pnt.add(-a);
pnt.add(-a); pnt.add(+a); pnt.add(-a); pnt.add(-a);
pnt.add(+a); pnt.add(+a); pnt.add(-a); pnt.add(-a);
pnt.add(-a); pnt.add(-a); pnt.add(+a); pnt.add(-a);
pnt.add(+a); pnt.add(-a); pnt.add(+a); pnt.add(-a);
pnt.add(-a); pnt.add(+a); pnt.add(+a); pnt.add(-a);
pnt.add(+a); pnt.add(+a); pnt.add(+a); pnt.add(-a);
pnt.add(-a); pnt.add(-a); pnt.add(-a); pnt.add(+a);
pnt.add(+a); pnt.add(-a); pnt.add(-a); pnt.add(+a);
pnt.add(-a); pnt.add(+a); pnt.add(-a); pnt.add(+a);
pnt.add(+a); pnt.add(+a); pnt.add(-a); pnt.add(+a);
pnt.add(-a); pnt.add(-a); pnt.add(+a); pnt.add(+a);
pnt.add(+a); pnt.add(-a); pnt.add(+a); pnt.add(+a);
pnt.add(-a); pnt.add(+a); pnt.add(+a); pnt.add(+a);
pnt.add(+a); pnt.add(+a); pnt.add(+a); pnt.add(+a);
// A0
lin.add( 0+0); lin.add( 0+1);
lin.add( 0+1); lin.add( 0+3);
lin.add( 0+3); lin.add( 0+2);
lin.add( 0+2); lin.add( 0+0);
// A1
lin.add( 4+0); lin.add( 4+1);
lin.add( 4+1); lin.add( 4+3);
lin.add( 4+3); lin.add( 4+2);
lin.add( 4+2); lin.add( 4+0);
// A=A0+A1
lin.add( 0+0); lin.add( 4+0);
lin.add( 0+1); lin.add( 4+1);
lin.add( 0+2); lin.add( 4+2);
lin.add( 0+3); lin.add( 4+3);
// B0
lin.add( 8+0); lin.add( 8+1);
lin.add( 8+1); lin.add( 8+3);
lin.add( 8+3); lin.add( 8+2);
lin.add( 8+2); lin.add( 8+0);
// B1
lin.add(12+0); lin.add(12+1);
lin.add(12+1); lin.add(12+3);
lin.add(12+3); lin.add(12+2);
lin.add(12+2); lin.add(12+0);
// B=B0+B1
lin.add( 8+0); lin.add(12+0);
lin.add( 8+1); lin.add(12+1);
lin.add( 8+2); lin.add(12+2);
lin.add( 8+3); lin.add(12+3);
// hyper cube = A+B
lin.add( 0+0); lin.add( 8+0);
lin.add( 0+1); lin.add( 8+1);
lin.add( 0+2); lin.add( 8+2);
lin.add( 0+3); lin.add( 8+3);
lin.add( 0+4); lin.add( 8+4);
lin.add( 0+5); lin.add( 8+5);
lin.add( 0+6); lin.add( 8+6);
lin.add( 0+7); lin.add( 8+7);
}
//---------------------------------------------------------------------------
void mesh4D::draw()
{
int i,j;
double _zero=1e-3;
vector<5> a,b;
glBegin(GL_LINES);
for (i=0;i<lin.num;)
{
// extrac first point
j=lin[i]*4; i++;
a.a[0]=pnt[j]; j++;
a.a[1]=pnt[j]; j++;
a.a[2]=pnt[j]; j++;
a.a[3]=pnt[j]; j++;
a.a[4]=1.0; // W=1
// extrac second point
j=lin[i]*4; i++;
b.a[0]=pnt[j]; j++;
b.a[1]=pnt[j]; j++;
b.a[2]=pnt[j]; j++;
b.a[3]=pnt[j]; j++;
b.a[4]=1.0; // W=1
// transform
a=rep*a;
b=rep*b;
// render
glVertex3dv(a.a); // use just x,y,z
glVertex3dv(b.a); // use just x,y,z
}
glEnd();
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
I used mine dynamic list.h template so:
List<double> xxx; is the same as double xxx[];
xxx.add(5); adds 5 to end of the list
xxx[7] access array element (safe)
xxx.dat[7] access array element (unsafe but fast direct access)
xxx.num is the actual used size of the array
xxx.reset() clears the array and set xxx.num=0
xxx.allocate(100) preallocate space for 100 items
the nd_math.h is mine lib for N-Dimensional computations. What you need is just 4D,5D vector and 4x4, 5x5 matrix math from linear algebra.
Both libs are a bit big in size and also legal issues prevent me to share their code here.
The usage is simple:
// globals and init
mesh4D mesh
double animx=-50.0,danimx=0.0;
double animy= 0.0,danimy=2.0;
mesh.set_hypercube(0.5);
// render
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D( -2.0, 2.0, -2.0, 2.0 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotated(animx,1.0,0.0,0.0);
glRotated(animy,0.0,1.0,0.0);
mesh.draw();
glFlush();
SwapBuffers(hdc);
// some timer
animx+=danimx; if (animx>=360.0) animx-=360.0;
animy+=danimy; if (animy>=360.0) animy-=360.0;
call_render_here();
// on key press or mouse wheel or what ever
mesh.set_randomrep();
And here preview for some rep rotations ...
so this way you can render any wire-frame mesh (even BR rendering should works this way).
If you want to upgrade to the cut then you should take each Wire-frame line and compute its intersection with cutting hyperplane. If we chose hyperplane that goes through point
O(0,0,0,u_cut)
and has normal
N(0,0,0,1)
Then the task will simplify a lot. there are 3 options. Lets consider edge line with endpoints A,B:
no intersection
((A.u > u_cut)&&(B.u > u_cut)) || ((A.u < u_cut)&&(B.u < u_cut))
just ignore such edge
1 intersection
((A.u >= u_cut)&&(B.u <= u_cut)) || ((A.u <= u_cut)&&(B.u >= u_cut))
so compute the intersection via linear interpolation
x = A.x + (B.x-A.x)*(u_cut-A.u)/(B.u-A.u)
y = A.y + (B.y-A.y)*(u_cut-A.u)/(B.u-A.u)
z = A.z + (B.z-A.z)*(u_cut-A.u)/(B.u-A.u)
and remember such point and also edge to which it belongs to.
fully inside
(A.u == u_cut)&&(B.u == u_cut)
just remember both endpoints and also render this edge.
After all edges are processed this way then you need to analyze the remembered intersection points and create new edges from them based on connectivity info between edges. I did not do this yet so I can not help with this. I would try to connect remembered points sharing the same neighbor but not sure if that is enough in 4D.
For more info take a look at related QAs I found or answer:
Visualising 4D objects in OpenGL
4D to 3D perspective projection
4D rotations
ND vector and square matrix math template
reper4D
configurable ND rendering engine + hyperspiral
[Edit1] code with perspective
//---------------------------------------------------------------------------
//--- Mesh 4D: ver 0.001 ----------------------------------------------------
//---------------------------------------------------------------------------
#ifndef _mesh4D_h
#define _mesh4D_h
//---------------------------------------------------------------------------
#include <math.h>
#include "nd_math.h"
#include "list.h"
//---------------------------------------------------------------------------
const double pi = M_PI;
const double pi2 =2.0*M_PI;
const double pipol=0.5*M_PI;
const double deg=M_PI/180.0;
const double rad=180.0/M_PI;
//---------------------------------------------------------------------------
class mesh4D
{
public:
matrix<5> rep; // 4D uniform 5x5 transform matrix
List<double> pnt; // 4D point list (x,y,z,u)
List<int> lin; // lines point indexes (i0,i1)
mesh4D() {}
mesh4D(mesh4D& a) { *this=a; }
~mesh4D() {}
mesh4D* operator = (const mesh4D *a) { *this=*a; return this; }
//mesh4D* operator = (const mesh4D &a) { ...copy... return this; }
void set_randomrep(); // random oriented uniform 4D transform matrix with origin (0,0,0,0)
void set_hypercube(double a);
void draw();
};
//---------------------------------------------------------------------------
void mesh4D::set_randomrep()
{
int i,j;
matrix<4> rot;
rot.rnd();
rot.orthonormal();
for (i=0;i<4;i++)
for (j=0;j<4;j++)
rep[i][j]=rot[i][j];
}
//---------------------------------------------------------------------------
void mesh4D::set_hypercube(double a)
{
rep.unit(); // reset orientation
rep[0][4]=0.0; // set position
rep[1][4]=0.0;
rep[2][4]=0.0;
rep[3][4]=3.0*a;
pnt.num=0; // clear point list
lin.num=0; // clear line list
pnt.add(-a); pnt.add(-a); pnt.add(-a); pnt.add(-a);
pnt.add(+a); pnt.add(-a); pnt.add(-a); pnt.add(-a);
pnt.add(-a); pnt.add(+a); pnt.add(-a); pnt.add(-a);
pnt.add(+a); pnt.add(+a); pnt.add(-a); pnt.add(-a);
pnt.add(-a); pnt.add(-a); pnt.add(+a); pnt.add(-a);
pnt.add(+a); pnt.add(-a); pnt.add(+a); pnt.add(-a);
pnt.add(-a); pnt.add(+a); pnt.add(+a); pnt.add(-a);
pnt.add(+a); pnt.add(+a); pnt.add(+a); pnt.add(-a);
pnt.add(-a); pnt.add(-a); pnt.add(-a); pnt.add(+a);
pnt.add(+a); pnt.add(-a); pnt.add(-a); pnt.add(+a);
pnt.add(-a); pnt.add(+a); pnt.add(-a); pnt.add(+a);
pnt.add(+a); pnt.add(+a); pnt.add(-a); pnt.add(+a);
pnt.add(-a); pnt.add(-a); pnt.add(+a); pnt.add(+a);
pnt.add(+a); pnt.add(-a); pnt.add(+a); pnt.add(+a);
pnt.add(-a); pnt.add(+a); pnt.add(+a); pnt.add(+a);
pnt.add(+a); pnt.add(+a); pnt.add(+a); pnt.add(+a);
// A0
lin.add( 0+0); lin.add( 0+1);
lin.add( 0+1); lin.add( 0+3);
lin.add( 0+3); lin.add( 0+2);
lin.add( 0+2); lin.add( 0+0);
// A1
lin.add( 4+0); lin.add( 4+1);
lin.add( 4+1); lin.add( 4+3);
lin.add( 4+3); lin.add( 4+2);
lin.add( 4+2); lin.add( 4+0);
// A=A0+A1
lin.add( 0+0); lin.add( 4+0);
lin.add( 0+1); lin.add( 4+1);
lin.add( 0+2); lin.add( 4+2);
lin.add( 0+3); lin.add( 4+3);
// B0
lin.add( 8+0); lin.add( 8+1);
lin.add( 8+1); lin.add( 8+3);
lin.add( 8+3); lin.add( 8+2);
lin.add( 8+2); lin.add( 8+0);
// B1
lin.add(12+0); lin.add(12+1);
lin.add(12+1); lin.add(12+3);
lin.add(12+3); lin.add(12+2);
lin.add(12+2); lin.add(12+0);
// B=B0+B1
lin.add( 8+0); lin.add(12+0);
lin.add( 8+1); lin.add(12+1);
lin.add( 8+2); lin.add(12+2);
lin.add( 8+3); lin.add(12+3);
// hyper cube = A+B
lin.add( 0+0); lin.add( 8+0);
lin.add( 0+1); lin.add( 8+1);
lin.add( 0+2); lin.add( 8+2);
lin.add( 0+3); lin.add( 8+3);
lin.add( 0+4); lin.add( 8+4);
lin.add( 0+5); lin.add( 8+5);
lin.add( 0+6); lin.add( 8+6);
lin.add( 0+7); lin.add( 8+7);
}
//---------------------------------------------------------------------------
void mesh4D::draw()
{
int i,j;
const double _zero=1e-3;
double focal_length=1.0;
vector<5> a,b;
glBegin(GL_LINES);
for (i=0;i<lin.num;)
{
// extrac first point
j=lin[i]*4; i++;
a.a[0]=pnt[j]; j++;
a.a[1]=pnt[j]; j++;
a.a[2]=pnt[j]; j++;
a.a[3]=pnt[j]; j++;
a.a[4]=1.0; // W=1
// extrac second point
j=lin[i]*4; i++;
b.a[0]=pnt[j]; j++;
b.a[1]=pnt[j]; j++;
b.a[2]=pnt[j]; j++;
b.a[3]=pnt[j]; j++;
b.a[4]=1.0; // W=1
// transform
a=rep*a;
b=rep*b;
// perspective: camera projection plane u=0, focus at (0,0,0,-focal_length)
if (a[3]>=0.0) a*=divide(focal_length,a[3]+focal_length); else a.zero();
if (b[3]>=0.0) b*=divide(focal_length,b[3]+focal_length); else b.zero();
// render
glVertex3dv(a.a); // use just x,y,z
glVertex3dv(b.a); // use just x,y,z
}
glEnd();
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
And preview:
[Edit2] solid mesh and cross-section
so I changed the architecture quite a bit. I moved the 4D 5x5 homogenuous transform matrix (reper4D) to separate file and added colors and mesh definition by 4D simplexes (4 point 4 side tetrahedrons). The cut is simply computing the intersection (as described above) of simplex and cutting hyperplane resulting in either 3 points (triangle) , 4 points (tetrahedron) or 0 points. Which can be rendered easily (no need to analyze the connections between edges). For more info see this:
Calculate 3D cross section of 4D shape (tesseract)
Btw. I think this is how Miegakure works. Here updated code:
//---------------------------------------------------------------------------
//--- Mesh 4D: ver 1.000 ----------------------------------------------------
//---------------------------------------------------------------------------
#ifndef _mesh4D_h
#define _mesh4D_h
//---------------------------------------------------------------------------
#include "list.h"
#include "reper4D.h"
//---------------------------------------------------------------------------
class mesh4D
{
public:
reper4D rep; // 4D uniform 5x5 transform matrix
List<double> pnt; // 4D point list (x,y,z,w)
List<int> lin; // 4D wireframe (i0,i1)
List<int> fac; // 4D simplexes (i0,i1,i2,i3)
List<DWORD> col; // simplex colors (RGB)
mesh4D() {}
mesh4D(mesh4D& a) { *this=a; }
~mesh4D() {}
mesh4D* operator = (const mesh4D *a) { *this=*a; return this; }
//mesh4D* operator = (const mesh4D &a) { ...copy... return this; }
void set_hypercube(double a);
void draw_cut(double w_cut); // render cross section by w=w_cut hyperplane
void draw (double focal_length=-1.0,double w_near=-1.0); // render mesh (focal_length<0) -> no perspective, else perspective view in W+ direction
void draw_wireframe(double focal_length=-1.0,double w_near=-1.0); // render wireframe (focal_length<0) -> no perspective, else perspective view in W+ direction
};
//---------------------------------------------------------------------------
void mesh4D::set_hypercube(double a)
{
const double tab_pnt[]=
{
-a, -a, -a, -a,
+a, -a, -a, -a,
-a, +a, -a, -a,
+a, +a, -a, -a,
-a, -a, +a, -a,
+a, -a, +a, -a,
-a, +a, +a, -a,
+a, +a, +a, -a,
-a, -a, -a, +a,
+a, -a, -a, +a,
-a, +a, -a, +a,
+a, +a, -a, +a,
-a, -a, +a, +a,
+a, -a, +a, +a,
-a, +a, +a, +a,
+a, +a, +a, +a,
};
const int tab_lin[]=
{
// A0
0+0, 0+1,
0+1, 0+3,
0+3, 0+2,
0+2, 0+0,
// A1
4+0, 4+1,
4+1, 4+3,
4+3, 4+2,
4+2, 4+0,
// A=A0+A1
0+0, 4+0,
0+1, 4+1,
0+2, 4+2,
0+3, 4+3,
// B0
8+0, 8+1,
8+1, 8+3,
8+3, 8+2,
8+2, 8+0,
// B1
12+0, 12+1,
12+1, 12+3,
12+3, 12+2,
12+2, 12+0,
// B=B0+B1
8+0, 12+0,
8+1, 12+1,
8+2, 12+2,
8+3, 12+3,
// hyper cube = A+B
0+0, 8+0,
0+1, 8+1,
0+2, 8+2,
0+3, 8+3,
0+4, 8+4,
0+5, 8+5,
0+6, 8+6,
0+7, 8+7,
};
// 5x simplex per cube
#define _cube(a0,a1,a2,a3,a4,a5,a6,a7) a1,a2,a4,a7, a0,a1,a2,a4, a2,a4,a6,a7, a1,a2,a3,a7, a1,a4,a5,a7
// 4D hypercube = 8 cubes
const int tab_fac[]=
{
_cube( 0, 1, 2, 3, 4, 5, 6, 7),
_cube( 0, 1, 2, 3, 8, 9,10,11),
_cube( 4, 5, 6, 7,12,13,14,15),
_cube( 8, 9,10,11,12,13,14,15),
_cube( 0, 1, 4, 5, 8, 9,12,13),
_cube( 0, 2, 4, 6, 8,10,12,14),
_cube( 1, 3, 5, 7, 9,11,13,15),
_cube( 2, 3, 6, 7,10,11,14,15),
};
#undef _cube
const DWORD tab_col[]=
{
// BBGGRR, BBGGRR, BBGGRR, BBGGRR, BBGGRR,
0x00FF0000,0x00FF0000,0x00FF0000,0x00FF0000,0x00FF0000,
0x0000FF00,0x0000FF00,0x0000FF00,0x0000FF00,0x0000FF00,
0x000000FF,0x000000FF,0x000000FF,0x000000FF,0x000000FF,
0x0000FFFF,0x0000FFFF,0x0000FFFF,0x0000FFFF,0x0000FFFF,
0x00FF00FF,0x00FF00FF,0x00FF00FF,0x00FF00FF,0x00FF00FF,
0x00FFFF00,0x00FFFF00,0x00FFFF00,0x00FFFF00,0x00FFFF00,
0x00FFFFFF,0x00FFFFFF,0x00FFFFFF,0x00FFFFFF,0x00FFFFFF,
0x004080FF,0x004080FF,0x004080FF,0x004080FF,0x004080FF,
};
int i,n;
vector<4> p;
rep.reset();
pnt.num=0; for (i=0,n=sizeof(tab_pnt)/sizeof(tab_pnt[0]);i<n;i++) pnt.add(tab_pnt[i]);
lin.num=0; for (i=0,n=sizeof(tab_lin)/sizeof(tab_lin[0]);i<n;i++) lin.add(tab_lin[i]);
fac.num=0; for (i=0,n=sizeof(tab_fac)/sizeof(tab_fac[0]);i<n;i++) fac.add(tab_fac[i]);
col.num=0; for (i=0,n=sizeof(tab_col)/sizeof(tab_col[0]);i<n;i++) col.add(tab_col[i]);
}
//---------------------------------------------------------------------------
void mesh4D::draw_cut(double w_cut)
{
const double _zero=1e-6;
const int edge2[]={0,1,0,2,0,3,1,2,2,3,3,1,-1}; // simplex wireframe i0,i1
const int edge3[]={0,1,2,3,0,1,3,1,2,3,2,0,-1}; // simplex triangles i0,i1,i2
int e,i,j,k,k0,k1,k2,inside[4];
DWORD rgb;
vector<4> p[4],q[4];
vector<3> xyz[4],nor,a,b;
for (i=0;i<fac.num;)
{
rgb=col[i>>2];
// extrac points (x,y,z,w)
for (k=0;k<4;k++)
{
j=fac[i]*4; i++;
p[k].a[0]=pnt[j]; j++;
p[k].a[1]=pnt[j]; j++;
p[k].a[2]=pnt[j]; j++;
p[k].a[3]=pnt[j]; j++;
// transform
rep.l2g(p[k],p[k]);
inside[k]=1;
}
// process edge2 and compute cross section cut intersection points
for (e=0,k=0;edge2[e]>=0;)
{
k0=edge2[e]; e++;
k1=edge2[e]; e++;
// fully inside
if (fabs(p[k0][3]-w_cut)+fabs(p[k1][3]-w_cut)<=_zero)
{
if ((k<4)&&(inside[k0])){ q[k]=p[k0]; k++; inside[k0]=0; }
if ((k<4)&&(inside[k1])){ q[k]=p[k1]; k++; inside[k1]=0; }
continue;
}
// no intersection
if (((p[k0][3]> w_cut)&&(p[k1][3]> w_cut))||((p[k0][3]< w_cut)&&(p[k1][3]< w_cut))) continue;
// 1 intersection
if (k<4)
{
q[k]=p[k1]-p[k0];
q[k]*=divide(w_cut-p[k0][3],p[k1][3]-p[k0][3]);
q[k]+=p[k0];
q[k][3]=w_cut;
k++;
continue;
}
}
// 4D -> 3D vector
for (k0=0;k0<k;k0++) for (k1=0;k1<3;k1++) xyz[k0][k1]=q[k0][k1];
// render triangle
if (k==3)
{
// normal
a=xyz[1]-xyz[0];
b=xyz[2]-xyz[1];
nor.cross(a,b);
nor.unit();
// render
glBegin(GL_TRIANGLES);
glNormal3dv(nor.a);
glColor4ubv((BYTE*)(&rgb));
glVertex3dv(xyz[0].a);
glVertex3dv(xyz[1].a);
glVertex3dv(xyz[2].a);
glEnd();
}
// render simplex
if (k==4)
for (e=0;edge3[e]>=0;)
{
k0=edge3[e]; e++;
k1=edge3[e]; e++;
k2=edge3[e]; e++;
// normal
a=xyz[k1]-xyz[k0];
b=xyz[k2]-xyz[k1];
nor.cross(a,b);
nor.unit();
// render
glBegin(GL_TRIANGLES);
glNormal3dv(nor.a);
glColor4ubv((BYTE*)(&rgb));
glVertex3dv(xyz[k0].a);
glVertex3dv(xyz[k1].a);
glVertex3dv(xyz[k2].a);
glEnd();
}
}
}
//---------------------------------------------------------------------------
void mesh4D::draw(double focal_length,double w_near)
{
const int edge3[]={0,1,2,3,0,1,3,1,2,3,2,0,-1}; // simplex triangles i0,i1,i2
int i,j,k,k0,k1,k2;
DWORD rgb;
vector<4> p;
vector<3> xyz[4],nor,a,b;
// 4D simplexes
glColor3f(0.3,0.3,0.3);
for (i=0;i<fac.num;)
{
rgb=col[i>>2];
// extrac points (x,y,z,w)
for (k=0;k<4;k++)
{
j=fac[i]*4; i++;
p[0]=pnt[j]; j++;
p[1]=pnt[j]; j++;
p[2]=pnt[j]; j++;
p[3]=pnt[j]; j++;
// transform
rep.l2g(p,p);
// perspective projection
if (focal_length>0.0)
{
p[3]-=w_near;
if (p[3]>=0.0) p*=divide(focal_length,p[3]+focal_length); else p.zero();
}
// 4D -> 3D vector
xyz[k].ld(p[0],p[1],p[2]);
}
// render simplex
for (k=0;edge3[k]>=0;)
{
k0=edge3[k]; k++;
k1=edge3[k]; k++;
k2=edge3[k]; k++;
// normal
a=xyz[k1]-xyz[k0];
b=xyz[k2]-xyz[k1];
nor.cross(a,b);
nor.unit();
// render
// glBegin(GL_LINE_LOOP);
glBegin(GL_TRIANGLES);
glNormal3dv(nor.a);
glColor4ubv((BYTE*)(&rgb));
glVertex3dv(xyz[k0].a);
glVertex3dv(xyz[k1].a);
glVertex3dv(xyz[k2].a);
glEnd();
}
}
}
//---------------------------------------------------------------------------
void mesh4D::draw_wireframe(double focal_length,double w_near)
{
int i,j,k;
vector<4> p[4];
// 4D wireframe
glColor3f(1.0,1.0,1.0);
glBegin(GL_LINES);
for (i=0;i<lin.num;)
{
// extrac points (x,y,z,w)
for (k=0;k<2;k++)
{
j=lin[i]*4; i++;
p[k].a[0]=pnt[j]; j++;
p[k].a[1]=pnt[j]; j++;
p[k].a[2]=pnt[j]; j++;
p[k].a[3]=pnt[j]; j++;
// transform
rep.l2g(p[k],p[k]);
// perspective projection
if (focal_length>0.0)
{
p[k][3]-=w_near;
if (p[k][3]>=0.0) p[k]*=divide(focal_length,p[k][3]+focal_length); else p[k].zero();
}
// render
glVertex3dv(p[k].a); // use just x,y,z
}
}
glEnd();
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
And the preview for cross section render:
The worst part was to define the hypercube as set of simplexes ...

Related

Unknown type name "Shape1" in C++ Builder

I'm trying to make a Tetris clone in C++ Builder. I started by making 2 classes. I have wrote them in a .h file and included it in my .cpp file:
class Stage
{
protected:
int Width;
int Height;
public:
Stage()
{
Width=300;
Height=733;
}
Stage(int Width0,int Height0)
{
Width=Width0;
Height=Height0;
}
};
This is my first class, Stage. I don't know for sure what this class should contain, so if you have any suggestions on what I should include in it, I'm glad to hear them.
class Tetromino : public Stage
{
protected:
Tshape *Shape1;
public:
Tetromino(Tshape *Shape1)
{
TetrisGame->Shape1->Width=33;
TetrisGame->Shape1->Height=33;
TetrisGame->Shape1->Brush->Color=clBlack;
TetrisGame->Shape1->Left=10;
TetrisGame->Shape1->Top=200;
}
void mLeft()
{
if(TetrisGame->Shape1->Left<=Widht)
TetrisGame->Shape1->Left=TetrisGame->Shape1->Left+33;
}
void mRight()
{
if(TetrisGame->Shape1->Left>=Width)
TetrisGame->Shape1->Left=TetrisGame->Shape1->Left-33;
}
void free_fall()
{
TetrisGame->PieceFallTime->Enabled=true;
}
};
That's my second class, the class for pieces.
I know that that's not how it should work. I have to make a matrix for example let's say:
piece[4][4]=
{(0,0,0,0),
(0,0,0,0),
(0,1,0,0),
(1,1,1,0)}
but I still have no clue how I should make use of it. I think that I should put a shape where there is 1 and it should create my piece, but I don't know how to do that.
Right now I'm trying to figure out how to make a shape fall down and move it right or left with the keyboard. I did this before, but with no classes. It's a bit more complicated using classes and I don't understand.
I tried to initialize an object Tetromino x(Shape1) (the code is in form.cpp) having a Shape1 in my form. I'm thinking that this should force the Shape1 in my Form to use the methods that I wrote in my class, right?
But I'm getting this error:
[bcc32c Error] StartGame.cpp(110): unknown type name 'Shape1'
You forgot to use new for the bitmap in the constructor so you are loading bmp file into bitmap objects that is not yet created hence the access violation. You can use transparency for the tetrominos bitmaps ... you just need to activate it on target canvas (like your Form1 ..within its properties). However I would render my tetrominos directly into bitmap instead of using files... You can use bitmap as a screen buffer ...
Here simple BDS2006 C++/VCL Tetris example I coded with my students during one lecture years ago as they wanted to code a game and voted for tetris ... (its ugly but works ...)
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
// window
TForm1 *Form1;
// keyboard map
enum{
_key_left =0,
_key_right,
_key_rot ,
_key_drop ,
_keys
};
int keys [_keys]; // is key pressed ?
WORD keys_code[_keys]; // scan code
// back/double buffer
Graphics::TBitmap *bmp = new Graphics::TBitmap;
// scoore, position of falling tetromino,next shape
int score=0,px=0,py=0,novy_dielik=0;
// actual tetromino map
BYTE pm[4][4]=
{
0,0,1,0,
0,1,1,0,
0,0,1,0,
0,0,1,0
};
// game board size
const int map_xs=10;
const int map_ys=20;
BYTE map[map_xs][map_ys];
// tetrominos
BYTE shp[7*16]=
{
0,1,0,0,
0,1,0,0,
0,1,0,0,
0,1,0,0,
0,1,0,0,
0,1,0,0,
0,1,1,0,
0,0,0,0,
0,0,1,0,
0,0,1,0,
0,1,1,0,
0,0,0,0,
0,1,0,0,
0,1,1,0,
0,0,1,0,
0,0,0,0,
0,0,1,0,
0,1,1,0,
0,1,0,0,
0,0,0,0,
0,0,0,0,
0,0,1,0,
0,1,1,1,
0,0,0,0,
0,0,0,0,
0,1,1,0,
0,1,1,0,
0,0,0,0
};
//---------------------------------------------------------------------------
// tetromino colision test
//---------------------------------------------------------------------------
int step()
{
int i,j,x,y;
for (i=0;i<4;i++)
for (j=0;j<4;j++)
if (pm[i][j]!=0)
{
x=px+j;
y=py+i;
if (x<0) return 1;
if (x>=map_xs) return 1;
if (y<0) return 1;
if (y>=map_ys) return 1;
if (map[x][y]!=0) return 1;
}
return 0;
}
//---------------------------------------------------------------------------
// render game
//---------------------------------------------------------------------------
void draw()
{
// clear screen
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(TRect(0,0,bmp->Width,bmp->Height));
// [game board]
int x,y,a,i,j,adr;
x=bmp->Width/(map_xs+4+3);
y=bmp->Height/(map_ys+2);
if (x<y) a=x; else a=y;
// border
x=a; y=a;
bmp->Canvas->Pen->Color=clBlue;
bmp->Canvas->Pen->Width=a>>1;
bmp->Canvas->Rectangle(x,y,x+(map_xs*a),y+(map_ys*a));
bmp->Canvas->Pen->Width=1;
// game board
for (j=0;j<map_ys;j++)
{
for (i=0;i<map_xs;i++)
{
// set color from map
if (map[i][j]==0) bmp->Canvas->Brush->Color=clBlack;
else bmp->Canvas->Brush->Color=clAqua;
// grid cell
bmp->Canvas->FillRect(TRect(x,y,x+a,y+a));
x+=a; // posun sa o policko vpravo
}
x-=map_xs*a; // vrat sa vlavo
y+=a; // posun sa o policko dole
}
y-=map_ys*a; // vrat sa hore
// falling tetromino
x+=px*a;
y+=py*a;
for (j=0;j<4;j++)
{
for (i=0;i<4;i++)
{
// cell
if (pm[j][i]!=0)
{
bmp->Canvas->Brush->Color=clGreen;
bmp->Canvas->FillRect(TRect(x,y,x+a,y+a));
}
x+=a; // posun sa o policko vpravo
}
x-=4*a; // vrat sa vlavo
y+=a; // posun sa o policko dole
}
y-=4*a; // vrat sa hore
x-=px*a;
y-=py*a;
x+=(map_xs+1)*a; // posun sa na pravy stlpec
// score:
bmp->Canvas->Font->Color=clWhite;
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->TextOutA(x,y,AnsiString().sprintf("Score: %i",score));
y+=a+a;
// border
bmp->Canvas->Pen->Color=clBlue;
bmp->Canvas->Pen->Width=a>>1;
bmp->Canvas->Rectangle(x,y,x+(4*a),y+(4*a));
bmp->Canvas->Pen->Width=1;
adr=16*novy_dielik;
for (j=0;j<4;j++)
{
for (i=0;i<4;i++)
{
// set color from map
if (shp[adr]==0) bmp->Canvas->Brush->Color=clBlack;
else bmp->Canvas->Brush->Color=clAqua;
// nakresli stvorcek
bmp->Canvas->FillRect(TRect(x,y,x+a,y+a));
x+=a; // go right
adr++;
}
x-=4*a; // return left
y+=a; // go down
}
y-=4*a; // return up
Form1->Canvas->Draw(0,0,bmp);
}
//--- konstruktor okna ------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
keys_code[_key_left ]='O';
keys_code[_key_right]='P';
keys_code[_key_rot ]='Q';
keys_code[_key_drop ]='A';
// new game
novy_dielik=rand()%7; // new tetromino
px=(map_xs>>1)-2; // position top/middle
py=0;
int adr=16*novy_dielik;
for (int j=0;j<4;j++) // copy it to pm
for (int i=0;i<4;i++)
{
pm[j][i]=shp[adr];
adr++;
}
for (int j=0;j<map_ys;j++) // clear map
for (int i=0;i<map_xs;i++)
map[i][j]=0;
novy_dielik=rand()%7;
}
//--- resize event ----------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
bmp->Width=ClientWidth;
bmp->Height=ClientHeight;
draw(); // force redraw
}
//--- redraw event ----------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
draw();
}
//--- falling timer ---------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
py++;
if (step()) // tetromino hit the ground?
{
py--;
int i,j,x,y,adr,e; // copy it to map
for (j=0;j<4;j++)
for (i=0;i<4;i++)
if (pm[j][i])
{
x=px+i;
y=py+j;
map[x][y]=1;
}
px=(map_xs>>1)-2; // new tetromino top/middle position
py=0;
adr=16*novy_dielik;
for (j=0;j<4;j++) // copy it to pm
for (i=0;i<4;i++)
{
pm[j][i]=shp[adr];
adr++;
}
// map full line test
int dscore=0;
for (j=map_ys-1;j>=0;j--)
{
e=1;
for (i=0;i<map_xs;i++)
if (map[i][j]==0)
{
e=0;
break;
}
if (e)
{
dscore<<=1;
dscore+=100;
e=j;
for (j=e-1;j>=0;j--)
for (i=0;i<map_xs;i++)
map[i][j+1]=map[i][j];
j=e+1;
draw();
Sleep(200);
}
}
score+=dscore;
if (step()) // end game? (no new tetromino)
{
int q;
q=bmp->Canvas->Font->Height;
bmp->Canvas->Font->Height=40;
bmp->Canvas->Font->Color=clRed;
bmp->Canvas->Brush->Style=bsClear;
AnsiString s="Game over";
bmp->Canvas->TextOutA(
(bmp->Width-bmp->Canvas->TextWidth(s))>>1,
(bmp->Height-bmp->Canvas->TextHeight(s))>>1,s);
bmp->Canvas->Brush->Style=bsSolid;
bmp->Canvas->Font->Height=q;
Form1->Canvas->Draw(0,0,bmp);
Sleep(1000);
// new game
novy_dielik=rand()%7; // new tetromino
px=(map_xs>>1)-2; // top/middle
py=0;
int adr=16*novy_dielik;
for (int j=0;j<4;j++) // copy it to pm
for (int i=0;i<4;i++)
{
pm[j][i]=shp[adr];
adr++;
}
for (int j=0;j<map_ys;j++) // clear board
for (int i=0;i<map_xs;i++)
map[i][j]=0;
novy_dielik=rand()%7;
}
novy_dielik=rand()%7; // random next tetromino 0-6
}
draw();
}
//--- keyboard timer --------------------------------------------------------
void __fastcall TForm1::Timer2Timer(TObject *Sender)
{
int e=0;
if (keys[_key_left ]!=0)
{
px--; if (step()) px++; else e=1;
}
if (keys[_key_right]!=0)
{
px++; if (step()) px--; else e=1;
}
if (keys[_key_rot ]!=0)
{
int i,j;
BYTE ori[4][4];
for (j=0;j<4;j++) for (i=0;i<4;i++) ori[i][j]=pm[i][j];
for (j=0;j<4;j++) for (i=0;i<4;i++) pm[3-j][i]=ori[i][j];
if (step()) for (j=0;j<4;j++) for (i=0;i<4;i++) pm[i][j]=ori[i][j];
else e=1;
}
if (keys[_key_drop ]!=0)
{
py++; if (step()) py--; else e=1;
}
if (e) draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
Caption=Key;
for (int i=0;i<_keys;i++) // test all keys
if (Key==keys_code[i]) // if match
keys[i]=1; // set it as pressed
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
{
for (int i=0;i<_keys;i++) // test all keys
if (Key==keys_code[i]) // if match
keys[i]=0; // set it as released
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormActivate(TObject *Sender)
{
for (int i=0;i<_keys;i++) // set all keys
keys[i]=0; // as released
}
//---------------------------------------------------------------------------
---------------------------------------
Its a single Form App with 2 timers on it
Timer1 is 250ms
Timer2 is 80ms
If you want to use mine you have to:
create empty form App
place 2 timers on it and set the intervals
create all events my code is using
copy the event and game codes to correct places
But you should use this more like as hint how the game architecture could look like. I know its ugly and without classes (those students where not yet OOP capable and we coded the game together so some consensus on all parts where made with the aim to keep this simple...)
So the bmp is like screen back buffer (to avoid flickering) controlling is done by O P Q A keys. One timer control the falling animation/simulation and the other handles keyboard. Rendering is done by using VCL encapsulated GDI and the bmp ...
for more info see:
Graphics rendering

Garbage values when accessing SDL_Rect members from an array?

I've been following LazyFoo's SDL tutorials (and also adding my own organization and coding style). When I got to his animation tutorial I decided to make a separate class to store the variables and methods related to the animation algorithm, rather than having global variables. He uses an array of SDL_Rects to define the boundaries of different sprites on a sprite sheet, so I used an SDL_Rect pointer to store the array in my custom class. When I compiled everything I didn't see an animation, when I compiled the original source code I did. When I started debugging things, I found that when I was rendering the sprites, the rects were actually full of garbage, even though when I initialize them the rects are just fine. I've tried to simplify the problem so many times, but every approach I take to recreate the bug in a simpler environment actually works as expected! So with that in mind I apologize for the large amount of code, because I can't seem to reduce the problem.
texture.h
#ifndef TEXTURE_H
#define TEXTURE_H
#include <SDL2/SDL.h>
#include <string>
class Animation {
public:
Animation(SDL_Renderer* renderer);
~Animation();
void load(std::string path, int frames, SDL_Rect* clips),
free(),
render(int x, int y),
next_frame();
private:
SDL_Renderer* _renderer=NULL;
SDL_Rect* _clips=NULL;
SDL_Texture* _texture=NULL;
int _frame=0, _frames=0, _width=0, _height=0;
};
#endif
texture.cpp
#include <stdio.h>
#include <SDL2/SDL_image.h>
#include "texture.h"
#include "error.h"
Animation::Animation(SDL_Renderer* renderer) {
_renderer = renderer;
}
Animation::~Animation() {
free();
_renderer = NULL;
}
void Animation::load(std::string path, int frames, SDL_Rect* clips) {
free();
SDL_Texture* texture = NULL;
SDL_Surface* surface = IMG_Load(path.c_str());
if (!surface)
throw ErrorIMG("Could not load image "+path);
SDL_SetColorKey(surface, SDL_TRUE,
SDL_MapRGB(surface->format, 0, 0xFF, 0xFF));
texture = SDL_CreateTextureFromSurface(_renderer, surface);
if (!texture)
throw ErrorSDL("Could not create texture from image "+path);
_width = surface->w;
_height = surface->h;
SDL_FreeSurface(surface);
_frames = frames;
_clips = clips;
printf("clips[%d]: w: %d h: %d\n", 0, _clips[0].w, _clips[0].h);
}
void Animation::free() {
if (_texture) {
SDL_DestroyTexture(_texture);
_texture = NULL;
_clips = NULL;
_frames = 0;
_frame = 0;
_width = 0;
_height = 0;
}
}
void Animation::render(int x, int y) {
SDL_Rect crect = _clips[_frame/4];
printf("in render (clips[%d]): w: %d, h: %d\n", _frame/4, crect.w, crect.h);
SDL_Rect render_space = {x, y, crect.w, crect.h};
SDL_RenderCopy(_renderer, _texture, &_clips[_frame], &render_space);
}
void Animation::next_frame() {
SDL_Rect crect = _clips[_frame/4];
printf("in next frame (clips[%d]): w: %d, h: %d\n", _frame/4, crect.w, crect.h);
++_frame;
if (_frame/4 >= _frames)
_frame = 0;
}
game.h
#ifndef GAME_H
#define GAME_H
#include "texture.h"
class Game {
public:
Game();
~Game();
void main();
private:
void load_media();
SDL_Window* _window=NULL;
SDL_Renderer* _renderer=NULL;
Animation* _anim=NULL;
const int SCREEN_WIDTH=640, SCREEN_HEIGHT=480;
};
#endif
game.cpp
#include <SDL2/SDL_image.h>
#include "game.h"
#include "error.h"
void Game::main() {
load_media();
bool has_quit = false;
SDL_Event event;
while (!has_quit) {
while (SDL_PollEvent(&event))
if (event.type == SDL_QUIT)
has_quit = true;
SDL_SetRenderDrawColor(_renderer, 0xff, 0xff, 0xff, 0xff);
SDL_RenderClear(_renderer);
_anim->render(100, 100);
_anim->next_frame();
SDL_RenderPresent(_renderer);
}
}
Game::Game() {
if (SDL_Init(SDL_INIT_VIDEO))
throw ErrorSDL("SDL could not initialize");
_window = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH,
SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (!_window)
throw ErrorSDL("Window could not be created");
Uint32 render_flags = SDL_RENDERER_ACCELERATED;
render_flags |= SDL_RENDERER_PRESENTVSYNC;
_renderer = SDL_CreateRenderer(_window, -1, render_flags);
if (!_renderer)
throw ErrorSDL("Renderer could not be created");
SDL_SetRenderDrawColor(_renderer, 0xff, 0xff, 0xff, 0xff);
if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG))
throw ErrorIMG("SDL_image could not initialize");
}
Game::~Game() {
delete _anim;
SDL_DestroyRenderer(_renderer);
SDL_DestroyWindow(_window);
_renderer = NULL;
_window = NULL;
IMG_Quit();
SDL_Quit();
}
void Game::load_media() {
const int nclips = 4;
SDL_Rect clips[nclips];
for (int i=0; i < nclips; i++) {
clips[i].x = i*64;
clips[i].y = 0;
clips[i].w = 64;
clips[i].h = 164;
}
_anim = new Animation(_renderer);
_anim->load("sheet.png", nclips, &clips[0]);
}
You're storing a pointer to a temporary. The SDL_Rect* clips pointer that you pass to Animation::load is assigned to the member _clips used after the function returns. For this to work correctly, the data that is pointed to needs to live for as long as the Animation class is using it. The problem arises here:
void Game::load_media() {
const int nclips = 4;
SDL_Rect clips[nclips];
...
_anim->load("sheet.png", nclips, &clips[0]);
}
In this piece of code, clips is a local variable. That means it gets destroyed at the end of load_media(), and the memory contents at that location will become garbage.
There are a number of ways you could fix this. A simple one would be to use std::vector<SDL_Rect> instead of SDL_Rect*. std::vector can safely be copied and manages its internals for you. Your new code could look like:
class Animation {
...
std::vector<SDL_Rect> _clips;
...
}
void Animation::load(std::string path, int frames, std::vector<SDL_Rect> clips) {
...
_clips = clips;
...
}
void Game::load_media() {
const int nclips = 4;
std::vector<SDL_Rect> clips;
clips.resize(nclips);
...
_anim->load("sheet.png", nclips, clips);
}
And dont forget to #include <vector>. Documentation for std::vector is here. Note that std::vector has a size() method that can probably replace frames everywhere it appears.
The stack-allocated Game::load_media()::clips array disappears when it goes out of scope. Make a copy in Animation::load() instead of only storing a pointer.

Compute objects moving with arrows and mouse

I am doing a maya simulator application with openGL, everything is progressing pretty well. Just have only one trouble, i can not calculate 100% accuracy of moving object by mouse.
I'm using the scaling of arrows in 3D coordinates and arrows in the screen coordinates and multiplying it with some scaling to calculate the degree of motion in the x and y direction of the screen coordinates. .
But these calculations do not give high accuracy, the mouse is deviated from the arrow. I need it always on the moving arrows, meaning that the accuracy is almost 100%
Anyone who has ideas or comments on this issue will always be appreciated
This is some code i try:
gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenVertex, &yScreenVertex, &zScreenVertex);
if (axis == 0)
{
gluProject((GLdouble)x3DVertex + 1, (GLdouble)y3DVertex, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);
}
else if (axis == 1)
{
gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex + 1, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);
}
else
{
gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex, (GLdouble)z3DVertex + 1, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);
}
float totalScaleXY = abs(xScreenArrow - xScreenVertex) + abs(yScreenArrow - yScreenVertex);
scaleX = abs(xScreenArrow - xScreenVertex) / totalScaleXY;
scaleY = abs(yScreenArrow - yScreenVertex) / totalScaleXY;
float lengthArrowOnScreen = sqrt(pow((xScreenArrow - xScreenVertex), 2) + pow((yScreenArrow - yScreenVertex), 2));
scale3dAndScreen = 1 / lengthArrowOnScreen;
This is the code I'm testing, it's not very accurate
Here small ugly but very simple C++ example of what I had in mind for this:
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
double mview[16],mmodel[16],mproj[16]; // OpenGL matrices
//---------------------------------------------------------------------------
// vector/matrix math
//---------------------------------------------------------------------------
double vector_len(double *a) { return sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2])); } // = |vector[3]|
void vector_sub(double *c,double *a,double *b) { for (int i=0;i<3;i++) c[i]=a[i]-b[i]; } // c[3] = a[3] - b[3]
void matrix_mul(double *c,double *a,double *b) // c[4x4] = a[4x4] * b [4x4]
{
double q[16];
q[ 0]=(a[ 0]*b[ 0])+(a[ 1]*b[ 4])+(a[ 2]*b[ 8])+(a[ 3]*b[12]);
q[ 1]=(a[ 0]*b[ 1])+(a[ 1]*b[ 5])+(a[ 2]*b[ 9])+(a[ 3]*b[13]);
q[ 2]=(a[ 0]*b[ 2])+(a[ 1]*b[ 6])+(a[ 2]*b[10])+(a[ 3]*b[14]);
q[ 3]=(a[ 0]*b[ 3])+(a[ 1]*b[ 7])+(a[ 2]*b[11])+(a[ 3]*b[15]);
q[ 4]=(a[ 4]*b[ 0])+(a[ 5]*b[ 4])+(a[ 6]*b[ 8])+(a[ 7]*b[12]);
q[ 5]=(a[ 4]*b[ 1])+(a[ 5]*b[ 5])+(a[ 6]*b[ 9])+(a[ 7]*b[13]);
q[ 6]=(a[ 4]*b[ 2])+(a[ 5]*b[ 6])+(a[ 6]*b[10])+(a[ 7]*b[14]);
q[ 7]=(a[ 4]*b[ 3])+(a[ 5]*b[ 7])+(a[ 6]*b[11])+(a[ 7]*b[15]);
q[ 8]=(a[ 8]*b[ 0])+(a[ 9]*b[ 4])+(a[10]*b[ 8])+(a[11]*b[12]);
q[ 9]=(a[ 8]*b[ 1])+(a[ 9]*b[ 5])+(a[10]*b[ 9])+(a[11]*b[13]);
q[10]=(a[ 8]*b[ 2])+(a[ 9]*b[ 6])+(a[10]*b[10])+(a[11]*b[14]);
q[11]=(a[ 8]*b[ 3])+(a[ 9]*b[ 7])+(a[10]*b[11])+(a[11]*b[15]);
q[12]=(a[12]*b[ 0])+(a[13]*b[ 4])+(a[14]*b[ 8])+(a[15]*b[12]);
q[13]=(a[12]*b[ 1])+(a[13]*b[ 5])+(a[14]*b[ 9])+(a[15]*b[13]);
q[14]=(a[12]*b[ 2])+(a[13]*b[ 6])+(a[14]*b[10])+(a[15]*b[14]);
q[15]=(a[12]*b[ 3])+(a[13]*b[ 7])+(a[14]*b[11])+(a[15]*b[15]);
for(int i=0;i<16;i++) c[i]=q[i];
}
void matrix_mul_vector(double *c,double *a,double *b) // c[3] = a[4x4] * (b[3],1)
{
double q[3];
q[0]=(a[ 0]*b[0])+(a[ 4]*b[1])+(a[ 8]*b[2])+(a[12]);
q[1]=(a[ 1]*b[0])+(a[ 5]*b[1])+(a[ 9]*b[2])+(a[13]);
q[2]=(a[ 2]*b[0])+(a[ 6]*b[1])+(a[10]*b[2])+(a[14]);
for(int i=0;i<3;i++) c[i]=q[i];
}
//---------------------------------------------------------------------------
class DragDrop3D // arrow translation controls (you need one for each objet)
{
public:
double *mm; // model matrix
double *mv; // view matrix
double *mp; // projection matrix
double o[3]; // start point
double l[3]; // length of the arrows
double a,b; // size of the arrow head a length, b width/2
int sel; // selected axis
DragDrop3D()
{
mm=NULL; mv=NULL; mp=NULL;
o[0]=-1.0; l[0]=3.5; a=0.5;
o[1]=-1.0; l[1]=3.5; b=0.25;
o[2]=-1.0; l[2]=3.5; sel=-1;
}
DragDrop3D(DragDrop3D& a) { *this=a; }
~DragDrop3D() {}
DragDrop3D* operator = (const DragDrop3D *a) { *this=*a; return this; }
//DragDrop3D* operator = (const DragDrop3D &a) { ...copy... return this; }
void project(double *scr,double *mmvp,double *obj) // obj -> scr
{
matrix_mul_vector(scr,mmvp,obj);
if (fabs(scr[2])>1e-10) scr[2]=1.0/scr[2]; else scr[2]=0.0;
scr[0]*=scr[2];
scr[1]*=scr[2];
}
void draw() // render arrows
{
int e;
double ang,dang=2.0*M_PI/36.0,u0,u1,v0,v1,q=1.0/sqrt((a*a)+(b*b));
// axis lines
glBegin(GL_LINES);
glColor3f(1.0,0.0,0.0); glVertex3dv(o); glVertex3d(o[0]+l[0],o[1],o[2]);
glColor3f(0.0,1.0,0.0); glVertex3dv(o); glVertex3d(o[0],o[1]+l[1],o[2]);
glColor3f(0.0,0.0,1.0); glVertex3dv(o); glVertex3d(o[0],o[1],o[2]+l[2]);
glEnd();
// cones
glBegin(GL_TRIANGLES);
u1=b*cos(-dang);
v1=b*sin(-dang);
for (e=1,ang=0.0;e;ang+=dang)
{
if (ang>=2.0*M_PI) { ang=2.0*M_PI; e=0; }
u0=u1; u1=b*cos(ang);
v0=v1; v1=b*sin(ang);
// X
if (sel==0) glColor3f(1.0,0.0,0.0); else glColor3f(0.5,0.1,0.1);
glNormal3f(a*q,(u0+u1)*0.5*q,(v0+v1)*0.5*q);
glVertex3d(o[0]+l[0] ,o[1] ,o[2] );
glVertex3d(o[0]+l[0]-a,o[1]+u0,o[2]+v0);
glVertex3d(o[0]+l[0]-a,o[1]+u1,o[2]+v1);
glNormal3f(-1.0,0.0,0.0);
glVertex3d(o[0]+l[0]-a,o[1],o[2]);
glVertex3d(o[0]+l[0]-a,o[1]+u1,o[2]+v1);
glVertex3d(o[0]+l[0]-a,o[1]+u0,o[2]+v0);
// Y
if (sel==1) glColor3f(0.0,1.0,0.0); else glColor3f(0.1,0.5,0.1);
glNormal3f((u0+u1)*0.5*q,a*q,(v0+v1)*0.5*q);
glVertex3d(o[0] ,o[1]+l[1] ,o[2] );
glVertex3d(o[0]+u1,o[1]+l[1]-a,o[2]+v1);
glVertex3d(o[0]+u0,o[1]+l[1]-a,o[2]+v0);
glNormal3f(0.0,-1.0,0.0);
glVertex3d(o[0] ,o[1]+l[1]-a,o[2] );
glVertex3d(o[0]+u0,o[1]+l[1]-a,o[2]+v0);
glVertex3d(o[0]+u1,o[1]+l[1]-a,o[2]+v1);
// Z
if (sel==2) glColor3f(0.0,0.0,1.0); else glColor3f(0.1,0.1,0.5);
glNormal3f((v0+v1)*0.5*q,(u0+u1)*0.5*q,a*q);
glVertex3d(o[0] ,o[1] ,o[2]+l[2] );
glVertex3d(o[0]+v1,o[1]+u1,o[2]+l[2]-a);
glVertex3d(o[0]+v0,o[1]+u0,o[2]+l[2]-a);
glNormal3f(0.0,0.0,-1.0);
glVertex3d(o[0] ,o[1] ,o[2]+l[2]-a);
glVertex3d(o[0]+v0,o[1]+u0,o[2]+l[2]-a);
glVertex3d(o[0]+v1,o[1]+u1,o[2]+l[2]-a);
}
glEnd();
}
bool mouse(double mx,double my,TShiftState sh) // handle mouse events return if redraw is needed
{
if ((mm==NULL)||(mv==NULL)||(mp==NULL)) return false;
int ql; double p[3],mmvp[16]; bool _redraw=false;
// MVP = M*V*P
matrix_mul(mmvp,mm,mv);
matrix_mul(mmvp,mmvp,mp);
// convert screen coords to <-1,+1> GL NDC
mx= (2.0*mx/double(xs))-1.0;
my=1.0-(2.0*my/double(ys)) ;
// mouse left button state (last,actual)
ql=sh.Contains(ssLeft);
// select (no mouse button)
if (!ql)
{
int sel0=sel; sel=-1;
// arrowhead center screen x,y distance to mouse select if close
p[0]=o[0]+l[0]-0.5*a; p[1]=o[1]; p[2]=o[2]; project(p,mmvp,p); p[0]-=mx; p[1]-=my; p[2]=0.0; if (vector_len(p)<=0.5*b) sel=0; // X
p[1]=o[1]+l[1]-0.5*a; p[0]=o[0]; p[2]=o[2]; project(p,mmvp,p); p[0]-=mx; p[1]-=my; p[2]=0.0; if (vector_len(p)<=0.5*b) sel=1; // Y
p[2]=o[2]+l[2]-0.5*a; p[1]=o[1]; p[0]=o[0]; project(p,mmvp,p); p[0]-=mx; p[1]-=my; p[2]=0.0; if (vector_len(p)<=0.5*b) sel=2; // Z
_redraw=(sel0!=sel);
}
// drag arrowhead center into mouse position (active button)
if ((ql)&&(sel>=0)&&(sel<3))
{
int i;
double len0,len;
double p0[3],dp[3]={0.0,0.0,0.0},t,dt,q[3]={mx,my,0.0};
// selected arrowhead position,direction
for (i=0;i<3;i++) p0[i]=o[i];
p0[sel]+=l[sel]-0.5*a;
dp[sel]=1.0;
// "closest" intersection between axis/mouse_ray
for (len0=-1.0,t=0.0,dt=1.0;fabs(dt)>1e-5;t+=dt)
{
// position on axis p(t) = p0 + t*dp
for (i=0;i<3;i++) p[i]=p0[i]+(t*dp[i]);
// len = distance to mouse
project(p,mmvp,p);
vector_sub(p,p,q); p[2]=0.0;
len=vector_len(p);
// handle iteration step
if (len0<-0.5) len0=len;
if (len>len0) dt=-0.1*dt;
len0=len;
}
// translate by t
double m[16]=
{
1.0,0.0,0.0,0.0,
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0,
};
m[12+sel]=t;
matrix_mul(mm,m,mm);
_redraw=true;
}
return _redraw;
}
};
//---------------------------------------------------------------------------
DragDrop3D ctrl; // I got single cube so single arrow drag drop control suffice
//---------------------------------------------------------------------------
void gl_draw() // main rendering code
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
// init
static bool _init=true;
if (_init)
{
_init=false;
// M,V init matrices
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslated(0.0,0.0,-25.0);
glGetDoublev(GL_MODELVIEW_MATRIX,mview);
glLoadIdentity();
glTranslated(-2.0,-1.0,-1.0);
glRotated(-35.0,0.0,1.0,0.0);
glGetDoublev(GL_MODELVIEW_MATRIX,mmodel);
glPopMatrix();
// matrices -> ctrl
glGetDoublev(GL_PROJECTION_MATRIX,mproj);
ctrl.mp=mproj;
ctrl.mv=mview;
ctrl.mm=mmodel;
}
// matrices -> OpenGL
glMatrixMode(GL_MODELVIEW);
glLoadMatrixd(mview);
glMultMatrixd(mmodel);
// draw VAO cube
ctrl.draw();
vao_draw(); // here render your object instead
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
// application init
gl_init(Handle);
vao_init();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
// application exit
gl_exit();
vao_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
// window resize
gl_resize(ClientWidth,ClientHeight);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
// window repaint
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheel(TObject *Sender, TShiftState Shift, int WheelDelta, TPoint &MousePos, bool &Handled)
{
// mouse wheel translates camera (like zoom)
GLfloat dz=2.0;
if (WheelDelta<0) dz=-dz;
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixd(mview);
glTranslatef(0,0,dz);
glGetDoublev(GL_MODELVIEW_MATRIX,mview);
glPopMatrix();
gl_draw();
}
//---------------------------------------------------------------------------
// mouse events
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw(); }
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw(); }
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw(); }
//---------------------------------------------------------------------------
It is based on this: simple complete GL+VAO/VBO+GLSL+shaders example in C++ where you will find more info and the gl_simple.h I am using for this. The code is VCL based so ignore the VCL stuff and port the events to your kind of programming.
The program is using just OpenGL 1.0 API (appart of the VAO/VBO cube representing my object) so it does not need anything apart math.h for sin,cos,fabs functions.
The idea is to have one control DragDrop3D object per each controlable object in the world/scene. Init each first and then call mouse for each on every mouse event and redraw if needed.
Here small preview:
Sadly my GIF capture SW does not catch mouse cursor but it matches the movement exactly.
I was too lazy to compute the intersection between axises directly instead I iterate to find closest match (that for loop with dt). That part could be replaced by intersection equation.
The axises are bounded to the x,y,z axises of the object coordinate system (mm,mmodel).
For more objects you should also add locks so only one object can be selected ...
PS. see related:
- OpenGL ray OBB intersection

how should i handle (morphing) 4D objects in opengl? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
i want to try writing a playground similar to this 4D toys,
so i started learning opengl.
from my current understanding, people use VBOs and uniform transformation matrix for mostly-static objects
(like cubes, skeletal animations etc., which usually just involves transformations)
i also heard that morphing between models also uses VBOs for caching both models, since both of them will be well defined and not so much intermediates.
but in the 4D toys mentioned above, objects are morphing and clipped a lot.
and it is likely that there is no defined models, and many transitions in between.
(it might be a simple square now, and a spiky ball clipped in half later ).
in this case, is updating-vertex-VBO-per-frame or Vertex Arrays(which i saw in another question) a suitable solution?
For starters I would use 4D -> 3D projection instead of cut by hyperplane. The result is not the same but will get you closer to your goal (so you can latter upgrade this to cut). So similarly like in 3D -> 2D conversions used in graphics you got 2 choices one is using perspective projection and second is just ignoring the 4th dimension coordinate while rendering. I will use the latter as it is simpler.
structures
To make this as simple as I can I will use wire-frame instead of BR rendering. So you need to handle 4D mesh (wire-frame). I would use 2 tables:
double pnt[]; // 4D point list (x,y,z,u)
int lin[]; // lines point indexes (i0,i1)
first one stores all the vertexes of your mesh and the second hold index pairs of points connected by lines in wire-frame representation.
transforms
If I would only ignore the 4th coordinate then we would not get the desired functionality. So to make the 4th dimension work we need to add 4D transform to orient our mesh in 4D before rendering. So use homogenous transform matrix and lets call ir rep. In 4D it should be 5x5 orthonormal matrix with 4x4 rotation part rot.
To make this even easier avoid smooth rotations for now (as in 4D that is not as easy) and compute random rotation 4x4 matrix instead. So just set all the cells randomly <-1,+1>. Handle each row as basis vector. To make them orthonormal just make them unit and exploit cross product. For more info see:
How to generate a randomly oriented, high dimension circle in python?
render
simply convert point table by your transform matrix
(x',y',z',u',W) = rep * (x,y,z,u,1)
then take the (x,y,z`) and render ...
Here simple OpenGL/C++ example of 4D hyper cube:
//---------------------------------------------------------------------------
//--- Mesh 4D: ver 0.000 ----------------------------------------------------
//---------------------------------------------------------------------------
#ifndef _mesh4D_h
#define _mesh4D_h
//---------------------------------------------------------------------------
#include <math.h>
#include "nd_math.h"
#include "list.h"
//---------------------------------------------------------------------------
const double pi = M_PI;
const double pi2 =2.0*M_PI;
const double pipol=0.5*M_PI;
const double deg=M_PI/180.0;
const double rad=180.0/M_PI;
//---------------------------------------------------------------------------
class mesh4D
{
public:
matrix<5> rep; // 4D uniform 5x5 transform matrix
List<double> pnt; // 4D point list (x,y,z,u)
List<int> lin; // lines point indexes (i0,i1)
mesh4D() {}
mesh4D(mesh4D& a) { *this=a; }
~mesh4D() {}
mesh4D* operator = (const mesh4D *a) { *this=*a; return this; }
//mesh4D* operator = (const mesh4D &a) { ...copy... return this; }
void set_randomrep(); // random oriented uniform 4D transform matrix with origin (0,0,0,0)
void set_hypercube(double a);
void draw();
};
//---------------------------------------------------------------------------
void mesh4D::set_randomrep()
{
int i,j;
matrix<4> rot;
rep.unit();
rot.rnd();
rot.orthonormal();
for (i=0;i<4;i++)
for (j=0;j<4;j++)
rep[i][j]=rot[i][j];
}
void mesh4D::set_hypercube(double a)
{
rep.unit(); // reset orientation
pnt.num=0; // clear point list
lin.num=0; // clear line list
pnt.add(-a); pnt.add(-a); pnt.add(-a); pnt.add(-a);
pnt.add(+a); pnt.add(-a); pnt.add(-a); pnt.add(-a);
pnt.add(-a); pnt.add(+a); pnt.add(-a); pnt.add(-a);
pnt.add(+a); pnt.add(+a); pnt.add(-a); pnt.add(-a);
pnt.add(-a); pnt.add(-a); pnt.add(+a); pnt.add(-a);
pnt.add(+a); pnt.add(-a); pnt.add(+a); pnt.add(-a);
pnt.add(-a); pnt.add(+a); pnt.add(+a); pnt.add(-a);
pnt.add(+a); pnt.add(+a); pnt.add(+a); pnt.add(-a);
pnt.add(-a); pnt.add(-a); pnt.add(-a); pnt.add(+a);
pnt.add(+a); pnt.add(-a); pnt.add(-a); pnt.add(+a);
pnt.add(-a); pnt.add(+a); pnt.add(-a); pnt.add(+a);
pnt.add(+a); pnt.add(+a); pnt.add(-a); pnt.add(+a);
pnt.add(-a); pnt.add(-a); pnt.add(+a); pnt.add(+a);
pnt.add(+a); pnt.add(-a); pnt.add(+a); pnt.add(+a);
pnt.add(-a); pnt.add(+a); pnt.add(+a); pnt.add(+a);
pnt.add(+a); pnt.add(+a); pnt.add(+a); pnt.add(+a);
// A0
lin.add( 0+0); lin.add( 0+1);
lin.add( 0+1); lin.add( 0+3);
lin.add( 0+3); lin.add( 0+2);
lin.add( 0+2); lin.add( 0+0);
// A1
lin.add( 4+0); lin.add( 4+1);
lin.add( 4+1); lin.add( 4+3);
lin.add( 4+3); lin.add( 4+2);
lin.add( 4+2); lin.add( 4+0);
// A=A0+A1
lin.add( 0+0); lin.add( 4+0);
lin.add( 0+1); lin.add( 4+1);
lin.add( 0+2); lin.add( 4+2);
lin.add( 0+3); lin.add( 4+3);
// B0
lin.add( 8+0); lin.add( 8+1);
lin.add( 8+1); lin.add( 8+3);
lin.add( 8+3); lin.add( 8+2);
lin.add( 8+2); lin.add( 8+0);
// B1
lin.add(12+0); lin.add(12+1);
lin.add(12+1); lin.add(12+3);
lin.add(12+3); lin.add(12+2);
lin.add(12+2); lin.add(12+0);
// B=B0+B1
lin.add( 8+0); lin.add(12+0);
lin.add( 8+1); lin.add(12+1);
lin.add( 8+2); lin.add(12+2);
lin.add( 8+3); lin.add(12+3);
// hyper cube = A+B
lin.add( 0+0); lin.add( 8+0);
lin.add( 0+1); lin.add( 8+1);
lin.add( 0+2); lin.add( 8+2);
lin.add( 0+3); lin.add( 8+3);
lin.add( 0+4); lin.add( 8+4);
lin.add( 0+5); lin.add( 8+5);
lin.add( 0+6); lin.add( 8+6);
lin.add( 0+7); lin.add( 8+7);
}
//---------------------------------------------------------------------------
void mesh4D::draw()
{
int i,j;
double _zero=1e-3;
vector<5> a,b;
glBegin(GL_LINES);
for (i=0;i<lin.num;)
{
// extrac first point
j=lin[i]*4; i++;
a.a[0]=pnt[j]; j++;
a.a[1]=pnt[j]; j++;
a.a[2]=pnt[j]; j++;
a.a[3]=pnt[j]; j++;
a.a[4]=1.0; // W=1
// extrac second point
j=lin[i]*4; i++;
b.a[0]=pnt[j]; j++;
b.a[1]=pnt[j]; j++;
b.a[2]=pnt[j]; j++;
b.a[3]=pnt[j]; j++;
b.a[4]=1.0; // W=1
// transform
a=rep*a;
b=rep*b;
// render
glVertex3dv(a.a); // use just x,y,z
glVertex3dv(b.a); // use just x,y,z
}
glEnd();
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
I used mine dynamic list.h template so:
List<double> xxx; is the same as double xxx[];
xxx.add(5); adds 5 to end of the list
xxx[7] access array element (safe)
xxx.dat[7] access array element (unsafe but fast direct access)
xxx.num is the actual used size of the array
xxx.reset() clears the array and set xxx.num=0
xxx.allocate(100) preallocate space for 100 items
the nd_math.h is mine lib for N-Dimensional computations. What you need is just 4D,5D vector and 4x4, 5x5 matrix math from linear algebra.
Both libs are a bit big in size and also legal issues prevent me to share their code here.
The usage is simple:
// globals and init
mesh4D mesh
double animx=-50.0,danimx=0.0;
double animy= 0.0,danimy=2.0;
mesh.set_hypercube(0.5);
// render
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D( -2.0, 2.0, -2.0, 2.0 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotated(animx,1.0,0.0,0.0);
glRotated(animy,0.0,1.0,0.0);
mesh.draw();
glFlush();
SwapBuffers(hdc);
// some timer
animx+=danimx; if (animx>=360.0) animx-=360.0;
animy+=danimy; if (animy>=360.0) animy-=360.0;
call_render_here();
// on key press or mouse wheel or what ever
mesh.set_randomrep();
And here preview for some rep rotations ...
so this way you can render any wire-frame mesh (even BR rendering should works this way).
If you want to upgrade to the cut then you should take each Wire-frame line and compute its intersection with cutting hyperplane. If we chose hyperplane that goes through point
O(0,0,0,u_cut)
and has normal
N(0,0,0,1)
Then the task will simplify a lot. there are 3 options. Lets consider edge line with endpoints A,B:
no intersection
((A.u > u_cut)&&(B.u > u_cut)) || ((A.u < u_cut)&&(B.u < u_cut))
just ignore such edge
1 intersection
((A.u >= u_cut)&&(B.u <= u_cut)) || ((A.u <= u_cut)&&(B.u >= u_cut))
so compute the intersection via linear interpolation
x = A.x + (B.x-A.x)*(u_cut-A.u)/(B.u-A.u)
y = A.y + (B.y-A.y)*(u_cut-A.u)/(B.u-A.u)
z = A.z + (B.z-A.z)*(u_cut-A.u)/(B.u-A.u)
and remember such point and also edge to which it belongs to.
fully inside
(A.u == u_cut)&&(B.u == u_cut)
just remember both endpoints and also render this edge.
After all edges are processed this way then you need to analyze the remembered intersection points and create new edges from them based on connectivity info between edges. I did not do this yet so I can not help with this. I would try to connect remembered points sharing the same neighbor but not sure if that is enough in 4D.
For more info take a look at related QAs I found or answer:
Visualising 4D objects in OpenGL
4D to 3D perspective projection
4D rotations
ND vector and square matrix math template
reper4D
configurable ND rendering engine + hyperspiral
[Edit1] code with perspective
//---------------------------------------------------------------------------
//--- Mesh 4D: ver 0.001 ----------------------------------------------------
//---------------------------------------------------------------------------
#ifndef _mesh4D_h
#define _mesh4D_h
//---------------------------------------------------------------------------
#include <math.h>
#include "nd_math.h"
#include "list.h"
//---------------------------------------------------------------------------
const double pi = M_PI;
const double pi2 =2.0*M_PI;
const double pipol=0.5*M_PI;
const double deg=M_PI/180.0;
const double rad=180.0/M_PI;
//---------------------------------------------------------------------------
class mesh4D
{
public:
matrix<5> rep; // 4D uniform 5x5 transform matrix
List<double> pnt; // 4D point list (x,y,z,u)
List<int> lin; // lines point indexes (i0,i1)
mesh4D() {}
mesh4D(mesh4D& a) { *this=a; }
~mesh4D() {}
mesh4D* operator = (const mesh4D *a) { *this=*a; return this; }
//mesh4D* operator = (const mesh4D &a) { ...copy... return this; }
void set_randomrep(); // random oriented uniform 4D transform matrix with origin (0,0,0,0)
void set_hypercube(double a);
void draw();
};
//---------------------------------------------------------------------------
void mesh4D::set_randomrep()
{
int i,j;
matrix<4> rot;
rot.rnd();
rot.orthonormal();
for (i=0;i<4;i++)
for (j=0;j<4;j++)
rep[i][j]=rot[i][j];
}
//---------------------------------------------------------------------------
void mesh4D::set_hypercube(double a)
{
rep.unit(); // reset orientation
rep[0][4]=0.0; // set position
rep[1][4]=0.0;
rep[2][4]=0.0;
rep[3][4]=3.0*a;
pnt.num=0; // clear point list
lin.num=0; // clear line list
pnt.add(-a); pnt.add(-a); pnt.add(-a); pnt.add(-a);
pnt.add(+a); pnt.add(-a); pnt.add(-a); pnt.add(-a);
pnt.add(-a); pnt.add(+a); pnt.add(-a); pnt.add(-a);
pnt.add(+a); pnt.add(+a); pnt.add(-a); pnt.add(-a);
pnt.add(-a); pnt.add(-a); pnt.add(+a); pnt.add(-a);
pnt.add(+a); pnt.add(-a); pnt.add(+a); pnt.add(-a);
pnt.add(-a); pnt.add(+a); pnt.add(+a); pnt.add(-a);
pnt.add(+a); pnt.add(+a); pnt.add(+a); pnt.add(-a);
pnt.add(-a); pnt.add(-a); pnt.add(-a); pnt.add(+a);
pnt.add(+a); pnt.add(-a); pnt.add(-a); pnt.add(+a);
pnt.add(-a); pnt.add(+a); pnt.add(-a); pnt.add(+a);
pnt.add(+a); pnt.add(+a); pnt.add(-a); pnt.add(+a);
pnt.add(-a); pnt.add(-a); pnt.add(+a); pnt.add(+a);
pnt.add(+a); pnt.add(-a); pnt.add(+a); pnt.add(+a);
pnt.add(-a); pnt.add(+a); pnt.add(+a); pnt.add(+a);
pnt.add(+a); pnt.add(+a); pnt.add(+a); pnt.add(+a);
// A0
lin.add( 0+0); lin.add( 0+1);
lin.add( 0+1); lin.add( 0+3);
lin.add( 0+3); lin.add( 0+2);
lin.add( 0+2); lin.add( 0+0);
// A1
lin.add( 4+0); lin.add( 4+1);
lin.add( 4+1); lin.add( 4+3);
lin.add( 4+3); lin.add( 4+2);
lin.add( 4+2); lin.add( 4+0);
// A=A0+A1
lin.add( 0+0); lin.add( 4+0);
lin.add( 0+1); lin.add( 4+1);
lin.add( 0+2); lin.add( 4+2);
lin.add( 0+3); lin.add( 4+3);
// B0
lin.add( 8+0); lin.add( 8+1);
lin.add( 8+1); lin.add( 8+3);
lin.add( 8+3); lin.add( 8+2);
lin.add( 8+2); lin.add( 8+0);
// B1
lin.add(12+0); lin.add(12+1);
lin.add(12+1); lin.add(12+3);
lin.add(12+3); lin.add(12+2);
lin.add(12+2); lin.add(12+0);
// B=B0+B1
lin.add( 8+0); lin.add(12+0);
lin.add( 8+1); lin.add(12+1);
lin.add( 8+2); lin.add(12+2);
lin.add( 8+3); lin.add(12+3);
// hyper cube = A+B
lin.add( 0+0); lin.add( 8+0);
lin.add( 0+1); lin.add( 8+1);
lin.add( 0+2); lin.add( 8+2);
lin.add( 0+3); lin.add( 8+3);
lin.add( 0+4); lin.add( 8+4);
lin.add( 0+5); lin.add( 8+5);
lin.add( 0+6); lin.add( 8+6);
lin.add( 0+7); lin.add( 8+7);
}
//---------------------------------------------------------------------------
void mesh4D::draw()
{
int i,j;
const double _zero=1e-3;
double focal_length=1.0;
vector<5> a,b;
glBegin(GL_LINES);
for (i=0;i<lin.num;)
{
// extrac first point
j=lin[i]*4; i++;
a.a[0]=pnt[j]; j++;
a.a[1]=pnt[j]; j++;
a.a[2]=pnt[j]; j++;
a.a[3]=pnt[j]; j++;
a.a[4]=1.0; // W=1
// extrac second point
j=lin[i]*4; i++;
b.a[0]=pnt[j]; j++;
b.a[1]=pnt[j]; j++;
b.a[2]=pnt[j]; j++;
b.a[3]=pnt[j]; j++;
b.a[4]=1.0; // W=1
// transform
a=rep*a;
b=rep*b;
// perspective: camera projection plane u=0, focus at (0,0,0,-focal_length)
if (a[3]>=0.0) a*=divide(focal_length,a[3]+focal_length); else a.zero();
if (b[3]>=0.0) b*=divide(focal_length,b[3]+focal_length); else b.zero();
// render
glVertex3dv(a.a); // use just x,y,z
glVertex3dv(b.a); // use just x,y,z
}
glEnd();
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
And preview:
[Edit2] solid mesh and cross-section
so I changed the architecture quite a bit. I moved the 4D 5x5 homogenuous transform matrix (reper4D) to separate file and added colors and mesh definition by 4D simplexes (4 point 4 side tetrahedrons). The cut is simply computing the intersection (as described above) of simplex and cutting hyperplane resulting in either 3 points (triangle) , 4 points (tetrahedron) or 0 points. Which can be rendered easily (no need to analyze the connections between edges). For more info see this:
Calculate 3D cross section of 4D shape (tesseract)
Btw. I think this is how Miegakure works. Here updated code:
//---------------------------------------------------------------------------
//--- Mesh 4D: ver 1.000 ----------------------------------------------------
//---------------------------------------------------------------------------
#ifndef _mesh4D_h
#define _mesh4D_h
//---------------------------------------------------------------------------
#include "list.h"
#include "reper4D.h"
//---------------------------------------------------------------------------
class mesh4D
{
public:
reper4D rep; // 4D uniform 5x5 transform matrix
List<double> pnt; // 4D point list (x,y,z,w)
List<int> lin; // 4D wireframe (i0,i1)
List<int> fac; // 4D simplexes (i0,i1,i2,i3)
List<DWORD> col; // simplex colors (RGB)
mesh4D() {}
mesh4D(mesh4D& a) { *this=a; }
~mesh4D() {}
mesh4D* operator = (const mesh4D *a) { *this=*a; return this; }
//mesh4D* operator = (const mesh4D &a) { ...copy... return this; }
void set_hypercube(double a);
void draw_cut(double w_cut); // render cross section by w=w_cut hyperplane
void draw (double focal_length=-1.0,double w_near=-1.0); // render mesh (focal_length<0) -> no perspective, else perspective view in W+ direction
void draw_wireframe(double focal_length=-1.0,double w_near=-1.0); // render wireframe (focal_length<0) -> no perspective, else perspective view in W+ direction
};
//---------------------------------------------------------------------------
void mesh4D::set_hypercube(double a)
{
const double tab_pnt[]=
{
-a, -a, -a, -a,
+a, -a, -a, -a,
-a, +a, -a, -a,
+a, +a, -a, -a,
-a, -a, +a, -a,
+a, -a, +a, -a,
-a, +a, +a, -a,
+a, +a, +a, -a,
-a, -a, -a, +a,
+a, -a, -a, +a,
-a, +a, -a, +a,
+a, +a, -a, +a,
-a, -a, +a, +a,
+a, -a, +a, +a,
-a, +a, +a, +a,
+a, +a, +a, +a,
};
const int tab_lin[]=
{
// A0
0+0, 0+1,
0+1, 0+3,
0+3, 0+2,
0+2, 0+0,
// A1
4+0, 4+1,
4+1, 4+3,
4+3, 4+2,
4+2, 4+0,
// A=A0+A1
0+0, 4+0,
0+1, 4+1,
0+2, 4+2,
0+3, 4+3,
// B0
8+0, 8+1,
8+1, 8+3,
8+3, 8+2,
8+2, 8+0,
// B1
12+0, 12+1,
12+1, 12+3,
12+3, 12+2,
12+2, 12+0,
// B=B0+B1
8+0, 12+0,
8+1, 12+1,
8+2, 12+2,
8+3, 12+3,
// hyper cube = A+B
0+0, 8+0,
0+1, 8+1,
0+2, 8+2,
0+3, 8+3,
0+4, 8+4,
0+5, 8+5,
0+6, 8+6,
0+7, 8+7,
};
// 5x simplex per cube
#define _cube(a0,a1,a2,a3,a4,a5,a6,a7) a1,a2,a4,a7, a0,a1,a2,a4, a2,a4,a6,a7, a1,a2,a3,a7, a1,a4,a5,a7
// 4D hypercube = 8 cubes
const int tab_fac[]=
{
_cube( 0, 1, 2, 3, 4, 5, 6, 7),
_cube( 0, 1, 2, 3, 8, 9,10,11),
_cube( 4, 5, 6, 7,12,13,14,15),
_cube( 8, 9,10,11,12,13,14,15),
_cube( 0, 1, 4, 5, 8, 9,12,13),
_cube( 0, 2, 4, 6, 8,10,12,14),
_cube( 1, 3, 5, 7, 9,11,13,15),
_cube( 2, 3, 6, 7,10,11,14,15),
};
#undef _cube
const DWORD tab_col[]=
{
// BBGGRR, BBGGRR, BBGGRR, BBGGRR, BBGGRR,
0x00FF0000,0x00FF0000,0x00FF0000,0x00FF0000,0x00FF0000,
0x0000FF00,0x0000FF00,0x0000FF00,0x0000FF00,0x0000FF00,
0x000000FF,0x000000FF,0x000000FF,0x000000FF,0x000000FF,
0x0000FFFF,0x0000FFFF,0x0000FFFF,0x0000FFFF,0x0000FFFF,
0x00FF00FF,0x00FF00FF,0x00FF00FF,0x00FF00FF,0x00FF00FF,
0x00FFFF00,0x00FFFF00,0x00FFFF00,0x00FFFF00,0x00FFFF00,
0x00FFFFFF,0x00FFFFFF,0x00FFFFFF,0x00FFFFFF,0x00FFFFFF,
0x004080FF,0x004080FF,0x004080FF,0x004080FF,0x004080FF,
};
int i,n;
vector<4> p;
rep.reset();
pnt.num=0; for (i=0,n=sizeof(tab_pnt)/sizeof(tab_pnt[0]);i<n;i++) pnt.add(tab_pnt[i]);
lin.num=0; for (i=0,n=sizeof(tab_lin)/sizeof(tab_lin[0]);i<n;i++) lin.add(tab_lin[i]);
fac.num=0; for (i=0,n=sizeof(tab_fac)/sizeof(tab_fac[0]);i<n;i++) fac.add(tab_fac[i]);
col.num=0; for (i=0,n=sizeof(tab_col)/sizeof(tab_col[0]);i<n;i++) col.add(tab_col[i]);
}
//---------------------------------------------------------------------------
void mesh4D::draw_cut(double w_cut)
{
const double _zero=1e-6;
const int edge2[]={0,1,0,2,0,3,1,2,2,3,3,1,-1}; // simplex wireframe i0,i1
const int edge3[]={0,1,2,3,0,1,3,1,2,3,2,0,-1}; // simplex triangles i0,i1,i2
int e,i,j,k,k0,k1,k2,inside[4];
DWORD rgb;
vector<4> p[4],q[4];
vector<3> xyz[4],nor,a,b;
for (i=0;i<fac.num;)
{
rgb=col[i>>2];
// extrac points (x,y,z,w)
for (k=0;k<4;k++)
{
j=fac[i]*4; i++;
p[k].a[0]=pnt[j]; j++;
p[k].a[1]=pnt[j]; j++;
p[k].a[2]=pnt[j]; j++;
p[k].a[3]=pnt[j]; j++;
// transform
rep.l2g(p[k],p[k]);
inside[k]=1;
}
// process edge2 and compute cross section cut intersection points
for (e=0,k=0;edge2[e]>=0;)
{
k0=edge2[e]; e++;
k1=edge2[e]; e++;
// fully inside
if (fabs(p[k0][3]-w_cut)+fabs(p[k1][3]-w_cut)<=_zero)
{
if ((k<4)&&(inside[k0])){ q[k]=p[k0]; k++; inside[k0]=0; }
if ((k<4)&&(inside[k1])){ q[k]=p[k1]; k++; inside[k1]=0; }
continue;
}
// no intersection
if (((p[k0][3]> w_cut)&&(p[k1][3]> w_cut))||((p[k0][3]< w_cut)&&(p[k1][3]< w_cut))) continue;
// 1 intersection
if (k<4)
{
q[k]=p[k1]-p[k0];
q[k]*=divide(w_cut-p[k0][3],p[k1][3]-p[k0][3]);
q[k]+=p[k0];
q[k][3]=w_cut;
k++;
continue;
}
}
// 4D -> 3D vector
for (k0=0;k0<k;k0++) for (k1=0;k1<3;k1++) xyz[k0][k1]=q[k0][k1];
// render triangle
if (k==3)
{
// normal
a=xyz[1]-xyz[0];
b=xyz[2]-xyz[1];
nor.cross(a,b);
nor.unit();
// render
glBegin(GL_TRIANGLES);
glNormal3dv(nor.a);
glColor4ubv((BYTE*)(&rgb));
glVertex3dv(xyz[0].a);
glVertex3dv(xyz[1].a);
glVertex3dv(xyz[2].a);
glEnd();
}
// render simplex
if (k==4)
for (e=0;edge3[e]>=0;)
{
k0=edge3[e]; e++;
k1=edge3[e]; e++;
k2=edge3[e]; e++;
// normal
a=xyz[k1]-xyz[k0];
b=xyz[k2]-xyz[k1];
nor.cross(a,b);
nor.unit();
// render
glBegin(GL_TRIANGLES);
glNormal3dv(nor.a);
glColor4ubv((BYTE*)(&rgb));
glVertex3dv(xyz[k0].a);
glVertex3dv(xyz[k1].a);
glVertex3dv(xyz[k2].a);
glEnd();
}
}
}
//---------------------------------------------------------------------------
void mesh4D::draw(double focal_length,double w_near)
{
const int edge3[]={0,1,2,3,0,1,3,1,2,3,2,0,-1}; // simplex triangles i0,i1,i2
int i,j,k,k0,k1,k2;
DWORD rgb;
vector<4> p;
vector<3> xyz[4],nor,a,b;
// 4D simplexes
glColor3f(0.3,0.3,0.3);
for (i=0;i<fac.num;)
{
rgb=col[i>>2];
// extrac points (x,y,z,w)
for (k=0;k<4;k++)
{
j=fac[i]*4; i++;
p[0]=pnt[j]; j++;
p[1]=pnt[j]; j++;
p[2]=pnt[j]; j++;
p[3]=pnt[j]; j++;
// transform
rep.l2g(p,p);
// perspective projection
if (focal_length>0.0)
{
p[3]-=w_near;
if (p[3]>=0.0) p*=divide(focal_length,p[3]+focal_length); else p.zero();
}
// 4D -> 3D vector
xyz[k].ld(p[0],p[1],p[2]);
}
// render simplex
for (k=0;edge3[k]>=0;)
{
k0=edge3[k]; k++;
k1=edge3[k]; k++;
k2=edge3[k]; k++;
// normal
a=xyz[k1]-xyz[k0];
b=xyz[k2]-xyz[k1];
nor.cross(a,b);
nor.unit();
// render
// glBegin(GL_LINE_LOOP);
glBegin(GL_TRIANGLES);
glNormal3dv(nor.a);
glColor4ubv((BYTE*)(&rgb));
glVertex3dv(xyz[k0].a);
glVertex3dv(xyz[k1].a);
glVertex3dv(xyz[k2].a);
glEnd();
}
}
}
//---------------------------------------------------------------------------
void mesh4D::draw_wireframe(double focal_length,double w_near)
{
int i,j,k;
vector<4> p[4];
// 4D wireframe
glColor3f(1.0,1.0,1.0);
glBegin(GL_LINES);
for (i=0;i<lin.num;)
{
// extrac points (x,y,z,w)
for (k=0;k<2;k++)
{
j=lin[i]*4; i++;
p[k].a[0]=pnt[j]; j++;
p[k].a[1]=pnt[j]; j++;
p[k].a[2]=pnt[j]; j++;
p[k].a[3]=pnt[j]; j++;
// transform
rep.l2g(p[k],p[k]);
// perspective projection
if (focal_length>0.0)
{
p[k][3]-=w_near;
if (p[k][3]>=0.0) p[k]*=divide(focal_length,p[k][3]+focal_length); else p[k].zero();
}
// render
glVertex3dv(p[k].a); // use just x,y,z
}
}
glEnd();
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
And the preview for cross section render:
The worst part was to define the hypercube as set of simplexes ...

how to find transformation for static mesh?

How to find transformation for static mesh?
I'll be using it for my picking function.
I have tried this but it didn't work...
void StaticMesh::Render(void)
{
D3DXMATRIX matWorld;
D3DXMatrixIdentity(&matWorld);
device->SetTransform(D3DTS_WORLD, &matWorld);
for(DWORD i = 0; i < numMaterials; i++) // loop through each subset
{
device->SetMaterial(&material[i]); // set the material for the subset
device->SetTexture(0, texture[i]); // ...then set the texture
mesh->DrawSubset(i); // draw the subset
}
s = &matWorld; // THIS WOULD BE THE TRANSFORMATION FOR THE OBJECT
}
thanks in advance.
Edit 2:
Here is full class
#include "StdAfx.h"
#include "StaticMesh.h"
StaticMesh::StaticMesh(LPDIRECT3DDEVICE9* dev)
{
d3ddev=dev;
device = *d3ddev;
}
StaticMesh::~StaticMesh(void)
{
}
void StaticMesh::Render(void)
{
LPDIRECT3DDEVICE9 device=*d3ddev;
D3DXMATRIX matWorld;
D3DXMatrixIdentity(&matWorld);
device->SetTransform(D3DTS_WORLD, &matWorld);
for(DWORD i = 0; i < numMaterials; i++) // loop through each subset
{
device->SetMaterial(&material[i]); // set the material for the subset
device->SetTexture(0, texture[i]); // ...then set the texture
mesh->DrawSubset(i); // draw the subset
}
s = matWorld;
}
StaticMesh* StaticMesh::LoadXFile(LPCWSTR fileName, LPDIRECT3DDEVICE9* dev)
{
StaticMesh *obj = this;
obj = new StaticMesh(dev);
obj->Load(fileName);
return obj;
}
void StaticMesh::Load(LPCWSTR fileName)
{
D3DXLoadMeshFromX(fileName, // load this file
D3DXMESH_SYSTEMMEM, // load the mesh into system memory
device, // the Direct3D Device
NULL, // we aren't using adjacency
&bufMeshMaterial, // put the materials here
NULL, // we aren't using effect instances
&numMaterials, // the number of materials in this model
&mesh); // put the mesh here
// retrieve the pointer to the buffer containing the material information
D3DXMATERIAL* tempMaterials = (D3DXMATERIAL*)bufMeshMaterial->GetBufferPointer();
// create a new material buffer and texture for each material in the mesh
material = new D3DMATERIAL9[numMaterials];
texture = new LPDIRECT3DTEXTURE9[numMaterials];
for(DWORD i = 0; i < numMaterials; i++) // for each material...
{
// Copy the material
material[i] = tempMaterials[i].MatD3D;
// Set the ambient color for the material (D3DX does not do this)
material[i].Ambient = material[i].Diffuse;
// Create the texture if it exists - it may not
texture[i] = NULL;
if (tempMaterials[i].pTextureFilename)
{
D3DXCreateTextureFromFileA(device, tempMaterials[i].pTextureFilename,&texture[i]);
}
}
}
void StaticMesh::CleanUp(void)
{
mesh->Release();
}
EDIT 3:
void GUIDialog::Picking(HWND hWnd, int status)
{
LPDIRECT3DDEVICE9 device = *d3ddev;
D3DXMATRIX matProj;
POINT pt;
D3DVIEWPORT9 vp;
D3DXMATRIX *matWorld=NULL;
D3DXMATRIX matView;
GetCursorPos(&pt);
ScreenToClient(hWnd, &pt);
device->GetTransform(D3DTS_PROJECTION, &matProj);
device->GetViewport(&vp);
device->GetTransform(D3DTS_VIEW, &matView);
for(int i=0; (int)edit.size() > i; i++)
{
matWorld=&edit.at(i)->staticMesh->s;
// Use inverse of matrix
D3DXVECTOR3 rayPos((float)pt.x, (float)pt.y,0); // near-plane position
D3DXVECTOR3 rayDir((float)pt.x, (float)pt.y,1); // far-plane position
D3DXVec3Unproject(&rayPos,&rayPos,&vp,&matProj,&matView,matWorld);
D3DXVec3Unproject(&rayDir,&rayDir,&vp,&matProj,&matView,matWorld);
rayDir -= rayPos; // make a direction from the 2 positions
D3DXVec3Normalize(&rayDir,&rayDir);
if(FAILED(D3DXIntersect(edit.at(i)->staticMesh->mesh, &rayPos, &rayDir, &edit.at(i)->staticMesh->hasHit, NULL, NULL, NULL, &edit.at(i)->staticMesh->
distanceToCollision, NULL, NULL)))
{
PostQuitMessage(0);
};
if(edit.at(i)->staticMesh->hasHit!=0&&status==WM_LBUTTONUP)
{
if(status==WM_LBUTTONUP)
EventProc(HIT, *edit.at(i));
}
}
}
Well your code above is correct matWorld IS the transform of the object. If you wish to transform a ray into object local space (ie to the space prior to the matWorld transformation) then you simply need to multiply your world space ray by the inverse of matWorld ...