Ray Casting
-
I think it's a good idea that I explain my code (
>> 16
does not do division).First you need to know, how binary counting works. If you don't know that yet, you can probably best google that (it's not as hard as it sounds).
The binary representation of
8421504
(so from base 10 to base 2) is100000001000000010000000
, so each color has 8 digits (called bits) to represent their value. And in this case, each color has the value10000000
.Now it's good to know that the base 2 value of
255
is11111111
(also 8 digits, all 1).the
&
operator checks the binary representation on both sides, and compares them digit by digit. If both digits are true (hence the &-sign forand
), then the resulting binary digit is also true. You can imagine lower values to be zero filled from the left (just like with our counting system, base 10, you don't write 0255 for 255, you leave out the leading 0's, you do that in binary too).So when we do
8421504 & 255
, we check100000001000000010000000 & 000000000000000011111111
. Lets put them below each other so it's better readable:100000001000000010000000 & 000000000000000011111111 = 000000000000000010000000 // only the last 1 of the larger number occurs on both sides
If you leave out the leading zero's of the result, you'll end up with 128:
10000000
.So this way we get 128 for the red value, but since colors in FUZE are expressed in percentage, we divide by
256
, this leaves us with a nice round.5
(you probably want to divide by255
to be accurate, since we start counting with 0, not 1, but that would lead to a slightly higher number than.5
, so it depends on what you try to do).Now we want to do the same thing for the other bits in the source value (basically it represents
bbbbbbbbggggggggrrrrrrrr
in RGB). So to do this, we shift the binary representation to the right 8 positions, using the bitshift operator:nr >> 8
, so this would leave us withbbbbbbbbgggggggg
or in this case with:1000000010000000
.Once again below each other:
100000001000000010000000 >> 8 = 000000001000000010000000 //assuming bitshifting in FUZE is Zero-fill // then: 000000001000000010000000 & 000000000000000011111111 = 000000000000000010000000 // It leaves us with the same result, but this time it's from the middle 8 bits
So now we have the 128 of the green value, divide it by 255 and you end up with the color percentage value that FUZE uses.
If you understood all this, then you probably also get that
>> 16
moves the bits 16 positions, in stead of 8, leaving us withbbbbbbbb
(or10000000
) or in this case, the blue value. Not by division, but by shifting bits (there could have been 1's before the number, they'd stay in):0100000001000000010000000 >> 16 = 0000000000000000010000000 // or just 10000000 if you leave out leading zero's // then: 0000000000000000010000000 & 0000000000000000011111111 = 0000000000000000010000000 // Still the same result, but this time it's from the first 8 bits
So I hope that explanation made sense, and also, you might want to use the following function in stead (depending on what you try to do):
function getColorForNumber(nr) return { (nr & 255) / 255, ((nr >> 8) & 255) / 255, ((nr >> 16) & 255) / 255 } getColorForNumber(8421504) // should return { 0.5019607843137255,0.5019607843137255,0.5019607843137255 }
-
This post is deleted! -
I didn't fully understand the binary business till now. Thanks for the time you put in to explain this in a clear and concise manner. Have a great weekend.
-
Hey,
great project, I am looking into something similar my self and am happy to see others also doing RayCasting. 🧙
I think the texture mapping could be doable with
drawImage
with it's source and target parameter version. Then finding the exact intersection point between the wall and the ray should determine which part of the texture to copy where... 🥳I'm also curious if sectioning the code into functions could be helpful for readability, or if you would expect any downsides to splitting the code into smaller chunks. 🤔
-
@PB____ said in Ray Casting:
function getColorForNumber(nr) return { (nr & 255) / 255, ((nr >> 8) & 255) / 255, ((nr >> 16) & 255) / 255 } getColorForNumber(8421504) // should return { 0.5019607843137255,0.5019607843137255,0.5019607843137255 }
That function can be further optimized for the latter 2 elements if you don't care much for readability.
function getColorForNumber(nr) return { (nr & 255) / 255, (nr & 65280) / 65280, (nr & 16711680) / 16711680 }
It's basically doing a l-shift on the mask, and using that value as the mask and division. The removal of 2 operators should speed it up.
255 << 8 = 65280
255 << 16 = 16711680 -
@mario-bodemann
I agree about readability being a problem but I'm just basically copying Lode's code as starters then I'll think about tidying it up !
Looking at the basic texture mapping (see Lode) - not the Wolfenstein section - I'm having extreme trouble with the multi dimensional array part. For example if I do texture[7] = 8421504 followed by mycol = texture[7] I get 3.7 fps. OK that's bad but I can speed that up somewhere along the line. BUT if I do texture[7][7] = 8421504 again followed by mycol = texture[7][7] I get 0.2 fps ! These are just example array elements of course. Fred[8] = 9 seems to be faster than fred[8][8] = 9 - if you see what I mean.
As for this section of his code - well I'm completely baffled ...
std::vector<Uint32> texture[8];
for(int i = 0; i < 8; i++) texture[i].resize(texWidth * texHeight);
This is going to take time to get working. Not sure if I'll pop off before I finish it !
Great that someone else is keen to get ray casting up and running. -
If we had getPixel texture mapping would be a doddle. I think I'll play Witcher 3 while waiting for the next Fuze update. Please Fuze team, we need a getPixel command. Thanks
-
Is there anyway to convert this C code snippet to an equivalent in Fuze …
std::vector<Uint32> texture[8];
for(int i = 0; i < 8; i++) texture[i].resize(gWidth * gHeight);I'm still battling with this texture mapping problem. Not giving up yet !
-
Hmmmmmm.
I'm thinking something like this:
output = createImage(gwidth, gheight, false, image_rgba) setDrawTarget(output) drawImage(input, {0,0,input_w, input_h}, {0,0,output_w,output_h}) setDrawTarget(framebuffer)
Could be doing the trick. It's drawing the input image in the size of the output image. (output_w etc is the width of the output, etc)
That said: Usually the scaling of images shouldn't be a big bottleneck on the code. Usually the math takes way longer. (GPU vs CPU). So I'm not very sure that you'd need to scale beforehand...
-
Managed to get the texture mapping running but it's less than 1 fps with the screen at gWidth/4 and gHeight/4. I think it's the maths (converting the textures to screen cords) where the problem is.
At full screen it's like 20 seconds before it even starts. Pressing on and on …. -
That's very interesting. I was hoping the speed might be enough ... Do you mind resharing your code? I would love to take a look at it.
(Once I'm at home that is)
-
The code is a mess - sorry.
NHT3DMND15 - still pending as I write this. -
World map need to be rewritten :-
int worldMap[mapWidth][mapHeight]=
{
{4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,7,7,7,7,7,7,7,7},
{4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,7},
{4,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7},
{4,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7},
{4,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,7},
{4,0,4,0,0,0,0,5,5,5,5,5,5,5,5,5,7,7,0,7,7,7,7,7},
{4,0,5,0,0,0,0,5,0,5,0,5,0,5,0,5,7,0,0,0,7,7,7,1},
{4,0,6,0,0,0,0,5,0,0,0,0,0,0,0,5,7,0,0,0,0,0,0,8},
{4,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,1},
{4,0,8,0,0,0,0,5,0,0,0,0,0,0,0,5,7,0,0,0,0,0,0,8},
{4,0,0,0,0,0,0,5,0,0,0,0,0,0,0,5,7,0,0,0,7,7,7,1},
{4,0,0,0,0,0,0,5,5,5,5,0,5,5,5,5,7,7,7,7,7,7,7,1},
{6,6,6,6,6,6,6,6,6,6,6,0,6,6,6,6,6,6,6,6,6,6,6,6},
{8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4},
{6,6,6,6,6,6,0,6,6,6,6,0,6,6,6,6,6,6,6,6,6,6,6,6},
{4,4,4,4,4,4,0,4,4,4,6,0,6,2,2,2,2,2,2,2,3,3,3,3},
{4,0,0,0,0,0,0,0,0,4,6,0,6,2,0,0,0,0,0,2,0,0,0,2},
{4,0,0,0,0,0,0,0,0,0,0,0,6,2,0,0,5,0,0,2,0,0,0,2},
{4,0,0,0,0,0,0,0,0,4,6,0,6,2,0,0,0,0,0,2,2,0,2,2},
{4,0,6,0,6,0,0,0,0,4,6,0,0,0,0,0,5,0,0,0,0,0,0,2},
{4,0,0,5,0,0,0,0,0,4,6,0,6,2,0,0,0,0,0,2,2,0,2,2},
{4,0,6,0,6,0,0,0,0,4,6,0,6,2,0,0,5,0,0,2,0,0,0,2},
{4,0,0,0,0,0,0,0,0,4,6,0,6,2,0,0,0,0,0,2,0,0,0,2},
{4,4,4,4,4,4,4,4,4,4,1,1,1,2,2,2,2,2,2,3,3,3,3,3}
}; -
With
[ ]
not { } of course ! -
I've tided up the code a bit - removed rems etc. 3VR63MND15
-
I think, on reflection, N9LKAMND15 is the preferred version. There's no way that true texture mapping without getPixel is feasible. I'm open to ideas but I think the maths is the bottleneck.
-
Changed the world map. It's a bit more interesting. Move and rotate speeds adjusted a touch.
Now called Ray Casting, XFF63MND15
30 - 33 fps with plain coloured blocks. -
I've updated Ray Casting to Raycasting2.
I think I can turn it into a game if I add a random exit and maybe monster sprites floating around the map. SY573MND15 -
@faz808 said in Ray Casting:
There's no way that true texture mapping without getPixel is feasible. I'm open to ideas but I think the maths is the bottleneck.
The problem is that the tutorial processes each pixel 1 by 1 in software. It is true that Wolfenstein 3D ran on a 286 computer, but no 286 computer had a 1920x1080 or even a 1280x720 pixel screen. Using the builtin line() function with a screen width reduced by factor of 4 as you have done alleviates this. You can also use the builtin Box() function to draw a 4 pixel wide vertical line with a single command.
However to do this with texture mapping, you will need to use a builtin image drawing command instead of slowly looping over all the pixels on the line. You will also need to generate the textures in video (image) memory using the createImage(), setDrawTarget() functions.
I cannot think of a reason why you think a 'getPixel' function would speed this up. Moving data from the video card just to send it back should only slow it down.
I have tried this with a few functions. drawImage() works but doesn't handle the clipping well. It will error if you are too close to a wall with out clipping code and it doesn't have a tint parameter to darken a side with. I was going to try drawImageEx(), but it doesn't have a source rectangle parameter which is needed to select a 1 pixel wide strip from the texture. It could probably still be used if you had a huge array of 1 pixel wide texture images.
I tried drawQuad(). It is a tiny bit slower because of the array of 4 vectors it needs as a parameter, but the results are very good. No clipping code is needed and the 'Tint' parameter enables the darkening of one side.
I also went ahead and wrote code to generate the generated textures in the tutorial. The way the tutorial loops over the pixels of all 8 textures in one loop is a bit awkward and causes Fuze to produce an error unless an update() call is placed in the loop. The update() call I put into the loop only cause a fraction of a second of black screen at startup. I also added modes to do no textures, and simple (no black screen startup) textures.
My modified program is named "ray casting w textures" and is shared, submitted (and pending atm) with code: D1SR3MND9L
-
That is pretty awesome.
Awesome.