Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Renderer: Texture mapping #49

Merged
merged 10 commits into from
Dec 19, 2018
4 changes: 3 additions & 1 deletion pebbleapp/PDoom.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ bool loadTextures()
static const int countIds = sizeof(resourceIds) / sizeof(uint32_t);
for (int i = 0; i < countIds; i++)
{
if (loadTextureFromResource(resourceIds[i]) == INVALID_TEXTURE_ID)
TextureId texId = loadTextureFromResource(resourceIds[i]);
if (texId == INVALID_TEXTURE_ID)
return false;
texture_load(NULL, texId);
}
return true;
}
Expand Down
1 change: 1 addition & 0 deletions renderer/algebra.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ real_t real_max(real_t a, real_t b);
real_t real_abs(real_t a);
int real_signInt(real_t a);
real_t real_lerp(real_t value, real_t start, real_t end); // input amplitude is normal 1
real_t real_norm_lerp(real_t value, real_t inputStart, real_t inputEnd, real_t outputStart, real_t outputEnd);
real_t real_clamp(real_t minimum, real_t value, real_t maximum);

real_t real_floor(real_t a);
Expand Down
6 changes: 6 additions & 0 deletions renderer/algebra_float.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ real_t real_lerp(real_t value, real_t start, real_t end)
return real_add(real_mul(value, ampl), start);
}

real_t real_norm_lerp(real_t value, real_t inputStart, real_t inputEnd, real_t outputStart, real_t outputEnd)
{
real_t normalized = real_div(real_sub(value, inputStart), real_sub(inputEnd, inputStart));
return real_lerp(normalized, outputStart, outputEnd);
}

