In irrlicht 1.8.5 this does not exist, this is a great implementation considering how expensive shadow volumes are.
Using only the CPU, at resolutions like 800x600, you can have a stable number of fps, even on 20-year-old computers (although the operating system will continue to render the screen pixels with the GPU).
Burningsvideo runs very well, I think it only uses a single thread of the processor, however directly emulating OpenGL with the cpu with the help of some .dll offers us about 10 more fps.
OpenGL CPU emulation(opengl32.dll emulated):

Burningsvideo:

Code: Select all
#include <irrlicht.h>
#include <vector>
#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
int main()
{
IrrlichtDevice* device = createDevice(
EDT_BURNINGSVIDEO,
dimension2d<u32>(800,600),
16, false, true, false, 0);
if (!device) return 1;
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
IGUIEnvironment* gui = device->getGUIEnvironment();
ITimer* timer = device->getTimer();
driver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);
// Check RTT support (required)
if (!driver->queryFeature(EVDF_RENDER_TO_TARGET))
{
device->drop();
return 1;
}
IAnimatedMesh* mesh = smgr->getMesh("../../media/dwarf.x");
//IAnimatedMesh* mesh = smgr->getMesh("../../media/sydney.md2");
if (!mesh)
{
device->drop();
return 1;
}
std::vector<IShadowVolumeSceneNode*> shadowNodes;
// Main character
IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode(mesh);
if (node)
{
node->setMaterialFlag(EMF_LIGHTING, true);
node->setMaterialFlag(EMF_BILINEAR_FILTER, false);
node->setMaterialFlag(EMF_NORMALIZE_NORMALS, true);
node->setMD2Animation(scene::EMAT_STAND);
node->setPosition(vector3df(0,-9,0));
//node->setMaterialTexture(0, driver->getTexture("../../media/sydney.bmp"));
//node->setPosition(vector3df(0,15,0));
IShadowVolumeSceneNode* shadow = node->addShadowVolumeSceneNode();
if (shadow)
{
shadow->setFreeze(ESF_FREEZE); // start frozen
shadowNodes.push_back(shadow);
}
}
// Extra characters in a circle
for (int i = 0; i < 3; ++i)
{
IAnimatedMeshSceneNode* newNode = smgr->addAnimatedMeshSceneNode(mesh);
if (newNode)
{
newNode->setMaterialFlag(EMF_LIGHTING, true);
newNode->setMaterialFlag(EMF_BILINEAR_FILTER, false);
newNode->setMaterialFlag(EMF_NORMALIZE_NORMALS, true);
//newNode->setMD2Animation(scene::EMAT_STAND);
//newNode->setMaterialTexture(0, driver->getTexture("../../media/sydney.bmp"));
f32 angle = i * (2 * PI / 8);
f32 radius = 60.0f;
f32 x = radius * cos(angle);
f32 z = radius * sin(angle);
newNode->setPosition(vector3df(x, -9, z));
// newNode->setPosition(vector3df(x, 15, z));
IShadowVolumeSceneNode* shadow = newNode->addShadowVolumeSceneNode();
if (shadow)
{
shadow->setFreeze(ESF_FREEZE);
shadowNodes.push_back(shadow);
}
}
}
smgr->setShadowColor(video::SColor(230,0,0,0));
// Light (must be present for shadows)
ILightSceneNode* sun = smgr->addLightSceneNode(0,
vector3df(0,150,0), SColorf(1.0f,1.0f,1.0f), 700.0f);
// Floor
IMeshSceneNode* floor = smgr->addCubeSceneNode(1.0f);
if (floor)
{
floor->setScale(vector3df(200,1,200));
floor->setPosition(vector3df(0,-10,0));
floor->setMaterialFlag(EMF_LIGHTING, true);
floor->setMaterialFlag(EMF_NORMALIZE_NORMALS, true);
floor->setMaterialTexture(0, driver->getTexture("../../media/wall.jpg"));
}
// Camera
ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
camera->setPosition(vector3df(0,20,-40));
camera->setTarget(vector3df(0,5,0));
device->getCursorControl()->setVisible(false);
// --- RTT setup ---
ITexture* rtt = driver->addRenderTargetTexture(dimension2d<u32>(128,128), "RTT_Scene");
if (!rtt)
{
device->drop();
return 1;
}
// Full‑screen quad vertices (for drawing the RTT texture)
S3DVertex vertices[4];
vertices[0] = S3DVertex(-1, 1,0, 0,0,1, SColor(255,255,255,255), 0,0);
vertices[1] = S3DVertex( 1, 1,0, 0,0,1, SColor(255,255,255,255), 1,0);
vertices[2] = S3DVertex( 1,-1,0, 0,0,1, SColor(255,255,255,255), 1,1);
vertices[3] = S3DVertex(-1,-1,0, 0,0,1, SColor(255,255,255,255), 0,1);
u16 indices[6] = {0,1,2, 0,2,3};
SMaterial quadMaterial;
quadMaterial.Lighting = false;
quadMaterial.ZBuffer = false;
quadMaterial.ZWriteEnable = EZW_OFF;
quadMaterial.setTexture(0, rtt);
quadMaterial.setFlag(EMF_TRILINEAR_FILTER, true);
// --- Shadow update timing ---
u32 lastShadowUpdate = timer->getRealTime();
const u32 shadowInterval = 1000 / 8; // 8 shadows volume FPS
// Force an initial shadow update (unfreeze, update, refreeze)
for (size_t i = 0; i < shadowNodes.size(); ++i)
{
IShadowVolumeSceneNode* shadow = shadowNodes[i];
if (shadow)
{
shadow->setFreeze(ESF_RUN);
shadow->updateShadowVolumes();
shadow->setFreeze(ESF_FREEZE);
}
}
int lastFPS = -1;
while (device->run())
{
if (!device->isWindowActive()) continue;
// 1. Update shadows at 8 FPS (temporary unfreeze)
u32 now = timer->getRealTime();
if (now - lastShadowUpdate >= shadowInterval)
{
for (size_t i = 0; i < shadowNodes.size(); ++i)
{
IShadowVolumeSceneNode* shadow = shadowNodes[i];
if (shadow)
{
shadow->setFreeze(ESF_RUN);
shadow->updateShadowVolumes();
shadow->setFreeze(ESF_FREEZE);
}
}
lastShadowUpdate = now;
}
// 2. Render the scene into the RTT
driver->setRenderTarget(rtt, ECBF_COLOR | ECBF_DEPTH, SColor(0,0,0,255));
smgr->drawAll();
// 3. Switch back to the back buffer and clear (setRenderTarget already cleared)
driver->setRenderTarget(0, ECBF_COLOR | ECBF_DEPTH, SColor(0,100,100,100));
driver->beginScene(false, false, SColor(0,0,0,0));
// 4. Draw the full‑screen quad with the RTT texture
driver->setMaterial(quadMaterial);
driver->setTransform(ETS_WORLD, IdentityMatrix);
driver->setTransform(ETS_VIEW, IdentityMatrix);
driver->setTransform(ETS_PROJECTION, IdentityMatrix);
driver->drawIndexedTriangleList(vertices, 4, indices, 2);
driver->endScene();
//device->sleep(16);
// 5. Update window title with FPS
int fps = driver->getFPS();
if (fps != lastFPS)
{
stringw str = L"RTT + Shadows 8 FPS - FPS: ";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
device->drop();
return 0;
}https://github.com/JHRobotics/mesa9x
https://github.com/JHRobotics/mesa9x/releases/
https://github.com/JHRobotics/mesa9x/re ... vmpipe.zip
For use, only opengl32.dll is copied into the executable file.
Rendering by CPU is very useful when you have a computer that you have not changed the thermal paste, and it turns off for safety when it detects that the temperatures rise too high lmao, I had two computers with that problem (they turned off when I used the GPU a lot, before it didn't happen but after several years it happened).