O great! I didn't know that about burning video, I had no idea it was a software renderer.... since it offered a good quality of textures, that justifies the FPS reduction if it's the caseCuteAlien wrote: Tue Jan 28, 2025 1:37 pm Quick answer about software renderers: There are 2 of those in Irrlicht- "Software" and "Burningsvideo".
First one was written by Niko (aka the Irrlicht founder). It is no longer under active development, but we'll keep it around as long as it doesn't cause troubles (so far that's the case). It's main use is for quick UI stuff without 3D. So mainly for dialogs before your application starts. It supports a bit 3D, but mainly just enough to show a single 3D model or so. Anything advanced isn't really supported, so don't use it for 3d scenes. I suspect it might also be useful when trying to get Irrlicht running on a new platform as it's likely easier to port than the other software renderer (way less complex).
Burnings video is the software renderer from Thomas Alten. And once in a while he still works on it. And he usually also reacts quickly when we give him any bug reports. And that one is for real 3D. As long as you don't use too much resolution it works pretty well even for some quite advanced stuff. And it also has another feature which I know some (non forum) users like: The colors are more reproducible than with any of the other drivers (like opengl with different drivers you might not get the exact same results on screen). Which can be quite important in some applications (although we never really guaranteed that, so people using it for that live a tiny bit risky...).
Graphics can be precalculated assuming limits, something like view frustum limit and fps limit with Sleep, for example, i have a code in c++98, an animated tetrahedron in 360 degrees, with 512 angle rotation variants and 26 for distance consumes you 16 mb of ram, with 128 angle rotation variant it's like 2 mb of ram, and the CPU consumption is only in reading(like 70 hz), an animated model probably consumes about 300 mb of ram or more, with textures maybe 400 mb, if you limit it to 256 rotation angles it would reduce considerably the weight, like 50 mb, with 128 i think 20 mb, the example code:
(i try this, but sometimes it doesn't work as it should, but with irrlicht I wouldn't have this problem, I would only have to create the double buffer in it)(limited to 66 updates per second)(zoom with righ click to left and right direction, move around with left click)


Code: Select all
#include <windows.h>
#include <cmath>
#include <string>
struct Point3D { float x, y, z; };
struct Face { int v1, v2, v3; float depth; };
Point3D baseVertices[] = {
{0.0f, 50.0f, 0.0f},
{-50.0f, -50.0f, -50.0f},
{50.0f, -50.0f, -50.0f},
{0.0f, -50.0f, 50.0f}
};
Face baseFaces[] = {
{0, 1, 2, 0},
{0, 2, 3, 0},
{0, 3, 1, 0},
{1, 3, 2, 0}
};
int xRot = 0;
int yRot = 0;
float scale = 1.0f;
const float minScale = 0.5f;
const float maxScale = 3.0f;
const int scaleSteps = 26;
int rotationDetail = 4; // 1: 64, 2: 128, 3: 256, 4: 512
int rotationSteps;
POINT*** precomputedProjections = NULL;
Face*** precomputedFaces = NULL;
void RotatePoint(Point3D& p, int xRot, int yRot) {
float theta = xRot * 3.14159265f / 180.0f;
float phi = yRot * 3.14159265f / 180.0f;
float y = p.y * cos(theta) - p.z * sin(theta);
float z = p.y * sin(theta) + p.z * cos(theta);
p.y = y; p.z = z;
float x = p.x * cos(phi) + z * sin(phi);
z = -p.x * sin(phi) + z * cos(phi);
p.x = x; p.z = z;
}
void SortFaces(Face* faces, int count) {
for (int i = 0; i < count - 1; i++) {
for (int j = i + 1; j < count; j++) {
if (faces[i].depth < faces[j].depth) {
Face temp = faces[i];
faces[i] = faces[j];
faces[j] = temp;
}
}
}
}
void PrecomputeRotationsAndScales() {
rotationSteps = 8 * (1 << rotationDetail);
if (precomputedProjections) {
for (int xStep = 0; xStep < rotationSteps; xStep++) {
for (int yStep = 0; yStep < rotationSteps; yStep++) {
delete[] precomputedProjections[xStep][yStep];
}
delete[] precomputedProjections[xStep];
}
delete[] precomputedProjections;
}
if (precomputedFaces) {
for (int xStep = 0; xStep < rotationSteps; xStep++) {
delete[] precomputedFaces[xStep];
}
delete[] precomputedFaces;
}
precomputedProjections = new POINT**[rotationSteps];
precomputedFaces = new Face**[rotationSteps];
for (int xStep = 0; xStep < rotationSteps; xStep++) {
precomputedProjections[xStep] = new POINT*[rotationSteps];
precomputedFaces[xStep] = new Face*[rotationSteps];
for (int yStep = 0; yStep < rotationSteps; yStep++) {
precomputedProjections[xStep][yStep] = new POINT[scaleSteps * 4];
precomputedFaces[xStep][yStep] = new Face[4];
}
}
for (int xStep = 0; xStep < rotationSteps; xStep++) {
for (int yStep = 0; yStep < rotationSteps; yStep++) {
Point3D rotatedVertices[4];
for (int i = 0; i < 4; i++) {
rotatedVertices[i] = baseVertices[i];
RotatePoint(rotatedVertices[i], xStep * (360 / rotationSteps), yStep * (360 / rotationSteps));
}
Face faces[4];
for (int i = 0; i < 4; i++) {
faces[i] = baseFaces[i];
faces[i].depth = (rotatedVertices[faces[i].v1].z + rotatedVertices[faces[i].v2].z + rotatedVertices[faces[i].v3].z) / 3.0f;
}
SortFaces(faces, 4);
for (int i = 0; i < 4; i++) {
precomputedFaces[xStep][yStep][i] = faces[i];
}
for (int s = 0; s < scaleSteps; s++) {
float currentScale = minScale + s * 0.1f;
for (int i = 0; i < 4; i++) {
precomputedProjections[xStep][yStep][s * 4 + i].x = (int)(rotatedVertices[i].x * currentScale);
precomputedProjections[xStep][yStep][s * 4 + i].y = (int)(rotatedVertices[i].y * currentScale);
}
}
}
}
}
void DrawTextOnFace(HDC hdc, POINT* points, const std::string& text, COLORREF color) {
int centerX = (points[0].x + points[1].x + points[2].x) / 3;
int centerY = (points[0].y + points[1].y + points[2].y) / 3;
SetTextColor(hdc, color);
SetBkMode(hdc, TRANSPARENT);
TextOut(hdc, centerX - 8, centerY - 8, text.c_str(), text.length());
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
static int startX, startY;
static int initialXRot, initialYRot;
static float initialScale;
static DWORD lastUpdateTime = 0;
switch (message) {
case WM_LBUTTONDOWN:
startX = LOWORD(lParam);
startY = HIWORD(lParam);
initialXRot = xRot;
initialYRot = yRot;
SetCapture(hWnd);
break;
case WM_RBUTTONDOWN:
startX = LOWORD(lParam);
initialScale = scale;
SetCapture(hWnd);
break;
case WM_MOUSEMOVE:
if (wParam & MK_LBUTTON || wParam & MK_RBUTTON) {
DWORD currentTime = GetTickCount();
if (currentTime - lastUpdateTime < 15) break; //1000:15 updates per second
lastUpdateTime = currentTime;
if (wParam & MK_LBUTTON) {
int currentX = LOWORD(lParam);
int currentY = HIWORD(lParam);
int deltaX = currentX - startX;
int deltaY = currentY - startY;
xRot = initialXRot + (deltaY / 10) * (360 / rotationSteps);
yRot = initialYRot + (deltaX / 10) * (360 / rotationSteps);
xRot = (xRot % 360 + 360) % 360;
yRot = (yRot % 360 + 360) % 360;
} else if (wParam & MK_RBUTTON) {
int currentX = LOWORD(lParam);
int deltaX = currentX - startX;
scale = initialScale + (deltaX / 10) * 0.1f;
scale = scale < minScale ? minScale : scale;
scale = scale > maxScale ? maxScale : scale;
}
InvalidateRect(hWnd, NULL, FALSE);
}
break;
case WM_LBUTTONUP:
case WM_RBUTTONUP:
ReleaseCapture();
break;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rect;
GetClientRect(hWnd, &rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
int centerX = width / 2;
int centerY = height / 2;
HDC memDC = CreateCompatibleDC(hdc);
HBITMAP memBM = CreateCompatibleBitmap(hdc, width, height);
HBITMAP oldBM = (HBITMAP)SelectObject(memDC, memBM);
HBRUSH hWhiteBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
FillRect(memDC, &rect, hWhiteBrush);
int xStep = xRot / (360 / rotationSteps);
int yStep = yRot / (360 / rotationSteps);
int sStep = (int)((scale - minScale) / 0.1f);
HPEN hBlackPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
HBRUSH hBlueBrush = CreateSolidBrush(RGB(0, 0, 255));
HPEN oldPen = (HPEN)SelectObject(memDC, hBlackPen);
HBRUSH oldBrush = (HBRUSH)SelectObject(memDC, hBlueBrush);
for (int i = 0; i < 4; i++) {
POINT projected[3];
projected[0].x = centerX + precomputedProjections[xStep][yStep][sStep * 4 + precomputedFaces[xStep][yStep][i].v1].x;
projected[0].y = centerY + precomputedProjections[xStep][yStep][sStep * 4 + precomputedFaces[xStep][yStep][i].v1].y;
projected[1].x = centerX + precomputedProjections[xStep][yStep][sStep * 4 + precomputedFaces[xStep][yStep][i].v2].x;
projected[1].y = centerY + precomputedProjections[xStep][yStep][sStep * 4 + precomputedFaces[xStep][yStep][i].v2].y;
projected[2].x = centerX + precomputedProjections[xStep][yStep][sStep * 4 + precomputedFaces[xStep][yStep][i].v3].x;
projected[2].y = centerY + precomputedProjections[xStep][yStep][sStep * 4 + precomputedFaces[xStep][yStep][i].v3].y;
Polygon(memDC, projected, 3);
if (i < 3) {
COLORREF colors[] = { RGB(255, 0, 0), RGB(0, 255, 0), RGB(255, 255, 0) };
DrawTextOnFace(memDC, projected, ":)", colors[i]);
}
}
BitBlt(hdc, 0, 0, width, height, memDC, 0, 0, SRCCOPY);
SelectObject(memDC, oldPen);
SelectObject(memDC, oldBrush);
DeleteObject(hBlackPen);
DeleteObject(hBlueBrush);
SelectObject(memDC, oldBM);
DeleteObject(memBM);
DeleteDC(memDC);
EndPaint(hWnd, &ps);
break;
}
case WM_ERASEBKGND:
return 1;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
PrecomputeRotationsAndScales();
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = "TetrahedronClass";
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
RegisterClass(&wc);
HWND hWnd = CreateWindow("TetrahedronClass", "3D Tetrahedron precalculated", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 512, 512, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (precomputedProjections) {
for (int xStep = 0; xStep < rotationSteps; xStep++) {
for (int yStep = 0; yStep < rotationSteps; yStep++) {
delete[] precomputedProjections[xStep][yStep];
}
delete[] precomputedProjections[xStep];
}
delete[] precomputedProjections;
}
if (precomputedFaces) {
for (int xStep = 0; xStep < rotationSteps; xStep++) {
delete[] precomputedFaces[xStep];
}
delete[] precomputedFaces;
}
return msg.wParam;
}
