Hope you enjoyed the video! Don't forget that the first 1,000 people to use this link will get a 1 month free trial of Skillshare: skl.sh/thecherno08211
Man can you one day ract to the playstation 5 teardown? because now than the SSD M2 NVME PCIe 4.0 came out to the market thats makes you choose well you M2 SSD CARD, and which one are you going to choose
Really appreciated this video, I never use c++ myself so I usually have to ignore some of the things I don't understand (assuming any) but C is so easy to understand which is why I love using it. As a side note I recently realised that the usual "do { CODE; } while (0)" trick that is used in macros would be better done by a simple "if ( 1 ) { CODE; }" which is easier for the compiler to understand and optimise regardless of how old the compiler is.
I loved the video. While there was lots of valid criticism, it did kind of seem more of a "review of how the code works" than an actual code review. Would be cool if you highlighted issues with the code and gave specific examples of why it's bad code and how to fix it.
I spotted a few bugs in your code: - when pressing L, % will not ensure that you won't go into the negatives (except if your value is unsigned)! (-1) & 5 is -1 - program.c, line 95: you probably meant + size.x, not + size.y. But it looks like you aren't really using that value anyways 😄 this splitting of work can be done easier by start = (i*width)/numberOfThreads, end = ((i+1)*width)/numberOfThreads. This is also less error prone (if you have numbers, where numberOfThreads * width < 2^31-1, if you're using 32 bit signed integers). Your work is then for(int x=start;x
Pthreads take a void* parameter that has to be casted in the function in whatever you passed in the pthread create. An old way to substitute variadic arguments. I enjoyed this so much! Keep it up
Still not a voxel engine, even all these decades later - it's rendering height fields. Seems like rotating the texture 90 degrees would increase cache performance with no added complexity.
Pretty sure many old programs did just that and flipped it back for display. For ray casting I remember that some rendered one column at time from near to far. This way you jumped to next pixel and started at same distance. (Have no idea if it was optimal though..)
@@pottuvoi2 Not entirely sure what you're considering "old", but in the context of the program presented, the 7 worker threads are writing column data into a texture, and then that texture is slapped onto the screen using OpenGL, while the original VoxelSpace (tm, sic) code would have been drawing straight to the frame buffer, which means drawing vertical lines means trashing your cache, as the frame buffer is row-major (rows are contiguous in memory). Since we're drawing to a texture, we can rotate the texture such that vertical lines go into rows of the texture, and thus contiguous parts of memory, and better cache performance. We then upload the texture to the graphics card (which is slow, regardless of orientation) and then render the "frame buffer" fullscreen quad.
@@DaveLeCompte Was thinking DOS era from 486 to early Pentium, when we had the usual voxel landscapes and tunnels. (Tunnel being the classic landscape render to array and then make it to tunnel with precalculated indirection UV map.) Sadly I do not remember if we did the flip when writing from array to framebuffer, but I know some did. There were fun tricks people did during those times, like framerate independent screen flashes by changing palette every refresh.
@@DaveLeCompte The "big days" of voxel space and related engines were the mid-1990s, where caching wasn't very important; drawing to the frame buffer meant a major bottleneck no matter which way you turned it. The first game with one was of course Comanche, and there were others: _Terra Nova: Centauri_ IIRC, _The Outcast_ and as one of the most recent, _Thunder Brigade,_ still in the 1990s. All of these could run well on a 300-MHz PII, if not always on max settings. Numerous demos had their height field segments, among them of course Future Crew with their uber-demo _Second Reality,_ but also the year before that with either _Unreal_ (unrealated to the game) or _PANIC!_ . Others followed suit, like the well acclaimed _dope_ in 1995 and the less known _Optic Nerve_ which at least IMO looked best. Since then, "voxxels" have appeared even on home computers like the Amiga 500, the SNES IIRC, and the ubiquitous C64. They're usually quite coarse and/or cheat with geometry (not allowing for rotation, distorting the terrain significantly, or otherwise providing a view that's very restricted or visibly unrealistic), but all of them check the "something that model was never meant to do" box.
I'm fairly sure that to run it, the intention was to run it from the same location as make was called. Something like ./bin/appname.bin. - i am assuming this, since the assets folder was up a level, and that the directory bash is at, becomes your working_dir for the process. Anyway, happy to see a c99 Linux review. It is the primary environment in which I code :)
I also programmed something like this years ago. you have to draw vertical bars of a certain height from back to front. if I remember correctly the technology is called voxel spacing
I did this as well, right after university. The idea was to have a futuristic city that mechs could fight on. I ended up abandoning it after getting obsessed with not wanting to draw fully occluded bars and wanting smooth rotations of the map. (Just imagine if I had put off optimization until the end.)
> draw vertical bars of a certain height from back to front That works, but slowly. I did the opposite and drew them _front to back._ For every pixel column on screen, I kept track of the y coordinate of the top pixel so far. I then computed the y coordinate of the pixel in question in 3D projection (easy if you don't tilt the camera up/down), and the x coordinate of 0.625 times the width to the left/right (fudge factor may vary). Then I worked one column at a time, skipping the column if the y coordinate is higher (i.e. the pixel is lower) than the terrain that's already been drawn. If it's not, you fill the difference in and update the y coordinate. The major hassle is the traversal of the array depending on what way you're looking. You want to do it line by line (a "line" being the direction where the z (depth) coordinate increases slowly) in order of increasing z, and within each line, you do it in order of increasing z too. That ensures proper overlap resolution. "The" _Voxel Space_ algorithm did it differently; it basically cast a ray (or a vertical stack of rays) and checked which terrain squares it flew through/over, generating one column of pixels per line of y values (rarely a literal line inside the array of y values, but a slightly zig-zagged path). It plays nice with cache and skips the x coordinate calculations, since only the relevant squares are hit by the ray cast, but it hits the closest squares repeatedly -- which isn't THAT bad, since the total # of intersection checks is still bound by (# of pixel columns) * (draw distance in square diameter) * (fudge factor close to 1) per frame.
Ah, the old Novalogic terrain rendering. It struck me while watching that you could just do the vertical span drawing as horizontal span and simply render the final quad to OpenGL with the texture coordinates flipped to draw it 90 degrees rotated. Then avoid the vertical span cache miss. Either way, it's a fun render method. Pretty sure I've seen one of these as a shader on shadertoy.
If algorithm require to render lines from bottom to top, then you can still make it CPU Cache friendly. Just render it on CPU with switched x and y, or rotated by 90 deg. And then change OpenGL quad texture coordinates. And remember to create texture with switched width and height.
I remember LOVING to play around in Ken Silverman's Voxlap Demo about 20 years ago. It didn't look that impressive at first sight, but the full destructibility of the whole environment was absolutely mindblowing (and seemed like something from the far away future at the time). There was that hidden rocket launcher that basically allowed you to freely blast your way through the entire voxel map. It was just a little tech demo - but it was so awesome because it did stuff I hadn't seen ANYWHERE before - and, to be honest, have never seen since! Can you imagine a voxel engine based game in the vein of "Zone of the Enders" in which EVERY shot you take and every enemy you down does realistic damage to a fully destructible environment? It would allow for some really transformative gameplay and immersion!
That's how I felt about the first 3d artillery games that tracked terrain damage (I think "Blast Doors"). It was a very simple predecessor of Scorch3D, which is worth checking out, too (free). > it's a blast Very _punny_
Agreed. I love voxels for seemingly no reason. I would love to see something other than cubes at some point, such as tetrahedral voxels. With only 4 faces in total and all acute angles they could be quite interesting to model with.
You can blame Nova Logic for that one; they trademarked it under the name "Voxel Space." And if you were in high school when Comanche came out, you probably had someone in your class who tried coding that stuff himself -- or _you_ were that one. And more than one who claimed they did it on an Amiga / C64. The sort of guy who never invited anybody -- for Reasons(tm).
You could say they are voxels and render them as such too if you wanted. It’s just a rendering optimization that makes them feel like they’re not voxels, but nothing in the data says they’re not.
Personally I wouldn't mind if you went even more in depth, I like getting to know how something works in a rather detailed manner, but I wouldn't be surprised if that's too much to ask for in a code review video. Still lovely video though 👍
Exactly. I think there's a youtuber called javidx who writes entire games on the console -- _without_ shader code. When he mentioned shader code, I thought "the WAT now?" because I've written something like that (well, let's say 50% of that) in both DOS and Windows. The DOS version handled a few special cases like terrain exceeding the top pixel line (which the demo shown here at 07:36 does NOT, or at least doesn't demonstrate) or the player submerging in water. The Windows version was simpler, basically what we can see here minus the coloring that's very close to Comanche, and it couldn't view the terrain at an angle either. I got 15fps in DOS in the 320x200 mode on a 486-50 (although I used the ultra-coarse mode, which only drew 80x200 pixels - Comanche could switch between that and 160x200 and was a bit faster). The Windows version could go full 640x480 with similar performance on an entry-level Athlon. Both 100% in CPU, no shaders added, or any 3d hardware for that matter. So, to someone who tried to recreate height field (or "voxel space") engines, that felt a bit underwhelming, like "You did 20 in a Model T? lel n00b, I did twice that in a Ferrari!" But not to belittle OP, *making that 3D engine was a solid piece of work,* even if "120fps at full HD" feels rather mediocre to me if done with a shader (which IS a dependency if you think about it). I'd like to see _more_ of that engine. For comparison, the DOS version had a 256x256 map, a primitive HUD of sorts, covered the top half of the screen with landscape and part of the bottom half with a minimap; I wrote it in Turbo Pascal, then the most popular choice among amateurs. The Windows version was written in Visual C++ 6 and far more primitive overall (no causality here), but used real geometry, indistinguishable from a triangle mesh, not the coarse approximation of the DOS graphics. It looks like I lost both sources over time, so there's only my word that remains, and memories of evenings spent to code that thing, test it, and reboot my PC countless times because I wrote the inner loops in Assembler.
25:14 Also when it constructs the whole image it does the sampling in the infamously suboptimal for(x){for(y){}} instead of for(y){for(x){}} if you're storing row after row instead of column after column. If the algorithm needs to deal with columns then why not just store the image in memory transposed (i.e. 90 degrees rotated)? Then you can do the "rotation" just in the fragment shader.
Not sure how the work the shaders do compares to the output bandwidth... Let's play devil's advocate and say that a ray crosses N boundary before hitting the landscape. That's N reads from the height map per pixel written. This doesn't look like it would affect performance a lot, unless you're directly facing a cliff or something -- and in this case, where N is unusually small, the FPS would be at its highest. It could well be that poor pixel generation order isn't affecting FPS for any sensible FPS value, but only the theoretical _max_ FPS in an abnormal setup. That might occur if the player character has crashed and even act as an unintended FPS limiter now that I think about it...
This was so enjoyable to watch. I’m a beginner programmer and I managed to understand At least 30 percent of this. Feels good. The rest was just super interesting. Developing in mainstream game engines everything is so abstracted. It’s cool seeing some of this low level stuff on a more contained project
OS shaming is terrible. Also with my colleagues I pretty much cannot mention that or the fact I am coding with C++, they only accept Rust and everything else is an idiot to them.
he is so pro he can read the code like an easy thing... props to yan for me is 1 month of figuring out functions and sintax. Then another month for the logic if its working properly. lol he fixed it in less than 5min
As game developer you spend 99% of your time READING code and 1% WRITING code. The best way to get good at reading code is to ... you guessed it ... read code.
I adore this channel! I have been coding C/C++ of and on for 31 years now. And I still learn new things from “The Cherno”! And Linux is the only way to go :)
Yeah especially on Windows, the CreateThread function is very expensive, I'm shocked that he was getting over a 100 fps and rendering the entire scene in about 1 second whilst spawning 7 new threads (and presumably destroying the old ones otherwise he'd run out of ram) every frame. Maybe pthread_create is cheaper ?!?
That's on Windows. I'm pretty sure Linux actually uses an underlying thread pool or some other funny optimization like that, so there is not much overhead from creating and destroying threads repeatedly. Keep in mind there were ~1000 threads being created and destroyed per second *while they were also doing the rendering work!*
@@chlorobyte_projects I'm sure Linux does not do that. Yes, the spawning of threads is a lot cheaper than on windows, but threadpools are common on Linux as well.
I think the biggest issue with this code is all the passing by value. They copy the Sprite struct and the ImageData struct (which includes their framebuffer so it's a pretty heavy copy) like 4 or 5 times per frame. 25:15 you can see all the functions that operate on Sprites take values, not pointers, as arguments.
I wouldn't mind longer videos in this series. Love them! Analyzing source code is always so interesting. I believe if you CTRL+Click on a symbol in VSCode, it should take you to the definition.
There a actually 2 ways you can realize a (retro voxelspace) Voxel Engine: 1. As a 2D Raycaster (like wolfenstein) 2. As a plane that gets transformed to screen (like mode7 on SNES) The latter method produces the shimmering effects when moving or turning. The first method is preferable for that reason (and was used by novalogics voxelspace engine back then)
Yeah the algorithm really has to draw vertical slices, you can't do it horizontally. What I would do is draw the image transposed and then just un-transpose when drawing the quad. The algorithm was used in early 3D terrain rendering because it's super simple - you project each column on screen to a line on the heightmap texture, read pixels one by one from nearest to farthest and keep track of the current height. If the pixel to be drawn is lower than the current height, skip it; if it's equal or higher - update the height and draw the pixel. There's a bit more math involved to get a correct perspective, but in essence you get a 3D terrain for not much more computation than Brezenham's line drawing algorithm.
Storing the data column major would do the trick (as is preferred by many linear algebra libs, e.g. Armadillo, BLAS/LAPACK, Eigen)... I'm saying the same thing really but we can do it as the data is loaded and just once if we don't load with the image lib.
i love this series. thanks for putting it together. i'm mainly a JS/PHP dev, but i love seeing these breakdowns of C,C++,C# etc. great work! make them as long as you like!
15:59 Passing by value is considered good practice in modern C as structs in C are pure PODs that contains primitive stack values. It's basically the same as you would pass long list of ints and pointers in arguments but packed in one struct. Better to do it that rather than introducing another level of indirection and dereferencing it all the time. In C++ it's not that obvious as structs are classes in disguise which consist whole bunch of implicit members.
@@superscatboy There is a copy of primitive values from stack, including pointer. But copying those is very very cheap, even in C++ it's not advised to pass ints by reference right? You just do void func(int x, int y) not void func(const int &x, const int &y). Now, since in C structs are pure POD and nothing else, compilers have easy time optimising those as long as you won't create oversized buffer on stack. So if you have such struct in C struct Point {int x, int y}; then it's better to pass it to function by copy void func(Point point) cause all what happens you copy bunch of primitives void func(int x, int y) which is cheap. If you pass it by pointer then you're introducing indirection level which is never free and you pay for it every time you dereference, for simple copy you pay only once.
13:18 Hazarding a guess as to why he is copying the strings rather than just the pointers, maybe it is because this is a demo of a library that, when used for real, may be dynamically loading and unloading files at runtime? So I would guess he wants the lifetime of the char buffer to be coupled with the lifetime of the containing object. But then he has named the type of the object Program, so that doesn't make any sense as surely there would only be one Program object. Bah, I give up... lol
You've been infected with this terrible virus. It's silly to make a program way more complex than it needs to be, to not only be able, but literally be forced to test it. Just think your programs through, use proper design patterns, and use tests on specific parts, where you can't be certain something weird won't happen. Most of the time it's way more useful to test complete use cases, where having parameters is irrelevant. Unit tests are so over rated.
@@TheAxeForgetsTheTreeRemembers I keep bumping into code which was well thought through, done with proper design patterns, and tested over specific parts, having ton of bugs... it's like pattern, I just look for values/configurations not tested, and unearth subtle off-by-one or doesn't-expect-zero or didn't-expect-that-previous-thing-ended-in-that-second-state I did recently receive 3 line change patch for one open project with comment "I did test it manually, no need for unit tests, it's very simple change" ... and yes, there was bug in it, not accounting for one type of input data. (and I did knew before I released the SW, because I actually do have unit tests for that particular function, so I knew 5 seconds after applying the patch that it did break something it should not have).
@@theoneandonly1833 but this "globals are bad" is kinda overdone, global variables make sense sometimes. But the implementing code should probably take some initial pointers, even to globals, to cater for both easy testing, and not hiding globals behind fancy names like singletons and similar.
@@ped7g A program is likely to have bugs, tested or not. There are plenty of softwares out there that are well tested but still have bugs. Some of them being critical, yet completely overlooked by improperly thought out test suites.
Delta Force is also a common game referenced as using the Voxel Space engine. Looking back in 2023, it retained some of the problems mentioned in the video, including shimmering. Delta Force 2 was a little more visually appealing due to engine improvements. Guessing the issues are why NovaLogic switched to more a hybrid voxelspace / 3d mesh engine for later titles, where terrains were visually improved / optimized, but still VoxelsSpace-rendered, where as objects were 3d models (to avoid the shimmering + sharp looking objects you see in DF1 and DF2). They kept the hybrid approach until their last games (which weren’t well received). This is such an interesting concept, though. However, you can see why it’s no longer used, even not considering how video cards are optimized for vertex rendering now.
he meant, that the actual scene that you are seeing, is not actual 3D space geometry, but rather texture projected onto a plane. What the difference is here, is that the engine has no definition of 3D model, as objects that you put on the scene and render collectively with each other, but rather 2D information of all objects on the scene, projected directly onto that plane. You can imagine that difference quite easily: Lets say basic today engine as Unreal, or Unity, treats 3D space as you would treat real life diorama made out of plastics. You put there objects, glue them onto the different surfaces, as you would play with toys as a kid. This engine takes a couple of photos from above of your playground. One for color and shadows, second for depth (this here is a height map). Now when you have that information, you go take a real life wire plane, bend it as height depth photo tells you. Then take the color photo print it on some cloth, and just throw that cloth on the bended wire. Now thats the retro voxel engine representation of more complicated, multi object diorama, with use of only one bended object with flat surface on top of it.
so the real difference is that retro way, whole scene is represented by simplified 2D space geometry information, when most of the engines now represent 3D multi object space geometry, in which every object information is stored as 3D vertex coordinates table by default
6:32 - I reckon there's an error with order of operations on line 30. Inconsequential because sizeof(char) is 1, so you're getting fileSize + 1 * 1. Definitely worth using parenthesis to ensure its (count+1) * sizeof(..) and avoid hard to debug errors. On this topic, though, sizeof(char) is guaranteed to be 1 and kind of redundant.
Couple of point's from me. He's casting his malloc results, which you shouldn't do as void* is implicitly and safely promoted to required type. Also, it can hide an error where you forgot to include stdlib.h. Second, there's loads of variables that could be const. IMO, it's a good habit to have.
@@chlorobyte_projects don't know about that, on arch I had to make some changes to the bios configuration because of the processor, gfx card and motherboard, fortunately there was someone having the same problem in the arch forum. The best thing was that he had the same processor, gfx card and mother board. The final solution was just to add some flags to the init command of the kernel, something like amd_soft and another flag
He DEFINITELY needs to split the threads in columns. Rows would be terrible. The vertical "behavior" of the algorithm is how foreground points hide background points. A massive gain in performance could probably be made by rotating the height and color images by 90 degrees. Render the images "sideways" and then rotate the final image back 90 degrees the other way to render. Maybe the GPU could do this second rotation. In this way the threads could run on horizontally ordered memory. The source data for each pixel is still collected from a "randomish" position based on the direction the player is facing. BTW I've never seen this algorithm before, but it is very cool how this 3D stuff was discovered in the good old days!
let me just quickly tell you: your videos are definitely not too long. i have ADD so i know if something is too long. but you make valuable content so it can have this length
Passing this as argument a lot is actually C++ programmers thing. And generally OOP programmers. C++ and other OOP languages just hide it from programmer. When you do object->foo(); C++ under the hood is actually passing &object to object->foo function as argument. And that's why you can access this inside methods. Also since paths are like "assets/snow-watery-height.png" they are relative to working directory. So at first it wasn't able to find them because you did "cd bin". I think.
I would actually also make Program this a global variable. But that is considered a bad practice by OOP gurus, uni professors, corporations managers, and other people that have never written a real application.
Hi cherno this is my first comment on your channel. I follow all your game engine series and CPP series. Request you to provide some info on logging and memory debugging, profiling and optimisation techniques which can be used for 3D n 2D games
Using Linux every day 8h and Windows about 3h, I have to say that Linux is just better than Windows. It's lightweight and just works, not like Windows 🤷🏼♂️ Edit: I think I have to add that this is just my opinion. As already people said, it depends on what you are doing. E.g. gaming isnt really great on Linux, but for me Linux is just better as a fairly low level developer. I have way too many bugs and blue screens in Windows and it is just packed full of stuff that is running in the background that I don't need, but I still have a dual boot setup. (And if there's one thing I hate, then it's the Windows API) I use Windows for gaming and yeah, if I develope applications for Windows. For everything else, be it server development, reverse engineering and generally low level stuff, I use Linux. For me, Linux works just much better than Windows. At work and at home.
Having run a Gentoo setup on a laptop in like 2004 or something, I've seen Linux break in some horrifying ways. Linux experience varies depending on lots of things. Keep in mind I love Linux, and am running a windows insider build with WSLg. Gaming in Linux just is not on par with windows yet, and I've been using windows since win95, so it's always been the default.
3 ปีที่แล้ว +1
@@Bobbias I mean, using Gentoo or Arch, specially older ones, is asking for trouble. I used to use Arch because I was a kid with too much time... And fixing the graphical interface every week was normal. With Ubuntu LTS the most exciting thing is installing it the first time
@@Bobbias While gaming on Linux will never be quite as good as on Windows, it has improved significantly lately.. and should improve more when the steam deck comes out
3 ปีที่แล้ว
@spaceLem Yeah, I've tried it and it's nice, but I'm staying on Ubuntu LTS, I've messed about with instability for enough years
Upgraded to Linux, good for you :D Im still on the "80% linux 20% windows" Train. Cant wait until i find that last few programs to get off windows for good. But to be honest, Ill always need windows around to compile my crosscode. Cmon world, hurry up, get off your purchased spyware.
Good video... BUT I think the problems you had to get it running were caused because you didn't fully follow the instructions :p The instructions said to use make run_main and not make and then execute the binary. The assets directory was in the main program folder and if the program was executed from the project directory which it would have been with the right command, it would have found it.
@@TheCherno Yeah the null termination thing was weird. I just find it strange that something like that would slip through on the branch he sent you. I mean if he would have run it in that state it could not have worked. Unless there was some other magic going on with that make command
@@TheCherno I think he is zero-ing his memory in a bad way ({0} instead of {}) that maybe works on his environment but not on yours, so his buffer was null terminated but yours wasn't.
@@darkfire2703 it may work for author, because he may have slightly different setup. Like different policy of kernel about heap memory, maybe zeroing it for him due to security reasons, or different compiler version which does that, etc... there's lot of security work on modern OS to prevent leak of data between processes, either clearing or randomizing memory content, so you can't just easily allocate or physical memory and rummage through old data of other apps. It's a bit painful to watch if you did grow with 8 bit computers and you understand all the extra costs involved, but that's the state of the world we are living in, even memory content has to be randomised by OS now... Anyway, author could have used calloc in this case to ensure that whatever is read from the shader file is the only non-zero stuff, even in case the file is loaded partially, and not up to the expected length.
@ 15:15. Are you familiar with Copy Elision and Return Value Optimization? Is perfectly acceptable to make use of value semantics, is easy, is fast and the whole world understands it. (Can't believe you rather use globals in this case!)
Copy elision wouldn't work with passing a struct as a function parameter, unless maybe the function is inlined and it doesn't change the object. It's also not really a thing in C, but I'm assuming compilers implement it as an optimization anyway. RVO is already used with programCreate(). I agree that using globals is not great, but passing pointers to the struct instead of the struct itself by value would still be an improvement.
How in the world do you recompile kernel on Ubuntu? The first thing I thought when you said that was "This guy works on Gentoo" but then why would anyone use Gentoo for game dev? Ubuntu is one of those distros that just works. Even though I use Arch based distro and never had problems with C++ configuration I believe Debian based distros like Ubuntu should just work. Sure you'll have to install some c compiler but that's even easier to do on Linux than on Windows because package managers are a thing. No hate or anything. I'm genuinely curious what makes you nag about Linux.
I mean you could make the algorithm work horizontally by rendering everything in a 90 degree angle and let the fragment or vertex shader rotate the texture before display.
I'm actually surprised you didn't know about this. Games like Comanche and Top Gun were pretty popular. Also the game Outcast also used something like that which looked awesome for a software rendered game. Good old times ;-)
* texture = image * texel = pixel of a texture * bumpmap = a texture where each texel stores a height. These are usually monochrome) * normalmap = a texture where each texel stores a normal vector. They typically have a purple tint due to the 3D vector 0,0,1 being stored as RGB 128,128,255.
@@MichaelPohoreski Awesome! Do you think I can catch up with all this by watching the game engine series from the cherno or do I need some fundamental knowledge?
@@nofeah89 Doubtful Cherno will go over the basics of Graphics. You'll want to learn the graphics pipeline. e.g. If you google for "bump_map" one of the links will take you to Valve's wiki.
5:17 is so relatable and one of the reasons I dislike linux projects. They always say "hey just type something and you should be ready to go", but you always have to dig down a rabbit hole of compiler errors
27:32 probably because pthreads expects void*(void*) functions as an argument, and wouldn't compile otherwise. (why would anyone use this shit with c++ available)
You can use WSL + VS Code to build Linux apps under Windows. Compiling a kernel is a CPU-bound process. It is normal to take time. Why did you need to recompile the kernel? I ask this question as an Embedded Linux engineer 🙂 Linux is worse than Windows in many or less ways but compilation time of the kernel is not one of these. If you do not need to compile Windows kernel (if applicable) you do not need to compile Linux kernel.
im not seeing the code detaily but i guess he had to make the voxel generated visually vertically from the camera view and you may suffer multiple samplings if you changed the generating into cs, any way this is all my disclaiming thought since thats the only way ik how height map works
Any thoughts on the new Battlefield 2042 Game? Or rather their Game Engine they've updated. Seems from a technical point of view interesting what u have to say abt it
Game dev here. You spend 99% of your READING code and only 1% of your time WRITING code. Learning how to read code is one of the most valuable skills you can develop. How do you get good at that? By reading code.
Hope you enjoyed the video! Don't forget that the first 1,000 people to use this link will get a 1 month free trial of Skillshare: skl.sh/thecherno08211
Man can you one day ract to the playstation 5 teardown? because now than the SSD M2 NVME PCIe 4.0 came out to the market thats makes you choose well you M2 SSD CARD, and which one are you going to choose
recompiling the kernel for the fifth time? -probably gentoo- reminds me of the gentoo user memes, i mean
Really appreciated this video, I never use c++ myself so I usually have to ignore some of the things I don't understand (assuming any) but C is so easy to understand which is why I love using it. As a side note I recently realised that the usual "do { CODE; } while (0)" trick that is used in macros would be better done by a simple "if ( 1 ) { CODE; }" which is easier for the compiler to understand and optimise regardless of how old the compiler is.
I loved the video. While there was lots of valid criticism, it did kind of seem more of a "review of how the code works" than an actual code review. Would be cool if you highlighted issues with the code and gave specific examples of why it's bad code and how to fix it.
Have you thought about trying out Rust (the language) for game development, and giving your thoughts on it?
Thank you very much for your patience to review my code. I learned a lot.
thanx to you we've learned a lot too
Thanks for sharing the Code :)
I spotted a few bugs in your code:
- when pressing L, % will not ensure that you won't go into the negatives (except if your value is unsigned)! (-1) & 5 is -1
- program.c, line 95: you probably meant + size.x, not + size.y. But it looks like you aren't really using that value anyways 😄
this splitting of work can be done easier by start = (i*width)/numberOfThreads, end = ((i+1)*width)/numberOfThreads. This is also less error prone (if you have numbers, where numberOfThreads * width < 2^31-1, if you're using 32 bit signed integers). Your work is then for(int x=start;x
When you comes to the point of naming your vars as "this", it's time to go C++ .. And for god sake keep it implicit.
what did you find most usefull?
Dang. Chernos debugging skills, on a program he didn't write, is insane. I hope I get that good someday!
So no credit to the author for writing fairly clean code in order to facilitate easier debugging?
Kudos to both of them honestly. Cherno picked up structure super quickly, but it was in part because the program was structured fairly clearly.
@@Borgilian sure, credit to the author IF he made a project that didn't have to be debugged as soon as you downloaded it
@@MPG42 Other than the images not being in the assets folder, it was probably partially due to a diffierent compiler being used.
@@LittleRainGames malloc'ing an additional byte at the end of a char array, not writing to it and hoping it happens to be zero is a bug.
Pthreads take a void* parameter that has to be casted in the function in whatever you passed in the pthread create. An old way to substitute variadic arguments.
I enjoyed this so much! Keep it up
Still not a voxel engine, even all these decades later - it's rendering height fields.
Seems like rotating the texture 90 degrees would increase cache performance with no added complexity.
Pretty sure many old programs did just that and flipped it back for display.
For ray casting I remember that some rendered one column at time from near to far.
This way you jumped to next pixel and started at same distance. (Have no idea if it was optimal though..)
@@pottuvoi2 Not entirely sure what you're considering "old", but in the context of the program presented, the 7 worker threads are writing column data into a texture, and then that texture is slapped onto the screen using OpenGL, while the original VoxelSpace (tm, sic) code would have been drawing straight to the frame buffer, which means drawing vertical lines means trashing your cache, as the frame buffer is row-major (rows are contiguous in memory).
Since we're drawing to a texture, we can rotate the texture such that vertical lines go into rows of the texture, and thus contiguous parts of memory, and better cache performance. We then upload the texture to the graphics card (which is slow, regardless of orientation) and then render the "frame buffer" fullscreen quad.
@@DaveLeCompte Was thinking DOS era from 486 to early Pentium, when we had the usual voxel landscapes and tunnels. (Tunnel being the classic landscape render to array and then make it to tunnel with precalculated indirection UV map.)
Sadly I do not remember if we did the flip when writing from array to framebuffer, but I know some did.
There were fun tricks people did during those times, like framerate independent screen flashes by changing palette every refresh.
i was expecting some sort of marching cubes implementation, rather than a heightfield.
@@DaveLeCompte The "big days" of voxel space and related engines were the mid-1990s, where caching wasn't very important; drawing to the frame buffer meant a major bottleneck no matter which way you turned it. The first game with one was of course Comanche, and there were others: _Terra Nova: Centauri_ IIRC, _The Outcast_ and as one of the most recent, _Thunder Brigade,_ still in the 1990s. All of these could run well on a 300-MHz PII, if not always on max settings.
Numerous demos had their height field segments, among them of course Future Crew with their uber-demo _Second Reality,_ but also the year before that with either _Unreal_ (unrealated to the game) or _PANIC!_ . Others followed suit, like the well acclaimed _dope_ in 1995 and the less known _Optic Nerve_ which at least IMO looked best.
Since then, "voxxels" have appeared even on home computers like the Amiga 500, the SNES IIRC, and the ubiquitous C64. They're usually quite coarse and/or cheat with geometry (not allowing for rotation, distorting the terrain significantly, or otherwise providing a view that's very restricted or visibly unrealistic), but all of them check the "something that model was never meant to do" box.
I'm fairly sure that to run it, the intention was to run it from the same location as make was called. Something like ./bin/appname.bin. - i am assuming this, since the assets folder was up a level, and that the directory bash is at, becomes your working_dir for the process. Anyway, happy to see a c99 Linux review. It is the primary environment in which I code :)
It literally says in the email, make run_main.
I also programmed something like this years ago. you have to draw vertical bars of a certain height from back to front. if I remember correctly the technology is called voxel spacing
I wonder if you couldn't perhaps do it horizontally instead and just draw the final quad rotated 90 degrees or something.
I did this as well, right after university. The idea was to have a futuristic city that mechs could fight on.
I ended up abandoning it after getting obsessed with not wanting to draw fully occluded bars and wanting smooth rotations of the map. (Just imagine if I had put off optimization until the end.)
VoxelSpace is just name given by Novalogic to their engine. The tech is mostly just 4dof heightmap rendering
@@PiesliceProductions Voxel Spacing was the name in the demo scene
> draw vertical bars of a certain height from back to front
That works, but slowly. I did the opposite and drew them _front to back._ For every pixel column on screen, I kept track of the y coordinate of the top pixel so far. I then computed the y coordinate of the pixel in question in 3D projection (easy if you don't tilt the camera up/down), and the x coordinate of 0.625 times the width to the left/right (fudge factor may vary). Then I worked one column at a time, skipping the column if the y coordinate is higher (i.e. the pixel is lower) than the terrain that's already been drawn. If it's not, you fill the difference in and update the y coordinate.
The major hassle is the traversal of the array depending on what way you're looking. You want to do it line by line (a "line" being the direction where the z (depth) coordinate increases slowly) in order of increasing z, and within each line, you do it in order of increasing z too. That ensures proper overlap resolution.
"The" _Voxel Space_ algorithm did it differently; it basically cast a ray (or a vertical stack of rays) and checked which terrain squares it flew through/over, generating one column of pixels per line of y values (rarely a literal line inside the array of y values, but a slightly zig-zagged path). It plays nice with cache and skips the x coordinate calculations, since only the relevant squares are hit by the ray cast, but it hits the closest squares repeatedly -- which isn't THAT bad, since the total # of intersection checks is still bound by (# of pixel columns) * (draw distance in square diameter) * (fudge factor close to 1) per frame.
27:31 it needs to be a void pointer because pthread expects that.
The little bit of multithreaded C that I wrote taught me that :P
Ah, the old Novalogic terrain rendering. It struck me while watching that you could just do the vertical span drawing as horizontal span and simply render the final quad to OpenGL with the texture coordinates flipped to draw it 90 degrees rotated. Then avoid the vertical span cache miss. Either way, it's a fun render method. Pretty sure I've seen one of these as a shader on shadertoy.
Render it sideways you mean? For cache performance?
If algorithm require to render lines from bottom to top, then you can still make it CPU Cache friendly.
Just render it on CPU with switched x and y, or rotated by 90 deg.
And then change OpenGL quad texture coordinates.
And remember to create texture with switched width and height.
Doom 1993 used rotates textures for rendering columns. Works in practice
Sometimes i don't have enough time to watch every video ,. But i do make sure that i open it and click like bcs of the amazing work you do .
Cherno : "Do I look old to you?"
Me, born in 1983 : 😢
It's nice that you've learnt how to say cache properly. Love the code review series
I remember LOVING to play around in Ken Silverman's Voxlap Demo about 20 years ago. It didn't look that impressive at first sight, but the full destructibility of the whole environment was absolutely mindblowing (and seemed like something from the far away future at the time). There was that hidden rocket launcher that basically allowed you to freely blast your way through the entire voxel map.
It was just a little tech demo - but it was so awesome because it did stuff I hadn't seen ANYWHERE before - and, to be honest, have never seen since! Can you imagine a voxel engine based game in the vein of "Zone of the Enders" in which EVERY shot you take and every enemy you down does realistic damage to a fully destructible environment? It would allow for some really transformative gameplay and immersion!
take a look at a game called Teardown, it's a blast
That's how I felt about the first 3d artillery games that tracked terrain damage (I think "Blast Doors"). It was a very simple predecessor of Scorch3D, which is worth checking out, too (free).
> it's a blast
Very _punny_
LOVE Voxel!! The most underrated modeling technique!
Agreed. I love voxels for seemingly no reason. I would love to see something other than cubes at some point, such as tetrahedral voxels. With only 4 faces in total and all acute angles they could be quite interesting to model with.
It's very important to cache friendliness so the next time you want to be friendly, it's right there.
I'm actually working on a voxel engine and I got really exited when I saw a voxel video from cherno. A shame they aren't actual voxels.
Yea I saw some nice voxel development channels on TH-cam but never got myself into the matter. Thought Cherno could be a good teacher
You can blame Nova Logic for that one; they trademarked it under the name "Voxel Space."
And if you were in high school when Comanche came out, you probably had someone in your class who tried coding that stuff himself -- or _you_ were that one. And more than one who claimed they did it on an Amiga / C64. The sort of guy who never invited anybody -- for Reasons(tm).
You could say they are voxels and render them as such too if you wanted. It’s just a rendering optimization that makes them feel like they’re not voxels, but nothing in the data says they’re not.
Personally I wouldn't mind if you went even more in depth, I like getting to know how something works in a rather detailed manner, but I wouldn't be surprised if that's too much to ask for in a code review video. Still lovely video though 👍
I'd easily have watched him mess about with this for an hour or 2.
@@Bobbias Same here!
Exactly. I think there's a youtuber called javidx who writes entire games on the console -- _without_ shader code. When he mentioned shader code, I thought "the WAT now?" because I've written something like that (well, let's say 50% of that) in both DOS and Windows. The DOS version handled a few special cases like terrain exceeding the top pixel line (which the demo shown here at 07:36 does NOT, or at least doesn't demonstrate) or the player submerging in water.
The Windows version was simpler, basically what we can see here minus the coloring that's very close to Comanche, and it couldn't view the terrain at an angle either. I got 15fps in DOS in the 320x200 mode on a 486-50 (although I used the ultra-coarse mode, which only drew 80x200 pixels - Comanche could switch between that and 160x200 and was a bit faster). The Windows version could go full 640x480 with similar performance on an entry-level Athlon. Both 100% in CPU, no shaders added, or any 3d hardware for that matter. So, to someone who tried to recreate height field (or "voxel space") engines, that felt a bit underwhelming, like "You did 20 in a Model T? lel n00b, I did twice that in a Ferrari!" But not to belittle OP, *making that 3D engine was a solid piece of work,* even if "120fps at full HD" feels rather mediocre to me if done with a shader (which IS a dependency if you think about it). I'd like to see _more_ of that engine.
For comparison, the DOS version had a 256x256 map, a primitive HUD of sorts, covered the top half of the screen with landscape and part of the bottom half with a minimap; I wrote it in Turbo Pascal, then the most popular choice among amateurs. The Windows version was written in Visual C++ 6 and far more primitive overall (no causality here), but used real geometry, indistinguishable from a triangle mesh, not the coarse approximation of the DOS graphics. It looks like I lost both sources over time, so there's only my word that remains, and memories of evenings spent to code that thing, test it, and reboot my PC countless times because I wrote the inner loops in Assembler.
25:14 Also when it constructs the whole image it does the sampling in the infamously suboptimal for(x){for(y){}} instead of for(y){for(x){}} if you're storing row after row instead of column after column.
If the algorithm needs to deal with columns then why not just store the image in memory transposed (i.e. 90 degrees rotated)? Then you can do the "rotation" just in the fragment shader.
Not sure how the work the shaders do compares to the output bandwidth...
Let's play devil's advocate and say that a ray crosses N boundary before hitting the landscape. That's N reads from the height map per pixel written. This doesn't look like it would affect performance a lot, unless you're directly facing a cliff or something -- and in this case, where N is unusually small, the FPS would be at its highest. It could well be that poor pixel generation order isn't affecting FPS for any sensible FPS value, but only the theoretical _max_ FPS in an abnormal setup. That might occur if the player character has crashed and even act as an unintended FPS limiter now that I think about it...
Thank you for posting all these, I can't help but feel like I'll be making better coding decisions in the future thanks to your videos!
This was so enjoyable to watch. I’m a beginner programmer and I managed to understand At least 30 percent of this. Feels good.
The rest was just super interesting. Developing in mainstream game engines everything is so abstracted. It’s cool seeing some of this low level stuff on a more contained project
Finally, someone who isn't afraid to admit that they mostly use Windows for coding XD
Splendid video, thank you a lot!
I guess it's easier to admit when you work in game engine development given how DirectX is windows only (officially)
OS shaming is terrible. Also with my colleagues I pretty much cannot mention that or the fact I am coding with C++, they only accept Rust and everything else is an idiot to them.
@@Mystixor Is Rust good?
@@martinjakab It is a powerful programming language but the lack of support and libraries makes it an easy choice for C++ for me
@@Mystixor I thought about learning Rust, because I heard that its similar to C. How much is it true?
And an another high quality satisfying video presented by Cherno. 😊
he is so pro he can read the code like an easy thing... props to yan
for me is 1 month of figuring out functions and sintax. Then another month for the logic if its working properly.
lol
he fixed it in less than 5min
*syntax
As game developer you spend 99% of your time READING code and 1% WRITING code.
The best way to get good at reading code is to ... you guessed it ... read code.
@@MichaelPohoreski true for just about all types of developers i guess. (take that from an automotive embedded guy)
@@urugulu1656 Good point!
I adore this channel! I have been coding C/C++ of and on for 31 years now. And I still learn new things from “The Cherno”!
And Linux is the only way to go :)
What did you learn?
@@climatechangedoesntbargain9140 Martial Arts
@@mr_madds how the climate always changed and how nuclear energy, instead of in efficient wind turbines is the way to go.
FINALLY We see something in Linux!
LOL I'm literally recompiling my kernel while watching this :P
24:50 from what experience I have with c++, creating new threads like this is actually slower than just using one thread in many cases
Yeah especially on Windows, the CreateThread function is very expensive, I'm shocked that he was getting over a 100 fps and rendering the entire scene in about 1 second whilst spawning 7 new threads (and presumably destroying the old ones otherwise he'd run out of ram) every frame. Maybe pthread_create is cheaper ?!?
That's on Windows. I'm pretty sure Linux actually uses an underlying thread pool or some other funny optimization like that, so there is not much overhead from creating and destroying threads repeatedly.
Keep in mind there were ~1000 threads being created and destroyed per second *while they were also doing the rendering work!*
@@chlorobyte_projects I'm sure Linux does not do that. Yes, the spawning of threads is a lot cheaper than on windows, but threadpools are common on Linux as well.
I think the biggest issue with this code is all the passing by value. They copy the Sprite struct and the ImageData struct (which includes their framebuffer so it's a pretty heavy copy) like 4 or 5 times per frame. 25:15 you can see all the functions that operate on Sprites take values, not pointers, as arguments.
The sprite struct is not that big it is a pointer + elementCount + size
This is not c++ with copy constructors
I wouldn't mind longer videos in this series. Love them! Analyzing source code is always so interesting.
I believe if you CTRL+Click on a symbol in VSCode, it should take you to the definition.
There a actually 2 ways you can realize a (retro voxelspace) Voxel Engine:
1. As a 2D Raycaster (like wolfenstein)
2. As a plane that gets transformed to screen (like mode7 on SNES)
The latter method produces the shimmering effects when moving or turning. The first method is preferable for that reason (and was used by novalogics voxelspace engine back then)
Awesome video; keep up the neat work.
Yeah the algorithm really has to draw vertical slices, you can't do it horizontally. What I would do is draw the image transposed and then just un-transpose when drawing the quad. The algorithm was used in early 3D terrain rendering because it's super simple - you project each column on screen to a line on the heightmap texture, read pixels one by one from nearest to farthest and keep track of the current height. If the pixel to be drawn is lower than the current height, skip it; if it's equal or higher - update the height and draw the pixel. There's a bit more math involved to get a correct perspective, but in essence you get a 3D terrain for not much more computation than Brezenham's line drawing algorithm.
*Bresenham
Storing the data column major would do the trick (as is preferred by many linear algebra libs, e.g. Armadillo, BLAS/LAPACK, Eigen)... I'm saying the same thing really but we can do it as the data is loaded and just once if we don't load with the image lib.
Really enjoyed this, I'd watch you go through the entire thing.
Dude, you're in the bin folder. The assets folder is on the workspace root. Just run bin/main.bin. Moving files around will only break stuff
i love this series. thanks for putting it together. i'm mainly a JS/PHP dev, but i love seeing these breakdowns of C,C++,C# etc. great work!
make them as long as you like!
15:59 Passing by value is considered good practice in modern C as structs in C are pure PODs that contains primitive stack values. It's basically the same as you would pass long list of ints and pointers in arguments but packed in one struct. Better to do it that rather than introducing another level of indirection and dereferencing it all the time. In C++ it's not that obvious as structs are classes in disguise which consist whole bunch of implicit members.
Interesting. Does that mean the data isn't copied? (I'm a C++ guy, very ignorant with regards to C). In this program a copy would seem undesirable.
@@superscatboy There is a copy of primitive values from stack, including pointer. But copying those is very very cheap, even in C++ it's not advised to pass ints by reference right? You just do void func(int x, int y) not void func(const int &x, const int &y). Now, since in C structs are pure POD and nothing else, compilers have easy time optimising those as long as you won't create oversized buffer on stack. So if you have such struct in C struct Point {int x, int y}; then it's better to pass it to function by copy void func(Point point) cause all what happens you copy bunch of primitives void func(int x, int y) which is cheap. If you pass it by pointer then you're introducing indirection level which is never free and you pay for it every time you dereference, for simple copy you pay only once.
Writing colums in multiple threads also leads to false sharing, when multiple threads access the same page (or pixels next to each other).
This was amazing. Thanks Cherno.
Reminds me of Outcast with its voxel engine.
13:18 Hazarding a guess as to why he is copying the strings rather than just the pointers, maybe it is because this is a demo of a library that, when used for real, may be dynamically loading and unloading files at runtime? So I would guess he wants the lifetime of the char buffer to be coupled with the lifetime of the containing object.
But then he has named the type of the object Program, so that doesn't make any sense as surely there would only be one Program object. Bah, I give up... lol
Love the linux stuff, old school C and algo viz!
Excellent review!
putting void as the parameter of main is (usually) uneccesary, but is good practice to prevent some possible issues
15:00 Using a global structure that would be accessible from everywhere would it make hard to do unit testing.
Also global structures is a huge code smell for a lot of people
You've been infected with this terrible virus. It's silly to make a program way more complex than it needs to be, to not only be able, but literally be forced to test it.
Just think your programs through, use proper design patterns, and use tests on specific parts, where you can't be certain something weird won't happen.
Most of the time it's way more useful to test complete use cases, where having parameters is irrelevant. Unit tests are so over rated.
@@TheAxeForgetsTheTreeRemembers I keep bumping into code which was well thought through, done with proper design patterns, and tested over specific parts, having ton of bugs... it's like pattern, I just look for values/configurations not tested, and unearth subtle off-by-one or doesn't-expect-zero or didn't-expect-that-previous-thing-ended-in-that-second-state
I did recently receive 3 line change patch for one open project with comment "I did test it manually, no need for unit tests, it's very simple change" ... and yes, there was bug in it, not accounting for one type of input data. (and I did knew before I released the SW, because I actually do have unit tests for that particular function, so I knew 5 seconds after applying the patch that it did break something it should not have).
@@theoneandonly1833 but this "globals are bad" is kinda overdone, global variables make sense sometimes. But the implementing code should probably take some initial pointers, even to globals, to cater for both easy testing, and not hiding globals behind fancy names like singletons and similar.
@@ped7g A program is likely to have bugs, tested or not. There are plenty of softwares out there that are well tested but still have bugs. Some of them being critical, yet completely overlooked by improperly thought out test suites.
Delta Force is also a common game referenced as using the Voxel Space engine. Looking back in 2023, it retained some of the problems mentioned in the video, including shimmering. Delta Force 2 was a little more visually appealing due to engine improvements.
Guessing the issues are why NovaLogic switched to more a hybrid voxelspace / 3d mesh engine for later titles, where terrains were visually improved / optimized, but still VoxelsSpace-rendered, where as objects were 3d models (to avoid the shimmering + sharp looking objects you see in DF1 and DF2). They kept the hybrid approach until their last games (which weren’t well received).
This is such an interesting concept, though. However, you can see why it’s no longer used, even not considering how video cards are optimized for vertex rendering now.
At 7:59 When he says "it's not actually rendered in 3D space, it's probably rendered to a texture" what does he mean?
he meant, that the actual scene that you are seeing, is not actual 3D space geometry, but rather texture projected onto a plane. What the difference is here, is that the engine has no definition of 3D model, as objects that you put on the scene and render collectively with each other, but rather 2D information of all objects on the scene, projected directly onto that plane.
You can imagine that difference quite easily:
Lets say basic today engine as Unreal, or Unity, treats 3D space as you would treat real life diorama made out of plastics. You put there objects, glue them onto the different surfaces, as you would play with toys as a kid. This engine takes a couple of photos from above of your playground. One for color and shadows, second for depth (this here is a height map). Now when you have that information, you go take a real life wire plane, bend it as height depth photo tells you. Then take the color photo print it on some cloth, and just throw that cloth on the bended wire. Now thats the retro voxel engine representation of more complicated, multi object diorama, with use of only one bended object with flat surface on top of it.
so the real difference is that retro way, whole scene is represented by simplified 2D space geometry information, when most of the engines now represent 3D multi object space geometry, in which every object information is stored as 3D vertex coordinates table by default
The "global variable" Cherno, back at it again!
6:32 - I reckon there's an error with order of operations on line 30. Inconsequential because sizeof(char) is 1, so you're getting fileSize + 1 * 1. Definitely worth using parenthesis to ensure its (count+1) * sizeof(..) and avoid hard to debug errors.
On this topic, though, sizeof(char) is guaranteed to be 1 and kind of redundant.
1 is a magic numer. 1 why?
@@Henry-sv3wv Null terminator. Not really a magic number, if you're seeing a char array allocated with one extra element it's fairly obvious
@@ciekce ah, ok. yea i realize that +1 is also often done in code
Couple of point's from me. He's casting his malloc results, which you shouldn't do as void* is implicitly and safely promoted to required type. Also, it can hide an error where you forgot to include stdlib.h. Second, there's loads of variables that could be const. IMO, it's a good habit to have.
insane video sir 🐐🐐🐐
I think sending the Cherno a project which is not running out of a box is getting a running gag :-)
Yeiii, best OS Ever, but i think that if you used Fedora It would be way easier, and you wouldn't be needing to compile the kernel
You don't have to compile the kernel on Ubuntu or Arch either, that must have been a joke
@@chlorobyte_projects don't know about that, on arch I had to make some changes to the bios configuration because of the processor, gfx card and motherboard, fortunately there was someone having the same problem in the arch forum. The best thing was that he had the same processor, gfx card and mother board. The final solution was just to add some flags to the init command of the kernel, something like amd_soft and another flag
@@lmtr0 still not compiling the kernel lol
@@chlorobyte_projects yeah true, have you tried Gentoo??
He DEFINITELY needs to split the threads in columns. Rows would be terrible. The vertical "behavior" of the algorithm is how foreground points hide background points.
A massive gain in performance could probably be made by rotating the height and color images by 90 degrees. Render the images "sideways" and then rotate the final image back 90 degrees the other way to render. Maybe the GPU could do this second rotation. In this way the threads could run on horizontally ordered memory. The source data for each pixel is still collected from a "randomish" position based on the direction the player is facing.
BTW I've never seen this algorithm before, but it is very cool how this 3D stuff was discovered in the good old days!
let me just quickly tell you: your videos are definitely not too long. i have ADD so i know if something is too long. but you make valuable content so it can have this length
I already love the first sentence
Passing this as argument a lot is actually C++ programmers thing. And generally OOP programmers.
C++ and other OOP languages just hide it from programmer.
When you do object->foo(); C++ under the hood is actually passing &object to object->foo function as argument.
And that's why you can access this inside methods.
Also since paths are like "assets/snow-watery-height.png" they are relative to working directory. So at first it wasn't able to find them because you did "cd bin". I think.
I would actually also make Program this a global variable.
But that is considered a bad practice by OOP gurus, uni professors, corporations managers, and other people that have never written a real application.
Liked the video as soon as he said Linux
Cherno I would love to see your take on optimizing this program! I feel like youve suggested really big features that can really improve it.
Hi cherno this is my first comment on your channel. I follow all your game engine series and CPP series. Request you to provide some info on logging and memory debugging, profiling and optimisation techniques which can be used for 3D n 2D games
Using Linux every day 8h and Windows about 3h, I have to say that Linux is just better than Windows. It's lightweight and just works, not like Windows 🤷🏼♂️
Edit:
I think I have to add that this is just my opinion. As already people said, it depends on what you are doing. E.g. gaming isnt really great on Linux, but for me Linux is just better as a fairly low level developer. I have way too many bugs and blue screens in Windows and it is just packed full of stuff that is running in the background that I don't need, but I still have a dual boot setup. (And if there's one thing I hate, then it's the Windows API) I use Windows for gaming and yeah, if I develope applications for Windows. For everything else, be it server development, reverse engineering and generally low level stuff, I use Linux. For me, Linux works just much better than Windows. At work and at home.
Having run a Gentoo setup on a laptop in like 2004 or something, I've seen Linux break in some horrifying ways. Linux experience varies depending on lots of things.
Keep in mind I love Linux, and am running a windows insider build with WSLg. Gaming in Linux just is not on par with windows yet, and I've been using windows since win95, so it's always been the default.
@@Bobbias I mean, using Gentoo or Arch, specially older ones, is asking for trouble. I used to use Arch because I was a kid with too much time... And fixing the graphical interface every week was normal. With Ubuntu LTS the most exciting thing is installing it the first time
@@Bobbias While gaming on Linux will never be quite as good as on Windows, it has improved significantly lately.. and should improve more when the steam deck comes out
@spaceLem Yeah, I've tried it and it's nice, but I'm staying on Ubuntu LTS, I've messed about with instability for enough years
@Fluxx Different tools for different users; I don't agree with your statement but it's just an opinion at the end of the day.
Upgraded to Linux, good for you :D
Im still on the "80% linux 20% windows" Train.
Cant wait until i find that last few programs to get off windows for good.
But to be honest, Ill always need windows around to compile my crosscode.
Cmon world, hurry up, get off your purchased spyware.
Good luck on your journey to find those last few programs! As for compilation, I have Windows in a VM just for that.
Lol I just started looking at voxel engines today! 😂
Good video... BUT I think the problems you had to get it running were caused because you didn't fully follow the instructions :p
The instructions said to use make run_main and not make and then execute the binary. The assets directory was in the main program folder and if the program was executed from the project directory which it would have been with the right command, it would have found it.
That I agree with (not too difficult to copy the assets folder into the bin directory), but the lack of null termination character??
@@TheCherno I caught it right away having done similar in Vita homebrew project. Most times it works okay until it doesn’t ;)
@@TheCherno Yeah the null termination thing was weird. I just find it strange that something like that would slip through on the branch he sent you. I mean if he would have run it in that state it could not have worked. Unless there was some other magic going on with that make command
@@TheCherno I think he is zero-ing his memory in a bad way ({0} instead of {}) that maybe works on his environment but not on yours, so his buffer was null terminated but yours wasn't.
@@darkfire2703 it may work for author, because he may have slightly different setup. Like different policy of kernel about heap memory, maybe zeroing it for him due to security reasons, or different compiler version which does that, etc... there's lot of security work on modern OS to prevent leak of data between processes, either clearing or randomizing memory content, so you can't just easily allocate or physical memory and rummage through old data of other apps. It's a bit painful to watch if you did grow with 8 bit computers and you understand all the extra costs involved, but that's the state of the world we are living in, even memory content has to be randomised by OS now... Anyway, author could have used calloc in this case to ensure that whatever is read from the shader file is the only non-zero stuff, even in case the file is loaded partially, and not up to the expected length.
For me it just worked out of the box no adaptions needed. Maybe changing the working directory confused the program!
8:24 is there a missing curly bracket right here?
I think he finishes writing the shader in the C code.
File is edited, Cherno might have deleted it by accident.
the columns are essential to the algorithm... oldschool advice. lol
would love to sit through this :D
man that fast debugging
@ 15:15. Are you familiar with Copy Elision and Return Value Optimization? Is perfectly acceptable to make use of value semantics, is easy, is fast and the whole world understands it. (Can't believe you rather use globals in this case!)
Copy elision wouldn't work with passing a struct as a function parameter, unless maybe the function is inlined and it doesn't change the object. It's also not really a thing in C, but I'm assuming compilers implement it as an optimization anyway. RVO is already used with programCreate(). I agree that using globals is not great, but passing pointers to the struct instead of the struct itself by value would still be an improvement.
How in the world do you recompile kernel on Ubuntu?
The first thing I thought when you said that was "This guy works on Gentoo" but then why would anyone use Gentoo for game dev?
Ubuntu is one of those distros that just works. Even though I use Arch based distro and never had problems with C++ configuration I believe Debian based distros like Ubuntu should just work. Sure you'll have to install some c compiler but that's even easier to do on Linux than on Windows because package managers are a thing.
No hate or anything. I'm genuinely curious what makes you nag about Linux.
While I'm pretty sure that was a joke, it *is* possible to recompile the kernel. It just takes 800 million years to do so.
It was just a bad joke that was probably not understood as such by 80% of the viewers.
I mean you could make the algorithm work horizontally by rendering everything in a 90 degree angle and let the fragment or vertex shader rotate the texture before display.
Could maybe render the whole scene rotated 90 degrees, to make the "draw vertical lines" much more cache-friendly, then display the image rotated back
yes finally somthing thats not hot garbage cpp and windows
I'm actually surprised you didn't know about this. Games like Comanche and Top Gun were pretty popular. Also the game Outcast also used something like that which looked awesome for a software rendered game. Good old times ;-)
really loved this review. Could you do more pure C stuff on linux?
I would like to watch you do something similar but setting it up using the GPU. I find getting into programming using the GPU more to be a pain.
Both SSBOs and MFLAs (modern four letter abbreviations) actually ;P
You should take a look at John Lin voxel engine. It is the most beautiful voxels I've ever seen.
Wait, headerguards are old school? Are they not needed in c++ classes?
I really wanna learn this stuff, but I cant follow on what's a texture, bumpmaps etc.? can anyone suggest a playlist from where I can start?
* texture = image
* texel = pixel of a texture
* bumpmap = a texture where each texel stores a height. These are usually monochrome)
* normalmap = a texture where each texel stores a normal vector. They typically have a purple tint due to the 3D vector 0,0,1 being stored as RGB 128,128,255.
@@MichaelPohoreski Awesome! Do you think I can catch up with all this by watching the game engine series from the cherno or do I need some fundamental knowledge?
@@nofeah89 Doubtful Cherno will go over the basics of Graphics. You'll want to learn the graphics pipeline.
e.g. If you google for "bump_map" one of the links will take you to Valve's wiki.
Search for: schreibt render pipeline
@@MichaelPohoreski Thank you Sir!
5:17 is so relatable and one of the reasons I dislike linux projects. They always say "hey just type something and you should be ready to go", but you always have to dig down a rabbit hole of compiler errors
Question, why were you compiling the Kernel on Ubuntu?
Ahhh yes voxels...or as we called them "muddy graphics!"
Could any of you please tell me what's the model of the coffee maker in the outro?
Hey, I have a question, do you code review for any programming languages, or specific ones
27:32 probably because pthreads expects void*(void*) functions as an argument, and wouldn't compile otherwise. (why would anyone use this shit with c++ available)
*Rust available
*VisualBasic available
@@epajarjestys9981 are you serious?
I remember Comanche game too :)
You can use WSL + VS Code to build Linux apps under Windows.
Compiling a kernel is a CPU-bound process. It is normal to take time. Why did you need to recompile the kernel? I ask this question as an Embedded Linux engineer 🙂 Linux is worse than Windows in many or less ways but compilation time of the kernel is not one of these. If you do not need to compile Windows kernel (if applicable) you do not need to compile Linux kernel.
Compiling the kernel was a badly delivered joke.
@@not_ever and a pretty annoying one because people will actually believe it.
@@Radgerayden-istagreed
Finally using Linux!
im not seeing the code detaily but i guess he had to make the voxel generated visually vertically from the camera view and you may suffer multiple samplings if you changed the generating into cs, any way this is all my disclaiming thought since thats the only way ik how height map works
Is that Ubuntu 16.04? Why such an old version?
12:35 I "want to sit through that"
Compiling your kernel? Did you install Gentoo?
8:23 My brain compiler can't look at that code without thinking "?Syntax error". Missing closing brace!!
use arch in the next code review instead please 😈
It's okay, steamos 3 will be out by then.
Any thoughts on the new Battlefield 2042 Game? Or rather their Game Engine they've updated. Seems from a technical point of view interesting what u have to say abt it
how the ???? did you get that code to compile? that's the most impressive thing i've ever seen
Game dev here. You spend 99% of your READING code and only 1% of your time WRITING code.
Learning how to read code is one of the most valuable skills you can develop. How do you get good at that? By reading code.
technically, 'int main()' doesn't break any rules, but the standard says the correct way is 'int main(void)'
Yeah, Linux gang!
Any chance this'll run on a Linux VM on a Windows machine? 😅
I would say, look into GPU passthrough or just install Linux via dual-boot. ;)
@@TheShadoDragon Alternatively... just use a live USB lol