diff --git a/src/render/software/CanvasFragment.cpp b/src/render/software/CanvasFragment.cpp index dcef1e0..2725cc3 100644 --- a/src/render/software/CanvasFragment.cpp +++ b/src/render/software/CanvasFragment.cpp @@ -1,7 +1,7 @@ #include "CanvasFragment.h" -CanvasFragment::CanvasFragment(double z, const Vector3 &location, int client, bool opaque): - opaque(opaque), z(z), location(location), client(client) +CanvasFragment::CanvasFragment(bool front_facing, const Vector3 &pixel, const Vector3 &location, int client, bool opaque): + opaque(opaque), front_facing(front_facing), pixel(pixel), location(location), client(client) { color = COLOR_WHITE; } diff --git a/src/render/software/CanvasFragment.h b/src/render/software/CanvasFragment.h index cb328cc..4a09af2 100644 --- a/src/render/software/CanvasFragment.h +++ b/src/render/software/CanvasFragment.h @@ -16,19 +16,22 @@ class SOFTWARESHARED_EXPORT CanvasFragment { public: CanvasFragment() = default; - CanvasFragment(double z, const Vector3 &location, int client=0, bool opaque=true); + CanvasFragment(bool front_facing, const Vector3 &pixel, const Vector3 &location, int client=0, bool opaque=true); void setColor(const Color &col); inline bool getOpaque() const {return opaque;} - inline double getZ() const {return z;} + inline bool isFrontFacing() const {return front_facing;} + inline double getZ() const {return pixel.z;} inline const Vector3 &getLocation() const {return location;} + inline const Vector3 &getPixel() const {return pixel;} inline int getClient() const {return client;} inline const Color &getColor() const {return color;} private: bool opaque; - double z; + bool front_facing; + Vector3 pixel; Vector3 location; int client; Color color; diff --git a/src/render/software/CanvasPixel.cpp b/src/render/software/CanvasPixel.cpp index 877195c..859ad37 100644 --- a/src/render/software/CanvasPixel.cpp +++ b/src/render/software/CanvasPixel.cpp @@ -35,7 +35,7 @@ void CanvasPixel::pushFragment(const CanvasFragment &fragment) } else { - if (fragments[0].getOpaque() and fragment.getZ() < fragments[0].getZ()) + if (fragments[0].getOpaque() and fragment.getZ() <= fragments[0].getZ()) { // behind opaque fragment, don't bother return; @@ -43,11 +43,17 @@ void CanvasPixel::pushFragment(const CanvasFragment &fragment) // find expected position int i = 0; - while (i < count and fragment.getZ() > fragments[i].getZ()) + while (i < count and fragment.getZ() >= fragments[i].getZ()) { i++; } + if (i > 0 and fragments[i - 1].getZ() == fragment.getZ() and fragments[i - 1].getClient() == fragment.getClient()) + { + // Pixel already pushed by same client, don't do anything + return; + } + if (fragment.getOpaque()) { // Discard fragments masked by the incoming opaque one @@ -62,28 +68,32 @@ void CanvasPixel::pushFragment(const CanvasFragment &fragment) } fragments[0] = fragment; } - else if (i < count) - { - // Need to make room for the incoming fragment - if (count < MAX_FRAGMENT_COUNT) - { - memmove(fragments + i + 1, fragments + i, sizeof(CanvasFragment) * (count - i)); - fragments[i] = fragment; - count++; - } - } else { - if (count == MAX_FRAGMENT_COUNT) + // Transparent pixel + if (i < count) { - // Replace nearest fragment - fragments[count - 1] = fragment; + // Need to make room for the incoming fragment + if (count < MAX_FRAGMENTS_PER_PIXEL) + { + memmove(fragments + i + 1, fragments + i, sizeof(CanvasFragment) * (count - i)); + fragments[i] = fragment; + count++; + } } else { - // Append - fragments[count] = fragment; - count++; + if (count == MAX_FRAGMENTS_PER_PIXEL) + { + // Replace nearest fragment + fragments[count - 1] = fragment; + } + else + { + // Append + fragments[count] = fragment; + count++; + } } } } diff --git a/src/render/software/CanvasPixel.h b/src/render/software/CanvasPixel.h index d2965d7..f21b6a9 100644 --- a/src/render/software/CanvasPixel.h +++ b/src/render/software/CanvasPixel.h @@ -5,7 +5,7 @@ #include "CanvasFragment.h" -const int MAX_FRAGMENT_COUNT = 2; +const int MAX_FRAGMENTS_PER_PIXEL = 2; namespace paysages { namespace software { @@ -32,7 +32,7 @@ public: private: int count; - CanvasFragment fragments[MAX_FRAGMENT_COUNT]; + CanvasFragment fragments[MAX_FRAGMENTS_PER_PIXEL]; Color composite; }; diff --git a/src/render/software/CanvasPixelShader.cpp b/src/render/software/CanvasPixelShader.cpp index 80468af..92cd1fc 100644 --- a/src/render/software/CanvasPixelShader.cpp +++ b/src/render/software/CanvasPixelShader.cpp @@ -45,11 +45,13 @@ void CanvasPixelShader::processParallelUnit(int unit) const CanvasPixel &pixel = portion->at(base_x + x, base_y + y); int n = pixel.getFragmentCount(); Color composite = COLOR_BLACK; + const CanvasFragment *previous = NULL; for (int i = 0; i < n; i++) { const CanvasFragment &fragment = pixel.getFragment(i); const Rasterizer &rasterizer = renderer.getRasterizer(fragment.getClient()); - composite.mask(rasterizer.shadeFragment(fragment)); + composite.mask(rasterizer.shadeFragment(fragment, previous)); + previous = &fragment; } // Fill the square area diff --git a/src/render/software/Rasterizer.cpp b/src/render/software/Rasterizer.cpp index 950592e..6e4fc2c 100644 --- a/src/render/software/Rasterizer.cpp +++ b/src/render/software/Rasterizer.cpp @@ -22,6 +22,7 @@ struct paysages::software::ScanPoint double z; } location; int client; + bool front_facing; }; struct paysages::software::RenderScanlines @@ -38,6 +39,7 @@ Rasterizer::Rasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int this->color = new Color(color); interrupted = false; + backface_culling = false; triangle_count = 0; auto_cut_limit = 0.01; @@ -58,6 +60,16 @@ void Rasterizer::setQuality(double) { } +void Rasterizer::setColor(const Color &color) +{ + *this->color = color; +} + +void Rasterizer::setBackFaceCulling(bool cull) +{ + this->backface_culling = cull; +} + void Rasterizer::setAutoCutLimit(double limit) { this->auto_cut_limit = limit; @@ -92,6 +104,14 @@ bool Rasterizer::pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pix return location1.sub(location2).getNorm() > auto_cut_limit && location2.sub(location3).getNorm() > auto_cut_limit && location3.sub(location1).getNorm() > auto_cut_limit; } + // Check the poylgon's facing (front-face or back-face) + Vector3 normal = dpixel2.sub(dpixel1).crossProduct(dpixel3.sub(dpixel1)); + bool front_facing = (normal.z >= 0.0); + if (backface_culling and not front_facing) + { + return false; + } + // Prepare vertices point1.pixel.x = dpixel1.x; point1.pixel.y = dpixel1.y; @@ -100,6 +120,7 @@ bool Rasterizer::pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pix point1.location.y = location1.y; point1.location.z = location1.z; point1.client = client_id; + point1.front_facing = front_facing; point2.pixel.x = dpixel2.x; point2.pixel.y = dpixel2.y; @@ -108,6 +129,7 @@ bool Rasterizer::pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pix point2.location.y = location2.y; point2.location.z = location2.z; point2.client = client_id; + point2.front_facing = front_facing; point3.pixel.x = dpixel3.x; point3.pixel.y = dpixel3.y; @@ -116,6 +138,7 @@ bool Rasterizer::pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pix point3.location.y = location3.y; point3.location.z = location3.z; point3.client = client_id; + point3.front_facing = front_facing; // Prepare scanlines // TODO Don't create scanlines for each triangles (one by thread is more appropriate) @@ -167,6 +190,7 @@ void Rasterizer::pushDisplacedTriangle(CanvasPortion *canvas, const Vector3 &v1, { Vector3 p1, p2, p3; + // TODO v1, v2 and v3 are lost, but may be useful (avoid need to unproject) p1 = getRenderer()->projectPoint(v1); p2 = getRenderer()->projectPoint(v2); p3 = getRenderer()->projectPoint(v3); @@ -225,6 +249,7 @@ void Rasterizer::scanInterpolate(CameraDefinition* camera, ScanPoint* v1, ScanPo result->location.y = ((1.0 - value) * (v1->location.y * v1depth) + value * (v1->location.y + diff->location.y) * v2depth) * factor; result->location.z = ((1.0 - value) * (v1->location.z * v1depth) + value * (v1->location.z + diff->location.z) * v2depth) * factor; result->client = v1->client; + result->front_facing = v1->front_facing; } void Rasterizer::pushScanPoint(CanvasPortion* canvas, RenderScanlines* scanlines, ScanPoint* point) @@ -397,13 +422,17 @@ void Rasterizer::renderScanLines(CanvasPortion *canvas, RenderScanlines* scanlin scanInterpolate(renderer->render_camera, &down, &diff, fy / dy, ¤t); } - CanvasFragment fragment(current.pixel.z, Vector3(current.location.x, current.location.y, current.location.z), current.client); + Vector3 pixel(current.pixel.x + canvas->getXOffset(), current.pixel.y + canvas->getYOffset(), current.pixel.z); + Vector3 location(current.location.x, current.location.y, current.location.z); + CanvasFragment fragment(current.front_facing, pixel, location, current.client, color->a == 1.0); Color frag_color = *color; + frag_color.a = 1.0; if (cury == starty || cury == endy) { frag_color.mask(Color(0.0, 0.0, 0.0, 0.3)); } + frag_color.a = color->a; fragment.setColor(frag_color); canvas->pushFragment(current.x, current.y, fragment); diff --git a/src/render/software/Rasterizer.h b/src/render/software/Rasterizer.h index b9360f6..beb1ac3 100644 --- a/src/render/software/Rasterizer.h +++ b/src/render/software/Rasterizer.h @@ -6,6 +6,10 @@ namespace paysages { namespace software { +const int RASTERIZER_CLIENT_SKY = 0; +const int RASTERIZER_CLIENT_WATER = 1; +const int RASTERIZER_CLIENT_TERRAIN = 2; + typedef struct ScanPoint ScanPoint; typedef struct RenderScanlines RenderScanlines; @@ -21,9 +25,6 @@ public: inline SoftwareRenderer *getRenderer() const {return renderer;} inline int getTriangleCount() const {return triangle_count;} - virtual Color shadeFragment(const CanvasFragment &fragment) const = 0; - virtual void interrupt(); - /** * Set the rasterization quality factor. */ @@ -34,20 +35,34 @@ public: */ void setAutoCutLimit(double limit); - /** - * Reset the internal triangle counter to 0. - */ - void resetTriangleCount(); - /** * Abstract method to prepare for the rasterization process, and return the estimated progress count. */ virtual int prepareRasterization() = 0; + /** * Abstract method to effectively do the rasterization on a canvas. */ virtual void rasterizeToCanvas(CanvasPortion *canvas) = 0; + /** + * Abstract method to render a fragment stored on a canvas, to a color. + */ + virtual Color shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const = 0; + + /** + * Ask for an interrupt in rasterization process. + */ + virtual void interrupt(); + + void setColor(const Color &color); + void setBackFaceCulling(bool cull); + + /** + * Reset the internal triangle counter to 0. + */ + void resetTriangleCount(); + void pushTriangle(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3); void pushDisplacedTriangle(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &ov1, const Vector3 &ov2, const Vector3 &ov3); @@ -72,6 +87,7 @@ private: int triangle_count; double auto_cut_limit; + bool backface_culling; }; } diff --git a/src/render/software/SkyRasterizer.cpp b/src/render/software/SkyRasterizer.cpp index 0386cb6..cc70a67 100644 --- a/src/render/software/SkyRasterizer.cpp +++ b/src/render/software/SkyRasterizer.cpp @@ -76,7 +76,7 @@ void SkyRasterizer::rasterizeToCanvas(CanvasPortion* canvas) } } -Color SkyRasterizer::shadeFragment(const CanvasFragment &fragment) const +Color SkyRasterizer::shadeFragment(const CanvasFragment &fragment, const CanvasFragment *) const { Vector3 location = fragment.getLocation(); Vector3 camera_location, direction; diff --git a/src/render/software/SkyRasterizer.h b/src/render/software/SkyRasterizer.h index 3989113..10a648f 100644 --- a/src/render/software/SkyRasterizer.h +++ b/src/render/software/SkyRasterizer.h @@ -15,7 +15,7 @@ public: virtual int prepareRasterization() override; virtual void rasterizeToCanvas(CanvasPortion* canvas) override; - virtual Color shadeFragment(const CanvasFragment &fragment) const override; + virtual Color shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const override; void setQuality(int res_i, int res_j); virtual void setQuality(double factor) override; diff --git a/src/render/software/SoftwareCanvasRenderer.cpp b/src/render/software/SoftwareCanvasRenderer.cpp index 372a113..d690e33 100644 --- a/src/render/software/SoftwareCanvasRenderer.cpp +++ b/src/render/software/SoftwareCanvasRenderer.cpp @@ -27,9 +27,9 @@ SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery): postprocess_enabled = true; - rasterizers.push_back(new SkyRasterizer(this, progress, 0)); - rasterizers.push_back(new WaterRasterizer(this, progress, 1)); - rasterizers.push_back(new TerrainRasterizer(this, progress, 2)); + rasterizers.push_back(new SkyRasterizer(this, progress, RASTERIZER_CLIENT_SKY)); + rasterizers.push_back(new WaterRasterizer(this, progress, RASTERIZER_CLIENT_WATER)); + rasterizers.push_back(new TerrainRasterizer(this, progress, RASTERIZER_CLIENT_TERRAIN)); current_work = NULL; } diff --git a/src/render/software/TerrainRasterizer.cpp b/src/render/software/TerrainRasterizer.cpp index 68c871c..676d9ba 100644 --- a/src/render/software/TerrainRasterizer.cpp +++ b/src/render/software/TerrainRasterizer.cpp @@ -245,7 +245,7 @@ void TerrainRasterizer::rasterizeToCanvas(CanvasPortion *canvas) performTessellation(canvas, false); } -Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment) const +Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment, const CanvasFragment *) const { Vector3 point = fragment.getLocation(); double precision = renderer->getPrecision(_getPoint(renderer, point.x, point.z)); diff --git a/src/render/software/TerrainRasterizer.h b/src/render/software/TerrainRasterizer.h index 89acedc..be7180b 100644 --- a/src/render/software/TerrainRasterizer.h +++ b/src/render/software/TerrainRasterizer.h @@ -36,7 +36,7 @@ public: virtual int prepareRasterization() override; virtual void rasterizeToCanvas(CanvasPortion* canvas) override; - virtual Color shadeFragment(const CanvasFragment &fragment) const override; + virtual Color shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const override; private: /** diff --git a/src/render/software/WaterRasterizer.cpp b/src/render/software/WaterRasterizer.cpp index 783d49d..26c451c 100644 --- a/src/render/software/WaterRasterizer.cpp +++ b/src/render/software/WaterRasterizer.cpp @@ -43,7 +43,7 @@ void WaterRasterizer::rasterizeToCanvas(CanvasPortion *canvas) performTessellation(canvas); } -Color WaterRasterizer::shadeFragment(const CanvasFragment &fragment) const +Color WaterRasterizer::shadeFragment(const CanvasFragment &fragment, const CanvasFragment *) const { Vector3 location = fragment.getLocation(); return renderer->getWaterRenderer()->getResult(location.x, location.z).final; diff --git a/src/render/software/WaterRasterizer.h b/src/render/software/WaterRasterizer.h index 233cde7..f90af89 100644 --- a/src/render/software/WaterRasterizer.h +++ b/src/render/software/WaterRasterizer.h @@ -17,7 +17,7 @@ public: virtual int prepareRasterization() override; virtual void rasterizeToCanvas(CanvasPortion* canvas) override; - virtual Color shadeFragment(const CanvasFragment &fragment) const override; + virtual Color shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const override; virtual void setQuality(double factor) override; diff --git a/src/tests/CanvasPixel_Test.cpp b/src/tests/CanvasPixel_Test.cpp new file mode 100644 index 0000000..db2e265 --- /dev/null +++ b/src/tests/CanvasPixel_Test.cpp @@ -0,0 +1,56 @@ +#include "BaseTestCase.h" + +#include "CanvasPixel.h" + +TEST(CanvasPixel, MaxFragments) +{ + CanvasPixel pixel; + + // One opaque fragment + pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, 0.0), Vector3(), 0, true)); + + // Overflow max fragment count with transparent fragments + for (int i = 0; i < MAX_FRAGMENTS_PER_PIXEL * 2; i++) + { + pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, (double)i), Vector3(), 0, false)); + } + + ASSERT_EQ(MAX_FRAGMENTS_PER_PIXEL, pixel.getFragmentCount()); + EXPECT_DOUBLE_EQ(pixel.getFragment(0).getZ(), 0.0); + EXPECT_EQ(pixel.getFragment(0).getOpaque(), true); + EXPECT_DOUBLE_EQ(pixel.getFragment(MAX_FRAGMENTS_PER_PIXEL - 1).getZ(), (double)(MAX_FRAGMENTS_PER_PIXEL * 2 - 1)); + EXPECT_EQ(pixel.getFragment(MAX_FRAGMENTS_PER_PIXEL - 1).getOpaque(), false); +} + +TEST(CanvasPixel, SameTransparent) +{ + CanvasPixel pixel; + + pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, 1.4), Vector3(), 0, false)); + pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, 1.4), Vector3(), 0, false)); + + ASSERT_EQ(1, pixel.getFragmentCount()); + + pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, 1.4), Vector3(), 1, false)); + + ASSERT_EQ(2, pixel.getFragmentCount()); + + EXPECT_EQ(pixel.getFragment(0).getClient(), 0); + EXPECT_EQ(pixel.getFragment(1).getClient(), 1); +} + +TEST(CanvasPixel, SameOpaque) +{ + CanvasPixel pixel; + + pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, 1.4), Vector3(), 0, true)); + pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, 1.4), Vector3(), 0, true)); + + ASSERT_EQ(1, pixel.getFragmentCount()); + + pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, 1.4), Vector3(), 1, true)); + + ASSERT_EQ(1, pixel.getFragmentCount()); + + EXPECT_EQ(pixel.getFragment(0).getClient(), 0); +} diff --git a/src/tests/CanvasPortion_Test.cpp b/src/tests/CanvasPortion_Test.cpp index c3e8cd2..d188693 100644 --- a/src/tests/CanvasPortion_Test.cpp +++ b/src/tests/CanvasPortion_Test.cpp @@ -44,19 +44,19 @@ TEST(CanvasPortion, pushFragment_opaque) portion.setSize(10, 10); portion.preparePixels(); - pushed = CanvasFragment(2.0, VECTOR_ZERO, 0); + pushed = CanvasFragment(true, Vector3(0.0, 0.0, 2.0), VECTOR_ZERO, 0); portion.pushFragment(2, 2, pushed); ASSERT_EQ(1, portion.getFragmentCount(2, 2)); EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ()); - pushed = CanvasFragment(1.0, VECTOR_ZERO, 0); + pushed = CanvasFragment(true, Vector3(0.0, 0.0, 1.0), VECTOR_ZERO, 0); portion.pushFragment(2, 2, pushed); ASSERT_EQ(1, portion.getFragmentCount(2, 2)); EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ()); - pushed = CanvasFragment(4.0, VECTOR_ZERO, 0); + pushed = CanvasFragment(true, Vector3(0.0, 0.0, 4.0), VECTOR_ZERO, 0); portion.pushFragment(2, 2, pushed); ASSERT_EQ(1, portion.getFragmentCount(2, 2)); @@ -71,19 +71,19 @@ TEST(CanvasPortion, pushFragment_transparent) portion.setSize(10, 10); portion.preparePixels(); - pushed = CanvasFragment(2.0, VECTOR_ZERO, 0, false); + pushed = CanvasFragment(true, Vector3(0.0, 0.0, 2.0), VECTOR_ZERO, 0, false); portion.pushFragment(2, 2, pushed); ASSERT_EQ(1, portion.getFragmentCount(2, 2)); EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ()); - pushed = CanvasFragment(3.0, VECTOR_ZERO, 0, true); + pushed = CanvasFragment(true, Vector3(0.0, 0.0, 3.0), VECTOR_ZERO, 0, true); portion.pushFragment(2, 2, pushed); ASSERT_EQ(1, portion.getFragmentCount(2, 2)); EXPECT_DOUBLE_EQ(3.0, portion.getFrontFragment(2, 2)->getZ()); - pushed = CanvasFragment(4.0, VECTOR_ZERO, 0, false); + pushed = CanvasFragment(true, Vector3(0.0, 0.0, 4.0), VECTOR_ZERO, 0, false); portion.pushFragment(2, 2, pushed); ASSERT_EQ(2, portion.getFragmentCount(2, 2)); diff --git a/src/tests/Rasterizer_Test.cpp b/src/tests/Rasterizer_Test.cpp index 66dd886..8e7f0c9 100644 --- a/src/tests/Rasterizer_Test.cpp +++ b/src/tests/Rasterizer_Test.cpp @@ -14,7 +14,7 @@ public: FakeRasterizer(SoftwareRenderer *renderer): Rasterizer(renderer, NULL, 0, COLOR_WHITE) { } - virtual Color shadeFragment(const CanvasFragment &) const override + virtual Color shadeFragment(const CanvasFragment &, const CanvasFragment *) const override { return COLOR_RED; } diff --git a/src/tests/tests.pro b/src/tests/tests.pro index 9c180f1..109d52d 100644 --- a/src/tests/tests.pro +++ b/src/tests/tests.pro @@ -21,6 +21,7 @@ SOURCES += main.cpp \ SpaceSegment_Test.cpp \ Canvas_Test.cpp \ CanvasPortion_Test.cpp \ + CanvasPixel_Test.cpp \ CanvasPreview_Test.cpp \ AtmosphereDefinition_Test.cpp \ Scenery_Test.cpp \