
Show HN: 3D Game Programming using bitblting in Windows - lowlevel86
https://github.com/lowlevel86/3D-Game-Programming
======
ePierre
If you're into game programming, Casey Muratori's Handmade Hero [1] project is
worth checking. From the website description, “Handmade Hero is an ongoing
project to create a complete, professional-quality game accompanied by videos
that explain every single line of its source code”.

[1] [https://handmadehero.org/](https://handmadehero.org/)

~~~
pooloo1
Casey goes through the math, programming, and the logical processes of
understanding how to build a an engine and a game with that engine. Its
extremely deep, and extremely thorough, but it is by far the most extensive
and explicit resource on the topic to date.

~~~
bikitan
"Extremely deep" is an understatement. It's long, deep and plodding enough as
to be completely inaccessible to any but the most dedicated, and you have to
be willing to spend hundreds, if not thousands of hours by the time it's over,
going through it.

I love and appreciate what he's trying to do, but it's overkill.

~~~
reificator
On the contrary, the market is flooded with quick, easy tutorials with the
(sometimes implied) disclaimer that it's not production ready. To have just a
single example of the opposite is wonderful.

------
cabaalis
Allow me to recommend this book as well: 3D Math Primer for Graphics and Game
Development [https://www.amazon.com/Primer-Graphics-Development-
Wordware-...](https://www.amazon.com/Primer-Graphics-Development-Wordware-
Library/dp/1556229119)

It helped me tremendously when I was into game development back in college.
The math was always the biggest hangup for me. I'm in healthcare software now,
so YMMV :)

~~~
Zolomon
This book is very good at explaining the underlying mathematics and models
used to create modern near-realtime 3D renderings today, like games.

There are several sites that teach OpenGL, which is one of the main APIs
today:

