Programming a specific 3d (star-like) model in OpenGL? [closed] - 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 4 years ago.
Improve this question
How can I create the following model:
by starting from the first drawing. Could it be programmed in OpenGL entirely or should I use other software like 3d Studio Max or Unity? Are there some specific algorithms that should be used?

Yes this can be done in C++/OpenGL
create random curves emitting from center
simple 3D quadratic polynomial curve will fit the bill.
convert the curves to cones
simply interpolate points along each curve and use it as a center for the cone slice. The direction is set by the previous or next point along the curve. Interpolate the cone slices and add their points to some point list. See:
Smoothly connecting circle centers
create faces
simply connect the computed points to form the cones using any primitive ... I would suggest GL_QUADs...
core
if you want to add also the core (nuclei?) it can be done a s a sphere with some noise added to its surface and probably some filtering to smooth it a bit...
Here simple curve generation C++ example:
List<double> pnt;
void spicule_init()
{
double t,tt,x,y,z;
double a0[3],a1[3],a2[3];
int ix0,ix,i,j;
Randomize();
for (i=0;i<20;i++) // cones
{
// random quadratic 3D curve coeff
for (j=0;j<3;j++)
{
a0[j]=0.0; // center (0,0,0)
a1[j]=2.0*(Random()-0.5); // main direction
a2[j]=1.0*(Random()-0.5); // curvature
}
// curve interpolation
ix0=pnt.num;
for (t=0.0;t<=1.0;t+=0.04)
for (tt=t*t,j=0;j<3;j++)
pnt.add(a0[j]+(a1[j]*t)+(a2[j]*tt));
}
}
Preview of the generated points:
[Edit1] When added the cones,normals and faces it looks like this:
Its far from perfect but I think is a good start point. Just tweak the radius r and the curve coefficients a1[],a2[] to achieve desired shape ... and may be add the core and or check for self intersections too, I am too lazy to do that...
Here the updated C++/GL code:
//---------------------------------------------------------------------------
List<double> pnt,nor; // points, normals
List<int> fac; // QUAD faces
//---------------------------------------------------------------------------
void Circle3D(List<double> &pnt,List<double> &nor,double *p0,double *n0,double r,int N)
{
int i;
double a,da=divide(pi2,N),p[3],dp[3],x[3],y[3];
vector_ld(x,1.0,0.0,0.0); if (fabs(vector_mul(x,n0)>0.7)) vector_ld(x,0.0,1.0,0.0);
vector_mul(x,x,n0); vector_one(x,x);
vector_mul(y,x,n0); vector_one(y,y);
for (a=0.0,i=0;i<N;i++,a+=da)
{
vector_mul( p,x,cos(a));
vector_mul(dp,y,sin(a));
vector_add(p,p,dp); nor.add(p[0]); nor.add(p[1]); nor.add(p[2]);
vector_mul(p,p,r);
vector_add(p,p,p0); pnt.add(p[0]); pnt.add(p[1]); pnt.add(p[2]);
}
}
//---------------------------------------------------------------------------
void spicule_init() // generate random spicule mesh
{
const int N=36; // points/circle
const int N3=3*N;
double t,tt,x,y,z,r;
double a0[3],a1[3],a2[3];
double p[3],n[3];
int e,i,j,i00,i01,i10,i11;
Randomize();
pnt.num=0; nor.num=0; fac.num=0;
for (i=0;i<20;i++) // cones
{
// random quadratic 3D curve coeff
for (j=0;j<3;j++)
{
a0[j]=0.0; // center (0,0,0)
a1[j]=2.0*(Random()-0.5); // main direction and size
a2[j]=1.0*(Random()-0.5); // curvature
}
// curve interpolation
vector_ld(n,0.0,0.0,0.0);
for (e=0,t=0.05;t<=1.0;t+=0.05)
{
// points,normals
for (tt=t*t,j=0;j<3;j++) p[j]=a0[j]+(a1[j]*t)+(a2[j]*tt);
r=0.15*(1.0-pow(t,0.1)); // radius is shrinking with t
vector_sub(n,p,n); // normal is p(t)-p(t-dt)
Circle3D(pnt,nor,p,n,r,N); // add circle to pnt (N points)
vector_copy(n,p); // remember last point
// faces
if (!e){ e=1; continue; } // ignore first slice of cone
i00=pnt.num- 3; i10=i00-N3;
i01=pnt.num-N3; i11=i01-N3;
for (j=0;j<N;j++)
{
fac.add(i00);
fac.add(i01);
fac.add(i11);
fac.add(i10);
i00=i01; i01+=3;
i10=i11; i11+=3;
}
}
}
}
//---------------------------------------------------------------------------
void spicule_draw() // render generated spicule
{
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
int i,j;
glColor3f(1.0,1.0,1.0);
glBegin(GL_QUADS);
for (i=0;i<fac.num;i++)
{
j=fac.dat[i];
glNormal3dv(nor.dat+j);
glVertex3dv(pnt.dat+j);
}
glEnd();
}
//---------------------------------------------------------------------------
If you do not know how to compute vector operations like cross/dot products or absolute value see:
// cross product: W = U x V
W.x=(U.y*V.z)-(U.z*V.y)
W.y=(U.z*V.x)-(U.x*V.z)
W.z=(U.x*V.y)-(U.y*V.x)
// dot product: a = (U.V)
a=U.x*V.x+U.y*V.y+U.z*V.z
// abs of vector a = |U|
a=sqrt((U.x*U.x)+(U.y*U.y)+(U.z*U.z))
vector_mul(a[3],b[3],c[3]) is cross product a = b x c
a = vector_mul(b[3],c[3]) is dot product a = (b.c)
vector_one(a[3],b[3]) is unit vector a = b/|b|
vector_copy(a[3],b[3]) is just copy a = b
vector_add(a[3],b[3],c[3]) is adding a = b + c
vector_sub(a[3],b[3],c[3]) is substracting a = b - c
vector_neg(a[3],b[3]) is negation a = -b
vector_ld(a[3],x,y,z) is just loading a = (x,y,z)
Also some (if not all the) Vector math used can be found here:
Understanding 4x4 homogenous transform matrices
I also use mine dynamic list 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