real_t real_clamp(real_t minimum, real_t value, real_t maximum)
{
return real_max(minimum, real_min(value, maximum));
Expand Down
39 changes: 26 additions & 13 deletions renderer/level.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,49 +8,62 @@ Level *level_load(int levelId)
Wall walls_template[] = {
// start triangle
{.startCorner = xz(real_from_int(70), real_from_int(50)),
.color = GColorFromRGB(0, 255, 0),
.texture = 0,
.texCoord = { xy_zero, xy_one},
.portalTo = 1},
{.startCorner = xz(real_from_int(0), real_from_int(50)),
.color = GColorFromRGB(255, 0, 255),
.texture = 0,
.texCoord = { xy_zero, xy_one},
.portalTo = -1},
{.startCorner = xz(real_from_int(0), real_from_int(-30)),
.color = GColorFromRGB(0, 255, 255),
.texture = 0,
.texCoord = { xy_zero, xy_one},
.portalTo = -1},

// northern trapez
{.startCorner = xz(real_from_int(70), real_from_int(50)),
.color = GColorFromRGB(0, 128, 0),
.texture = 0,
.texCoord = { xy_zero, xy_one},
.portalTo = 2},
{.startCorner = xz(real_from_int(50), real_from_int(80)),
.color = GColorFromRGB(128, 0, 128),
.texture = 0,
.texCoord = { xy_zero, xy_one},
.portalTo = -1},
{.startCorner = xz(real_from_int(20), real_from_int(80)),
.color = GColorFromRGB(0, 128, 128),
.texture = 0,
.texCoord = { xy_zero, xy_one},
.portalTo = 3},
{.startCorner = xz(real_from_int(00), real_from_int(50)),
.color = GColorFromRGB(128, 128, 0),
.texture = 0,
.texCoord = { xy_zero, xy_one},
.portalTo = 0},

// right wing
{.startCorner = xz(real_from_int(70), real_from_int(50)),
.color = GColorFromRGB(128, 0, 0),
.texture = 0,
.texCoord = { xy_zero, xy_one},
.portalTo = -1},
{.startCorner = xz(real_from_int(90), real_from_int(65)),
.color = GColorFromRGB(0, 0, 128),
.texture = 0,
.texCoord = { xy_zero, xy_one},
.portalTo = -1},
{.startCorner = xz(real_from_int(50), real_from_int(80)),
.color = GColorFromRGB(255, 0, 128),
.texture = 0,
.texCoord = { xy_zero, xy_one},
.portalTo = 1},

// left wing
{.startCorner = xz(real_from_int(00), real_from_int(50)),
.color = GColorFromRGB(255, 128, 0),
.texture = 0,
.texCoord = { xy_zero, xy_one},
.portalTo = 1},
{.startCorner = xz(real_from_int(20), real_from_int(80)),
.color = GColorFromRGB(255, 128, 128),
.texture = 0,
.texCoord = { xy_zero, xy_one},
.portalTo = -1},
{.startCorner = xz(real_from_int(-20), real_from_int(65)),
.color = GColorFromRGB(128, 255, 128),
.texture = 0,
.texCoord = { xy_zero, xy_one},
.portalTo = -1},
};
Sector sectors_template[] = {
Expand Down
3 changes: 2 additions & 1 deletion renderer/level.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ typedef struct Location {

typedef struct Wall {
xz_t startCorner;
GColor color;
int portalTo;
TextureId texture;
TexCoord texCoord;
} Wall;

typedef struct Sector {
Expand Down
64 changes: 54 additions & 10 deletions renderer/renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ typedef struct
} left, right;
} WallSection;

bool_t renderer_clipByFov(const Renderer* me, lineSeg_t* wallSeg)
bool_t renderer_clipByFov(const Renderer* me, lineSeg_t* wallSeg, TexCoord* texCoord)
{
xz_t leftIntersection, rightIntersection;
bool_t intersectsLeft = xz_lineIntersect(*wallSeg, me->leftFovSeg, &leftIntersection);
Expand All @@ -91,23 +91,29 @@ bool_t renderer_clipByFov(const Renderer* me, lineSeg_t* wallSeg)
real_t wallPhaseRight = intersectsRight ? xz_linePhase(*wallSeg, rightIntersection) : real_zero;
bool_t inWallSegLeft = real_inBetween(wallPhaseLeft, real_zero, real_one);
bool_t inWallSegRight = real_inBetween(wallPhaseRight, real_zero, real_one);
real_t texCoordAmpl = real_abs(real_sub(texCoord->start.x, texCoord->end.x));
real_t texCoordStart = real_min(texCoord->start.x, texCoord->end.x);
bool_t result = true;

if (real_compare(wallSeg->start.xz.z, me->leftFovSeg.start.xz.z) <= 0)
{

wallSeg->start.xz = (real_compare(leftIntersection.z, real_zero) > 0 && inWallSegLeft)
? leftIntersection
bool_t useLeftIntersection = (real_compare(leftIntersection.z, real_zero) > 0 && inWallSegLeft);
wallSeg->start.xz = useLeftIntersection
? leftIntersection
: inWallSegRight ? rightIntersection : (result = false, xz_zero);
if (useLeftIntersection)
texCoord->start.x = real_add(real_mul(wallPhaseLeft, texCoordAmpl), texCoordStart);
}

if (real_compare(wallSeg->end.xz.z, me->leftFovSeg.start.xz.z) <= 0)
{
wallSeg->end.xz = (real_compare(rightIntersection.z, real_zero) > 0 && inWallSegRight)
bool_t useRightIntersection = (real_compare(rightIntersection.z, real_zero) > 0 && inWallSegRight);
wallSeg->end.xz = useRightIntersection
? rightIntersection
: inWallSegLeft ? leftIntersection : (result = false, xz_zero);
if (useRightIntersection)
texCoord->end.x = real_add(real_mul(wallPhaseRight, texCoordAmpl), texCoordStart);
}

return result;
}

Expand Down Expand Up @@ -139,12 +145,14 @@ void renderer_renderWall(Renderer* me, GColor* framebuffer, const DrawRequest* r
const Sector* const sector = request->sector;
const Wall* const wall = &sector->walls[wallIndex];
const real_t nearPlane = me->leftFovSeg.start.xz.z;

lineSeg_t wallSeg;
renderer_transformWall(me, sector, wallIndex, &wallSeg);
if (real_compare(wallSeg.start.xz.z, nearPlane) < 0 && real_compare(wallSeg.end.xz.z, nearPlane) < 0)
return;

if (!renderer_clipByFov(me, &wallSeg))
TexCoord texCoord = wall->texCoord;
if (!renderer_clipByFov(me, &wallSeg, &texCoord))
return;

WallSection p;
Expand All @@ -162,7 +170,10 @@ void renderer_renderWall(Renderer* me, GColor* framebuffer, const DrawRequest* r
}

// render wall
for (int x = max(request->left, p.left.x); x <= min(request->right, p.right.x); x++) {
const Texture* const texture = texture_load(me->textureManager, wall->texture);
const int renderLeft = max(request->left, p.left.x);
const int renderRight = min(request->right, p.right.x);
for (int x = renderLeft; x <= renderRight; x++) {
const int yBottom = me->yBottom[x];
const int yTop = me->yTop[x];
GColor* curPixel = framebuffer + x * RENDERER_HEIGHT + yBottom;
Expand All @@ -177,21 +188,54 @@ void renderer_renderWall(Renderer* me, GColor* framebuffer, const DrawRequest* r
me->yTop[x] = yPortalEnd = clampi(yBottom, lerpi(portalNomEnd, 0, sector->height, yCurStart, yCurEnd), yTop);
}

real_t xNorm = real_div(real_from_int(x - p.left.x), real_from_int(p.right.x - p.left.x));
real_t invZLerped = real_lerp(xNorm, real_reciprocal(wallSeg.start.xz.z), real_reciprocal(wallSeg.end.xz.z));
real_t invTexLerped = real_lerp(xNorm,
real_div(texCoord.start.x, wallSeg.start.xz.z),
real_div(texCoord.end.x, wallSeg.end.xz.z)
);
real_t texLerped = real_div(invTexLerped, invZLerped);
int texCol = real_to_int(real_mul(texLerped, real_from_int(texture->size.w)));

real_t yNormalized = yCurStart >= 0 ? real_zero
: real_div(real_from_int(-yCurStart), real_from_int(yCurEnd - yCurStart));
real_t texRow = real_mul(real_lerp(yNormalized, texCoord.start.y, texCoord.end.y), real_from_int(texture->size.h));
real_t texRowIncr = real_div(
real_mul(real_sub(texCoord.end.y, texCoord.start.y), real_from_int(texture->size.h)),
real_from_int(yCurEnd - yCurStart + 1));

int y;
for (y = yBottom; y < max(yBottom, yCurStart); y++)
*(curPixel++) = sector->floorColor;
for (; y <= min(yTop, yPortalStart - 1); y++)
*(curPixel++) = wall->color;
{
int texRowI = real_to_int(texRow);
*(curPixel++) = texture->pixels[
(texRowI % texture->size.h) * texture->size.w +
(texCol % texture->size.w)
];
texRow = real_add(texRow, texRowIncr);
}
if (wall->portalTo >= 0) {
//for (; y <= min(yTop, yPortalEnd); y++)
//*(curPixel++) = ((y / 4) % 2) ? GColorFromRGB(255, 0, 255) : GColorFromRGB(0, 0, 0);
texRow = real_add(texRow, real_mul(texRowIncr, real_from_int(min(yTop, yPortalEnd) - y + 1)));
curPixel += yPortalEnd - y;
for (y = yPortalEnd; y <= min(yTop, yCurEnd); y++)
*(curPixel++) = wall->color;
{
int texRowI = real_to_int(texRow);
*(curPixel++) = texture->pixels[
(texRowI % texture->size.h) * texture->size.w +
(texCol % texture->size.w)
];
texRow = real_add(texRow, texRowIncr);
}
}
for (; y <= yTop; y++)
*(curPixel++) = sector->ceilColor;
}

texture_free(me->textureManager, texture);
}

void renderer_renderSector(Renderer* renderer, GColor* framebuffer, const DrawRequest* request)
Expand Down
2 changes: 1 addition & 1 deletion renderer/renderer_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ void renderer_renderDebugSector(Renderer* me, SDL_Renderer* sdlRenderer, xz_t of
lineSeg_t wallLine;
wallLine.start.xz = curWall->startCorner;
wallLine.end.xz = sector->walls[(i + 1) % sector->wallCount].startCorner;
renderer_setDebugColor(sdlRenderer, curWall->color);
renderer_setDebugColor(sdlRenderer, GColorFromRGB((i % 3 == 0) * 255, (i % 3 == 1) * 255, (i % 3 == 2) * 255));
renderer_renderSDLDebugLine(me, sdlRenderer, offset, wallLine, opts);
}
}
Expand Down
1 change: 1 addition & 0 deletions test/fixtures.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class MathFixture : public testing::Test {
const real_t three = real_from_int(3);
const real_t four = real_from_int(4);
const real_t five = real_from_int(5);
const real_t eight = real_from_int(8);
const real_t ten = real_from_int(10);
const real_t halfpi = real_div(real_pi, real_from_int(2));

Expand Down
13 changes: 13 additions & 0 deletions test/test_real.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,19 @@ TEST_F(TestAlgebraReal, real_lerp) {
expectApproxEq(real_from_int(-20), real_lerp(real_neg(two), real_zero, ten));
}

TEST_F(TestAlgebraReal, real_norm_lerp) {
EXPECT_EQ(real_zero, real_norm_lerp(real_zero, real_zero, four, real_zero, ten));
EXPECT_EQ(ten, real_norm_lerp(four, real_zero, four, real_zero, ten));
EXPECT_EQ(five, real_norm_lerp(two, real_zero, four, real_zero, ten));
EXPECT_EQ(real_zero, real_norm_lerp(two, two, four, real_zero, ten));
EXPECT_EQ(five, real_norm_lerp(two, two, four, five, ten));
EXPECT_EQ(five, real_norm_lerp(real_zero, real_neg(four), four, real_zero, ten));
EXPECT_EQ(real_neg(ten), real_norm_lerp(two, two, four, real_neg(ten), ten));
EXPECT_EQ(five, real_norm_lerp(two, real_neg(four), four, real_neg(ten), ten));
EXPECT_EQ(real_from_int(20), real_norm_lerp(eight, real_zero, four, real_zero, ten));
EXPECT_EQ(real_from_int(-20), real_norm_lerp(real_neg(eight), real_zero, four, real_zero, ten));
}

TEST_F(TestAlgebraReal, real_clamp) {
const real_t eleven = real_from_int(11);
expectApproxEq(five, real_clamp(real_zero, five, ten));
Expand Down