Evaldraw rocks! Im curious to see what you forumers have made with it!
First off, the classic fireeffect. Often seen in demoscene productions. I've added metaballs and text on top of the fire. The metaballs look cool rendered with fire :D
// Now we have those tables, we can use them to calculate the // total multiplied distance2 value. for( y=0; y<screen_height; y++) { for(x=0; x<screen_width; x++) { // Calculate the multiplied distance2: // Set it to one initially. // This is so that the first time we multiply it // by the first value it becomes that value.
// this could be optimized by placing the text in a buf saveTextToBuf() { cls(); setfont(36,64,1);//setfont(9,16); setcol(0xffffff); moveto(0,0); printf("EVALDRAW");
Evildraw needs more games! :o For example, a .kc racing game with voxels, car physics, dynamic lights and music etc etc :o
Edited by ConsistentCallsign at
torbj?rn at
Woho! You modded my script ;)
I've made a game. It doesn't voxels tho... it's an "achtung die kruve"/zatacka clone.
The game is for two players. Each player controls a "snake" that always moves, but that can be steered. Try to outsmart your opponent and make him crash!
Controls: Player1: A and D Player2: Left and right.
http://bayimg.com/thumb/caaijaacb.jpg
Code:
// this is my version of "achtung die kurve" // written 24. may 2009 by Torbjoern. // check out ipcurve. a networked version.
enum{maxlength=3000, numplayers=2};
// player class static plx[numplayers], ply[numplayers]; //current pos head static playerx[maxlength][numplayers]; //tail positions static playery[maxlength][numplayers]; static angle[numplayers] = 0; // current heading degrees 0-360 //static collision[numplayers]; //has player collided with self or others? static score[numplayers]; //survive round and get +1 point
// global for all players static turnSpeed = 4000; static newbit; //index headpos static speed=3500; //player speed in pixels per update, affects collision AND drawing... static size = 5; //size of a single snake block in pixel
if(tick) { // update player pos for(player=0; player<numplayers; player++) { plx[player] = plx[player]+cos(angle[player]*pi/180)*speed*dt; ply[player] = ply[player]+sin(angle[player]*pi/180)*speed*dt; playerx[newbit][player] = plx[player]; playery[newbit][player] = ply[player]; } // When the snake has gone almost as far as one snake-bit // increase array size. // you could call this "sampling the snake" :P // im multiplying with 0.7 so the snake is "sampled" more often // than it travels, so that it doesnt have holes. // you could multiply by 1 or higher and see what happens. // just dont go lower than 0.61, as thats the collision size. static bitsize; bitsize += speed*dt; if(bitsize > size*0.7){ newbit++; bitsize=0; } } }
calcDelta() { timePassed = klock()-renderTime; //time used to do frame dt = timePassed/timePerFrame; // calculate the new delta based on expected time renderTime = klock(); //ready to compare next frame }
// collision takes more cpu power the longer the snakes becomes... // all those sqrts each frame :( // .. any optimizations possible? dont check every frame for instance? // IDEA: goto head position, check stuff thats around it! // not every single snake-piece!
Check out more about the orignal and other versions here: http://en.wikipedia.org/wiki/Achtung,_die_Kurve!
I thought of adding maps and obstancles, but someone already has made a version: http://psykurve.sourceforge.net/en/screenshots.php
Edited by torbj?rn at
Awesoken at
Nice game! Too bad I don't have anybody in the same room to test it with. You should implement network support.. or maybe computer AI : ) I looked through your code. Here are some general comments: * The 2-parameter form of setfont() must be called with one of the built-in bitmap font sizes, or else it will do nothing. See EVALDRAW.TXT for a list of supported sizes. * Your fillRect() function would be faster if it drew horizontal lines to the screen instead of vertical. * You should use drawsph() instead of fillRect(). It looks much nicer. * calcDelta() should be rewritten to only call klock() once. On many systems, klock() takes on the order of 3000 clock cycles. * You don't need to calculate sqrt() for comparing distance. Instead, compare the squared distance.
torbj?rn at
Thanks for some great comments. I've fixed -the collision detection -delta calc. -fillRect (its a little faster than drawsph), tho I am using drawsph now.
I had almost forgotten that evaldraw has network support! I haven't seen any examples of it. Edit: In the newest version of evaldraw there are examples in /geeky.
But I got a sphere moving to the mouse in two local evaldraw windows with this:
Edit: WARNING: Dont use this code! This code uses net_recv in a very bad and completely wrong way. Keeping it so others don't do the same mistake. I should have read more carefully. It did seem to kinda work at the time ;)
Since I don't have 2 computers to test networking on I opened evaldraw.ini and set
alwaysactive=1 //1=run script when window is in background
So that both the scripts ran simultaniously on the local machine.
Ken, you should perhaps mention this in the network section of evaldraw.txt.
The game updated:
// this is my version of "achtung die kurve" // written 24. may 2009 by Torbjoern. // updated 25. // check out ipcurve. a networked version.
//todo: //random holes. //choose playern num
enum{maxlength=3000, numplayers=2, maxplayers=4};
// player class static plx[maxplayers], ply[maxplayers]; //current pos head static playerx[maxlength][numplayers]; //tail positions static playery[maxlength][numplayers]; static angle[maxplayers] = 0; // current heading degrees 0-360 //static collision[numplayers]; //has player collided with self or others? static score[numplayers]; //survive round and get +1 point
// global for all players static turnSpeed = 1500; static newbit; //index headpos static speed= 1200; //player speed in pixels per update, affects collision AND drawing... static size = 8; //size of a single snake block in pixels static collisionSize = 0.3; // 30 percent of draw size.
// global game settings static wrapScreenMode = 1; static gameStart = 0;
if(tick) { // update player pos for(player=0; player<numplayers; player++) { plx[player] = plx[player]+cos(angle[player]*pi/180)*speed*dt; ply[player] = ply[player]+sin(angle[player]*pi/180)*speed*dt; playerx[newbit][player] = plx[player]; playery[newbit][player] = ply[player]; } // When the snake has gone almost as far as one snake-bit // increase array size. // you could call this "sampling the snake" :P // im saving its position more often than the time it takes to move on snake bit (size) // just dont go lower than collisionSize. static bitsize; bitsize += speed*dt; if(bitsize > size*0.6){ newbit++; bitsize=0; } } }
calcDelta() { time = klock(); timePassed = time-renderTime; //time used to do frame dt = timePassed/timePerFrame; // calculate the new delta based on expected time renderTime = time; //ready to compare next frame }
updateInput() { if(gamestart==0 && keystatus[kb_space]) { gamestart = 1; } if(keystatus[kb_space]) { resetGame(); } if(keystatus[kb_left]) { angle[0]-=turnSpeed*dt; //angle[1]-=turnSpeed*dt; //controlling all with one control looks fun :) //angle[2]-=turnSpeed*dt; //angle[3]-=turnSpeed*dt; } if(keystatus[kb_right]) { angle[0]+=turnSpeed*dt; //angle[1]+=turnSpeed*dt; //angle[2]+=turnSpeed*dt; //angle[3]+=turnSpeed*dt; } if(keystatus[kb_w]) { angle[1]-=turnSpeed*dt; } if(keystatus[kb_d]) { angle[1]+=turnSpeed*dt; }
// collision takes more cpu power the longer the snakes becomes... // all those sqrts each frame :( // .. any optimizations possible? dont check every frame for instance? // IDEA: goto head position, check stuff thats around it! // not every single snake-piece!
Edit: changed game fps from 500 to 100 and increased snake speed instead.
Edited by torbj?rn at
ConsistentCallsign at
The speed is nice now; not too slow and not too fast. And the snakes can go out of the box! :D
When both snakes collide into each other, only one of the players scores, it's impossible to get a draw.
Edited by ConsistentCallsign at
Awesoken at
Your use of net_recv() is wrong and even destructive! You are overwriting NET_ALLELSE (which is intended to be a constant) with the player in which the packet came from! Also, net_recv() returns 0 if it received nothing. It looks like you're assuming it waits for the next packet, which it is definitely not. You will find a minimalistic example of proper net code here: geeky\netsimp.kc
I will add a note about 'alwaysactive=1' for my next release, which should be very soon. Note that I have fixed some bugs related to networking since the last release. I will also include this batch file (net.bat), which is useful for testing net code on a local machine:
net geeky\netsimp.kc <- starts with 2 players net 3 geeky\netsimp.kc <- starts with 3 players
torbj?rn at
Whew! Thanks for telling me. I probably would have messed about with it for hours on end if I wasn't aware of the correct use of net_recv.
I've made a new version of the snake game. Changes: -thinner snakes. -1-4 players. Controls: Arrows left&right, A & D, 1 & 2, and mouse buttons. -the last snake to be slithering wins. Now the game continues untill there is only one left.
Edit1: Fix: Only players that are alive (dead==0) get points when someone else collides..
/*this is my version of "achtung die kurve" written 24. may 2009 by Torbjoern. last updated 28. may 09. check out ipcurve. a networked version.
Fixed thanks to Awesoken for pointing them out: - Collision, checking square distance instead of sqrt - deltaCalc() - fillRect() */
//todo: //choose playern num //customize controls //network play //random obstancles //scale according to resolution
// player class static plx[maxplayers], ply[maxplayers]; //current pos head static playerx[maxlength][maxplayers]; //tail positions static playery[maxlength][maxplayers]; static angle[maxplayers] = 0; // current heading degrees 0-360 static collided[maxplayers]; // the length of the snake at the point of its death static score[maxplayers]; //survive round and get +1 point static dead[maxplayers];
static color[4];
// global for all players static turnSpeed = 1500; static newbit; //index headpos static speed= 1200; //player speed in pixels per update, affects collision AND drawing... static size = 4; //size of a single snake block in pixels static collisionSize = 0.3; // 30 percent of draw size. static holeProbability = 0.001; static holeSize = 4; //snake bits to remove static deadCount = 0; // no. of players dead so far static maxScore = 5;
// global game settings static wrapScreenMode = 1; static gameState = 0;
if(newbit > 10 && rnd<holeProbability) { for(h=0; h<holeSize; h++){ playerx[newbit-2-h][player] = -1; playery[newbit-2-h][player] = -1; } } } // When the snake has gone almost as far as one snake-bit // increase array size. // you could call this "sampling the snake" :P // im saving its position more often than the time it takes to move on snake bit (size) // just dont go lower than collisionSize. static bitsize; bitsize += speed*dt; if(bitsize > size*0.6){ newbit++; bitsize=0; } } }
calcDelta() { time = klock(); timePassed = time-renderTime; //time used to do frame dt = timePassed/timePerFrame; // calculate the new delta based on expected time renderTime = time; //ready to compare next frame }
// collision takes more cpu power the longer the snakes becomes... // .. any optimizations possible? dont check every frame for instance? // IDEA: goto head position, check stuff thats around it! // not every single snake-piece!
for(player=0; player<numplayers; player++) { if( (i > collided[player]) && dead[player] ) continue; if(deadCount == numplayers-1){ setup(); break; } //do this before checking skipping holes ;) if( playerx[i][player] ==-1 ) continue; //if there is a "hole" in the snake, dont check coll or draw.
//first check collision if(i < (newbit-1) && collided[player]==0) { for(otherPlayer=0; otherPlayer<numplayers; otherPlayer++) { if(playerCollided( player, playerx[i][otherPlayer], playery[i][otherPlayer] )) { /* MEH! in either case, player loses >_< idea: could perhaps check for ties. if(player==otherPlayer) //crashed with self. yes. this doesnt make much sense in english. { updateScore(player) setup(); break; } else // crashed with enemy, so this player loses. { updateScore(player); setup(); break; } */ updateScore(player); deadCount++; dead[player] = 1; collided[player] = newbit-1; //length at death break; } } }
I've been using Evaldraw a long while and I've recently done an interesting algorithm for procedural texture generating, here it is. It's customizable by changing the initial variables. Move the mouse x-wise to regulate the drawing speed.
torbj?rn at
CraigFatman: Thats really neat! Its cool to watch the shapes as they are drawling.
Today I made some clouds with the noise function. They are using the same pallette as the metaballs, so it looks like an explosion.
Edit: I forgot to mention some code I borrowed making this; the part that renders the perlinnoise in 2D and the rotationmatrix code is taken from a tutorial on angelcode.com on how to make your own perlin noise function.
There is a lot of really good stuff there on effects and programming.
Edit 2: 1. Updated to use Evaldraw's new rgb() function for storing color into a single variable. No more need for r*2^16 + g*2^8 + b, just rgb( r, g, b ). 2. Removed a cls that I had forgotten >< ... I should review code my line-by-line before I post ::)
//motion along x x = 1/128.0; dest[0] = x*mtx[0]; dest[1] = x*mtx[3]; dest[2] = x*mtx[6];
for(ix=0; ix < 256; ix++) { // Add frequency components (octaves) v1 = (noise(pix[0],pix[1],pix[2])); v2 = 0.5*(noise(2*pix[0],2*pix[1],2*pix[2])); v3 = 0.25*(noise(4*pix[0],4*pix[1],4*pix[2])); v4 = 0.125*(noise(8*pix[0],8*pix[1],8*pix[2])); v5 = 1/16*(noise(16*pix[0],16*pix[1],16*pix[2])); value = mousy/(yres);
v = 255*sin((abs(v1)+abs(v2)+abs(v3)+abs(v4)+abs(v5))/(value*4));
linebuf[ix] = palette[v];
// New perlin input, scroll dest pix[0] += dest[0]; pix[1] += dest[1]; pix[2] += dest[2]; } sethlin(0, iy, linebuf, 256); } moveto(0,256); setfont(18,36); printf("v=%g",v); printf("\nvalue=%g",value); printf("\nmove mouse up and down"); }
initPalette() { // Fire palette, taken from a tinyptc or openptc example c = 0; i = 0;
// blue while(i<64) { palette[i] = rgb(c,0,0); c+=4; i++; }
// blue to darkblue c = 0; while(i<96) { palette[i] = rgb(255,c,0); c+=4; i++; } // from 96-128 yellow to white c = 0; while(i<128) { palette[i] = rgb(255,255,c); c+=4; i++; } // All above 128 white hot! while(i<256) { palette[i] = rgb(255,255,255); c+=4; i++; } }
InitRotationMatrix(pAxis[3], r) { // The axis vector must be of unit length x=0, y=0, z=0, m=0; m = sqrt(pAxis[0]*pAxis[0] + pAxis[1]*pAxis[1] + pAxis[2]*pAxis[2]); x = pAxis[0]/m; y = pAxis[1]/m; z = pAxis[2]/m;
// Compute the rotation matrix c = cos(r); s = sin(r);
Mtx[0] = (x * x) * (1.0 - c) + c; Mtx[1] = (y * x) * (1.0 - c) + (z * s); Mtx[2] = (z * x) * (1.0 - c) - (y * s);
Mtx[3] = (x * y) * (1.0 - c) - (z * s); Mtx[4] = (y * y) * (1.0 - c) + c; Mtx[5] = (z * y) * (1.0 - c) + (x * s);
Mtx[6] = (x * z) * (1.0 - c) + (y * s); Mtx[7] = (y * z) * (1.0 - c) - (x * s); Mtx[8] = (z * z) * (1.0 - c) + c; }
Edited by torbj?rn at
Awesoken at
Cool effect, torbjoern. I noticed you are calling cls() twice per frame, which is wasteful. I am looking at all of the scripts posted on this thread. BTW, the next release of Evaldraw will have a built-in rgb(r,g,b) function, which will simplify the storage and generation of your palette. : )
torbj?rn at
Have you played Black&White 1? I fired it up just a day ago. Do you remember the Lion Head logo when you started the game? It consisted of thousands of tiny sparkles, that where at first placed randomly, but then joined togheter to form the Lion Head.
I wanted to mimic this effect.
Here's the picture exploded: http://bayimg.com/image/labibaaco.jpg
Assembled: http://bayimg.com/image/labifaaco.jpg
This picture of Mario is 16x16, but Ive tried images up to 200x200 and evaldraw still runs at an interactive rate (10 fps perhaps). The effect would be really fast with hardware accelerated point sprites in DirectX or OpenGL.
The pixelmorphing idea was taken from Denthor Of Asphyxias tutorial: http://www.gamedev.net/reference/articles/article363.asp The interpolation (pixels quickly assemble, slowly disassemble) was taken from Sol's tutorial on interpolation: http://sol.gfxile.net/interpolation/index.html
Here's the code:
// Pixelmorph 3D // written by Torbjoern, last update 1/7-2009 enum{WIDTH=16, HEIGHT=16, STEPS=200}; // Image size and iterpolation steps static pixelstart[width*height][3]; // Start-off pos XYZ for all pixels static pixeltarg[width*height][3]; // Target vector XYZ for all pixels static pixelcol[width*height]; // Our image
static tim; static posx, posy, posz, posh, posv; // Camera pos and look-direction
// Tweening static Alfa=0; // start tween static Beta=200.05; // end tween static positions[steps]; // values in range Alfa-Beta static dir=1; // tween direction static tweenPos = 0; // indexes the positions[] array static tweenSpeed = 30;
static point[3]; // store rotation () { if(numframes==0) setup(); cls(); clz(1e32); otim = tim; tim = klock(); dt = tim-otim;
UpdateTween(dt);
// Get position in time from interpolated values array, // the image will show when this equals steps positionTime = positions[int(tweenPos)];
// Go thru all pixels for(i=0; i<=width*height; i++){ setmorph(i,positionTime); // set a pixel to a position in time (t) }
// Debug text setcol(0xffffff); moveto(0,100); setfont(18,36); printf("\ntweenPos:%g",tweenPos); printf("\npositionTime=%g",positionTime); printf("\nPoints on screen:%g",width*height); }
UpdateTween(dt) { tweenPos += dir * dt * tweenSpeed; // Make shure that we dont index the array // at an illegal position if(tweenPos >= STEPS-1){ dir =-dir; // Important. Array goes from 0-N, keep below N-1 tweenPos = STEPS-2; // tweenpos may be way over N-1 thanks to dt, so set it manually below. } else if(tweenPos < 1){ dir =-dir; tweenPos = 1; } }
// Based on the direction vector in pixeltarg[] // and the pixelstart position // set the pixelsposition for a point t in time // the image is fully assembled when t equals steps setmorph(i, t) { sx = pixelstart[i][0]; sy = pixelstart[i][1]; sz = pixelstart[i][2];
tx = -pixeltarg[i][0] * t + sx; ty = -pixeltarg[i][1] * t + sy; tz = -pixeltarg[i][2] * t + sz;
r=0,g=0,b=0; hex2rgb(pixelcol[i],r,g,b); setcol(r*1.2,g*1.2,b*1.2); // extra bright colors, setcol(r,g,b) takes nums in 0-511 range //setcol(pixelcol[i]); //setpix(xres/2+tx-width/2,yres/2+ty-height/2); // draw 2d
//rotPointY(tx,ty,0,tim*2); // Rotate points, turn this off if you wish drawsph(tx+point[0],ty+point[1],tz,1); }
// convert a hexadecimal colour value to RGB (0-255) // store RGB values in the passed RGB variables // & (ampersand) means "adress-of" hex2rgb(rgb,&r,&g,&b) { r = (rgb / 2^16); g = (rgb / 2^8) % 256; b = rgb % 256; }
// Rotate a point xyz phi radians around the y-axis // store the resulting pos in point[] rotPointY(x,y,z,phi) { c = cos(phi); s = sin(phi); point[0] = x*c + z*-s; point[1] = y; point[2] = x*s+ z*c; }
// Setup function! // 1. // Store start pixelposition (for example a random position) // and also the final pixelposition when the image is // "assembled" // Divide the inbetween positions into a number of steps. Store in pixeltarg[] // 2. // Also, store the color of each pixel in the image // 3. // Save tween / interpolation positions to array for playback setup() { i=0; for(y=0; y<height; y++) for(x=0; x<width; x++) { // Set start and target positions // Can be anything. sx = nrnd*100; sy = nrnd*100; sz = nrnd*100;
// Target position, when the pixel is at this position the image will be visible tx = x-width/2; ty = y-height/2; tz = 250;
// calc and store target vector // source/method: Denthor of Asphyxias pixelmorph tutorial // find it on gamedev.net pixeltarg[i][0] = ((sx-tx))/steps; pixeltarg[i][1] = ((sy-ty))/steps; pixeltarg[i][2] = ((sz-tz))/steps;
// Save the pictures colors pixelcol[i] = pic(x,y); i++; }
// Save tween positions // I used Sols interpolation tutorial for this // check out sol.gfxile.net powerTween(); tweenPos = STEPS-2; // Start with explosion }
// Camera and movement code taken from /games/ //Handle input and player movement playercontrols(f,&posx,&posy,&posz,&posh,&posv) { f *= 100; if (keystatus[0x2a]) f /= 4; if (keystatus[0x36]) f *= 4; vx = (keystatus[0xcd]-keystatus[0xcb])*f; //Right-Left vy = (keystatus[0xc8]-keystatus[0xd0])*f; //Up-Down vx2 = (keystatus[32]-keystatus[30])*f; //D-A vy2 = (keystatus[17]-keystatus[31])*f; //W-S if(vx==0) vx=vx2; if(vy==0) vy=vy2;
powerTween() { for (i = 0; i < STEPS; i++) { v = i / STEPS; // range 0-1 v = v^5; // The higher the power, the slower the end accel x = (Alfa * v) + (Beta * (1 - v)); positions[i] = x; } }
Edited by torbj?rn at
torbj?rn at
Metaballs and marching cubes
Today I've converted a program that uses the marching cubes algo for polygonising iso-surfaces. I think iso-surfaces are really cool :)
It was a really quick process thanks to the program not using any objects. Pure C I guess. Also, just pasted the statically defined arrays into evaldraw. No modifications needed :D
The orignal used bitwise operators in to if's. I replaced them with pow(2, numberHere ) ... Seems like they are correct, but I'm not sure... The orignal source and how marching cubes works can be found here: http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/ The source is "marchingsource.cpp" by Cory Bloyd.
Controls: Press SPACE to toggle normals on/off. Press S to toggle between metaballs, lines and a sin/cos 3D-ripple heightfield. Move the mouse left/right to scale.
/* Original source: Cory Bloyd. Evaldraw conversion: Torbjoern Haugen. 2. oktober 2009. The orignal source is in C++ and uses opengl + glut. It also included the marching tetrahedons method, not included here.
Original source notice:
Marching Cubes Example Program by Cory Bloyd (corysama@yahoo.com)
A simple, portable and complete implementation of the Marching Cubes and Marching Tetrahedrons algorithms in a single source file. There are many ways that this code could be made faster, but the intent is for the code to be easy to understand.
For a description of the algorithm go to http://astronomy.swin.edu.au/pbourke/modelling/polygonise/
//a2fVertexOffset lists the positions, relative to vertex0, of each of the 8 vertices of a cube static a2iEdgeConnection[12][2] = { {0,1}, {1,2}, {2,3}, {3,0}, {4,5}, {5,6}, {6,7}, {7,4}, {0,4}, {1,5}, {2,6}, {3,7} };
//a2fEdgeDirection lists the direction vector (vertex1-vertex0) for each edge in the cube static a2fEdgeDirection[12][3] = { {1.0, 0.0, 0.0},{0.0, 1.0, 0.0},{-1.0, 0.0, 0.0},{0.0, -1.0, 0.0}, {1.0, 0.0, 0.0},{0.0, 1.0, 0.0},{-1.0, 0.0, 0.0},{0.0, -1.0, 0.0}, {0.0, 0.0, 1.0},{0.0, 0.0, 1.0},{ 0.0, 0.0, 1.0},{0.0, 0.0, 1.0} };
// For any edge, if one vertex is inside of the surface and the other is outside of the surface // then the edge intersects the surface // For each of the 8 vertices of the cube can be two possible states : either inside or outside of the surface // For any cube the are 2^8=256 possible sets of vertex states // This table lists the edges intersected by the surface for all 256 possible vertex states // There are 12 edges. For each entry in the table, if edge #n is intersected, then bit #n is set to 1
// For each of the possible vertex states listed in aiCubeEdgeFlags there is a specific triangulation // of the edge intersection points. a2iTriangleConnectionTable lists all of them in the form of // 0-5 edge triples with the list terminated by the invalid value -1. // For example: a2iTriangleConnectionTable[3] list the 2 triangles formed when corner[0] // and corner[1] are inside of the surface, but the rest of the cube is not. // // I found this table in an example program someone wrote long ago. It was probably generated by hand
DrawScene() { // rotate around center. t = klock(); tim = t / 4; spd = 2; r=4; offset = drawscale/2; posx = offset; posy = sin(t*spd) *-r + offset; posz = cos(t*spd) *-r + offset; posh = 0; posv = t*spd; setcam(posx,posy,posz,posh,posv);
glBegin(GL_TRIANGLES); //LINES // goes thru every point x,y,z in space (fStepSize^3 iterations) // can be optimised by for instance only checking space near a iso-surface // or by finding the surfice of one iso-object and tracing its surface, adding // each voxel / cube to a list that needs to be computed. // check // the Hugi diskmag at http://hugi.scene.org/ // http://www.angelcode.com/dev/metaballs/metaballs.asp // http://www.paulsprojects.net/opengl/metaballs/metaballs.html MarchingCubesBrute(); glEnd();
}
//Generate a sample data set. fSample1(), fSample2() and fSample3() define three scalar fields whose // values vary by the X,Y and Z coordinates and by the fTime value set by vSetTime() vSetTime(fNewTime) { fOffset = 0; iSourceNum = 0;
fSample(fX,fY,fZ) { v=0; // wasn't possible to just return "fSample1". have to store. if(activeSampleType == meta){ v = fSample1(fX,fY,fZ);} if(activeSampleType == lines){ v = fSample2(fX,fY,fZ);} if(activeSampleType == heightfield){ v= fSample3(fX,fY,fZ);} return v; }
//fSample1 finds the distance of (fX, fY, fZ) from three moving points fSample1(fX, fY, fZ) { fResult = 0.0; fDx=0; fDy=0; fDz=0; for(i=0; i<numSources; i++) { fDx = fX - sSourcePoint[i][0]; fDy = fY - sSourcePoint[i][1]; fDz = fZ - sSourcePoint[i][2]; fResult += 0.3/(fDx*fDx + fDy*fDy + fDz*fDz); }
return fResult; } //fSample2 finds the distance of (fX, fY, fZ) from three moving lines fSample2(fX, fY, fZ) { fResult = 0.0;
//fSample2 defines a height field by plugging the distance from the center into the sin and cos functions fSample3(fX, fY, fZ) { fHeight = 20.0*(tim + sqrt((0.5-fX)*(0.5-fX) + (0.5-fY)*(0.5-fY))); fHeight = 1.5 + 0.1*(sin(fHeight) + cos(fHeight)); fResult = (fHeight - fZ)*50.0;
return fResult; }
//fGetOffset finds the approximate point of intersection of the surface // between two points with the values fValue1 and fValue2 fGetOffset( fValue1, fValue2, fValueDesired) { fDelta = fValue2 - fValue1;
//vGetNormal() finds the gradient of the scalar field at a point //This gradient can be used as a very accurate vertx normal for lighting calculations vGetNormal(rfNormal[3], fX, fY, fZ) { rfNormal[0] = fSample(fX-0.01, fY, fZ) - fSample(fX+0.01, fY, fZ); rfNormal[1] = fSample(fX, fY-0.01, fZ) - fSample(fX, fY+0.01, fZ); rfNormal[2] = fSample(fX, fY, fZ-0.01) - fSample(fX, fY, fZ+0.01); vNormalizeVector(rfNormal, rfNormal); }
//Make a local copy of the values at the cube's corners for(iVertex = 0; iVertex < 8; iVertex++) { afCubeValue[iVertex] = fSample(fX + a2fVertexOffset[iVertex][0]*fScale, fY + a2fVertexOffset[iVertex][1]*fScale, fZ + a2fVertexOffset[iVertex][2]*fScale); }
//Find which vertices are inside of the surface and which are outside iFlagIndex = 0; for(iVertexTest = 0; iVertexTest < 8; iVertexTest++) { if(afCubeValue[iVertexTest] <= fTargetValue) { iFlagIndex += pow(2,iVertexTest); } }
//Find which edges are intersected by the surface iEdgeFlags = aiCubeEdgeFlags[iFlagIndex];
//If the cube is entirely inside or outside of the surface, then there will be no intersections if(iEdgeFlags == 0) { return; }
//Find the point of intersection of the surface with each edge //Then find the normal to the surface at those points for(iEdge = 0; iEdge < 12; iEdge++) {
//if there is an intersection on this edge //if(iEdgeFlags & (1<<iEdge))
// did i get this right? The bitwise expresion over that is.
// Slow? Copying normals x,y,z back into edgeNorm... static norm[3]; vGetNormal(norm, asEdgeVertex[iEdge][0], asEdgeVertex[iEdge][1], asEdgeVertex[iEdge][2]); asEdgeNorm[iEdge][0] = norm[0]; asEdgeNorm[iEdge][1] = norm[1]; asEdgeNorm[iEdge][2] = norm[2]; } } //Draw the triangles that were found. There can be up to five per cube for(iTriangle = 0; iTriangle < 5; iTriangle++) { if(a2iTriangleConnectionTable[iFlagIndex][3*iTriangle] < 0) break;
// does this look diffrent because of opengl lighting in original source? //vGetColor generates a color from a given position and normal of a point <--- NO. just the normal. vGetColor(rfColor[3], fX,fY,fZ) { //rfColor[0]=0; rfColor[1]=0; rfColor[2]=0; if(fX > 0.0) rfColor[0] = fX; if(fY < 0.0) rfColor[0] += -0.5 * fY; if(fZ < 0.0) rfColor[0] += -0.5 * fZ;
Sounds cool, but upon using CopyPasta action on the above segment of code, I get a "Error: Bad Static Init Syntax" Error.
CraigFatman at
Jinroh said at
Sounds cool, but upon using CopyPasta action on the above segment of code, I get a "Error: Bad Static Init Syntax" Error.
Maybe you need updating your Evaldraw copy.
Jinroh at
CraigFatman said at
Jinroh said at
Sounds cool, but upon using CopyPasta action on the above segment of code, I get a "Error: Bad Static Init Syntax" Error.
Maybe you need updating your Evaldraw copy.
Perhaps, I'll have to check. I thought I had teh latest version, but maybe not. :P
torbj?rn at
Jinroh: The metaballs/isosurfaces script requires the newest version of evaldraw. There are some enums that are dependant on enums higher up I think. I've used that feature in my newest script like this enum{imgW = 180, imgH = 164, tileSiz = 4} // adjust these according to the image size enum{W=imgW/tileSiz+1, H=imgH/tileSiz+1 }; Saves having to redefine W and H when I change imgW and imgH. Great :D
For quite some time I've wanted to see how this looks in action: http://freespace.virgin.net/hugo.elias/graphics/x_warp.htm
Sat down and converted Hugo's C-code to evaldraw. I've been tweaking it all day! :)
Variables to tweak: If you want your own image (CTRL+P) scroll down to // Tweak em! and set useImg = 1;
If you want the screen to be scaled correctly according to the size of your image set imgW and imgH to the size in pixels of your image. For instance imgW=640, imgH=480
If the code runs to slowly, try increasing the tiled image size, tileSiz = 4. Also, just try adjusting it for fun. If you set tileSiz really big. Like 32,64 or 128 and the spring dampning high, and the elasticity very low, like this: static springDampning = 1; // but not higher. static elasticity = 0.0000002; //0.00001; The program behaves like one of those cheesy "manipulate someones face" programs (pinch, pull the picture with mouse).
Try disabling the mixing of colors. Comment/uncomment mix() The mix() function takes the previous frame, the currently warped frame and the source image and blend/mix them togheter. Try tweaking the mixing factors.
One more thing. Left/right click the image to push/pull the grid nodes that define the flow map. They are springs, so they will bounce back.
This is what it looks like: http://bayimg.com/image/kaefbaacn.jpg
And If you don't spec and image with CTRL+P http://bayimg.com/image/kaefdaacn.jpg
enum{imgW = 180, imgH = 164, tileSiz = 4} // adjust these according to the image size enum{W=imgW/tileSiz+1, H=imgH/tileSiz+1 }; static offsX[W][H]; static offsY[W][H]; static offsXV[W][H]; static offsYV[W][H]; static offsXINT[W][H]; static offsYINT[W][H]; static offsXacc[W][H]; static offsYacc[W][H]; static offsC[W][H];
// img1 -> img2 TextureBlock(xo,yo) { xi = xo * tileSiz-1; yi = yo * tileSiz-1;
Ax=offsXint[xo][yo]; Ay=offsYint[xo][yo];
Bx=offsXint[xo+1][yo]; By=offsYint[xo+1][yo];
Cx=offsXint[xo][yo+1]; Cy=offsYint[xo][yo+1];
Dx=offsXint[xo+1][yo+1]; Dy=offsYint[xo+1][yo+1];
// Image corners, Left, right // A B // C D TX1=0, TY1=0, TX2=0, TY2=0; tx=0; ty=0;
VLDx = (Cx-Ax)/tileSiz; // Rate of change X down left VRDx = (Dx-Bx)/tileSiz; // X down right VLDy = (Cy-Ay)/tileSiz; // Y down left VRDy = (Dy-By)/tileSiz; // Y down right
TX1 = Ax; TY1 = Ay; TX2 = Bx; TY2 = By;
for(y=yi; y<yi+tileSiz; y++) { HDx = (TX2-TX1) / tileSiz; // Rate of change across polygon HDy = (TY2-TY1) / tileSiz; // Rate of change across polygon tx = TX1; ty = TY1;
for(x=xi; x<xi+tileSiz; x++) { img2[x][y] = img1[ tx ][ ty ]; tx += HDx; ty += HDy; }
while(1) //Infinite loop { cls(0); for(y = horizon; y < yres; y++) //Loop through all the vertical scanlines { //Calculate the Coordinates at the periphery of the frustum. u1 = cos(ang - (160 * (radians))) * distLUT[y]; v1 = sin(ang - (160 * (radians))) * distLUT[y]; u2 = cos(ang + (159 * (radians))) * distLUT[y]; v2 = sin(ang + (159 * (radians))) * distLUT[y];
u1 += camX; v1 += camY;
u2 += camX; v2 += camY;
//The Change In the Coordinates Across the scanline dU = (u2 - u1) * invRes; dV = (v2 - v1) * invRes;
tU = u1; tV = v1;
for(x = 0; x < xres; x++) { hbuf[x] = pic("E:\Index.png", tU, tV); tU += dU; tV += dV; } sethlin(0,y,hbuf,xres); }
ang += .01;
refresh(); } }
Edited by Jinroh at
torbj?rn at
Today I modifed the Magfield example that comes with evaldraw in /demos to have multiple poles. Use leftmouse click to drag poles around. Press Space to restart particle flow.
if (numframes==0 || keystatus[57]) { j=0; for(i=0;i<NUMP;i++) { p = positivePole[j]; px[i] = polex[p]; py[i] = poley[p]; j++;//evaldraw handles out of bounds. } otim = klock(0); }
hx = xres*.5; hy = yres*.5; tim = klock(0); dt = tim-otim; otim = tim; setcol(0xffffff);
for(i=0;i<NUMP;i++) { x = px[i]; y = py[i]; dead = 0; if( x < -10 || x > xres+10 ) dead = 1; if( y < -10 || y > yres+10 ) dead = 1;
/*if ((abs(x-hx) >= hx) || (abs(y-hy) >= hy)) { //Select random point on border j = (xres+yres)*2*rnd; if (j < xres) { x = j; y = 0; } else if (j < xres*2) { x = j-xres; y = yres; } else if (j < xres*2+yres) { x = 0; y = j-xres*2; } else { x = xres; y = j-xres*2-yres; } }*/
1. There is no 5x8 bitmap font, so setfont() is ignoring your parameters and using the default size: 8x12. 2. A much easier way to print an integer in EVALDRAW is: printf("%.f",num);