This has been my hobby project for quite a few years now
It started as a small engine to serve as a sandbox to try out new 3d graphics ideas
After adding many features through out the years and re-writing the entire engine a few times, this is the latest state
It currently supports loading models with animations, textures, lights, shadow maps, normal maps, and some other goodies
I've also recently added voxel raymarching as an alternative renderer, along with a fun physics simulation :)
There is one instance where I saw you take a shared_ptr and then proceed to move from it. That could literally have been a unique_ptr and if you are making an assumption that that shared_ptr is still valid in another part of the codebase you at best now have UB but it is almost certainly a bug.
Just replace shared_ptr with unique_ptr, the advantage of managed languages is that you are in control of the memory, to then proceed to just use ref counting means you may as well have written the code in Java / C# since you incur all the same overhead of atomic references and don't get the advantages of a sophisticated GC.
unique_ptr can also trivially be upgraded to shared if you actually do need ref counting.
I do have quite a lot of shared_ptrs throughout the entire code base, that could be reduced
The main reason behind that is I wanted a lot of flexibility between the components and didn't want to end up centralising too much logic in a single one
For example: there is a resource_manager which is a shared pointer created in the game component, it's shared with the scene_renderer (it needs to use it to get the resource data), but I like to keep it also in the game component for loading new resources
of course I could have it owned by just the scene_renderer and access it from there avoiding the reference counting
but this was a design decision that I stand behind as it really helped with clearer separation between components, and the performance, well let's say that the reference counting isn't the bottleneck here as using the console for output is pretty slow
Also the moving of shared pointers is just an optimization to avoid increasing and then immediately decreasing the ref count, no UB there since its passed by value in the argument, so it gets copied before being moved :)
I saw that, that function is probably completely inlined by the optimiser as well so likely the move doesn't even happen there.
Just wanted to make sure since I know a lot of devs not super proficient in C++ just sticks a shared pointer on things to get around worries about ownership and don't concider the tradeoffs.
For me I concider using a unique_ptr a form of compile time check for how I'm thinking about the code. I find shared_ptr to be a smell. It's not necessarily wrong but probably needs some reasoning about.
Another note, I saw some comments somewhere about SIMD vectorisation that needs to be implemented. I would check whether the compiler isn't already doing that and if it isn't I would see about changing the code to make if possible for the optimiser to generate vectorised code.
I still haven't been at a PC so haven't been able to properly look through the code but it is nice to at least see modern C++ being used in a codebase
Also, speaking as a C++ game developer by profession, taking a shared_ptr copy and moving from it is a common way of saying "I will take a copy of this and keep it alive" whereas a shared_ptr ref only communicates that it can or might
Regarding "I will take a copy and ... keep it alive..." That is literally what the copy constructor of a sharedpointer does, you wouldn't need a move for that, the move literally just prevents the atomic increment.
I'm not saying there isn't a use case for shared_ptr, just that in this codebase at least in parts of it you could string replace with unique_ptr and get a free performance boost ( as trivial as it would be if the compiler hasn't already just done that ) because the atomic ref count is not needed.
OP did however clarify some design decisions on why the shared_ptr in some parts of the code and that is fine.
I stick by my statement. Stop using std::shared_ptr. Use std::unique_ptr and then convert to shared when it is actually necessary.
My point in C# was a mean spirited dig, but really though, you are giving up a ton of library features from C# by coding in C++ to then just treat it like C#. Write it in C# using the library that has already been optimised...
void Consol3Engine::RegisterFrameDrawer(std::shared_ptr<IFrameDrawer> frame_drawer)
{
frame_drawers.push_back(std::move(frame_drawer));
...
}In the calling code you then dereference that shared_ptr because it should still be valid but it isn't because it is now a moved from value.
I would in this specific case just replace shared_ptr with unique_ptr in the entire call stack up until here. If there isn't an implicit conversation here, put the conversation to shared in this function.
That makes it explicit ownership of the pointer is being moved into this function, it wasn't clear at all that that is happening with the shared_ptr.
EDIT:
You have to think of shared_ptr as global, if you moved from it ownership was changed but there is no way for everyone else holding a reference to thay shared pointer to know that.
make
[ 1%] Building CXX object CMakeFiles/Consol3_raster.dir/src/Consol3.cpp.o
In file included from /home/john/Consol3/src/Consol3.cpp:23:
/home/john/Consol3/src/Engine/Input/LinuxInputManager.hpp:8:10: fatal error: linux/input.h: No such file or directory
8 | #include <linux/input.h>
| ^~~~~~~~~~~~~~~
compilation terminated.
Oh, well ;)I've compiled it in Windows and Linux and didn't have any issues
In this case he's probably missing linux-headers for his kernel version?
If people download your project and the build fails, it's very, very, very likely that they'll give up right away. You have to be very, very interested in a project to be bothered to research and install undocumented dependencies and their versions and then fight the build.
Recently, I've personally and professionally been going between TUI and deep 3D (mesh shading FTW) so this was fun to see. Your work is inspiring me to think about how to apply those principles to my own work. There have been other ascii renders but I've not seen anything quite like Consol3.
I made a half-baked PR with some initial Mac support [1]. I don't have time this weekend but I'll pull on it later -- or maybe somebody here with more low-level Mac skills than me can take a look?
Depends on the scene and the CPU of course, typically I get between 20-60 FPS on a small scene with a floor and a few models, but features like shadow maps or normal maps bring the performance down a lot though
The scenes themselves don't have a lot of triangles, for the first scene with marvin and the car in the rasterization video there's 7427 triangles
There is no parallelization whatsoever in the engine at the moment, so I feel like it could get much faster if I multi thread it, or use SIMD vectors
Another aspect that influences the performance a lot is the output mode being used, for example the TextOnly mode is a lot faster than others since it uses no colors at all, the full RGB color mode has to have an escape sequence prepended to each character "pixel" so its quite slow
It also depends on the frame disposition itself, the Windows console does some attribute caching when its rendering continuous character rows, so if a frame has a lot of different colors horizontally, it will be slightly slower than if it didn't
Textualize/Frogmouth has a TUI tree control: https://github.com/Textualize/frogmouth
FWICS browsh supports WebGL over SSH/MoSH https://www.brow.sh/docs/introduction/ :
> The terminal client updates and renders in realtime so that, for instance, you can watch videos. It uses the UTF-8 half-block trick () to get 2 colours from every character cell, thus simulating basic graphics.
https://github.com/fathyb/carbonyl :
> Carbonyl originally started as html2svg and is now the runtime behind it.
Always wondered how brew.sh added the brew sprite there; that's real nice.
TIL that e.g. Kitty term can basically framebuffer modified Chrome?
https://github.com/chase/awrit :
> Yep, actual Chromium being rendered in your favorite terminal that supports the Kitty terminal graphics protocol.
FWIW Cloudflare has clientless Remote Browser Isolation that also splits the browser at the rendering engine.
A TUI Manim renderer would be neat. Re: Teaching math with Manim and interactive 3d: https://github.com/bernhard-42/jupyter-cadquery/issues/99
What would you add to make it easier to teach with this entirely CPU + software rendering codebase?
What prompts for learning would you suggest?
- Pixar in a Box, Wikipedia history of CG industry,: https://westurner.github.io/hnlog/#comment-36265807
- "Rotate a wireframe cube or the camera perspective with just 2d pixels to paint to; And then rotate the cube about a point other than the origin, and then move the camera while the cube is rotating"
- OTOH, ManimML, Yellowbrick, and the ThreeJS Wave/Particle simulator might be neat with a slow terminal framebuffer too
Well done! We need more textmode out there!