* [https://learnopengl.com](https://learnopengl.com)

* [http://www.opengl-tutorial.org/beginners-tutorials/tutorial-...](http://www.opengl-tutorial.org/beginners-tutorials/tutorial-1-opening-a-window/)

* [https://open.gl](https://open.gl)

------
Ace17
This is nice! Too bad it's tied to one platform (Windows), instead of some
more generic API (like SDL or opengl).

~~~
lostgame
My thoughts exactly. I guess I’m so buried in the coding world that I actually
have to do a double-take when reminded Windows is an option, or that people
actually use it for development.

------
bitwize
Wowwwwwww... This takes me back to the early 90s when I tried to write simple
(2D) sprite games using Borland C++, Windows 3.x, and the BitBlt and DIB APIs.

------
NKCSS
ALways nice to see simple examples, but just going through an example, I see
this:

imgWidth = pow(2, log(sqrt(stat_p.st_size/3))/log(2));

And I have no clue why because if you take 128 as input value; it converts it
either to ~126 if you don't trunc to int before doing imgWidth * imgWidth * 3,
if you truncate to int, you get 108; for what reason?

~~~
quincunx
From the comments that precede it (3DPlatformer/platformer.c line 89-91), I
think the idea is that the input is the size (in bytes) of the bitmap W * H *
3, where W == H and W is a power of two. So the input for a square bitmap as
the comments suggest would be 128 * 128 * 3, if we unravel from there, log(x)
/ log(2) extracts the most significant bit, pow(2, log(x) / log(2)) sets that
bit.

pow(2, log(sqrt(128 * 128 * 3 / 3)) / log(2))

pow(2, log(128) / log(2))

pow(2, 7)

128

It's funny that on the one hand the code is beautifully terse enough that this
ugly wart is easily discovered, yet on the other that the code would have such
a wart to begin with.

~~~
NKCSS
If I run this in C#; it truncates to 6 and not 7... does C do an implicit
round?

Broken down, you get this:

    
    
                int toTest = 128;
                var div = toTest / 3;
                var sqrt = Math.Sqrt(div);
                var logSqrt = Math.Log(sqrt);
                var log2 = Math.Log(2);
                var logDiv = logSqrt / log2;
                var pow = Math.Pow(2, logDiv);
                var result = pow * pow * 3;
    
                int result2 = (int)Math.Pow(2, Math.Log(Math.Sqrt(128 / 3)) / Math.Log(2));
                int total = result2 * result2 * 3;
    
    		this	{Clusters.DAL.Tests.UtilitiesTests}	Clusters.DAL.Tests.UtilitiesTests
    		toTest	128	int
    		div	42	int
    		sqrt	6.48074069840786	double
    		logSqrt	1.8688348091416842	double
    		log2	0.69314718055994529	double
    		logDiv	2.6961587113893803	double
    		pow	6.4807406984078613	double
    		result	126.00000000000004	double
    		result2	6	int
    		total	108	int

~~~
quincunx
toTest should be 128 * 128 * 3 to reflect 128 by 128 pixels of 3 bytes each,
as would be the file size.

edit: just to add, I think "width = sqrt(filesize/3)" would be a better
version for newbies. Even better would be to replace the code altogether.

~~~
NKCSS
I found what was causing the off-result for me; if you do: 128 / 3 (2 int's)
in C# it will truncate to int; if you do 128 / 3.0 you'll get the expected
output and everything starts to make sense :)

~~~
quincunx
ok, two other things that may be throwing you off, the input is a filesize,
not a width, the result of filesize / 3 is always an integer (even if float
representation,) and the result of sqrt(filesize/3) is also always an integer
(even if float representation,) as per the comments.

Also, taking the 2log and then 2power is a no-op because it doesn't truncate /
ceiling in-between the two.

You kind of see where the code was going to, but it never quite made it there.

------
partycoder
I do not recommend learning the Windows API. There are convenient
crossplatform alternatives to it for this use-case.

~~~
chii
There's no harm in using/coding against the windows api, provided that you
know what's a windows api quirk vs what's normal coding idiom. Granted,
beginners may not be able to differentiate, but that's due to inexperience,
which will be fixed in time. There's nothing wrong with learning a platform.

~~~
dkersten
> There's no harm in using/coding against the windows api

It needlessly restricts your platform though. For example, I was very
interested in the handmade hero tutorials linked elsewhere here, but was
dismayed that the first few lessons are all windows api. I don't have a
windows machine anymore, so I have no use for those lessons. I'm sure I can
still learn from the later lessons, but I won't be able to simply follow along
with his code. A significant number of people, especially people who would
play Indie games, are now using macbooks nowadays. Maybe still not the windows
gamers numbers, but enough that I wouldn't ignore it, and I've passed on quite
a number of promising looking Indie games because they were windows only.
Windows API is also notoriously clunky, at least, compared to the wrapper
libraries.

Obviously if you only have a windows machine, by all means, focus on making it
work for that, but why put roadblocks in your way for potential future ports
when nowadays you really don't have to because great cross platform
abstractions exist (SDL2, GLFW, SMFL, ...)

> There's nothing wrong with learning a platform.

Sure. If that's your goal. If its just a means to an end, why not use a
library to do the hard work so you can focus on what you actually want to do
(and not locking you out of other platforms later without a ton of work is an
added bonus).

~~~
cturner
"It needlessly restricts your platform though"

Not necessarily. Casey discusses this issue well into his series, but it would
be twenty or thirty hours in.

There are two general approaches to multiplatform.

Approach 1: the SDL way. Have an abstract system that represents a generic
desktop environment (e.g. QT suite). Build your apps into that. The upside of
this is that you can build once, run everywhere. Some downsides are that it
will not necessarily feel like it fit into the ecosystem, and you need to
learn an abstract platform. That platform may have license overhead. When they
do version upgrades, you might not like what they do, but you are stuck with
them.

Approach 2: native wrappers. Build your codebase in generic C/C++. Then write
endpoints interface wrappers for each platform you deliver to. In this case,
you could create a Windows codebase that #includes your game code, and calls
into it from WinAPI. For linux, you can create a ncurses or GTK frontend, and
then #include into the samre game codebase. Your product will feel like it was
built for your target platform. Yes, you need to learn platform-specific ways
of doing things. But once you have, your end-users get native feel. If you
want to call a Windows voice API for the Windows version of your product, you
can do it without depending on a vendor.

If you were building a forms application, Approach 2 would be painful. But if
you are building a game, Approach 2 is viable. You probably need a single-
window wrapper. It would need to have a menu system, to be able to display an
image, and to be able to consume keystrokes and mouse events. I don't have
much audio experience, but the little experience I have is that the moment you
want to do something remotely sophisticated with audio (e.g. creating a music
sequencer) you want to be working against native APIs. I would rather build
something from first principles on a robust system API than debug bizarre
threading behaviour in the SDL mixer library.

~~~
partycoder
No, on Windows, SDL will interface directly with the Windows API, not GTK or
Qt. And will give you a native window, the same as if you do it by hand.

Proof:
[http://hg.libsdl.org/SDL/file/35da714ed287/src/core/windows](http://hg.libsdl.org/SDL/file/35da714ed287/src/core/windows)

If you don't like SDL that is your opinion or choice, but don't come here
presenting false facts to influence other people into having your same
preferences (e.g: over half of what you said was technically incorrect and not
true). You should really do 1 minute of research before making ill-intentioned
accusations like these.

Strictly speaking, SDL does all the Windows API boilerplate for you. And also
does it on macOS, and on Linux with no substantial cost since SDL itself is
very lightweight.

It also does more stuff for you, like loading fonts, images, sounds, etc...
and again, in a crossplatform manner.

All of this was provided to the community as open source, for free. Sam
Lantinga is a hero.

If you don't like a specific part of SDL or whatever project, find an
alternative project, submit a bug report, submit a fix, or fork it. That
benefits everyone. That's how open source works.

~~~
cturner
I introduced Qt because it is an example of a high-quality, full-system
framework that you can chose to live in. I did not mean to imply that SDL used
Qt, but I can see how it reads that way.

When I was following Handmade Hero, some of the fanbase were maintaining a
linux version of the project in SDL, and moving them forward at the same pace
as Casey's project.

    
    
       > It also does more stuff for you, like loading
       > fonts, images, sounds, etc... and again, in
       > a crossplatform manner.
    

I found it to be awkward for audio. When you pick a library, it can steer the
direction of your software. You can end up having to do things the library-way
instead of the you-way. As a general rule, the more the library does for you,
the more likely this is to happen.

This is going back a bit, but I remember SDL media having background threads.
If you let SDL draw you into a multithreaded approach, you would lose tuning
options you might need later, because you would not be able to reason cache
behaviour.

    
    
        > If you don't like a specific part of SDL or
        > whatever project, find an alternative project
    

Through your forceful words, here and elsewhere, I get the impression that you
think that this is the "one true approach". But I think there is a respectable
alternative: suck up the native-integration cost in order to forgo the
dependency cost. Bedrock against the system where it is non-horrific to do so,
in order to own your codebase. I don't think a hacker should have to apologise
for choosing that approach.

~~~
dkersten
I haven't used SDL for audio in many years, but most games nowadays use FMOD
or Wwise and not platform native audio API's and they work just as well with
SDL (or GLFW or whatever) as without. Both FMOD and Wwise offer a free
version[1].

Note also, in case you're not already aware, that SDL 2 is quite a different
beast than SDL 1.x and is much improved in many ways, although I can't speak
for addon libraries (I don't know what SDL media is like, for example).

[1] The free version of Wwise has a limit of 500 sounds and your budget must
be under $150K; while FMOD is free as long as your budget under $500k and you
can only publish one game a year under this license -- either way, both are
financially accessible to Indies and both are free during
evaluation/development.

