From c643f5e92d8f1f69b3df743b18a4791a5ad45dd5 Mon Sep 17 00:00:00 2001 From: James Packer Date: Tue, 13 Oct 2026 08:59:28 +0100 Subject: [PATCH 1/4] Allow 3d rendering to be split, if user has a single window spanning multiple physical screens arranged around the user. --- bin/bc5.ini | 2 ++ src/SimulationModel.cpp | 5 +++ src/SimulationModel.hpp | 2 ++ src/main.cpp | 72 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/bin/bc5.ini b/bin/bc5.ini index 6b84835e..b9bdf940 100644 --- a/bin/bc5.ini +++ b/bin/bc5.ini @@ -36,6 +36,8 @@ disable_shaders_DESC=Default of 0 to simulate a more realistic water surface, or anti_alias=4 view_angle=90 view_angle_DESC=The angle of view in degrees +split_views=1 +split_views_DESC=How many sections to split the view into (useful for panoramic view on a very wide screen) look_angle=0 look_angle_DESC=The initial view angle (from the ships head) minimum_distance=0.05 diff --git a/src/SimulationModel.cpp b/src/SimulationModel.cpp index 96393f9f..eda1674b 100644 --- a/src/SimulationModel.cpp +++ b/src/SimulationModel.cpp @@ -1838,6 +1838,11 @@ SimulationModel::~SimulationModel() camera.update(0, quat, pos, lensShift, true); } + void SimulationModel::updateCamera() + { + camera.update(0); + } + void SimulationModel::update() { diff --git a/src/SimulationModel.hpp b/src/SimulationModel.hpp index 31c2fe9d..3f5cf119 100644 --- a/src/SimulationModel.hpp +++ b/src/SimulationModel.hpp @@ -373,6 +373,8 @@ class SimulationModel //Start of the 'Model' part of MVC Lines* getLines(); // Get pointer to lines object void updateCameraVRPos(irr::core::quaternion quat, irr::core::vector3df pos, irr::core::vector2df lensShift); + + void updateCamera(); // Update camera view point without updating anything else void update(); diff --git a/src/main.cpp b/src/main.cpp index 646872c0..496c674f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -540,6 +540,13 @@ int main(int argc, char ** argv) viewAngle = 90; } + irr::u32 splitViews = IniFile::iniFileTou32(iniFilename, "split_views"); //How many cameras to split the view across + if (splitViews < 1) { + splitViews = 1; + } + + viewAngle = viewAngle / (irr::f32)splitViews; + bool viewOwnShip = (IniFile::iniFileTou32(iniFilename, "own_ship_hidden") != 1); irr::f32 cameraMinDistance = IniFile::iniFileTof32(iniFilename, "minimum_distance"); @@ -809,8 +816,8 @@ int main(int argc, char ** argv) graphicsHeight = sh; irr::u32 graphicsWidth3d = su; irr::u32 graphicsHeight3d = sh * VIEW_PROPORTION_3D; - irr::f32 aspect = (irr::f32)su / (irr::f32)sh; - irr::f32 aspect3d = (irr::f32)graphicsWidth3d / (irr::f32)graphicsHeight3d; + irr::f32 aspect = (irr::f32)su / (irr::f32)sh / (irr::f32)splitViews; + irr::f32 aspect3d = (irr::f32)graphicsWidth3d / (irr::f32)graphicsHeight3d / (irr::f32)splitViews; std::cout << "graphicsWidth: "<< graphicsWidth << " graphicsHeight: " << graphicsHeight << std::endl; @@ -1222,17 +1229,66 @@ int main(int argc, char ** argv) // Normal rendering if (!fullScreenRadar) { + + // Count view increment so it can be reset + int viewIncrement = 0; + + // Set initial angle for camera if we're splitting the rendering + int centreCamera = 0; + + int renderWindowWidthPx; + int renderWindowHeightPx; + irr::f32 renderWindowAspectRatio; if (guiMain.getShowInterface()) { - driver->setViewPort(irr::core::rect(0, 0, graphicsWidth3d, graphicsHeight3d)); - model.updateViewport(aspect3d); + // 2d view shown + renderWindowWidthPx = graphicsWidth3d; + renderWindowHeightPx = graphicsHeight3d; + renderWindowAspectRatio = aspect3d; // Already includes split effect if needed } else { - driver->setViewPort(irr::core::rect(0, 0, graphicsWidth, graphicsHeight)); - model.updateViewport(aspect); + // Just 3d view, use the whole screen + renderWindowWidthPx = graphicsWidth; + renderWindowHeightPx = graphicsHeight; + renderWindowAspectRatio = aspect; // Already includes split effect if needed + } + + if (splitViews > 1) { + // TODO: We could make the centre camera a contollable parameter for the user? + centreCamera = floor((irr::f32)splitViews / 2.0); + for (int cameraSplitStep = 0; cameraSplitStep < centreCamera; cameraSplitStep++) { + model.lookStepLeft(); + viewIncrement -= 1; + } + model.updateCamera(); + + renderWindowWidthPx = renderWindowWidthPx / splitViews; + } + + + // Set the right aspect ratio (may be changed by radar/VR render) + model.updateViewport(renderWindowAspectRatio); + + for (int cameraSplitStep = 0; cameraSplitStep < splitViews; cameraSplitStep++) + { + driver->setViewPort(irr::core::rect(cameraSplitStep * renderWindowWidthPx, 0, (cameraSplitStep + 1) * renderWindowWidthPx, renderWindowHeightPx)); + if (guiMain.getShow3d()) { + smgr->drawAll(); + } + + if (cameraSplitStep < splitViews - 1) { + // Don't need to do this if on the last step + model.lookStepRight(); // TODO: Try using quaternion method like for VR instead? + viewIncrement += 1; + model.updateCamera(); + } } - if (guiMain.getShow3d()) { - smgr->drawAll(); + // Reset camera if needed + if (splitViews > 1) { + for (int cameraSplitStep = 0; cameraSplitStep < viewIncrement; cameraSplitStep++) { + model.lookStepLeft(); + } + model.updateCamera(); } } From 9e30616799f26c0bcf2ba8ef1ddf28e149981270 Mon Sep 17 00:00:00 2001 From: James Packer Date: Tue, 13 Oct 2026 18:09:48 +0100 Subject: [PATCH 2/4] Change approach to using quaternions for camera angle offset --- src/SimulationModel.cpp | 9 +++++++-- src/SimulationModel.hpp | 3 ++- src/main.cpp | 21 +++++++++++---------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/SimulationModel.cpp b/src/SimulationModel.cpp index eda1674b..7aa605fb 100644 --- a/src/SimulationModel.cpp +++ b/src/SimulationModel.cpp @@ -1437,6 +1437,11 @@ SimulationModel::~SimulationModel() setZoom(zoomOn); } + irr::f32 SimulationModel::getZoom() const + { + return currentZoom; + } + void SimulationModel::setViewAngle(irr::f32 viewAngle) { modelParameters.viewAngle = viewAngle; @@ -1838,9 +1843,9 @@ SimulationModel::~SimulationModel() camera.update(0, quat, pos, lensShift, true); } - void SimulationModel::updateCamera() + void SimulationModel::updateCamera(irr::core::quaternion quat) { - camera.update(0); + camera.update(0, quat); } void SimulationModel::update() diff --git a/src/SimulationModel.hpp b/src/SimulationModel.hpp index 3f5cf119..ab410752 100644 --- a/src/SimulationModel.hpp +++ b/src/SimulationModel.hpp @@ -303,6 +303,7 @@ class SimulationModel //Start of the 'Model' part of MVC void setMouseDown(bool isMouseDown); void setZoom(bool zoomOn); void setZoom(bool zoomOn, irr::f32 zoomLevel); + irr::f32 getZoom() const; void setViewAngle(irr::f32 viewAngle); irr::u32 getLoopNumber() const; std::string getSerialisedScenario() const; @@ -374,7 +375,7 @@ class SimulationModel //Start of the 'Model' part of MVC void updateCameraVRPos(irr::core::quaternion quat, irr::core::vector3df pos, irr::core::vector2df lensShift); - void updateCamera(); // Update camera view point without updating anything else + void updateCamera(irr::core::quaternion quat); // Update camera view without updating anything else void update(); diff --git a/src/main.cpp b/src/main.cpp index 496c674f..142a4a57 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1239,6 +1239,10 @@ int main(int argc, char ** argv) int renderWindowWidthPx; int renderWindowHeightPx; irr::f32 renderWindowAspectRatio; + + irr::core::quaternion quat; + irr::f32 viewAngleWithZoom = viewAngle / model.getZoom(); + if (guiMain.getShowInterface()) { // 2d view shown renderWindowWidthPx = graphicsWidth3d; @@ -1256,11 +1260,10 @@ int main(int argc, char ** argv) // TODO: We could make the centre camera a contollable parameter for the user? centreCamera = floor((irr::f32)splitViews / 2.0); for (int cameraSplitStep = 0; cameraSplitStep < centreCamera; cameraSplitStep++) { - model.lookStepLeft(); viewIncrement -= 1; } - model.updateCamera(); - + quat.fromAngleAxis(viewIncrement * viewAngleWithZoom * irr::core::DEGTORAD, irr::core::vector3df(0, 1, 0)); + model.updateCamera(quat); renderWindowWidthPx = renderWindowWidthPx / splitViews; } @@ -1277,18 +1280,16 @@ int main(int argc, char ** argv) if (cameraSplitStep < splitViews - 1) { // Don't need to do this if on the last step - model.lookStepRight(); // TODO: Try using quaternion method like for VR instead? viewIncrement += 1; - model.updateCamera(); - } + quat.fromAngleAxis(viewIncrement * viewAngleWithZoom * irr::core::DEGTORAD, irr::core::vector3df(0, 1, 0)); + model.updateCamera(quat); + } } // Reset camera if needed if (splitViews > 1) { - for (int cameraSplitStep = 0; cameraSplitStep < viewIncrement; cameraSplitStep++) { - model.lookStepLeft(); - } - model.updateCamera(); + quat.fromAngleAxis(0, irr::core::vector3df(0, 1, 0)); + model.updateCamera(quat); } } From f4f5d01829e95f1891d7f1c68eebc49e3b54b241 Mon Sep 17 00:00:00 2001 From: James Packer Date: Tue, 13 Oct 2026 18:36:34 +0100 Subject: [PATCH 3/4] Simplify and allow user to choose which screen is the 'front' one. --- bin/bc5.ini | 2 ++ src/main.cpp | 30 ++++++++---------------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/bin/bc5.ini b/bin/bc5.ini index b9bdf940..317f0614 100644 --- a/bin/bc5.ini +++ b/bin/bc5.ini @@ -38,6 +38,8 @@ view_angle=90 view_angle_DESC=The angle of view in degrees split_views=1 split_views_DESC=How many sections to split the view into (useful for panoramic view on a very wide screen) +centre_view=1 +centre_view_DESC=If split views is used, which screen should be the one looking ahead. Can be fractional. look_angle=0 look_angle_DESC=The initial view angle (from the ships head) minimum_distance=0.05 diff --git a/src/main.cpp b/src/main.cpp index 142a4a57..08a6c48b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -545,6 +545,9 @@ int main(int argc, char ** argv) splitViews = 1; } + irr::f32 centreView = IniFile::iniFileTof32(iniFilename, "centre_view", 1.0); //Which screen should be centred, can be fractional + centreView = centreView - 1; // Indexing from 1 for users, from 0 internally + viewAngle = viewAngle / (irr::f32)splitViews; bool viewOwnShip = (IniFile::iniFileTou32(iniFilename, "own_ship_hidden") != 1); @@ -1230,9 +1233,6 @@ int main(int argc, char ** argv) // Normal rendering if (!fullScreenRadar) { - // Count view increment so it can be reset - int viewIncrement = 0; - // Set initial angle for camera if we're splitting the rendering int centreCamera = 0; @@ -1257,16 +1257,8 @@ int main(int argc, char ** argv) } if (splitViews > 1) { - // TODO: We could make the centre camera a contollable parameter for the user? - centreCamera = floor((irr::f32)splitViews / 2.0); - for (int cameraSplitStep = 0; cameraSplitStep < centreCamera; cameraSplitStep++) { - viewIncrement -= 1; - } - quat.fromAngleAxis(viewIncrement * viewAngleWithZoom * irr::core::DEGTORAD, irr::core::vector3df(0, 1, 0)); - model.updateCamera(quat); renderWindowWidthPx = renderWindowWidthPx / splitViews; } - // Set the right aspect ratio (may be changed by radar/VR render) model.updateViewport(renderWindowAspectRatio); @@ -1274,23 +1266,17 @@ int main(int argc, char ** argv) for (int cameraSplitStep = 0; cameraSplitStep < splitViews; cameraSplitStep++) { driver->setViewPort(irr::core::rect(cameraSplitStep * renderWindowWidthPx, 0, (cameraSplitStep + 1) * renderWindowWidthPx, renderWindowHeightPx)); + if (guiMain.getShow3d()) { - smgr->drawAll(); - } - - if (cameraSplitStep < splitViews - 1) { - // Don't need to do this if on the last step - viewIncrement += 1; - quat.fromAngleAxis(viewIncrement * viewAngleWithZoom * irr::core::DEGTORAD, irr::core::vector3df(0, 1, 0)); + quat.fromAngleAxis((cameraSplitStep - centreView)* viewAngleWithZoom* irr::core::DEGTORAD, irr::core::vector3df(0, 1, 0)); model.updateCamera(quat); + smgr->drawAll(); } } // Reset camera if needed - if (splitViews > 1) { - quat.fromAngleAxis(0, irr::core::vector3df(0, 1, 0)); - model.updateCamera(quat); - } + quat.fromAngleAxis(0, irr::core::vector3df(0, 1, 0)); + model.updateCamera(quat); } if (vr3dMode && vrSuccess == 0) { From f66fc3fad375e1ac5194a49a12f5ef9b8abf9839 Mon Sep 17 00:00:00 2001 From: James Packer Date: Tue, 13 Oct 2026 18:45:47 +0100 Subject: [PATCH 4/4] Remove unused variable. --- src/main.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 08a6c48b..be0458cf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1233,9 +1233,6 @@ int main(int argc, char ** argv) // Normal rendering if (!fullScreenRadar) { - // Set initial angle for camera if we're splitting the rendering - int centreCamera = 0; - int renderWindowWidthPx; int renderWindowHeightPx; irr::f32 renderWindowAspectRatio;