RETRO VOXEL ENGINE! // Code Review

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
today we are using linux what's up guys my name is the channel welcome back to the code review series the series in which you guys just email me code and i i take a look at it today as i mentioned we are on linux don't ask me why i decided this would be a good idea after spending like two hours recompiling the kernel for the fifth time i think we finally have a build of linux running that can run this program that someone sent me in my quest for diversity when it comes to these code review videos i mean last time we did like a university assignment i'll have that linked up there but in my quest to be a little bit you know different i've decided that we're not even going to be looking at c plus plus code today it's going to be c c 99 to be precise and we're not going to be looking at it on windows we're going to be looking at it on linux now windows is kind of my my my development specialty platform if that makes sense like 90 of the development i've done in my life has been on windows and for like windows and other platforms but these days especially i'm kind of on windows targeting windows so this is uh this is this is going to be interesting back when i worked at ea a lot of my time was spent developing a mobile engine so android and ios development so i am pretty familiar with like the kind of linuxc libraries and stuff like that but that being said it was a terrible experience developing for android and it has been a very long time so this is going to be a bundle of joy this project it actually is quite interesting i mean let's take a look at the actual uh email that i got so this was sent to me by pablo who does not value anonymity it seems this is a voxel renderer that mimics the old comanche engine that was popular in nova lodging in the 90s do i look old to you i have no idea what that is ah okay right maximum overkill 1992. that's kind of cool so i guess all of this terrain is like a kind of a voxel algorithm situation can you please share your opinion on this piece of code it's a voxel engine implementation with multiple threads the most relevant insights i like to hear from you are overall program structure use of threads um cache the friendliness of this solution does that mean like how cache friendly it is or i'm supposed to cache the friendliness of the okay i'm just gonna assume he's talking about the cpu cache here functions are okay whatever we'll we'll discover that for ourselves to build and run should be enough to run okay here is the url i will have this linked in the description below as always so you guys can take a look at it be sure to be in the multi-thread branch looks like the multi-thread branch is the default anyway so yeah i mean this is a screenshot of what it looks like pretty cool i haven't really taken a look at anything like this yet so i'm actually kind of excited he did mention that it's got no dependencies i guess uh although here it's dependent on glfw but that's okay i think it's gonna be pretty amazing to take a look at this and uh see what we can kind of discover in the source code and see if there are any kind of tips that i can provide as well but first this video is sponsored by skillshare so for those of you who don't know skillshare is an amazing online learning community where millions of creative and curious people from all around the world can come together into one place into one awesome community to learn pretty much any new skill you can think of like literally anything that you are interested in that you want to learn more about skillshare probably has a class for it whether you are interested in like creative topics like photography videography illustrations stuff like that or you're more interested in like business productivity how to like run a youtube channel and how to make good youtube videos stuff like that skillshare has got you covered my single most favorite feature about skillshare is the fact that the videos are just so concise they're so they don't have fluff to them they're really to the point and they they're not very long they don't need to be very long they're really kind of digestible because they're just so well made that i mean i could probably benefit from that let's be honest but for a limited time skillshare is also giving the first 1000 people who click the link in the description below a one month free trial of skillshare premium so for free you can take a look at skillshare's entire catalog of videos and see if there are any classes that you are interested in huge thank you to skillshare as always for sponsoring this video let's move on and take a look at this voxel space engine okay so i've cloned the repository you're looking at it there's an assets a lib's a source folder all of this stuff now this uses make which is pretty standard on linux i've also opened up a terminal to this directory now outside of this video just to not bore everyone i've also installed just using apt-get install glfw and also the audio library that's being used by this repository it's this uh so loud thing so i've installed that on the actual like linux computer so that i don't have to worry about actually downloading it for this repository specifically the beauty of linux is the package managers i mean i have to give them that so now we should just do make and it should just work which is pretty exciting and it looks like it has in fact worked so let's take a look at what's happened there's a bin folder there's a bunch of files in here which are presumably the assets that are being used and there's a binary file so let's go ahead and get into that bin folder and try and run that binary file and we get an error error opening file okay so two files i guess the shaders probably tried to be opened they didn't work i swear if someone sends me another project that doesn't work okay so it looks like we are gonna have to fix this let's go ahead and open this up in visual studio code and take a look at presumably some kind of shader file that couldn't be loaded now you guys can watch how i quickly fix code that i receive um okay so assets share okay well actually if we go back to this you can see that there is no assets folder so what if we move everything into an assets folder let's just quickly do this we'll move literally everything except that also into here let's go back to this and see if we can run it okay different error syntax error unexpected exclamation mark inside the shader code i should probably just fast forward through this but i'm not going to so that you can also feel the pain i'm going through what is this exclamation mark in the shader what's going on where are the shaders let's uh let's go to the shaders so assets shaders that looks pretty standard pretty good should probably make the font size a bit larger okay so this vertex shader looks pretty good to me i certainly don't see any exclamation marks neither do i see them here so i don't know what that error is all about shader.c okay let's let's see if we can print the shader code that we're actually like getting okay so we're reading it here shader code let's just go ahead and uh right before we compile it let's just print it to see if we're actually reading the file properly because it does look like way it does look like we're reading the file properly so i don't know why this specifically isn't working so let's go back make is that gonna remove my assets directory i hope not no okay so we'll run it again here's the shader code ah okay look look at all this okay so it looks like someone has probably forgotten a null termination character so let's take a look at this i love the go to's but it says i'll forgive you maybe so uh shader code uh we read shader code yeah okay so this is obviously not going to read to the plus one character which you shouldn't assume is zero so there is no null termination character so let's just go ahead and quickly chuck that in there and now make and main all right it's running good looks pretty cool actually so this is i guess some kind of like okay it's cycling through all of the different uh levels here this this is pretty cool i like taking i like taking a look at stuff like this now since he mentioned that this is uh obviously something that i guess is emulating some really old ms-dos uh engine uh i'm assuming that none of this is actually rendered in 3d space i think it's probably all rendered to some kind of texture and that's always using opengl for just to display the texture it's running at 120fps which also implies that it's probably just like software rasterizing essentially so let's let's actually dive in and take a look at the code oh escape worked that's nice so the actual kind of layout uh seems pretty uh simple here we've got a source folder which has all of the kind of main stuff in it he's got a pretty nice main function again this is c so i have to remember that this is c and there are certain things that we have to do like right void here i don't know if that's standard c99 kind of code i have written very little actual c code that is compiled with a c compiler not a class plus compiler in my life so i don't even know what is permissible and what is not but um we're going to assume that obviously this is uh you have to write void here okay so program this equals program creates a program is some kind of struct which we find in program.h let's quickly take a look at that so in general i like the kind of um the the way that the main function is really clean and we just kind of create the program which probably initializes some variables maybe sets up the windows stuff like that we have a main loop that we call which probably has like a while loop in there that runs until the window is closed and then finally we have a destroyer to kind of just destroy everything and free all the memory which you know is questionably like usable and then he's using this as the kind of variable name because of course this isn't a keyword in c because there are no classes so uh that's kind of nice as well it kind of almost makes me makes me it tricks me as like a c plus plus or really as any object-oriented programmer i think in c sharp and java it's also like this is also a keyword that refers to the current class instance so anyway let's go into program create um and actually i want to take a look at program.h so where would that be program.h okay we've got some nice uh header guards here old school header guards love this stuff um map type we've got you got a capital y there bro i'm sorry that's not gonna fly um and then we have uh okay color map name is map count so what is map count okay map count is just the last name here so it's going to be the amount of these one two three four five six we have six of them okay so you've got color maps and height maps so if we actually go to the assets for a minute this might help us decipher this so we have like what looks like are these color maps but then we have height maps as well so the color map just determines like i guess the color of each voxel so each pixel becomes like a voxel it's not really a voxel it's just that it's at a certain height like and all of this like all this kind of depth and height and these kinds of words like they're all they all just get fed into a mathematical algorithm that actually determines where they are in 2d space anyway because of course at the end of the day we're simply rendering a texture that we display on our screen so um it's important to kind of i think it's important to like if you're actually curious as to how these algorithms work and how we're able to kind of project all of this in 3d space don't forget that at the end of the day you end up with a 2d coordinate of where to place a pixel and of what color so don't get too kind of carried away with this so again we have like a color map which i guess determines all this know that the shadows are actually kind of baked into these maps so those shadows that we saw are not really they're not actually calculated i actually thought they were um they're not actually calculated it turns out they're actually encoded into the color map but then the height map which will line up with this color map you can actually see here that you can still see those rivers and they're in the exact same place here this height map just determines how high things are so how high is each pixel here that we render as a voxel in 3d space well in in 3d space right so we have the black which is a lower value like zero which is probably like the base and that's why we have these rivers here but then as we get higher the value increases which kind of makes it go more towards white and it becomes lighter here so these these pixels are of a higher value than these pixels because they're brighter which means that they will be higher conceptually so that's kind of how these two maps work which is kind of interesting and again i have never looked into engines like this before uh in the 90s i was busy like learning to walk and talk and do that kind of stuff so i definitely wasn't playing these games and i definitely wasn't studying the engines so i love looking at this stuff honestly because it's it's kind of cool it's a blast from the past anyway back to where we were inside programs let's go let's go to the c file and actually kind of take a look at this again i could spend hours looking at this i'm sure you guys don't want to sit through that um although if you do let me know uh so let's just go into program create and see what's up so it looks like we are high map names and stuff so we're cop so we're doing a little string copy we're copying that into this array that is fine uh to do it that way i guess again c you know c is always fun i'm not sure why these even have to go here i guess they get loaded at some kind of yeah sprite create probably loads them using an array so this kind of just sets up that array uh i don't really see why you couldn't have just like assigned these cons char pointers to the uh to what is presumably yeah oh right okay because it's a pre-allocated buffer of 50 but they're const chars you're not going to modify them are you because if they're cons sharp pointers they're stored in read-only memory which means you can always you can obviously point to them and they're just in a place in the binary so there's no actual memory that needs to be uh like accessible during runtime and by accessible during runtime i don't mean accessible because obviously we can read the read-only memory we just can't really write to it or at least we're not supposed to uh so like if you were to allocate like like a good example is like the shader code that we load during runtime that needs to be in a kind of dynamic place because we don't know what the data is before we start running whereas this stuff since these strings are literally stored here they are just stored in the reader in this section which means that instead of copying them into another place now which has a buffer that is i need to double click on these files so they stay open instead of doing that which goes into a buffer that is like 50 characters at the most right we don't have to do any of that we can just store const chart pointers and not worry about stack memory allocation which happens here so i'm not sure why it's done this way i probably wouldn't do it this way these are clearly fixed strings anyway but whatever so we create the graphics i'm not going to dive into the graphics creation too much because i don't think that's the point of this uh and then we oh there's this sound i didn't even hear any sound anyway and then we go through all the maps and we do sprite create so what is sprite create let me just uh i'll just control shift f that so here's sprite create in sprite that uh okay uses stb image to actually load the image data and then we return this which is sprite which just contains the image data okay cool so we allocate a buffer well sdbi will do that actually uh which will contain our image so we just create the images basically now we've got them inside height maps and color maps and then we set some other variables again to this which is our program which we then kind of copy back to main dot c and then we pass into program main loop so you know this is kind of uh i guess like a structure i'm not a huge fan of this because we are kind of creating this program object which we then return and then pass into everywhere passing variables around is very common in c-style programming i'm honestly not even sure why they like to do it this way i would have just stored this in global memory in some kind of structure so that we could just kind of access it everywhere without having to pass it in everywhere again it is a pretty small program so i think that's not that big of a deal but like structurally that's probably something i would have done and then obviously passing it by value like that is also not great because we're kind of copying all of these variables around for no reason so um because we don't have references in c you would use a pointer uh and then actually pass it in like that and that would just be something that would be a little bit better um again you know not a huge deal we're not calling mainly preparedly we're not copying masses amount of data repeatedly and doing like heap allocations through copy constructors because there's none of that here but it's still something that i probably would do just as a habit okay and then we have p thread in for p threads and we have these p thread counts so we have um he did mention this was multi-threaded of course we have seven threads it looks like and these seven threads so i'm probably going to focus in on the threads in this video because we haven't really talked about multi-threading before too much in this code review series and i you know how long have i been recording for this video is probably way too long already i always these videos always end up being so long um but anyway uh i'm just going to focus in on that aspect of things and then we'll kind of brush everything else aside so what do we do here so we create seven threads with p thread info what is p thread info uh let me try and find that that might be like in the header file uh what is that program.h uh okay here's p thread in first so we have an actual p thread which is just like a unix kind of linux style uh uh thread it's i guess the equivalent of whatever win32 threads would be which i don't know i mean in c plus plus you would use sd thread and then behind the scenes it would it would probably be a p thread on like posix platforms but anyway so we have the actual thread right i wouldn't probably call it handler but we have the actual kind of thread um and then we have stock column and column oh yeah i assume this is just for image data so what this is is probably a struct of data that is going to be local to a particular thread um so it looks like the way that he's probably uh you know sharing data between threads is he's not really sharing data between threads every thread has its own kind of local struct of of data and it probably gets dispatched to do a particular task now the way that i assume this is working is he's probably and i haven't looked at the code yet this is just a guess but i assume he's got regions of the image that he's processing from these individual threads so maybe like a little uh kind of uh square that like a tile that is going to be kind of each thread is going to be but there's seven threads that doesn't really work with tiles anyway we'll see um and then each thread can kind of just focus in on just one region of the image and that way they're never working on the same the same kind of image the same portion of the image the same memory at the same time so there's no real kind of need to synchronize those threads which is good because as soon as you have things like spin locks and various other synchronization like weightings behaviors then obviously they're kind of bottlenecks for slowing down multi-threaded uh programming okay so yeah so this is basically just us kind of uh setting that up that up we actually allocate a bunch of i guess color data here which is going to probably be the destination so image data is destructive image data which let's see if we can quickly find that here we go so image data dot h so we have color a point a an unsigned point a float point that's actually a double and uh destructive image data which i guess has a size which are two ins uh a buffer size element size and then the color pointer data which is probably the destination so that's actually kind of the image that we're creating that we then later display on the screen so that's our rendering target basically like a frame buffer um and then uh okay sure okay okay so we go through the image our size which image does size that's the whole image i think why and then next developer p thread oh okay okay so it looks like he split it up into columns um and that would explain why there's a start column and an end column so he's probably rendering seven columns and and they're kind of each thread is just focusing on its column that it's rendering so the image is split into seven columns and then each thread is rendering a column of the image okay that's interesting now he did um ask me to talk about the cpu cache a bit so i will mention immediately that this this kind of already doesn't sit well with me i don't know how the algorithm works whatsoever maybe it has to be like this but you almost never want to deal with columns going down columns is always almost always worse than going across rows right because assuming the way he's storing this image is uh you know in the way that i guess opengl will expect it when it's done anyway is the fact that like zero zero is probably like the bottom left or the top left probably usually the bottom left and then like the memory kind of advances like as the pixels advance in them in their memory position they're going horizontally so that's kind of friendly in terms of like the cpu cache because as you're kind of looping through that that rendering that you're doing you're you're kind of uh working on memory that is contiguous it's kind of right next to each other and that's a lot more cash friendly than if you were for example going down rows so i'm not i'm not sure if you're actually doing that but if you are that's probably a big performance boost that you'd get if you were doing it horizontally rather than vertically um but again i don't know how the algorithm works maybe it has to go vertically maybe there's i don't know maybe it has to be like that because i did see kind of a lot of vertical lines in that image so it might just be the way it has to be okay there's a demo mode which we can turn off i guess uh if demo mode then it just moves the camera around by itself oh otherwise we have some keys here so scale x distance so f3 f4 scale x okay so we can change the scale i'm just going to see if we can actually do this so left right i will move the camera position i and a that's a a and d is the looking angle i and o is the height up and down is the camera position left and right it's also the camera position x okay so let's go ahead and turn demo mode off and then let's go ahead and oops let's make it again and then i'll just do bin slash main dot bin oh cool okay yeah so see like the way that it's like there are a lot of i don't like that it's like shimmering but there are it looks like it's just made up of vertical lines of color so i maybe the algorithm has to be like that we can rotate which looks absolutely awful with all the shimmering it looks fine when it stops but for some reason when it's moving it's it's a bit weird and then what was it f1 f2 for wall that that's trippy so that's playing with like the perspective which is where you really start to see that it's actually a 2d image um in case there was there ever any doubt and then okay so this is like the draw distance which increases the fps if we make it shorter okay and what else was that i and o and then d and a was just to rotate i think that's it cool right oh what did i press k k l okay to cycle through the different maps wow that's pretty cool yeah this is really nice let's take a look at the actual rendering code so this is all just setting up like camera stuff i'm not gonna bother with that um okay so here's okay so this is the main loop right yeah while so this is inside the main loop so when we get past all of this kind of input handling stuff the first thing we do is create a whole bunch of threads okay that's the first thing i want to tell you is is not ideal you shouldn't be creating threads every like frame what you should use is either a thread pull or in this case since you actually have seven regions on the screen that you're rendering just create the threads as part of your program create function and that's it like you don't have to you can just keep them running right so you can set up like a while loop inside the threads that is actually constantly rendering and then some basic synchronization to make sure that everything's done and kind of like what you're doing here you're waiting for the thread to finish execution and then you're um at that point you're drawing everything else so each thread has to finish its execution and then i guess you're doing sprite draw and then you're moving on so you're blocking the main thread to make sure that all the threads are done and then once they're done you're moving on that's fine but again because it's like you're creating threads like this isn't this isn't very cheap so i would definitely do something like that if you really like the kind of you don't want to have like each thread be its own constantly running thread with synchronization because that i will admit that will make it slightly more complex than this kind of threading model but just use a thread pull right so you have a pool of threads when it's done it gets returned to the pool and then you can obtain a new thread to do your actual work which is this i am render voxel space slice function right so that's something that i would do just to kind of uh avoid the penalty of creating threads every creating like seven threads every frame um and then waiting for them to join and stuff like that um but yeah so each thread gets dispatched to this i'm render voxel space slice which i'm assuming is the actual voxel rendering code we'll take a look at that in a minute and then we have sprite draw with the image data so i actually want to see what this does which let's see program.c core sprite draw and sprite does it there's a lot of these sprite draw okay so put pixel so this stuff will actually uh like blit the pixel into place into a target buffer i guess um but where is it gonna put it where is this function that might be part of like some other library or something i'm gonna image data image data let's see here we go now here it is right this dot data dot position okay so it puts it into image data so we're just copying into image data again each thread would have its own region that it copies into so they're not really overlapping or there's nothing uh bad i guess going on there and then i'm assuming once you have that how are you getting that texture on screen so let's see so sprite draw just does that um and then [Music] printf graphic swap buffers what does that do aha okay well yeah misleading function name i had a feeling it was in here um because that's like the that's like the next thing really but you can see we bind the texture texture sub image 2d which gets that image data from the cpu to the gpu now and then we just issue a like we draw um like six uh vertices here which makes up a quad uh you could make this just a single triangle or something like that but um it just simply does like a quad and then that fragment shader i think that we very briefly saw just basically samples from that texture and then draws the pixels onto the screen so that's kind of how the rendering side of it works again there is just an image data that gets uploaded to the gpu every frame and that's what we're kind of doing it'd be kind of interesting to move this into a compute shader or into like a fragment shader that would be a cool little exercise as well but that being said we've come to the moment of truth where we actually take a look at this voxel kind of algorithm so let's take a look at this and maybe we can actually visualize um in some way how the algorithm is working because i think that'll be cool so render voxel space slides so we take in a bunch of parameters those were those kind of p thread info parameters that we take in here not sure why you had to make it a void pointer considering you obviously know the type but sure we have angle just a bunch of other things sine cosine whatever we go through all this determine the max height i guess i am clear which probably clears the image data and then while that okay so we render it in depth slices and then we go through um all the image data again i'm not really familiar with this algorithm i'm not going to dwell on it too much i'm just looking at the actual code and then we go through uh x okay so right so we're going through horizontally but then we're drawing vertical lines into the buffer yeah so this is what i was talking about with the whole cpu cache situation um we're kind of going through max height and we're putting star dot y to i so this loop that actually draws the pixels is going like it's very possible that i mean to be fed like the the image size isn't very big because it looks so low resolution but it could be like a a cache miss every time you go down that row because obviously the memory that you're drawing to is far apart it's not close together like it would be if you were you know doing start x equals i so that's just something to kind of think about um in terms of uh like how your memory is laid out and how you're rendering to it uh but okay and then we basically draw vertical lines um which draw some kind of yeah vertical line so that could also be difficult to do if you switch it to horizontal i'm not sure how you would do that but um that seems to be uh what you're doing here and then that's the position color maps map index okay so we get like the position from the map where's the there's probably like a height sample that we do here height map color from height maps again i'm trying to be very brief here um and then we draw a vertical line based on uh like the color that we've read in and then i guess the distance is also going to be height on screen which comes from the height map but then also probably to do with how far away it is which is the z value yeah so height on screen you can see it gets divided by z times like the scale which we played with again during a run time so stuff is further away obviously smaller and closer to the center of the screen which is our vanishing point so yeah pretty pretty cool again really interesting to see the fact that i guess this does in fact uh get dispatched on multiple threads and we're actually operating on separate regions of this image the actual code is identical for each region it's just the coordinates are offset and so it's kind of easy to multi-thread this is how like ray tracers and offline renderers like to do things as well they have these tiles on the screen and then like each cpu core or even better each like gpu processing unit as well uh if you're rendering on the gpu instead of the cpu can actually be dispatched to handle like a particular region of the image and do the calculations for them that's also how just normal hardware accelerated rendering works so we have like groups of pixels in the fragment shader that get processed at the same time because they're all kind of independent of each other uh and that's like a huge optimization to having to kind of do it in a serial fashion where you go through every like pixel one by one that's like a disaster um because again the pixels aren't really dependent on each other so it's really easy to kind of uh i guess multi-thread this so you could you could definitely like seven threads is fine again cpus aren't really massive parallel machines so i think that it would be really cool for you to take this project and move it to the gpu because that would be really cool and that would just be way way way better performing so that's kind of my little challenge for you but anyway let's see if we can visualize the rendering because that would be i think it would be cool to actually see the threads being drawn so for that to happen we'll have to slow down the rendering so let's see what we can do with the main loop so my idea is just to basically have like a max iteration that we can draw to so if i do like um [Music] let's do this uh but then we can't really synchronize it between threads very easily can we or maybe we can so let's just make like a static end s max iteration so i'm going to be a little bit loose with my synchronization because i don't really care about like this isn't this is just to test things um but if we make that what i want to do is basically tell it oh no no i don't have to okay this is what i'll do so i've got my so what this variable is going to be max iteration is as as many pixels or as it's not going to be directly pixels but as many iterations of the rendering that we're allowing right so in other words like we can tell it hey i only want you to draw five lines this frame and then next frame it'll be six lines and then seven frames sorry seven lines so we'll actually kind of see the image being created over time um and so to do that what i'll do is let's just do it here for now all i'm going to do is i'm going to say if and we're going to be only reading this variable here so it's going to be fine across threads if iteration which we'll keep track of up here iteration equals zero if iteration uh is like if iteration uh is greater than max iteration then we're going to break right and then we'll also increment iteration so we have a max iteration which will just basically increment every frame and then we'll uh prevent it from doing any further rendering if it gets to the point of like our max desired iteration so over here after we join all the threads i'm going to increment max iteration this might be too slow might be too fast i'm not sure we might have to add like a little thread sleep as well which we could do to slow it down but let's go ahead and i'll do make and bin slash main dot bin and let's see if we can visualize the kind of threads doing their rendering okay okay cool so that was very very fast but you kind of saw how it was being drawn from the front to the back and that's obviously thanks to the fact that this while loop goes from z which is like one all the way off into the distance but let's also add like a little thread sleep so i think it's like i think we can use like you sleep which is in microseconds so let's leave for like 10 milliseconds uh and see if that kind of slows us down a little bit okay yeah so you can see that there it still kind of looks like it's okay we really need to separate the threads i think and we can do that by um by telling it to uh draw to show how it's actually doing the actual pixel rendering so i'll move this into here as well and then this is probably going to be very slow now because it's going to break it's going to break a whole lot easier so maybe we'll reduce the sleeping let's see if we can get rid of sleeping entirely okay cool so we had to wait a while but here it is and you can see how those vertical lines are all being drawn kind of from left to right but then they're actually ending at a certain point right like we're drawing left to right but then it stops and they're all also happening kind of at the same time which is pretty cool if we wait a little bit longer we can see more of it being drawn and you can see how you can kind of see how the screen's divided into these seven regions and then we're kind of going through and drawing these vertical lines which might not be very deep i guess but some of them are a little bit higher than others so that's pretty cool i love visualizing algorithms like this and seeing like how they actually work um and like what order they do things in uh it's i think it's really kind of educational and it's also just fun to look at [Music] so yeah pretty cool little project i definitely enjoy this code review you can also take a look at this if you like because i've left a link in the description below to this repository hope you guys enjoyed this little code review leave your thoughts in the comment section below as always for this series you can send in your code to cherno review gmail.com there'll be a not a link but like the email address will be in the description along with the instructions of what you need to do if you want to send in your code for me to take a look at but otherwise i hope you guys enjoyed don't forget to hit the like button if you did and check out skillshare using the link in description below as well see you guys later goodbye do [Music] you
Info
Channel: The Cherno
Views: 83,439
Rating: 4.913558 out of 5
Keywords: thecherno, thechernoproject, cherno, c++, programming, learn c++, c++ tutorial, code review, c99, c programming language, minecraft, voxel engine, voxel renderer, comanche, retro, old school, pixel art
Id: 7A3YmJH4iyk
Channel Id: undefined
Length: 36min 17sec (2177 seconds)
Published: Fri Aug 06 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.