Skip to content

Commit

Permalink
Renderer: Texture mapping (#49)
Browse files Browse the repository at this point in the history
* renderer: initial texture mapping

* renderer: use new lerp function

* renderer: vertical texture-coordinates

* renderer: integrate texture-mapping with level and texture -loading

* algebra: add real_norm_lerp

* renderer: use real_norm_lerp

* renderer: fix texture not being freed properly

* renderer: perspective correction

* renderer: fix logical merge errors

* pebble: Fix texture loading
  • Loading branch information
Helco committed Dec 19, 2018
1 parent 6a5cd88 commit 7132408
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 26 deletions.
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

0 comments on commit 7132408

Please sign in to comment.