Here’s a flyer, using some bits from @behreajj, that uses quaternions for the rotation. Use the arrows for pitch and yaw, and a
and d
for roll. w
and s
control the speed.
Each box is included 8 times (twice in each dimension) to wrap the space. I can go up to nShapes = 40000
and still get a comfortable frame rate. At 50000, Processing runs out of memory somewhere.
int nShapes = 8000;
int[] keys;
PVector xAxis, yAxis, zAxis;
Ship ship;
PShape stuff;
void setup() {
fullScreen( P3D );
//size( 900, 600, P3D );
keys = new int[256];
xAxis = new PVector( 1, 0, 0 );
yAxis = new PVector( 0, 1, 0 );
zAxis = new PVector( 0, 0, 1 );
noStroke();
makeStuff();
ship = new Ship();
}
void draw() {
background( 0 );
ship.move();
ship.setCamera();
shape( stuff );
}
class Quat {
PVector v;
float w;
Quat( ) { v = new PVector( 0., 0., 0. ); w = 1.; }
Quat( PVector axis, float ang ) {
v = PVector.mult( axis, sin( ang*0.5 )/axis.mag() );
w = cos( ang*0.5 );
}
Quat normalize() {
float t = 1./sqrt( v.magSq() + w*w );
v.mult( t );
w *= t;
return this;
}
PVector rotate( PVector p ) {
// ( w*w - dot( v, v )) * p + 2 * dot( p, v ) * v + 2 * w * cross( v, p )
return PVector.mult( p, w*w - v.dot(v) )
.add( PVector.mult( v, 2 * p.dot(v) ) )
.add( PVector.mult( v, 2*w ).cross( p ) );
}
Quat rotateBy( Quat q ) {
float t = w*q.w - v.dot( q.v );
v = q.v.cross( v )
.add( PVector.mult( v, q.w ) )
.add( PVector.mult( q.v, w ) );
w = t;
normalize();
return this;
}
}
class Ship {
PVector pos;
Quat rot;
float speed;
Ship() {
pos = new PVector();
speed = 2;
rot = new Quat();
}
void move() {
// UP, DOWN, LEFT, RIGHT, 'd', 'a'
PVector turn = new PVector( keys[38]-keys[40], keys[37]-keys[39], keys[68]-keys[65] );
if( turn.magSq() > 0 )
rot = new Quat( turn, 0.01 ).rotateBy(rot);
speed += (keys[87]-keys[83]) * 0.015; // 'w', 's'
pos.add( rot.rotate( zAxis ).mult(-speed) );
pos.x = (pos.x+1000) % 1000;
pos.y = (pos.y+1000) % 1000;
pos.z = (pos.z+1000) % 1000;
}
void setCamera() {
PVector back = rot.rotate( zAxis );
PVector up = rot.rotate( yAxis );
camera( pos.x, pos.y, pos.z,
pos.x-back.x, pos.y-back.y, pos.z-back.z,
up.x, up.y, up.z );
frustum( -1.*width/height, 1.*width/height, -1, 1, 1, 400 );
//lightFalloff( 1, 0, 1./80000 );
pointLight( 255, 255, 255, pos.x, pos.y, pos.z );
}
}
PShape makeStuff() {
colorMode( HSB, 1, 1, 1 );
stuff = createShape( GROUP );
for( int is=0; is<nShapes; is++ ) {
float size = pow(random(1),2) * 5.5 + 0.5;
float px = random(-500, 500);
float py = random(-500, 500);
float pz = random(-500, 500);
PVector axis = PVector.random3D();
float ang = random(TAU);
color c = color(random(1), 0.7, 1);
for( int i=0; i<2000; i+=1000 )
for( int j=0; j<2000; j+=1000 )
for( int k=0; k<2000; k+=1000 ) {
PShape thing = createShape( BOX, size, size, size );
thing.setFill( c );
thing.rotate( ang, axis.x, axis.y, axis.z );
thing.translate( px+i, py+j, pz+k );
stuff.addChild( thing );
}
}
colorMode( RGB, 255, 255, 255 );
return stuff;
}
void keyPressed() {
if( keyCode < 256 ) keys[keyCode] = 1;
}
void keyReleased() {
if( keyCode < 256 ) keys[keyCode] = 0;
}