2
0
Эх сурвалжийг харах

The first version of the s/w rasterizer

Panagiotis Christopoulos Charitos 9 жил өмнө
parent
commit
b8ef666358

+ 3 - 4
include/anki/collision/Functions.h

@@ -5,8 +5,9 @@
 
 #pragma once
 
-#include <anki/collision/Plane.h>
+#include <anki/collision/Common.h>
 #include <anki/collision/Frustum.h>
+#include <anki/Math.h>
 
 namespace anki
 {
@@ -20,9 +21,7 @@ namespace anki
 /// @param[out] planes Pointers to the planes. Elements can be nullptr
 ///
 /// @note plane_count * 8 muls, plane_count sqrt
-extern void extractClipPlanes(
-	const Mat4& mvp, Plane* planes[(U)Frustum::PlaneType::COUNT]);
-
+void extractClipPlanes(const Mat4& mvp, Array<Plane*, 6>& planes);
 /// @}
 
 } // end namespace anki

+ 71 - 0
include/anki/scene/SoftwareRasterizer.h

@@ -0,0 +1,71 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/scene/Common.h>
+#include <anki/Math.h>
+#include <anki/collision/Plane.h>
+
+namespace anki
+{
+
+/// @addtogroup scene
+/// @{
+
+/// Software rasterizer for visibility tests.
+class SoftwareRasterizer
+{
+public:
+	SoftwareRasterizer()
+	{
+	}
+
+	~SoftwareRasterizer()
+	{
+		m_zbuffer.destroy(m_alloc);
+	}
+
+	/// Initialize.
+	void init(const GenericMemoryPoolAllocator<U8>& alloc)
+	{
+		m_alloc = alloc;
+	}
+
+	/// Prepare for rendering. Call it before every draw.
+	void prepare(const Mat4& mv, const Mat4& p, U width, U height);
+
+	/// Render some verts.
+	/// @param[in] verts Pointer to the first vertex to draw.
+	/// @param vertCount The number of verts to draw.
+	/// @param stride The stride (in bytes) of the next vertex.
+	void draw(const F32* verts, U vertCount, U stride);
+
+public: // XXX
+	GenericMemoryPoolAllocator<U8> m_alloc;
+	Mat4 m_mv; ///< ModelView.
+	Mat4 m_p; ///< Projection.
+	Array<Plane, 6> m_planes; ///< In view space.
+	U32 m_width;
+	U32 m_height;
+	DArray<Atomic<U32>> m_zbuffer;
+
+	/// @param tri In clip space.
+	void rasterizeTriangle(const Vec4* tri);
+
+	Bool computeBarycetrinc(const Vec2& a,
+		const Vec2& b,
+		const Vec2& c,
+		const Vec2& p,
+		Vec3& uvw) const;
+
+	/// Clip triangle in the near plane.
+	/// @note Triangles in view space.
+	void clipTriangle(
+		const Vec4* inTriangle, Vec4* outTriangles, U& outTriangleCount) const;
+};
+/// @}
+
+} // end namespace anki

+ 20 - 72
include/anki/util/Atomic.h

@@ -41,7 +41,7 @@ public:
 	{
 	}
 
-	Atomic(const Value a)
+	Atomic(const Value& a)
 		: m_val(a)
 	{
 	}
@@ -68,28 +68,8 @@ public:
 #endif
 	}
 
-	/// @copybrief load
-	Value load(AtomicMemoryOrder memOrd = MEMORY_ORDER) const volatile
-	{
-#if defined(__GNUC__)
-		return __atomic_load_n(&m_val, static_cast<int>(memOrd));
-#else
-#error "TODO"
-#endif
-	}
-
 	/// Store
