diff --git a/README.md b/README.md index db8b80c..0a66121 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,6 @@ interface. ## limitations -- Some values, like screen resolution, are a compile-time option due to performance and simplicity, and **can't change during runtime**. - **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**. @@ -73,7 +72,7 @@ The basic philosophy is: draw pixels. It is basically a fragment/pixel shader function that the library will call. You will be passed info about the pixel and can decide what to do with it, so you can process it, discard it, or simply write it to the screen. -- Also define `S3L_RESOLUTION_X` and `S3L_RESOLUTION_Y` to the resolution of your rendering screen. +- Also init screen resolution, either by defining `S3L_RESOLUTION_X` and `S3L_RESOLUTION_Y` (before including the library) or by setting `S3L_resolutionX` and `S3L_resolutionY` variables. - Use the provided Python tools to convert your model and textures to C arrays, include them in your program and set up the scene struct. - Init the 3D models and the scene with provided init functions (`S3L_init*`), set the position of the camera. @@ -91,6 +90,7 @@ The basic philosophy is: - Don't forget to **compile with -O3!** This drastically improves performance. - Your pixel drawing function (`S3L_PIXEL_FUNC`) will mostly be the performance bottleneck, try to make it as fast as possible. The number of pixels is usually much higher than the number of triangles or vertices processed, so you should focus on pixels the most. - In your `S3L_PIXEL_FUNC` **use a per-triangle cache!** This saves a lot of CPU time. Basically make sure you don't compute per-triangle values per-pixel, but only once, with the first pixel of the triangle. You can do this by remembering the last `triangleID` and only recompute the value when the ID changes. See the examples for how this is done. +- Some things, such as screen resolution, can be specified as static (compile time, can't change during run time) or dynamic. If you can, prefer setting them to static and a power of two (e.g. `#define S3L_RESOLUTION_X 512`) to increase performance! - Seeing buggy triangles flashing in front of the camera? With the limited 32bit arithmetic far-away things may be overflowing. Try to scale down the scene. If you also don't mind it, set `S3L_STRICT_NEAR_CULLING` to `1` -- this should probably solve it. - Seeing triangles weirdly deform in front of the camera? Due to the lack of proper near plane culling one of the options (`S3L_STRICT_NEAR_CULLING == 0`) deals with this by pushing the vertices in front of the near plane. To fix this either manually subdivide your model into more triangles or turn on `S3L_STRICT_NEAR_CULLING` (which will however make the close triangles disappear). - Seeing triangles disappear randomly in sorted modes? This is because the size of the memory for triangle sorting is limited by default -- increase `S3L_MAX_TRIANGLES_DRAWN`. diff --git a/programs/level.c b/programs/level.c index 21c9383..c5cbf74 100644 --- a/programs/level.c +++ b/programs/level.c @@ -28,8 +28,7 @@ #define S3L_PIXEL_FUNCTION drawPixel -#define S3L_RESOLUTION_X 640 -#define S3L_RESOLUTION_Y 480 +#define S3L_MAX_PIXELS (1024 * 1024) #include "../small3dlib.h" @@ -40,7 +39,7 @@ S3L_Scene scene; S3L_Vec4 teleportPoint; -uint32_t pixels[S3L_RESOLUTION_X * S3L_RESOLUTION_Y]; +uint32_t pixels[S3L_MAX_PIXELS]; uint32_t frame = 0; uint8_t *texture = 0; @@ -49,7 +48,7 @@ S3L_Vec4 uv0, uv1, uv2; void clearScreen() { - memset(pixels,255,S3L_RESOLUTION_X * S3L_RESOLUTION_Y * sizeof(uint32_t)); + memset(pixels,255,S3L_MAX_PIXELS * sizeof(uint32_t)); } static inline void setPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue) @@ -63,7 +62,7 @@ static inline void setPixel(int x, int y, uint8_t red, uint8_t green, uint8_t bl uint32_t b = blue & 0x000000FF; b = b << 8; - pixels[y * S3L_RESOLUTION_X + x] = r | g | b; + pixels[y * S3L_resolutionX + x] = r | g | b; } void sampleTexture(S3L_Unit u, S3L_Unit v, uint8_t *r, uint8_t *g, uint8_t *b) @@ -88,9 +87,9 @@ void drawTeleport(int16_t x, int16_t y, S3L_ScreenCoord size) int16_t halfSize = size / 2; S3L_ScreenCoord x0 = S3L_max(0,x - halfSize); - S3L_ScreenCoord x1 = S3L_min(S3L_RESOLUTION_X,x + halfSize); + S3L_ScreenCoord x1 = S3L_min(S3L_resolutionX,x + halfSize); S3L_ScreenCoord y0 = S3L_max(0,y - halfSize); - S3L_ScreenCoord y1 = S3L_min(S3L_RESOLUTION_Y,y + halfSize); + S3L_ScreenCoord y1 = S3L_min(S3L_resolutionY,y + halfSize); S3L_ScreenCoord row = y0 - (y - halfSize); @@ -197,8 +196,8 @@ void draw() project3DPointToScreen(teleportPoint,scene.camera,&screenPoint); if (screenPoint.w > 0 && - screenPoint.x >= 0 && screenPoint.x < S3L_RESOLUTION_X && - screenPoint.y >= 0 && screenPoint.y < S3L_RESOLUTION_Y && + screenPoint.x >= 0 && screenPoint.x < S3L_resolutionX && + screenPoint.y >= 0 && screenPoint.y < S3L_resolutionY && screenPoint.z < S3L_zBufferRead(screenPoint.x,screenPoint.y)) drawTeleport(screenPoint.x,screenPoint.y,screenPoint.w); @@ -221,9 +220,12 @@ void draw() int main() { - SDL_Window *window = SDL_CreateWindow("level demo", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, S3L_RESOLUTION_X, S3L_RESOLUTION_Y, SDL_WINDOW_SHOWN); + S3L_resolutionX = 640; + S3L_resolutionY = 480; + + SDL_Window *window = SDL_CreateWindow("level demo", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, S3L_resolutionX, S3L_resolutionY, SDL_WINDOW_SHOWN); SDL_Renderer *renderer = SDL_CreateRenderer(window,-1,0); - SDL_Texture *texture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBX8888, SDL_TEXTUREACCESS_STATIC, S3L_RESOLUTION_X, S3L_RESOLUTION_Y); + SDL_Texture *texture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBX8888, SDL_TEXTUREACCESS_STATIC, S3L_resolutionX, S3L_resolutionY); SDL_Surface *screenSurface = SDL_GetWindowSurface(window); SDL_Event event; @@ -242,8 +244,10 @@ int main() while (running) // main loop { + int newWidth = -1, newHeight = -1; + draw(); - SDL_UpdateTexture(texture,NULL,pixels,S3L_RESOLUTION_X * sizeof(uint32_t)); + SDL_UpdateTexture(texture,NULL,pixels,S3L_resolutionX * sizeof(uint32_t)); while (SDL_PollEvent(&event)) if (event.type == SDL_QUIT) @@ -273,6 +277,34 @@ int main() else if (state[SDL_SCANCODE_RIGHT]) S3L_vec3Add(&scene.camera.transform.translation,camR); + if (state[SDL_SCANCODE_K]) + newHeight = S3L_resolutionY + 4; + else if (state[SDL_SCANCODE_I]) + newHeight = S3L_resolutionY - 4; + else if (state[SDL_SCANCODE_J]) + newWidth = S3L_resolutionX - 4; + else if (state[SDL_SCANCODE_L]) + newWidth = S3L_resolutionX + 4; + + if ( + ( + (newWidth != -1 && newWidth > 0) || + (newHeight != -1 && newHeight > 0) + ) && + (newWidth * S3L_resolutionY <= S3L_MAX_PIXELS) && + (newHeight * S3L_resolutionX <= S3L_MAX_PIXELS)) + { + if (newWidth != -1) + S3L_resolutionX = newWidth; + + if (newHeight != -1) + S3L_resolutionY = newHeight; + + SDL_DestroyTexture(texture); + texture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBX8888, SDL_TEXTUREACCESS_STATIC, S3L_resolutionX, S3L_resolutionY); + SDL_SetWindowSize(window,S3L_resolutionX,S3L_resolutionY); + } + SDL_RenderClear(renderer); SDL_RenderCopy(renderer,texture,NULL,NULL); SDL_RenderPresent(renderer); diff --git a/small3dlib.h b/small3dlib.h index 3f6fa81..034f477 100644 --- a/small3dlib.h +++ b/small3dlib.h @@ -10,12 +10,12 @@ license: CC0 1.0 (public domain) found at https://creativecommons.org/publicdomain/zero/1.0/ + additional waiver of all IP - version: 0.85 + version: 0.851 Before including the library, define S3L_PIXEL_FUNCTION to the name of the function you'll be using to draw single pixels (this function will be called - by the library to render the frames). Also define S3L_RESOLUTION_X and - S3L_RESOLUTION_Y. + by the library to render the frames). Also either init S3L_resolutionX and + S3L_resolutionY or define S3L_RESOLUTION_X and S3L_RESOLUTION_Y. You'll also need to decide what rendering strategy and other settings you want to use, depending on your specific usecase. You may want to use a @@ -131,12 +131,35 @@ #include +#ifdef S3L_RESOLUTION_X + #ifdef S3L_RESOLUTION_Y + #define S3L_MAX_PIXELS (S3L_RESOLUTION_X * S3L_RESOLUTION_Y) + #endif +#endif + #ifndef S3L_RESOLUTION_X -#define S3L_RESOLUTION_X 640 ///< Redefine to screen x resolution. + #ifndef S3L_MAX_PIXELS + #error Dynamic resolution set (S3L_RESOLUTION_X not defined), but\ + S3L_MAX_PIXELS not defined! + #endif + + uint16_t S3L_resolutionX = 512; /**< If a static resolution is not set with + S3L_RESOLUTION_X, this variable can be + used to change X resolution at runtime, + in which case S3L_MAX_PIXELS has to be + defined (to allocate zBuffer etc.)! */ + #define S3L_RESOLUTION_X S3L_resolutionX #endif #ifndef S3L_RESOLUTION_Y -#define S3L_RESOLUTION_Y 480 ///< Redefine to screen y resolution. + #ifndef S3L_MAX_PIXELS + #error Dynamic resolution set (S3L_RESOLUTION_Y not defined), but\ + S3L_MAX_PIXELS not defined! + #endif + + uint16_t S3L_resolutionY = 512; /**< Same as S3L_resolutionX, but for Y + resolution. */ + #define S3L_RESOLUTION_Y S3L_resolutionY #endif /** Units of measurement in 3D space. There is S3L_FRACTIONS_PER_UNIT in one @@ -735,11 +758,11 @@ static inline void S3L_rotate2DPoint(S3L_Unit *x, S3L_Unit *y, S3L_Unit angle); #if S3L_Z_BUFFER == 1 #define S3L_MAX_DEPTH 2147483647 - S3L_Unit S3L_zBuffer[S3L_RESOLUTION_X * S3L_RESOLUTION_Y]; + S3L_Unit S3L_zBuffer[S3L_MAX_PIXELS]; #define S3L_zBufferFormat(depth) (depth) #elif S3L_Z_BUFFER == 2 #define S3L_MAX_DEPTH 255 - uint8_t S3L_zBuffer[S3L_RESOLUTION_X * S3L_RESOLUTION_Y]; + uint8_t S3L_zBuffer[S3L_MAX_PIXELS]; #define S3L_zBufferFormat(depth)\ S3L_min(255,(depth) >> S3L_REDUCED_Z_BUFFER_GRANULARITY) #endif @@ -1707,6 +1730,10 @@ void S3L_initDrawConfig(S3L_DrawConfig *config) config->visible = 1; } +#ifndef S3L_PIXEL_FUNCTION + #error Pixel rendering function (S3L_PIXEL_FUNCTION) not specified! +#endif + static inline void S3L_PIXEL_FUNCTION(S3L_PixelInfo *pixel); // forward decl /** Serves to accelerate linear interpolation for performance-critical