Related

3D interpolation between curves [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 1 year ago.
Improve this question
I have a set of curves that are temperature dependent.
I.e curve Mat1 is for temperature 310C and Mat2 is for temp 420C.
As you can see, the data looks better when put in a logarithmic scale;
Now I need to get Mat3 curve for temperature 370C by interpolating Mat1 and Mat2 curves. What is the best way to get around doing this? I'm guessing that I might need to do some sort of 3D interpolation. The nature of the data (logarithmic behavior) also needs to be considered.
Here's the data for Mat1
9.43E+06 6.00E+04
3.96E+06 6.20E+04
1.78E+06 6.40E+04
8.52E+05 6.60E+04
4.28E+05 6.80E+04
2.25E+05 7.00E+04
1.23E+05 7.20E+04
6.95E+04 7.40E+04
4.05E+04 7.60E+04
2.43E+04 7.80E+04
1.49E+04 8.00E+04
9.39E+03 8.20E+04
Here's the data for Mat2
5.14E+08 4.80E+04
1.35E+08 5.00E+04
4.36E+07 5.20E+04
1.64E+07 5.40E+04
6.90E+06 5.60E+04
3.18E+06 5.80E+04
1.58E+06 6.00E+04
8.35E+05 6.20E+04
4.64E+05 6.40E+04
2.69E+05 6.60E+04
1.62E+05 6.80E+04
1.01E+05 7.00E+04
6.47E+04 7.20E+04
4.25E+04 7.40E+04
2.86E+04 7.60E+04
1.96E+04 7.80E+04
1.37E+04 8.00E+04
9735.23 8.20E+04
Any help would be appreciated.
Edit:
I'm adding data for two additional curves;
Curve at temperature 21C
3.98E+07 6.30E+04
1.58E+07 6.40E+04
4.03E+06 6.60E+04
1.47E+06 6.80E+04
6.57E+05 7.00E+04
3.37E+05 7.20E+04
1.91E+05 7.40E+04
1.16E+05 7.60E+04
7.49E+04 7.80E+04
5.04E+04 8.00E+04
3.52E+04 8.20E+04
2.53E+04 8.40E+04
1.87E+04 8.60E+04
1.41E+04 8.80E+04
1.08E+04 9.00E+04
8.47E+03 9.20E+04
Curve at temperature 537C
7.91E+06 3.80E+04
3.29E+06 4.00E+04
1.51E+06 4.20E+04
7.48E+05 4.40E+04
3.95E+05 4.60E+04
2.20E+05 4.80E+04
1.28E+05 5.00E+04
7.77E+04 5.20E+04
4.87E+04 5.40E+04
3.14E+04 5.60E+04
2.08E+04 5.80E+04
1.41E+04 6.00E+04
9.73E+03 6.20E+04
6.85E+03 6.40E+04
More info about the curves - These are alternating stress (y axis), number of cycles to failure (x axis) curves for a material at different temperatures.
Thanks.
I managed to get simple example working. First of all your data must be ordered so the measurements must be sorted by temperature and each measurement must be ordered by y (stress). I used ascending order. First algorith:
compute BBOX
simply compute min and max x,y coordinates of all measurements together. This will be used for conversion between logarithmic and linear scale and also for aligning.
resample and align all measurements
so convert all of your measurements to form that it's samples are at the same y values (across all measurements). I used uniformly sampled y axis. So simply step is (ymax-ymin)/(n-1) where n is number of points of the resampled data. So all measurements will have the same size and all the y values will be the same across measurement on the same index. Missing x data will be filled with 0.
The resampling can be done in linear scale. I used piecewise cubic interpolation.
create new measurement for new temperature
so simply create new measurement again containing n-points. The y value is the same as before (so just copy it from any of the aligned measurements) and then just take 1 point from each of the 4 measurements corresponding to the same point as we are processing and cubicaly interpolate its position. However this must be done in logarithmic scale!
The valid range of temperature is between the 2nd and 3th measurement temperature.
Here preview using your data and 370 C:
And here C++/VCL example for this (just ignore the VCL stuff):
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
int xs,ys; // screen resolution
Graphics::TBitmap *bmp; // back buffer bitmap for rendering
//---------------------------------------------------------------------------
// here starts the important stuff
//---------------------------------------------------------------------------
float in[4][40]= // input measureements format is: { temperature,x0,y0,x1,y1...,-1 }
{{ 21.0,
3.98E+07,6.30E+04,
1.58E+07,6.40E+04,
4.03E+06,6.60E+04,
1.47E+06,6.80E+04,
6.57E+05,7.00E+04,
3.37E+05,7.20E+04,
1.91E+05,7.40E+04,
1.16E+05,7.60E+04,
7.49E+04,7.80E+04,
5.04E+04,8.00E+04,
3.52E+04,8.20E+04,
2.53E+04,8.40E+04,
1.87E+04,8.60E+04,
1.41E+04,8.80E+04,
1.08E+04,9.00E+04,
8.47E+03,9.20E+04,
-1.0 },
{ 310.0,
9.43E+06,6.00E+04,
3.96E+06,6.20E+04,
1.78E+06,6.40E+04,
8.52E+05,6.60E+04,
4.28E+05,6.80E+04,
2.25E+05,7.00E+04,
1.23E+05,7.20E+04,
6.95E+04,7.40E+04,
4.05E+04,7.60E+04,
2.43E+04,7.80E+04,
1.49E+04,8.00E+04,
9.39E+03,8.20E+04,
-1.0 },
{ 420.0,
5.14E+08,4.80E+04,
1.35E+08,5.00E+04,
4.36E+07,5.20E+04,
1.64E+07,5.40E+04,
6.90E+06,5.60E+04,
3.18E+06,5.80E+04,
1.58E+06,6.00E+04,
8.35E+05,6.20E+04,
4.64E+05,6.40E+04,
2.69E+05,6.60E+04,
1.62E+05,6.80E+04,
1.01E+05,7.00E+04,
6.47E+04,7.20E+04,
4.25E+04,7.40E+04,
2.86E+04,7.60E+04,
1.96E+04,7.80E+04,
1.37E+04,8.00E+04,
9735.23 ,8.20E+04,
-1.0 },
{ 537.0,
7.91E+06,3.80E+04,
3.29E+06,4.00E+04,
1.51E+06,4.20E+04,
7.48E+05,4.40E+04,
3.95E+05,4.60E+04,
2.20E+05,4.80E+04,
1.28E+05,5.00E+04,
7.77E+04,5.20E+04,
4.87E+04,5.40E+04,
3.14E+04,5.60E+04,
2.08E+04,5.80E+04,
1.41E+04,6.00E+04,
9.73E+03,6.20E+04,
6.85E+03,6.40E+04,
-1.0 }};
//---------------------------------------------------------------------------
// temp and output data
//---------------------------------------------------------------------------
const n=40; // points to resmaple curves with
float dat[4][2+n+n]; // resampled input curves
float out[2+n+n]; // interpolated curve
float xmin,xmax,ymin,ymax; // BBOX
void resample(float *out,float *in,float y0,float y1) // resample and align y to range and n points and store it to out
{
float t,d1,d2,a0,a1,a2,a3,x,y,x0,x1,x2,x3;
int i,ii,i0,i1,i2,i3,nn;
// scan how many points in[] has
for (nn=0,i=1;in[i]>=0.0;i+=2) nn++;
// resample input curves to n points
out[0]=in[0]; // copy T
out[n+n+1]=-1; // end of data
for (i=0;i<n;i++)
{
// y uniformly distributed and aligned in the dat array
y=y0+((y1-y0)*float(i)/float(n-1));
ii=1+i +i ;
// check if range present
if ((y<in[1+1])||(y>in[1+nn-1+nn-1+1]))
{
out[ii+0]=0.0;
out[ii+1]=y;
continue;
}
// find i1 so in[i1] <= y < in[i1+1]
// linear search, can be replaced with binary search
for (i1=0;i1<nn;i1++) if (in[1+i1+i1+1]>=y) break;
if (in[1+i1+i1+1]>y) i1--;
// neigboring indexes
i0=i1-1; if (i0< 0) i0= 0;
i2=i1+1; if (i2>=nn) i2=nn-1;
i3=i1+2; if (i3>=nn) i3=nn-1;
// convert to array index
i0=1+i0+i0;
i1=1+i1+i1;
i2=1+i2+i2;
i3=1+i3+i3;
// parameter is based on y value
d1=y-in[i1+1];
d2=in[i2+1]-in[i1+1];
if (fabs(d2)>1e-6) t=d1/d2; else t=0.0;
// points to interpolate
x0=in[i0];
x1=in[i1];
x2=in[i2];
x3=in[i3];
// cubic interpoaltion of x
d1=0.5*(x2-x0);
d2=0.5*(x3-x1);
a0=x1;
a1=d1;
a2=(3.0*(x2-x1))-(2.0*d1)-d2;
a3=d1+d2+(2.0*(-x2+x1));
x=a0+(a1*t)+(a2*t*t)+(a3*t*t*t);
if (x<0.0) x=0.0; // just to be sure data is not messed up
// copy point
out[ii+0]=x;
out[ii+1]=y;
}
}
//---------------------------------------------------------------------------
void interpolate(float *out,float T) // interpolate out[] as n point curve from dat[4][] matching temperature T
{ // dat[][] must be ordered ascending by T,x,y
int i,ii; // valid T range is <dat[1][0],dat[2][0]>
float t,d1,d2,a0,a1,a2,a3,x,x0,x1,x2,x3,t0,t1,t2,t3;
out[0]=T; // copy T
out[n+n+1]=-1; // end of data
// parameter from T
t=(T-dat[1][0])/(dat[2][0]-dat[1][0]);
t0=dat[0][0];
t1=dat[1][0];
t2=dat[2][0];
t3=dat[3][0];
// cubic interpolation between curves
for (i=0;i<n;i++)
{
// points to interpolate
ii=1+i+i;
x0=dat[0][ii];
x1=dat[1][ii];
x2=dat[2][ii];
x3=dat[3][ii];
// logarithm scale
(x0>=xmin)?x0=log(x0/xmin)/log(xmax/xmin):x0=0.0;
(x1>=xmin)?x1=log(x1/xmin)/log(xmax/xmin):x1=0.0;
(x2>=xmin)?x2=log(x2/xmin)/log(xmax/xmin):x2=0.0;
(x3>=xmin)?x3=log(x3/xmin)/log(xmax/xmin):x3=0.0;
out[ii+1]=dat[0][ii+1]; // copy y
// too much missing data
if ((x1<=0.0)||(x2<=0.0)){ out[ii+0]=0; continue; }
// mirror missing data
if (x0<=0.0) x0=x1-((x2-x1)*(t1-t0)/(t2-t1));
if (x3<=0.0) x3=x2+((x2-x1)*(t3-t2)/(t2-t1));
// interpolate x
d1=0.5*(x2-x0);
d2=0.5*(x3-x1);
a0=x1;
a1=d1;
a2=(3.0*(x2-x1))-(2.0*d1)-d2;
a3=d1+d2+(2.0*(-x2+x1));
x=a0+(a1*t)+(a2*t*t)+(a3*t*t*t);
if (x<0.0) x=0.0; // just to be sure data is not messed up
else x=exp(x*log(xmax/xmin))*xmin; // back to linear scale
out[ii+0]=x;
}
}
//---------------------------------------------------------------------------
void minmax(float *dat,bool _reset) // compute BBOX of the curves
{
int i;
float x,y;
for (i=1;dat[i]>=0.0;)
{
x=dat[i]; i++;
y=dat[i]; i++;
if (x<=0.0) continue;
if (_reset){ xmin=xmax=x; ymin=ymax=y; _reset=false; }
if (xmin>x) xmin=x;
if (xmax<x) xmax=x;
if (ymin>y) ymin=y;
if (ymax<y) ymax=y;
}
}
//---------------------------------------------------------------------------
void toscr(float &x,float &y) // convert x,y from plot data to screen coordinates (just for rendering)
{
float x0,dx,y1,dy;
// range <0,1>
// x=(x-xmin)/(xmax-xmin); // linear
// y=(y-ymin)/(ymax-ymin); // linear
(x>=xmin)?x=log(x/xmin)/log(xmax/xmin):x=0.0; // logarithmic
(y>=ymin)?y=log(y/ymin)/log(ymax/ymin):y=0.0; // logarithmic
// view
x0=0.1*xs; dx=0.8*xs;
y1=0.9*ys; dy=0.8*ys;
// [pixels]
x=x0+x*dx;
y=y1-y*dy;
}
//---------------------------------------------------------------------------
void plot(float *dat,TColor col)// renders measurement data (just for rendering)
{
int i,e;
float x,y,r=2;
// curve
bmp->Canvas->Pen->Color=col;
bmp->Canvas->Font->Color=col;
for (e=1,i=1;dat[i]>=0.0;)
{
x=dat[i]; i++;
y=dat[i]; i++;
if (x<=0.0) continue;
toscr(x,y);
if (e)
{
bmp->Canvas->TextOutA(x,y,AnsiString().sprintf("%.0f C",dat[0]));
bmp->Canvas->MoveTo(x,y);
e=0;
}
else bmp->Canvas->LineTo(x,y);
}
// points
for (i=1;dat[i]>=0.0;)
{
x=dat[i]; i++;
y=dat[i]; i++;
if (x<=0.0) continue;
toscr(x,y);
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
}
}
//---------------------------------------------------------------------------
void draw() // just render of my App
{
bmp->Canvas->Brush->Color=clWhite;
bmp->Canvas->FillRect(TRect(0,0,xs,ys));
plot(dat[0],clRed);
plot(dat[1],clGreen);
plot(dat[2],clBlue);
plot(dat[3],clBlack);
plot(out,clMaroon);
Form1->Canvas->Draw(0,0,bmp);
// bmp->SaveToFile("out.bmp");
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner) // init of my app
{
// init backbuffer
bmp=new Graphics::TBitmap;
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf32bit;
// here prepare data (important)
int i;
for (i=0;i<4;i++) minmax(in[i],i==0);
for (i=0;i<4;i++) resample(dat[i],in[i],ymin,ymax);
// here create new data for T=370[C]
interpolate(out,370.0);
// and also include it to the BBOX for rendering
minmax(out,false);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender) // not important just destructor of my App
{
delete bmp;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender) // not important just resize event
{
xs=ClientWidth;
ys=ClientHeight;
bmp->Width=xs;
bmp->Height=ys;
draw();
}
//-------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender) // not important just repaint event
{
draw();
}
//---------------------------------------------------------------------------
See function TForm1::TForm1(TComponent* Owner) on how to use this.
However physical validity is questionable You should test if this kind of interpolation leads to valid data by having 5 measurements. Use 4 to interpolate the 5th and check if they overlap If not then this might need additional tweaking like increasing the interpolation polynomial degree, or use log scale also for resampling etc ...

Frustum Culling Bug

So I've implemented Frustum Culling in my game engine and I'm experiencing a strange bug. I am rendering a building that is segmented into chunks and I'm only rendering the chunks which are in the frustum. My camera starts at around (-.033, 11.65, 2.2) and everything looks fine. I start moving around and there is no flickering. When I set a breakpoint in the frustum culling code I can see that it is indeed culling some of the meshes. Everything seems great. Then when I reach the center of the building, around (3.9, 4.17, 2.23) meshes start to disappear that are in view. The same is true on the other side as well. I can't figure out why this bug could exist.
I implement frustum culling by using the extraction method listed here Extracting View Frustum Planes (Gribb & Hartmann method). I had to use glm::inverse() rather than transpose as it suggested and I think the matrix math was given for row-major matrices so I flipped that. All in all my frustum plane calculation looks like
std::vector<Mesh*> render_meshes;
auto comboMatrix = proj * glm::inverse(view * model);
glm::vec4 p_planes[6];
p_planes[0] = comboMatrix[3] + comboMatrix[0]; //left
p_planes[1] = comboMatrix[3] - comboMatrix[0]; //right
p_planes[2] = comboMatrix[3] + comboMatrix[1]; //bottom
p_planes[3] = comboMatrix[3] - comboMatrix[1]; //top
p_planes[4] = comboMatrix[3] + comboMatrix[2]; //near
p_planes[5] = comboMatrix[3] - comboMatrix[2]; //far
for (int i = 0; i < 6; i++){
p_planes[i] = glm::normalize(p_planes[i]);
}
for (auto mesh : meshes) {
if (!frustum_cull(mesh, p_planes)) {
render_meshes.emplace_back(mesh);
}
}
I then decide to cull each mesh based on its bounding box (as calculated by ASSIMP with the aiProcess_GenBoundingBoxes flag) as follows (returning true means culled)
glm::vec3 vmin, vmax;
for (int i = 0; i < 6; i++) {
// X axis
if (p_planes[i].x > 0) {
vmin.x = m->getBBoxMin().x;
vmax.x = m->getBBoxMax().x;
}
else {
vmin.x = m->getBBoxMax().x;
vmax.x = m->getBBoxMin().x;
}
// Y axis
if (p_planes[i].y > 0) {
vmin.y = m->getBBoxMin().y;
vmax.y = m->getBBoxMax().y;
}
else {
vmin.y = m->getBBoxMax().y;
vmax.y = m->getBBoxMin().y;
}
// Z axis
if (p_planes[i].z > 0) {
vmin.z = m->getBBoxMin().z;
vmax.z = m->getBBoxMax().z;
}
else {
vmin.z = m->getBBoxMax().z;
vmax.z = m->getBBoxMin().z;
}
if (glm::dot(glm::vec3(p_planes[i]), vmin) + p_planes[i][3] > 0)
return true;
}
return false;
Any guidance?
Update 1: Normalizing the full vec4 representing the plane is incorrect as only the vec3 represents the normal of the plane. Further, normalization is not necessary for this instance as we only care about the sign of the distance (not the magnitude).
It is also important to note that I should be using the rows of the matrix not the columns. I am achieving this by replacing
p_planes[0] = comboMatrix[3] + comboMatrix[0];
with
p_planes[0] = glm::row(comboMatrix, 3) + glm::row(comboMatrix, 0);
in all instances.
You are using GLM incorrectly. As per the paper of Gribb and Hartmann, you can extract the plane equations as a sum or difference of different rows of the matrix, but in glm, mat4 foo; foo[n] will yield the n-th column (similiar to how GLSL is designed).
This here
for (int i = 0; i < 6; i++){
p_planes[i] = glm::normalize(p_planes[i]);
}
also doesn't make sense, since glm::normalize(vec4) will simply normalize a 4D vector. This will result in the plane to be shifted around along its normal direction. Only thexyz components must be brought to unit length, and w must be scaled accordingly. It is even explained in details in the paper itself. However, since you only need to know on which half-space a point lies, normalizing the plane equation is a waste of cycles, you only care about the sign, not the maginitude of the value anyway.
After following #derhass solution for normalizing the planes correctly for intersection tests you would do as follows
For bounding box plane intersection after projecting your box onto that plane which we call p and after calculating the midpoint of the box say m and after calculating the distance of that mid point from the plane say d to check for intersection we do
d<=p
But for frustum culling we just don't want our box to NOT intersect wih our frustum plane but we want it to be at -p distance from our plane and only then we know for sure that NO PART of our box is intersecting our plane that is
if(d<=-p)//then our box is fully not intersecting our plane so we don't draw it or cull it[d will be negative if the midpoint lies on the other side of our plane]
Similarly for triangles we have check if the distance of ALL 3 points of the triangle from the plane are negative.
To project a box onto a plane we take the 3 axises[x,y,z UNIT VECTORS] of the box,scale them by the boxes respective HALF width,height,depth and find the sum of each of their dot products[Take only the positive magnitude of each dot product NO SIGNED DISTANCE] with the planes normal which will be your 'p'
Not with the above approach for an AABB you can also cull against OOBB's with the same approach cause only the axises will change.
EDIT:
how to project a bounding box onto a plane?
Let's consider an AABB for our example
It has the following parameters
Lower extent Min(x,y,z)
Upper extent Max(x,y,z)
Up Vector U=(0,1,0)
Left Vector. L=(1,0,0)
Front Vector. F=(0,0,1)
Step 1: calculate half dimensions
half_width=(Max.x-Min.x)/2;
half_height=(Max.y-Min.y)/2;
half_depth=(Max.z-Min.z)/2;
Step 2: Project each individual axis of the box onto the plane normal,take only the positive magnitude of each dot product scaled by each half dimension and find the total sum. make sure both the box axis and the plane normal are unit vectors.
float p=(abs(dot(L,N))*half_width)+
(abs(dot(U,N))*half_height)+
(abs(dot(F,N))*half_depth);
abs() returns absolute magnitude we want it to be positive
because we are dealing with distances
Where N is the planes normal unit vector
Step 3: compute mid point of box
M=(Min+Max)/2;
Step 4: compute distance of the mid point from plane
d=dot(M,N)+plane.w
Step 5: do the check
d<=-p //return true i.e don't render or do culling
U can see how to use his for OOBB where the U,F,L vectors are the axises of the OOBB and the centre(mid point) and half dimensions are parameters you pass in manually
For an sphere as well you would calculate the distance of the spheres center from the plane (called d) but do the check
d<=-r //radius of the sphere
Put this in an function called outside(Plane,Bounds) which returns true if the bounds is fully outside the plane then for each of the 6 planes
bool is_inside_frustum()
{
for(Plane plane:frustum_planes)
{
if(outside(plane,AABB))
{
return false
}
}
return true;
}

how to alternate colors in a circle, so that circle looks like rotating?

The expected output should be like this with the colors changing their position as well:
Expected output-:
the colors should change their positions in a circle so that it looks like they are moving without changing the position of circle.
though my code is written in codeblocks in c/c++, i will be happy to get answers in any other programming languages.
my present code
#include<graphics.h>
#include<stdlib.h>
#include<stdio.h>
#include<conio.h>
#include<math.h>
#include<string.h>
#include<iostream>
using namespace std;
void vvcircle(float xk,float yk,float radius);
int i=0;
int main()
{
float xk,yk,radius;
int gdriver=DETECT,gmode,errorcode;
initgraph(&gdriver,&gmode,"C:\\TURBOC3\\BGI");
// cout<<"enter the value of x, y and radius of circle"<<endl;
//cin>>xk>>yk>>radius;
vvcircle(200,200,100);
getch();
closegraph();
return 0;
}
void vvcircle(float xk,float yk,float radius)
{
int color[60]={0,1,2,3,4,5,6,7,8,9};
while(radius>0)
{
float xo,yo;
float P;
xo=0.0;
yo=radius;
P=1-radius;
/// vvcircle(200,200,100);
for(;xo<=yo;)
{
putpixel(xo+xk,yo+yk,1);
putpixel(yo+xk,xo+yk,1);
putpixel(-yo+xk,xo+yk,2);
putpixel(xo+xk,-yo+yk,2);
putpixel(-yo+xk,-xo+yk,4);
putpixel(-xo+xk,-yo+yk,4);
putpixel(yo+xk,-xo+yk,4);
putpixel(-xo+xk,+yo+yk,4);
if(P<0)
{
xo=xo+1;
yo=yo;
P=P+2*xo+1;
}
else
{
xo=xo+1;
yo=yo-1;
P=P+(2*xo)-(2*yo)+1;
// putpixel(xo,yo,WHITE);
}
}
radius=radius-1;
}
}
Present output-:
i get many concentric circles with colors. but i want to move the colors so that it looks like the circle is moving and it is not achieved.
How about something like this:
#include <math.h>
void my_circle(int xc,int yc,int r,float a) // center(x,y), radius, animation angle [rad]
{
const int n=4; // segments count
int x,sx,xx,x0,x1,rr=r*r,
y,sy,yy,y0,y1,i,
dx[n+1],dy[n+1], // segments edges direction vectors
c[n]={5,1,2,3}; // segments colors
float da=2.0*M_PI/float(n);
// BBOX
x0=xc-r; x1=xc+r;
y0=yc-r; y1=yc+r;
// compute segments
for (i=0;i<=n;i++,a+=da)
{
dx[i]=100.0*cos(a);
dy[i]=100.0*sin(a);
}
// all pixels in BBOX
for (sx=x0,x=sx-xc;sx<=x1;sx++,x++){ xx=x*x;
for (sy=y0,y=sy-yc;sy<=y1;sy++,y++){ yy=y*y;
// outside circle?
if (xx+yy>rr) continue;
// compute segment
for (i=0;i<n;i++)
if ((x*dy[i ])-(y*dx[i ])>=0)
if ((x*dy[i+1])-(y*dx[i+1])<=0)
break;
// render
putpixel(sx,sy,c[i]);
}}
}
It simply loop through all pixels of outscribed square to your circle, determines if pixel is inside and then detect which segment it is in and color it with segments color.
The segments are described by direction vectors from circle center towards the segments edges. So if pixel is inside it mean its CW to one edge and CCW to the other so in 2D inspecting z coordinate of the cross product between vector to pixel and vectors to edges will tell if the pixel is in or not ...
As you can see I did not use floating point math in the rendering it self, its needed only to compute the segments edge vectors prior rendering...
I used standard 256 color VGA palette (not sure what BGI uses I expect 16 col) so the colors might be different on your platform here preview:
The noise is caused by my GIF capturing tool dithering the render itself is clean ...
Do not forget to call the my_circle repeatedly with changing angle ...
PS. I encoded this in BDS2006 without BGI so in different compiler there might be some minor syntax problem related to used language quirks...
I faked the putpixel with this:
void putpixel(int x,int y,BYTE c)
{
static const DWORD pal[256]=
{
0x00000000,0x000000A8,0x0000A800,0x0000A8A8,0x00A80000,0x00A800A8,0x00A85400,0x00A8A8A8,
0x00545454,0x005454FC,0x0054FC54,0x0054FCFC,0x00FC5454,0x00FC54FC,0x00FCFC54,0x00FCFCFC,
0x00000000,0x00101010,0x00202020,0x00343434,0x00444444,0x00545454,0x00646464,0x00747474,
0x00888888,0x00989898,0x00A8A8A8,0x00B8B8B8,0x00C8C8C8,0x00DCDCDC,0x00ECECEC,0x00FCFCFC,
0x000000FC,0x004000FC,0x008000FC,0x00BC00FC,0x00FC00FC,0x00FC00BC,0x00FC0080,0x00FC0040,
0x00FC0000,0x00FC4000,0x00FC8000,0x00FCBC00,0x00FCFC00,0x00BCFC00,0x0080FC00,0x0040FC00,
0x0000FC00,0x0000FC40,0x0000FC80,0x0000FCBC,0x0000FCFC,0x0000BCFC,0x000080FC,0x000040FC,
0x008080FC,0x009C80FC,0x00BC80FC,0x00DC80FC,0x00FC80FC,0x00FC80DC,0x00FC80BC,0x00FC809C,
0x00FC8080,0x00FC9C80,0x00FCBC80,0x00FCDC80,0x00FCFC80,0x00DCFC80,0x00BCFC80,0x009CFC80,
0x0080FC80,0x0080FC9C,0x0080FCBC,0x0080FCDC,0x0080FCFC,0x0080DCFC,0x0080BCFC,0x00809CFC,
0x00B8B8FC,0x00C8B8FC,0x00DCB8FC,0x00ECB8FC,0x00FCB8FC,0x00FCB8EC,0x00FCB8DC,0x00FCB8C8,
0x00FCB8B8,0x00FCC8B8,0x00FCDCB8,0x00FCECB8,0x00FCFCB8,0x00ECFCB8,0x00DCFCB8,0x00C8FCB8,
0x00B8FCB8,0x00B8FCC8,0x00B8FCDC,0x00B8FCEC,0x00B8FCFC,0x00B8ECFC,0x00B8DCFC,0x00B8C8FC,
0x00000070,0x001C0070,0x00380070,0x00540070,0x00700070,0x00700054,0x00700038,0x0070001C,
0x00700000,0x00701C00,0x00703800,0x00705400,0x00707000,0x00547000,0x00387000,0x001C7000,
0x00007000,0x0000701C,0x00007038,0x00007054,0x00007070,0x00005470,0x00003870,0x00001C70,
0x00383870,0x00443870,0x00543870,0x00603870,0x00703870,0x00703860,0x00703854,0x00703844,
0x00703838,0x00704438,0x00705438,0x00706038,0x00707038,0x00607038,0x00547038,0x00447038,
0x00387038,0x00387044,0x00387054,0x00387060,0x00387070,0x00386070,0x00385470,0x00384470,
0x00505070,0x00585070,0x00605070,0x00685070,0x00705070,0x00705068,0x00705060,0x00705058,
0x00705050,0x00705850,0x00706050,0x00706850,0x00707050,0x00687050,0x00607050,0x00587050,
0x00507050,0x00507058,0x00507060,0x00507068,0x00507070,0x00506870,0x00506070,0x00505870,
0x00000040,0x00100040,0x00200040,0x00300040,0x00400040,0x00400030,0x00400020,0x00400010,
0x00400000,0x00401000,0x00402000,0x00403000,0x00404000,0x00304000,0x00204000,0x00104000,
0x00004000,0x00004010,0x00004020,0x00004030,0x00004040,0x00003040,0x00002040,0x00001040,
0x00202040,0x00282040,0x00302040,0x00382040,0x00402040,0x00402038,0x00402030,0x00402028,
0x00402020,0x00402820,0x00403020,0x00403820,0x00404020,0x00384020,0x00304020,0x00284020,
0x00204020,0x00204028,0x00204030,0x00204038,0x00204040,0x00203840,0x00203040,0x00202840,
0x002C2C40,0x00302C40,0x00342C40,0x003C2C40,0x00402C40,0x00402C3C,0x00402C34,0x00402C30,
0x00402C2C,0x0040302C,0x0040342C,0x00403C2C,0x0040402C,0x003C402C,0x0034402C,0x0030402C,
0x002C402C,0x002C4030,0x002C4034,0x002C403C,0x002C4040,0x002C3C40,0x002C3440,0x002C3040,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
};
if ((x<0)||(x>=Main->xs)) return;
if ((y<0)||(y>=Main->ys)) return;
Main->pyx[y][x]=pal[c];
}
Where Main->xs, Main->ys is my window resolution and Main->pyx is direct pixel acces to its canvas for more info see:
Graphics rendering: (#4 GDI Bitmap)

Wormhole effect: warp space with 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 6 years ago.
Improve this question
I am looking for a way to do a wormhole effect like this:
- https://www.youtube.com/watch?v=WeOBXmLeJWo&feature=youtu.be&t=43s
I have already found nice tunnels in the examples, but here it is a little bit more involved. Actually the space appears to be warped somehow and the movement is high velocity, so it is not just entering into a simple tunnel. Any idea how to do the space warping part of it?
I decided to add more info because this was too broad:
I have a galaxy and each star has a 3d coord, size, etc. in this galaxy. I can visit these stars with a space ship. There are very distant stars and it would take a lot of time to get to them, that's why I need warp (faster than light) speed. This does not necessarily requires a wormhole according to physics, but this app does not have to be overly realistic. I don't want to solve this with pure OpenGL so ofc. we can use shaders. I'd like to warp the space in the middle of the screen when accelerating to warp speeds. After that a tunnel effect can come, because I think it would consume a lot of resources to update every star by very high speeds, so I'd like to update only the close stars. This can't be a prerendered animation, because the destination is not always certain, so this has sometimes exploration purposes and sometimes traveling purposes. I don't think warping only the sky box is enough, but I am not sure about this.
There are 2 things going on there:
space curvature around the hole
You need to construct equation that describe the curvature of space around hole parametrized by hole parameters (mass,position,orientation) and time so you can animate it. Then from this curvature you can compute relative displacement of pixel/voxel around it. I would start with cylindrical cones with radius modulated by sin of the distance from hole +/- some animation parameters (need experimentation).
Something like this:
and for example start with (in wormhole local coordinates LCS):
r = R * sin(z*0.5*M_PI/wormhole_max_depth)
Then modulate it by additional therms. The wormhole_max_depth,R should be functions of time even linear or with some periodic therm so it is pulsating a bit.
The displacement can be done by simply computing distance of concerned point to cone surface and push it towards it the more the closer to the cone it is (inside cone voxels are considered below surface so apply max displacement strength)
particle/light/matter bursting out of the hole
I would go for this only when #1 is alredy done. It should be simple particle effect with some nice circular blended alpha texture animated on the surface of the cone from #1. I see it as few for loops with pseudo random displacement in position and speed ...
Techniques
This topic depends on how you want to do this. I see these possibilities:
Distort geometry during rendering (3D vector)
So you can apply the cone displacement directly on rendered stuff. This would be best applicable in GLSL but the geometry rendered must have small enough primitives to make this work on vertex level ...
Distort skybox/stars only (3D vector or 2D raster but objects stay unaffected)
So you apply the displacement on texture coordinates of skybox or directly on the star positions.
Distort whole rendered scene in second pass (2D raster)
This need to use 2 pass rendering and in the second pass just wrap the texture coordinates near hole.
As you got different local stars in each sector I would use star background generated from star catalogue (list of all your stars) And apply the distortion on them directly in 3D vector space (so no skybox.. option #2). And also because my engines already use such representation and rendering for the same reasons.
[Edit1] cone geometry
I haven't much time for this recently until today so I did not make much of a progress. I decided to start with cone geometry so here it is:
class wormhole
{
public:
reper rep; // coordinate system transform matrix
double R0,R1,H,t; // radiuses,depth
wormhole(){ R0=10.0; R1=100.0; H=50.0; t=0.0; };
wormhole(wormhole& a){ *this=a; };
~wormhole(){};
wormhole* operator = (const wormhole *a) { *this=*a; return this; };
/*wormhole* operator = (const wormhole &a) { ...copy... return this; };*/
void ah2xyz(double *xyz,double a,double h) // compute cone position from parameters a=<0,2pi>, h=<0,1>
{
double r,tt;
tt=t; if (t>0.5) tt=0.5; r=2.0*R0*tt; // inner radius R0
tt=t; if (t>1.0) tt=1.0; r+=(R1-r)*h*h*tt; // outer radius R1
xyz[0]=r*cos(a);
xyz[1]=r*sin(a);
xyz[2]=H*h*tt;
rep.l2g(xyz,xyz);
}
void draw_cone()
{
int e;
double a,h,da=pi2*0.04,p[3];
glColor3f(0.2,0.2,0.2);
for (h=0.0;h<=1.0;h+=0.1){ glBegin(GL_LINE_STRIP); for (e=1,a=0.0;e;a+=da) { if (a>=pi2) { e=0; a=0.0; } ah2xyz(p,a,h); glVertex3dv(p); } glEnd(); }
for (e=1,a=0.0;e;a+=da){ glBegin(GL_LINE_STRIP); for (h=0.0;h<=1.0;h+=0.1) { if (a>=pi2) { e=0; a=0.0; } ah2xyz(p,a,h); glVertex3dv(p); } glEnd(); }
}
} hole;
Where rep is my class for homogenous 4x4 transform matrix (remembering both direct and inverse matrices at the same time) function l2g just transforms from local coordinates to global. The cone parameters are:
R0 - inner cone radius when fully grown
R1 - outer cone radius when fully grown
H - the height/depth of the cone when fully grown
t - is animation parameter values <0.0,1.0> are the growth and values above 1.0 are reserved for wormhole fully grown animation
Here how it looks like:
What I would do is simply calculate a vector from the texture coordinate of the screen center to the texture coordinate of the pixel you're shading.
Then modify that vector in any way you want (time based for example) and apply it to the texture coordinate of the pixel you're shading and then use the resulting coordinate to sample your texture.
In pseudocode this would be something like this:
vec2 vector_to_screen_center = vec2(0.5) - texture_coordinate;
texture_coordinate += vector_to_screen_center * sin(time) * 0.1; // Time based modulation of the vector.
gl_FragColor = texture2D(screen_texture, texture_coordinate);
Your question does not have a GLSL tag. If you plan to do this without shaders, this is going to be hard and/or inefficient.

cocos2dx detect intersection with polygon sprite

I am using cocos2d-x 3.8.
I try to create two polygon sprites with the following code.
I know we can detect intersect with BoundingBox but is too rough.
Also, I know we can use Cocos2d-x C++ Physics engine to detect collisions but doesn't it waste a lot of resource of the mobile device? The game I am developing does not need physics engine.
is there a way to detect the intersect of polygon sprites?
Thank you.
auto pinfoTree = AutoPolygon::generatePolygon("Tree.png");
auto treeSprite= Sprite::create(pinfoTree);
treeSprite-> setPosition(width / 4 * 3 - 30 , height / 2 - 200);
this->addChild(treeSprite);
auto pinfoBird = AutoPolygon::generatePolygon("Bird.png");
auto Bird= Sprite::create(pinfoTree);
Bird->setPosition(width / 4 * 3, height / 2);
this->addChild(Bird)
This is a bit more complicated: AutoPolygon gives you a bunch of triangles - the PhysicsBody::createPolygon requires a convex polygon with clockwise winding… so these are 2 different things. The vertex count might even be limited. I think Box2d’s maximum count for 1 polygon is 8.
If you want to try this you’ll have to merge the triangles to form polygons. An option would be to start with one triangle and add more as long as the whole thing stays convex. If you can’t add any more triangles start a new polygon. Add all the polygons as PhysicsShapes to your physics body to form a compound object.
I would propose that you don’t follow this path because
Autopolygon is optimized for rendering - not for best fitting
physics - that is a difference. A polygon traced with Autopolygon will always be bigger than the original sprite - Otherwise you would see rendering artifacts.
You have close to no control over the generated polygons
Tracing the shape in the app will increase your startup time
Triangle meshes and physics outlines are 2 different things
I would try some different approach: Generate the collision shapes offline. This gives you a bunch of advantages:
You can generate and tweak the polygons in a visual editor e.g. by
using PhysicsEditor
Loading the prepares polygons is way faster
You can set additional parameters like mass etc
The solution is battle proven and works out of the box
But if you want to know how polygon intersect work. You can look at this code.
// Calculate the projection of a polygon on an axis
// and returns it as a [min, max] interval
public void ProjectPolygon(Vector axis, Polygon polygon, ref float min, ref float max) {
// To project a point on an axis use the dot product
float dotProduct = axis.DotProduct(polygon.Points[0]);
min = dotProduct;
max = dotProduct;
for (int i = 0; i < polygon.Points.Count; i++) {
flaot d = polygon.Points[i].DotProduct(axis);
if (d < min) {
min = dotProduct;
} else {
if (dotProduct> max) {
max = dotProduct;
}
}
}
}
// Calculate the distance between [minA, maxA] and [minB, maxB]
// The distance will be negative if the intervals overlap
public float IntervalDistance(float minA, float maxA, float minB, float maxB) {
if (minA < minB) {
return minB - maxA;
} else {
return minA - maxB;
}
}
// Check if polygon A is going to collide with polygon B.
public boolean PolygonCollision(Polygon polygonA, Polygon polygonB) {
boolean result = true;
int edgeCountA = polygonA.Edges.Count;
int edgeCountB = polygonB.Edges.Count;
float minIntervalDistance = float.PositiveInfinity;
Vector edge;
// Loop through all the edges of both polygons
for (int edgeIndex = 0; edgeIndex < edgeCountA + edgeCountB; edgeIndex++) {
if (edgeIndex < edgeCountA) {
edge = polygonA.Edges[edgeIndex];
} else {
edge = polygonB.Edges[edgeIndex - edgeCountA];
}
// ===== Find if the polygons are currently intersecting =====
// Find the axis perpendicular to the current edge
Vector axis = new Vector(-edge.Y, edge.X);
axis.Normalize();
// Find the projection of the polygon on the current axis
float minA = 0; float minB = 0; float maxA = 0; float maxB = 0;
ProjectPolygon(axis, polygonA, ref minA, ref maxA);
ProjectPolygon(axis, polygonB, ref minB, ref maxB);
// Check if the polygon projections are currentlty intersecting
if (IntervalDistance(minA, maxA, minB, maxB) > 0)
result = false;
return result;
}
}
The function can be used this way
boolean result = PolygonCollision(polygonA, polygonB);
I once had to program a collision detection algorithm where a ball was to collide with a rotating polygon obstacle. In my case the obstacles where arcs with certain thickness. and where moving around an origin. Basically it was rotating in an orbit. The ball was also rotating around an orbit about the same origin. It can move between orbits. To check the collision I had to just check if the balls angle with respect to the origin was between the lower and upper bound angles of the arc obstacle and check if the ball and the obstacle where in the same orbit.
In other words I used the various constrains and properties of the objects involved in the collision to make it more efficient. So use properties of your objects to cause the collision. Try using a similar approach depending on your objects