diff --git a/README.md b/README.md index a280bc7..f2b4db1 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,16 @@ PC (SDL, offline rendering, terminal): - **Different drawing strategies** to choose from: none, z-buffer (none, full, reduced), triangle sorting (back-to-front, fron-to-back with stencil buffer). - Triangles provide **barycentric coordinates**, thanks to which practically anything that can be achieved with OpenGL can be achieved (texturing, shading, normal-mapping, texture fitering, transparency, PBR, shadow mapping, MIP mapping, ...). - **Top-left rasterization rule**, pixels of adjacent triangles don't overlap or have holes (just like in OpenGL). -- **Tested on multiple platforms** (PC, Pokitto, Gamebuino META). +- **Tested on many platforms**: + - PC (little endian, 64bit GNU) + - PowerPC emulator (big endian) + - compilers: gcc, clang + - Arduboy (only experimental) + - Gamebuino META (32bit resource-limited embedded ARM) + - TODO: + - Android + - Windows + - emscripten (web browser, JavaScript transpile) - **Many compile-time options** to tune the performance vs quality. - **Similar to OpenGL** in principle, but simpler, easier to use, with higher-level features. - **Tools** (Python scripts) for converting 3D models and textures to C array format used by the library. @@ -51,8 +60,22 @@ be an as-is set of tools that the users is welcome to adjust for their specific project. So new features will be preferred to keeping the same interface. +## why? + +You just need to make a small mini 3D game, quick 3D animation or visualization and don't want to go through the horror of learning and setting +up OpenGL, installing drivers and libraries? Don't want to be tied to HW, 3rd party API or libraries and their dependencies? Don't want to install +gigabytes of heavy super ultra graphics engines just for playing around with a few low poly models? You want to create extremely portable 3D +graphics that will run on small and obscure platforms that don't have OpenGL, good specs or even standard C library? Want to just render something +offline simply and not caring about highest rendering speed? You want to toy around with modifying something in the rendering pipeline that you +can't easily do or debug in OpenGL (such as the rasterization algorithm)? Want to hack around in the demo scene? Want to create something public +domain and need a public domain renderer? Or just don't want to be bothered by conditions such as proper attribution or copyleft? + +This library may help you. + ## limitations +And advantages at the same time :) + - **No scenegraph** (object parenting), just a scene list. Parenting can still be achieved by using cutom transform matrices. - Though performance is high, due to multiplatformness it **probably can't match platform-specific rasterizers written in assembly**. - There is **no far plane**. diff --git a/programs/helloWorld.c b/programs/helloWorld.c new file mode 100644 index 0000000..4298137 --- /dev/null +++ b/programs/helloWorld.c @@ -0,0 +1,74 @@ +/** + Simple hello world for small3dlib. Renders a triangle in terminal as ASCII. + + by drummyfish, released under CC0 1.0, public domain +*/ + +#include // for IO + +// This tells the library the resolution of our ASCII screen. +#define S3L_RESOLUTION_X 64 +#define S3L_RESOLUTION_Y 30 + +// This tells the library the name of a function we use to write pixels. +#define S3L_PIXEL_FUNCTION drawPixel + +#include "../small3dlib.h" // now include the library + +#define U S3L_FRACTIONS_PER_UNIT // this is the library unit, like e.g. 1 meter + +S3L_Unit triangleVertices[] = { // x, y, z + U, 0, 0, // vertex 1 + 0, U, 0, // vertex 2 + -U, U/2, 0 }; // vertex 3 + +S3L_Index triangleTriangles[] = { 0, 1, 2 }; // our single triangle + +#undef U + +#define SCREEN_SIZE (S3L_RESOLUTION_X * S3L_RESOLUTION_Y) + +char screen[SCREEN_SIZE]; // our ASCII screen + +/* This function will be called by the library to draw individual rasterized + pixels to our screen. We should try to make this function as fast as possible + as it tends to be the performance bottle neck. */ +void drawPixel(S3L_PixelInfo *p) +{ + screen[p->y * S3L_RESOLUTION_X + p->x] = 'X'; +} + +int main() +{ + for (int i = 0; i < SCREEN_SIZE; ++i) // init the screen + screen[i] = '.'; + + S3L_Model3D triangleModel; // 3D model representing our triangle + S3L_initModel3D(triangleVertices,9,triangleTriangles,1,&triangleModel); + + S3L_Scene scene; // scene of 3D models (we only have 1) + S3L_initScene(&triangleModel,1,&scene); + + // shift the camera a little bit so that we can see the triangle + scene.camera.transform.translation.z = -2 * S3L_FRACTIONS_PER_UNIT; + scene.camera.transform.translation.y = S3L_FRACTIONS_PER_UNIT / 2; + + S3L_newFrame(); // has to be called before each frame + S3L_drawScene(scene); /* this starts the scene rendering, the library + will now start calling our drawPixel function to + render the camera view */ + int index = 0; + + for (int y = 0; y < S3L_RESOLUTION_Y; ++y) // now display the screen + { + for (int x = 0; x < S3L_RESOLUTION_X; ++x) + { + putchar(screen[index]); + index++; + } + + putchar('\n'); + } + + return 0; // done! +} diff --git a/programs/helloTerminal.c b/programs/testTerminal.c similarity index 67% rename from programs/helloTerminal.c rename to programs/testTerminal.c index 6c930f8..d60224e 100644 --- a/programs/helloTerminal.c +++ b/programs/testTerminal.c @@ -1,26 +1,22 @@ -/* +/** Simple example of small3dlib, rendering in terminal. - license: CC0 1.0 + by drummyfish, released under CC0 1.0, public domain */ #include -#include // for usleep +#include // for usleep // we need to define screen resolution before including the library: - #define S3L_RESOLUTION_X 80 #define S3L_RESOLUTION_Y 40 // and a name of the function we'll be using to draw individual pixels: - #define S3L_PIXEL_FUNCTION drawPixel -// now include the library: +#include "../small3dlib.h" // now include the library -#include "../small3dlib.h" - -// we'll use a predefined geometry of a cube that's in the library: +// we'll use a predefined geometry of a cube from the library: S3L_Unit cubeVertices[] = { S3L_CUBE_VERTICES(S3L_FRACTIONS_PER_UNIT) }; S3L_Index cubeTriangles[] = { S3L_CUBE_TRIANGLES }; @@ -28,7 +24,10 @@ S3L_Index cubeTriangles[] = { S3L_CUBE_TRIANGLES }; S3L_Model3D cubeModel; // 3D model, has a geometry, position, rotation etc. S3L_Scene scene; // scene we'll be rendring (can have multiple models) -uint8_t screen[S3L_RESOLUTION_X * S3L_RESOLUTION_Y]; // our screen +#define FRAME_OFFSET 20 // how many newlines will be printed before each frame +#define SCREEN_SIZE (FRAME_OFFSET + (S3L_RESOLUTION_X + 1) * S3L_RESOLUTION_Y + 1) + +uint8_t screen[SCREEN_SIZE]; // ASCII screen /* This function will be called by the library to draw individual rasterized pixels to the screen. We should try to make this function as fast as possible @@ -50,34 +49,8 @@ void drawPixel(S3L_PixelInfo *p) else c = '.'; - screen[p->y * S3L_RESOLUTION_X + p->x] = c; // draw the pixel to the screen -} - -void clearScreen() -{ - for (int j = 0; j < S3L_RESOLUTION_X * S3L_RESOLUTION_Y; ++j) - screen[j] = ' '; -} - -// This function prints out the content of the screen to the terminal. - -void drawScreen() -{ - for (int i = 0; i < 20; ++i) - printf("\n"); - - int pos = 0; - - for (int y = 0; y < S3L_RESOLUTION_Y; ++y) - { - for (int x = 0; x < S3L_RESOLUTION_X; ++x) - { - printf("%c",screen[pos]); - pos++; - } - - printf("\n"); - } + // draw to ASCII screen + screen[FRAME_OFFSET + p->y * (S3L_RESOLUTION_X + 1) + p->x] = c; } int main() @@ -89,9 +62,8 @@ int main() S3L_CUBE_TRIANGLE_COUNT, &cubeModel); - S3L_initScene( // Initialize the scene we'll be rendering. - &cubeModel, /* We only have one model (the cube), this is like an array - with only one model in it. */ + S3L_initScene( // Initialize the scene we'll be rendering. + &cubeModel, // This is like an array with only one model in it. 1, &scene); @@ -99,20 +71,25 @@ int main() scene.camera.transform.translation.z = -2 * S3L_FRACTIONS_PER_UNIT; - for (int i = 0; i < 200; ++i) // render 200 frames + for (int i = 0; i < 200; ++i) // render 200 frames { - clearScreen(); + // clear the screen + for (int j = 0; j < FRAME_OFFSET; ++j) + screen[j] = '\n'; + + for (int j = FRAME_OFFSET; j < SCREEN_SIZE; ++j) + screen[j] = ((j - FRAME_OFFSET + 1) % (S3L_RESOLUTION_X + 1) ? ' ' : '\n'); + + screen[SCREEN_SIZE - 1] = 0; // terminate the string S3L_newFrame(); // has to be called before each frame - S3L_drawScene(scene); /* This starts the scene rendering. The drawPixel function will be called to draw it. */ - drawScreen(); + puts(screen); // display the frame usleep(100000); // wait a bit to let the user see the frame // now move and rotate the cube a little to see some movement: - scene.models[0].transform.rotation.y += 10; scene.models[0].transform.rotation.x += 4; scene.models[0].transform.translation.x = S3L_sin(i * 4);