-	void store(const Value a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
-	{
-#if defined(__GNUC__)
-		__atomic_store_n(&m_val, a, static_cast<int>(memOrd));
-#else
-#error "TODO"
-#endif
-	}
-
-	/// @copybrief store
-	void store(const Value a, AtomicMemoryOrder memOrd = MEMORY_ORDER) volatile
+	void store(const Value& a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
 	{
 #if defined(__GNUC__)
 		__atomic_store_n(&m_val, a, static_cast<int>(memOrd));
@@ -100,18 +80,7 @@ public:
 
 	/// Fetch and add.
 	template<typename Y>
-	Value fetchAdd(const Y a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
-	{
-#if defined(__GNUC__)
-		return __atomic_fetch_add(&m_val, a, static_cast<int>(memOrd));
-#else
-#error "TODO"
-#endif
-	}
-
-	/// @copybrief fetchAdd
-	template<typename Y>
-	Value fetchAdd(const Y a, AtomicMemoryOrder memOrd = MEMORY_ORDER) volatile
+	Value fetchAdd(const Y& a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
 	{
 #if defined(__GNUC__)
 		return __atomic_fetch_add(&m_val, a, static_cast<int>(memOrd));
@@ -122,18 +91,7 @@ public:
 
 	/// Fetch and subtract.
 	template<typename Y>
-	Value fetchSub(const Y a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
-	{
-#if defined(__GNUC__)
-		return __atomic_fetch_sub(&m_val, a, static_cast<int>(memOrd));
-#else
-#error "TODO"
-#endif
-	}
-
-	/// @copybrief fetchSub
-	template<typename Y>
-	Value fetchSub(const Y a, AtomicMemoryOrder memOrd = MEMORY_ORDER) volatile
+	Value fetchSub(const Y& a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
 	{
 #if defined(__GNUC__)
 		return __atomic_fetch_sub(&m_val, a, static_cast<int>(memOrd));
@@ -152,7 +110,7 @@ public:
 	/// }
 	/// @endcode
 	Bool compareExchange(Value& expected,
-		const Value desired,
+		const Value& desired,
 		AtomicMemoryOrder memOrd = MEMORY_ORDER)
 	{
 #if defined(__GNUC__)
@@ -167,42 +125,32 @@ public:
 #endif
 	}
 
-	/// @copybrief compareExchange
-	Bool compareExchange(Value& expected,
-		const Value desired,
-		AtomicMemoryOrder memOrd = MEMORY_ORDER) volatile
+	/// Set @a a to the atomic and return the previous value.
+	Value exchange(const Value& a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
 	{
 #if defined(__GNUC__)
-		return __atomic_compare_exchange_n(&m_val,
-			&expected,
-			desired,
-			false,
-			static_cast<int>(memOrd),
-			__ATOMIC_RELAXED);
+		return __atomic_exchange_n(&m_val, a, static_cast<int>(memOrd));
 #else
 #error "TODO"
 #endif
 	}
 
-	/// Set @a a to the atomic and return the previous value.
-	Value exchange(const Value a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
+	/// Store the minimum using compare-and-swap.
+	void min(const Value& a)
 	{
-#if defined(__GNUC__)
-		return __atomic_exchange_n(&m_val, a, static_cast<int>(memOrd));
-#else
-#error "TODO"
-#endif
+		Value prev = load();
+		while(a < prev && !compareExchange(prev, a))
+		{
+		}
 	}
 
-	/// @copybrief exchange
-	Value exchange(
-		const Value a, AtomicMemoryOrder memOrd = MEMORY_ORDER) volatile
+	/// Store the maximum using compare-and-swap.
+	void max(const Value& a)
 	{
-#if defined(__GNUC__)
-		return __atomic_exchange_n(&m_val, a, static_cast<int>(memOrd));
-#else
-#error "TODO"
-#endif
+		Value prev = load();
+		while(a > prev && !compareExchange(prev, a))
+		{
+		}
 	}
 
 private:

+ 4 - 2
sandbox/Main.cpp

@@ -107,8 +107,10 @@ Error MyApp::userMainLoop(Bool& quit)
 
 	if(in.getKey(KeyCode::L) == 1)
 	{
-		Vec3 origin = mover->getWorldTransform().getOrigin().xyz();
-		printf("%f %f %f\n", origin.x(), origin.y(), origin.z());
+		/*Vec3 origin = mover->getWorldTransform().getOrigin().xyz();
+		printf("%f %f %f\n", origin.x(), origin.y(), origin.z());*/
+		mover->setLocalOrigin(Vec4(0.0));
+		mover->setLocalRotation(Mat3x4::getIdentity());
 	}
 
 	if(in.getKey(KeyCode::F1) == 1)

+ 29 - 45
src/collision/Functions.cpp

@@ -4,75 +4,59 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/collision/Functions.h>
+#include <anki/collision/Plane.h>
 
 namespace anki
 {
 
 //==============================================================================
-void extractClipPlanes(
-	const Mat4& mvp, Plane* planes[(U)Frustum::PlaneType::COUNT])
+static void setPlane(const Vec4& abcd, Plane& p)
+{
+	Vec4 n = abcd.xyz0();
+	F32 len = n.getLength();
+	p = Plane(n / len, -abcd.w() / len);
+}
+
+//==============================================================================
+void extractClipPlanes(const Mat4& mvp, Array<Plane*, 6>& planes)
 {
 	// Plane equation coefficients
-	F32 a, b, c, d;
+	Vec4 abcd;
 
-	if(planes[(U)Frustum::PlaneType::NEAR])
+	if(planes[Frustum::PlaneType::NEAR])
 	{
-		a = mvp(3, 0) + mvp(2, 0);
-		b = mvp(3, 1) + mvp(2, 1);
-		c = mvp(3, 2) + mvp(2, 2);
-		d = mvp(3, 3) + mvp(2, 3);
-
-		*planes[(U)Frustum::PlaneType::NEAR] = Plane(a, b, c, d);
+		abcd = mvp.getRow(3) + mvp.getRow(2);
+		setPlane(abcd, *planes[Frustum::PlaneType::NEAR]);
 	}
 
-	if(planes[(U)Frustum::PlaneType::FAR])
+	if(planes[Frustum::PlaneType::FAR])
 	{
-		a = mvp(3, 0) - mvp(2, 0);
-		b = mvp(3, 1) - mvp(2, 1);
-		c = mvp(3, 2) - mvp(2, 2);
-		d = mvp(3, 3) - mvp(2, 3);
-
-		*planes[(U)Frustum::PlaneType::FAR] = Plane(a, b, c, d);
+		abcd = mvp.getRow(3) - mvp.getRow(2);
+		setPlane(abcd, *planes[Frustum::PlaneType::FAR]);
 	}
 
-	if(planes[(U)Frustum::PlaneType::LEFT])
+	if(planes[Frustum::PlaneType::LEFT])
 	{
-		a = mvp(3, 0) + mvp(0, 0);
-		b = mvp(3, 1) + mvp(0, 1);
-		c = mvp(3, 2) + mvp(0, 2);
-		d = mvp(3, 3) + mvp(0, 3);
-
-		*planes[(U)Frustum::PlaneType::LEFT] = Plane(a, b, c, d);
+		abcd = mvp.getRow(3) + mvp.getRow(0);
+		setPlane(abcd, *planes[Frustum::PlaneType::LEFT]);
 	}
 
-	if(planes[(U)Frustum::PlaneType::RIGHT])
+	if(planes[Frustum::PlaneType::RIGHT])
 	{
-		a = mvp(3, 0) - mvp(0, 0);
-		b = mvp(3, 1) - mvp(0, 1);
-		c = mvp(3, 2) - mvp(0, 2);
-		d = mvp(3, 3) - mvp(0, 3);
-
-		*planes[(U)Frustum::PlaneType::RIGHT] = Plane(a, b, c, d);
+		abcd = mvp.getRow(3) - mvp.getRow(0);
+		setPlane(abcd, *planes[Frustum::PlaneType::RIGHT]);
 	}
 
-	if(planes[(U)Frustum::PlaneType::TOP])
+	if(planes[Frustum::PlaneType::TOP])
 	{
-		a = mvp(3, 0) - mvp(1, 0);
-		b = mvp(3, 1) - mvp(1, 1);
-		c = mvp(3, 2) - mvp(1, 2);
-		d = mvp(3, 3) - mvp(1, 3);
-
-		*planes[(U)Frustum::PlaneType::TOP] = Plane(a, b, c, d);
+		abcd = mvp.getRow(3) - mvp.getRow(1);
+		setPlane(abcd, *planes[Frustum::PlaneType::TOP]);
 	}
 
-	if(planes[(U)Frustum::PlaneType::BOTTOM])
+	if(planes[Frustum::PlaneType::BOTTOM])
 	{
-		a = mvp(3, 0) + mvp(1, 0);
-		b = mvp(3, 1) + mvp(1, 1);
-		c = mvp(3, 2) + mvp(1, 2);
-		d = mvp(3, 3) + mvp(1, 3);
-
-		*planes[(U)Frustum::PlaneType::BOTTOM] = Plane(a, b, c, d);
+		abcd = mvp.getRow(3) + mvp.getRow(1);
+		setPlane(abcd, *planes[Frustum::PlaneType::BOTTOM]);
 	}
 }
 

+ 0 - 1
src/core/App.cpp

@@ -21,7 +21,6 @@
 #include <anki/script/ScriptManager.h>
 #include <anki/resource/ResourceFilesystem.h>
 
-#include <signal.h>
 #if ANKI_OS == ANKI_OS_ANDROID
 #include <android_native_app_glue.h>
 #endif

+ 127 - 30
src/renderer/Dbg.cpp

@@ -21,6 +21,7 @@
 #include <anki/collision/ConvexHullShape.h>
 #include <anki/util/Rtti.h>
 #include <anki/Ui.h> /// XXX
+#include <anki/scene/SoftwareRasterizer.h> /// XXX
 
 namespace anki
 {
@@ -82,10 +83,10 @@ Error Dbg::run(RenderingContext& ctx)
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 	cmdb->beginRenderPass(m_fb);
 
-	FrustumComponent& camFr = *ctx.m_frustumComponent;
-	SceneNode& cam = camFr.getSceneNode();
+	FrustumComponent& camFrc = *ctx.m_frustumComponent;
+	SceneNode& cam = camFrc.getSceneNode();
 	m_drawer->prepareFrame(cmdb);
-	m_drawer->setViewProjectionMatrix(camFr.getViewProjectionMatrix());
+	m_drawer->setViewProjectionMatrix(camFrc.getViewProjectionMatrix());
 	m_drawer->setModelMatrix(Mat4::getIdentity());
 	// m_drawer->drawGrid();
 
@@ -125,7 +126,10 @@ Error Dbg::run(RenderingContext& ctx)
 		{
 			Error err = node.iterateComponentsOfType<FrustumComponent>(
 				[&](FrustumComponent& frc) -> Error {
-					sceneDrawer.draw(frc);
+					if(&frc != &camFrc)
+					{
+						sceneDrawer.draw(frc);
+					}
 					return ErrorCode::NONE;
 				});
 			(void)err;
@@ -156,32 +160,125 @@ Error Dbg::run(RenderingContext& ctx)
 
 #if 0
 	{
-		static Bool firstTime = true;
-		static UiInterfaceImpl* interface;
-		static Canvas* canvas;
+		m_drawer->setViewProjectionMatrix(camFrc.getViewProjectionMatrix());
+		m_drawer->setModelMatrix(Mat4::getIdentity());
+		CollisionDebugDrawer cd(m_drawer);
+		Mat4 proj = camFrc.getProjectionMatrix();
 
-		if(firstTime)
-		{
-			firstTime = false;
+		Array<Plane, 6> planes;
+		Array<Plane*, 6> pplanes = {&planes[0],
+			&planes[1],
+			&planes[2],
+			&planes[3],
+			&planes[4],
+			&planes[5]};
+		extractClipPlanes(proj, pplanes);
 
-			auto alloc = getAllocator();
-			interface = alloc.newInstance<UiInterfaceImpl>(alloc);
-			ANKI_CHECK(interface->init(&getGrManager(), &getResourceManager()));
+		planes[5].accept(cd);
 
-			canvas = alloc.newInstance<Canvas>(interface);
-			canvas->setDebugDrawEnabled();
-		}
+		m_drawer->setViewProjectionMatrix(camFrc.getViewProjectionMatrix());
+		m_drawer->setModelMatrix(Mat4::getIdentity());
 
-		cmdb->setViewport(10, 10, 1024, 1024);
-		canvas->update(0.1);
-		interface->beginRendering(cmdb);
-		canvas->paint();
-		interface->endRendering();
-		cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
+		m_drawer->setColor(Vec4(0.0, 1.0, 1.0, 1.0));
+		PerspectiveFrustum frc;
+		const PerspectiveFrustum& cfrc =
+			(const PerspectiveFrustum&)camFrc.getFrustum();
+		frc.setAll(
+			cfrc.getFovX(), cfrc.getFovY(), cfrc.getNear(), cfrc.getFar());
+		cd.visit(frc);
+
+		m_drawer->drawLine(Vec3(0.0), planes[5].getNormal().xyz() * 100.0, 
+			Vec4(1.0));
 	}
 #endif
 
 #if 1
+	{
+		m_drawer->setViewProjectionMatrix(Mat4::getIdentity());
+		m_drawer->setModelMatrix(Mat4::getIdentity());
+		Mat4 proj = camFrc.getProjectionMatrix();
+		Mat4 view = camFrc.getViewMatrix();
+
+		Array<Vec4, 12> ltriangle = {Vec4(0.0, 2.0, 2.0, 1.0),
+			Vec4(4.0, 2.0, 2.0, 1.0),
+			Vec4(0.0, 8.0, 2.0, 1.0),
+
+			Vec4(0.0, 8.0, 2.0, 1.0),
+			Vec4(4.0, 2.0, 2.0, 1.0),
+			Vec4(4.0, 8.0, 2.0, 1.0),
+
+			Vec4(0.9, 2.0, 1.9, 1.0),
+			Vec4(4.9, 2.0, 1.9, 1.0),
+			Vec4(0.9, 8.0, 1.9, 1.0),
+
+			Vec4(0.9, 8.0, 1.9, 1.0),
+			Vec4(4.9, 2.0, 1.9, 1.0),
+			Vec4(4.9, 8.0, 1.9, 1.0)};
+
+		SoftwareRasterizer r;
+		r.init(getAllocator());
+		r.prepare(
+			view, proj, m_r->getTileCountXY().x(), m_r->getTileCountXY().y());
+		r.draw(&ltriangle[0][0], 12, sizeof(Vec4));
+
+		m_drawer->begin(PrimitiveTopology::TRIANGLES);
+		U count = 0;
+		for(U y = 0; y < m_r->getTileCountXY().y(); ++y)
+		{
+			for(U x = 0; x < m_r->getTileCountXY().x(); ++x)
+			{
+				F32 d = r.m_zbuffer[y * m_r->getTileCountXY().x() + x].get()
+					/ F32(MAX_U32);
+
+				if(d < 1.0)
+				{
+					F32 zNear = camFrc.getFrustum().getNear();
+					F32 zFar = camFrc.getFrustum().getFar();
+					F32 ld =
+						(2.0 * zNear) / (zFar + zNear - d * (zFar - zNear));
+					m_drawer->setColor(Vec4(ld));
+
+					++count;
+					Vec2 min(F32(x) / m_r->getTileCountXY().x(),
+						F32(y) / m_r->getTileCountXY().y());
+
+					Vec2 max(F32(x + 1) / m_r->getTileCountXY().x(),
+						F32(y + 1) / m_r->getTileCountXY().y());
+
+					min = min * 2.0 - 1.0;
+					max = max * 2.0 - 1.0;
+
+					m_drawer->pushBackVertex(Vec3(min.x(), min.y(), 0.0));
+					m_drawer->pushBackVertex(Vec3(max.x(), min.y(), 0.0));
+					m_drawer->pushBackVertex(Vec3(min.x(), max.y(), 0.0));
+
+					m_drawer->pushBackVertex(Vec3(min.x(), max.y(), 0.0));
+					m_drawer->pushBackVertex(Vec3(max.x(), min.y(), 0.0));
+					m_drawer->pushBackVertex(Vec3(max.x(), max.y(), 0.0));
+				}
+			}
+		}
+		m_drawer->end();
+		// printf("%u\n", count);
+
+		m_drawer->setViewProjectionMatrix(camFrc.getViewProjectionMatrix());
+		Vec3 offset(0.0, 0.0, 0.0);
+		for(U i = 0; i < ltriangle.getSize() / 3; ++i)
+		{
+			m_drawer->drawLine(ltriangle[i * 3 + 0].xyz() + offset,
+				ltriangle[i * 3 + 1].xyz() + offset,
+				Vec4(0, 0.2, 0, 1));
+			m_drawer->drawLine(ltriangle[i * 3 + 1].xyz() + offset,
+				ltriangle[i * 3 + 2].xyz() + offset,
+				Vec4(0.0, 0.2, 0.0, 1.0));
+			m_drawer->drawLine(ltriangle[i * 3 + 2].xyz() + offset,
+				ltriangle[i * 3 + 0].xyz() + offset,
+				Vec4(0.0, 0.2, 0.0, 1.0));
+		}
+	}
+#endif
+
+#if 0
 	{
 		CollisionDebugDrawer cd(m_drawer);
 
@@ -200,40 +297,40 @@ Error Dbg::run(RenderingContext& ctx)
 		m_drawer->setModelMatrix(Mat4::getIdentity());
 		m_drawer->drawLine(polyw[0], polyw[1], Vec4(1.0));
 
-		Vec4 p0 = camFr.getViewMatrix() * polyw[0].xyz1();
+		Vec4 p0 = camFrc.getViewMatrix() * polyw[0].xyz1();
 		p0.w() = 0.0;
-		Vec4 p1 = camFr.getViewMatrix() * polyw[1].xyz1();
+		Vec4 p1 = camFrc.getViewMatrix() * polyw[1].xyz1();
 		p1.w() = 0.0;
 
 		Vec4 r = p1 - p0;
 		r.normalize();
 
-		Vec4 a = camFr.getProjectionMatrix() * p0.xyz1();
+		Vec4 a = camFrc.getProjectionMatrix() * p0.xyz1();
 		a /= a.w();
 
 		Vec4 i;
 		if(r.z() > 0)
 		{
-			// Plane near(Vec4(0, 0, -1, 0), camFr.getFrustum().getNear() +
+			// Plane near(Vec4(0, 0, -1, 0), camFrc.getFrustum().getNear() +
 			// 0.001);
 			// Bool in = near.intersectRay(p0, r * 100000.0, i);
-			i.z() = -camFr.getFrustum().getNear();
+			i.z() = -camFrc.getFrustum().getNear();
 			F32 t = (i.z() - p0.z()) / r.z();
 			i.x() = p0.x() + t * r.x();
 			i.y() = p0.y() + t * r.y();
 
-			i = camFr.getProjectionMatrix() * i.xyz1();
+			i = camFrc.getProjectionMatrix() * i.xyz1();
 			i /= i.w();
 		}
 		else
 		{
-			i = camFr.getProjectionMatrix() * (r * 100000.0).xyz1();
+			i = camFrc.getProjectionMatrix() * (r * 100000.0).xyz1();
 			i /= i.w();
 		}
 
 		/*r *= 0.01;
 		Vec4 b = polyw[0].xyz0() + r;
-		b = camFr.getViewProjectionMatrix() * b.xyz1();
+		b = camFrc.getViewProjectionMatrix() * b.xyz1();
 		Vec4 d = b / b.w();*/
 
 		m_drawer->setViewProjectionMatrix(Mat4::getIdentity());

+ 24 - 8
src/renderer/DebugDrawer.cpp

@@ -235,22 +235,38 @@ void DebugDrawer::drawGrid()
 		// if the middle line then change color
 		if(x == 0)
 		{
+			setColor(col0 * 0.5 + col1 * 0.5);
+			pushBackVertex(Vec3(x, 0.0, -GRID_HALF_SIZE));
+			pushBackVertex(Vec3(x, 0.0, 0.0));
+
 			setColor(col1);
+			pushBackVertex(Vec3(x, 0.0, 0.0));
+			pushBackVertex(Vec3(x, 0.0, GRID_HALF_SIZE));
+		}
+		else
+		{
+			// line in z
+			pushBackVertex(Vec3(x, 0.0, -GRID_HALF_SIZE));
+			pushBackVertex(Vec3(x, 0.0, GRID_HALF_SIZE));
 		}
-
-		// line in z
-		pushBackVertex(Vec3(x, 0.0, -GRID_HALF_SIZE));
-		pushBackVertex(Vec3(x, 0.0, GRID_HALF_SIZE));
 
 		// if middle line change col so you can highlight the x-axis
 		if(x == 0)
 		{
+			setColor(col0 * 0.5 + col2 * 0.5);
+			pushBackVertex(Vec3(-GRID_HALF_SIZE, 0.0, x));
+			pushBackVertex(Vec3(0.0, 0.0, x));
+
 			setColor(col2);
+			pushBackVertex(Vec3(0.0, 0.0, x));
+			pushBackVertex(Vec3(GRID_HALF_SIZE, 0.0, x));
+		}
+		else
+		{
+			// line in the x
+			pushBackVertex(Vec3(-GRID_HALF_SIZE, 0.0, x));
+			pushBackVertex(Vec3(GRID_HALF_SIZE, 0.0, x));
 		}
-
-		// line in the x
-		pushBackVertex(Vec3(-GRID_HALF_SIZE, 0.0, x));
-		pushBackVertex(Vec3(GRID_HALF_SIZE, 0.0, x));
 	}
 
 	// render

+ 303 - 0
src/scene/SoftwareRasterizer.cpp

@@ -0,0 +1,303 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/scene/SoftwareRasterizer.h>
+#include <anki/collision/Functions.h>
+
+namespace anki
+{
+
+//==============================================================================
+void SoftwareRasterizer::prepare(
+	const Mat4& mv, const Mat4& p, U width, U height)
+{
+	m_mv = mv;
+	m_p = p;
+
+	Array<Plane*, 6> planes = {&m_planes[0],
+		&m_planes[1],
+		&m_planes[2],
+		&m_planes[3],
+		&m_planes[4],
+		&m_planes[5]};
+	extractClipPlanes(p, planes);
+
+	// Reset z buffer
+	ANKI_ASSERT(width > 0 && height > 0);
+	m_width = width;
+	m_height = height;
+	U size = width * height;
+	if(m_zbuffer.getSize() < size)
+	{
+		m_zbuffer.destroy(m_alloc);
+		m_zbuffer.create(m_alloc, size);
+	}
+	memset(&m_zbuffer[0], 0xFF, sizeof(m_zbuffer[0]) * size);
+}
+
+//==============================================================================
+void SoftwareRasterizer::clipTriangle(
+	const Vec4* inVerts, Vec4* outVerts, U& outVertCount) const
+{
+	ANKI_ASSERT(inVerts && outVerts);
+
+	const Plane& plane = m_planes[Frustum::PlaneType::NEAR];
+	F32 clipZ = -plane.getOffset() - getEpsilon<F32>();
+	ANKI_ASSERT(clipZ < 0.0);
+
+	Array<Bool, 3> vertInside;
+	U vertInsideCount = 0;
+	for(U i = 0; i < 3; ++i)
+	{
+		vertInside[i] = inVerts[i].z() < clipZ;
+		vertInsideCount += (vertInside[i]) ? 1 : 0;
+	}
+
+	switch(vertInsideCount)
+	{
+	case 0:
+		// All out
+		outVertCount = 0;
+		break;
+	case 3:
+		// All in
+		outVertCount = 3;
+		outVerts[0] = inVerts[0];
+		outVerts[1] = inVerts[1];
+		outVerts[2] = inVerts[2];
+		break;
+	case 1:
+	{
+		U i, next, prev;
+		if(vertInside[0])
+		{
+			i = 0;
+			next = 1;
+			prev = 2;
+		}
+		else if(vertInside[1])
+		{
+			i = 1;
+			next = 2;
+			prev = 0;
+		}
+		else
+		{
+			i = 2;
+			next = 0;
+			prev = 1;
+		}
+
+		// Find first intersection
+		Vec4 rayOrigin = inVerts[i].xyz0();
+		Vec4 rayDir = (inVerts[next].xyz0() - rayOrigin).getNormalized();
+
+		Vec4 intersection0;
+		Bool intersects = plane.intersectRay(rayOrigin, rayDir, intersection0);
+		(void)intersects;
+		ANKI_ASSERT(intersects);
+
+		// Find second intersection
+		rayDir = (inVerts[prev].xyz0() - rayOrigin).getNormalized();
+
+		Vec4 intersection1;
+		intersects = plane.intersectRay(rayOrigin, rayDir, intersection1);
+		(void)intersects;
+		ANKI_ASSERT(intersects);
+
+		// Finalize
+		outVerts[0] = inVerts[i];
+		outVerts[1] = intersection0.xyz1();
+		outVerts[2] = intersection1.xyz1();
+		outVertCount = 3;
+	}
+	break;
+	case 2:
+	{
+		U in0, in1, out;
+		if(vertInside[0] && vertInside[1])
+		{
+			in0 = 0;
+			in1 = 1;
+			out = 2;
+		}
+		else if(vertInside[1] && vertInside[2])
+		{
+			in0 = 1;
+			in1 = 2;
+			out = 0;
+		}
+		else
+		{
+			ANKI_ASSERT(vertInside[2] && vertInside[0]);
+			in0 = 2;
+			in1 = 0;
+			out = 1;
+		}
+
+		// Find first intersection
+		Vec4 rayOrigin = inVerts[in1].xyz0();
+		Vec4 rayDir = (inVerts[out].xyz0() - rayOrigin).getNormalized();
+
+		Vec4 intersection0;
+		Bool intersects = plane.intersectRay(rayOrigin, rayDir, intersection0);
+		(void)intersects;
+		ANKI_ASSERT(intersects);
+
+		// Find second intersection
+		rayOrigin = inVerts[in0].xyz0();
+		rayDir = (inVerts[out].xyz0() - rayOrigin).getNormalized();
+
+		Vec4 intersection1;
+		intersects = plane.intersectRay(rayOrigin, rayDir, intersection1);
+		(void)intersects;
+		ANKI_ASSERT(intersects);
+
+		// Two triangles
+		outVerts[0] = inVerts[in1];
+		outVerts[1] = intersection0;
+		outVerts[2] = intersection1;
+		outVerts[3] = intersection1;
+		outVerts[4] = inVerts[in0];
+		outVerts[5] = inVerts[in1];
+		outVertCount = 6;
+	}
+	break;
+	}
+}
+
+//==============================================================================
+void SoftwareRasterizer::draw(const F32* verts, U vertCount, U stride)
+{
+	ANKI_ASSERT(verts && vertCount > 0 && (vertCount % 3) == 0);
+	ANKI_ASSERT(stride >= sizeof(F32) * 3 && (stride % sizeof(F32)) == 0);
+
+	U floatStride = stride / sizeof(F32);
+	const F32* vertsEnd = verts + vertCount * floatStride;
+	while(verts != vertsEnd)
+	{
+		// Convert triangle to view space
+		Array<Vec4, 3> triVspace;
+		for(U j = 0; j < 3; ++j)
+		{
+			triVspace[j] = m_mv * Vec4(verts[0], verts[1], verts[2], 1.0);
+			verts += floatStride;
+		}
+
+		// Cull if backfacing
+		Vec4 norm =
+			(triVspace[1] - triVspace[0]).cross(triVspace[2] - triVspace[1]);
+		ANKI_ASSERT(norm.w() == 0.0);
+
+		Vec4 eye = triVspace[0].xyz0();
+		if(norm.dot(eye) >= 0.0)
+		{
+			continue;
+		}
+
+		// Clip it
+		Array<Vec4, 6> clippedTrisVspace;
+		U clippedCount = 0;
+		clipTriangle(&triVspace[0], &clippedTrisVspace[0], clippedCount);
+		if(clippedCount == 0)
+		{
+			// Outside view
+			continue;
+		}
+
+		// Rasterize
+		Array<Vec4, 3> clip;
+		for(U j = 0; j < clippedCount; j += 3)
+		{
+			for(U k = 0; k < 3; k++)
+			{
+				clip[k] = m_p * clippedTrisVspace[j + k].xyz1();
+				ANKI_ASSERT(clip[k].w() > 0.0);
+			}
+
+			rasterizeTriangle(&clip[0]);
+		}
+	}
+}
+
+//==============================================================================
+Bool SoftwareRasterizer::computeBarycetrinc(
+	const Vec2& a, const Vec2& b, const Vec2& c, const Vec2& p, Vec3& uvw) const
+{
+	Vec2 dca = c - a;
+	Vec2 dba = b - a;
+	Vec2 dap = a - p;
+
+	Vec3 n(dca.x(), dba.x(), dap.x());
+	Vec3 m(dca.y(), dba.y(), dap.y());
+
+	Vec3 k = n.cross(m);
+
+	Bool skip = false;
+	if(!isZero(k.z()))
+	{
+		uvw = Vec3(1.0 - (k.x() + k.y()) / k.z(), k.y() / k.z(), k.x() / k.z());
+
+		if(uvw.x() < 0.0 || uvw.y() < 0.0 || uvw.z() < 0.0)
+		{
+			skip = true;
+		}
+	}
+	else
+	{
+		skip = true;
+	}
+
+	return skip;
+}
+
+//==============================================================================
+void SoftwareRasterizer::rasterizeTriangle(const Vec4* tri)
+{
+	ANKI_ASSERT(tri);
+
+	const Vec2 windowSize(m_width, m_height);
+	Array<Vec3, 3> ndc;
+	Array<Vec2, 3> window;
+	Vec2 bboxMin(MAX_F32), bboxMax(MIN_F32);
+	for(U i = 0; i < 3; i++)
+	{
+		ndc[i] = tri[i].xyz() / tri[i].w();
+		window[i] = (ndc[i].xy() / 2.0 + 0.5) * windowSize;
+
+		for(U j = 0; j < 2; j++)
+		{
+			bboxMin[j] = floor(min(bboxMin[j], window[i][j]));
+			bboxMin[j] = clamp(bboxMin[j], 0.0f, windowSize[j]);
+
+			bboxMax[j] = ceil(max(bboxMax[j], window[i][j]));
+			bboxMax[j] = clamp(bboxMax[j], 0.0f, windowSize[j]);
+		}
+	}
+
+	for(F32 y = bboxMin.y() + 0.5; y < bboxMax.y() + 0.5; y += 1.0)
+	{
+		for(F32 x = bboxMin.x() + 0.5; x < bboxMax.x() + 0.5; x += 1.0)
+		{
+			Vec2 p(x, y);
+			Vec3 bc;
+			if(!computeBarycetrinc(window[0], window[1], window[2], p, bc))
+			{
+				F32 z0 = ndc[0].z() / 2.0 + 0.5;
+				F32 z1 = ndc[1].z() / 2.0 + 0.5;
+				F32 z2 = ndc[2].z() / 2.0 + 0.5;
+
+				F32 depth = z0 * bc[0] + z1 * bc[1] + z2 * bc[2];
+				ANKI_ASSERT(depth >= 0.0 && depth <= 1.0);
+
+				// Store the min of the current value and new one
+				U32 depthi = depth * MAX_U32;
+				m_zbuffer[U(y) * m_width + U(x)].min(depthi);
+			}
+		}
+	}
+}
+
+} // end namespace anki