diff --git a/pebbleapp/PDoom.c b/pebbleapp/PDoom.c index 06b3264..f5bb6db 100644 --- a/pebbleapp/PDoom.c +++ b/pebbleapp/PDoom.c @@ -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; } diff --git a/renderer/algebra.h b/renderer/algebra.h index 38fa060..2ee4d98 100644 --- a/renderer/algebra.h +++ b/renderer/algebra.h @@ -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); diff --git a/renderer/algebra_float.c b/renderer/algebra_float.c index e12233c..6258dd1 100644 --- a/renderer/algebra_float.c +++ b/renderer/algebra_float.c @@ -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)); diff --git a/renderer/level.c b/renderer/level.c index 0e94551..f38ab1c 100644 --- a/renderer/level.c +++ b/renderer/level.c @@ -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[] = { diff --git a/renderer/level.h b/renderer/level.h index 997fa3c..1d8e261 100644 --- a/renderer/level.h +++ b/renderer/level.h @@ -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 { diff --git a/renderer/renderer.c b/renderer/renderer.c index bf8dfa9..0d5d75a 100644 --- a/renderer/renderer.c +++ b/renderer/renderer.c @@ -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); @@ -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; } @@ -139,12 +145,14 @@ void renderer_renderWall(Renderer* me, GColor* framebuffer, const DrawRequest* r const Sector* const sector = request->sector; const Wall* const wall = §or->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; @@ -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; @@ -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) diff --git a/renderer/renderer_debug.c b/renderer/renderer_debug.c index c6b0fa5..c4fc675 100644 --- a/renderer/renderer_debug.c +++ b/renderer/renderer_debug.c @@ -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); } } diff --git a/test/fixtures.h b/test/fixtures.h index 105e06b..feb514f 100644 --- a/test/fixtures.h +++ b/test/fixtures.h @@ -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)); diff --git a/test/test_real.cpp b/test/test_real.cpp index 57471f6..14ea172 100644 --- a/test/test_real.cpp +++ b/test/test_real.cpp @@ -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));