瀏覽代碼

Merge pull request #38 from godlikepanos/bullet

Move the physics engine to bullet.
Panagiotis Christopoulos Charitos 7 年之前
父節點
當前提交
cea40fce48
共有 100 個文件被更改,包括 2184 次插入2277 次删除
  1. 3 0
      .gitignore
  2. 13 18
      CMakeLists.txt
  3. 1 0
      samples/CMakeLists.txt
  4. 3 7
      samples/common/Framework.cpp
  5. 4 0
      samples/physics_playground/CMakeLists.txt
  6. 249 0
      samples/physics_playground/Main.cpp
  7. 29 0
      samples/physics_playground/assets/Material-material.ankimtl
  8. 29 0
      samples/physics_playground/assets/Material_001-material.ankimtl
  9. 5 0
      samples/physics_playground/assets/Suzanne.ankicl
  10. 二進制
      samples/physics_playground/assets/Suzanne.ankimesh
  11. 9 0
      samples/physics_playground/assets/SuzanneMaterial-material.ankimdl
  12. 5 0
      samples/physics_playground/assets/floor.ankicl
  13. 二進制
      samples/physics_playground/assets/floor.ankimesh
  14. 9 0
      samples/physics_playground/assets/floorMaterial-material.ankimdl
  15. 二進制
      samples/physics_playground/assets/physics_playground.blend
  16. 68 0
      samples/physics_playground/assets/scene.lua
  17. 5 0
      samples/physics_playground/assets/walls.ankicl
  18. 二進制
      samples/physics_playground/assets/walls.ankimesh
  19. 9 0
      samples/physics_playground/assets/wallsMaterial_001-material.ankimdl
  20. 23 10
      shaders/ExponentialShadowmappingResolve.glslp
  21. 4 0
      shaders/LightFunctions.glsl
  22. 1 1
      shaders/TemporalAAResolve.glslp
  23. 0 1
      src/anki/AnKi.h
  24. 0 12
      src/anki/Event.h
  25. 5 0
      src/anki/Physics.h
  26. 0 1
      src/anki/Renderer.h
  27. 11 1
      src/anki/Scene.h
  28. 15 80
      src/anki/core/App.cpp
  29. 1 1
      src/anki/core/Config.cpp
  30. 0 2
      src/anki/event/CMakeLists.txt
  31. 0 153
      src/anki/event/EventManager.cpp
  32. 0 71
      src/anki/event/MainRendererPpsHdrEvent.cpp
  33. 0 47
      src/anki/event/MainRendererPpsHdrEvent.h
  34. 4 1
      src/anki/gr/gl/GrManager.cpp
  35. 5 1
      src/anki/gr/vulkan/GrManager.cpp
  36. 1 1
      src/anki/gr/vulkan/GrManagerImpl.cpp
  37. 24 0
      src/anki/math/Transform.h
  38. 7 1
      src/anki/physics/Common.cpp
  39. 44 19
      src/anki/physics/Common.h
  40. 36 98
      src/anki/physics/PhysicsBody.cpp
  41. 48 36
      src/anki/physics/PhysicsBody.h
  42. 42 56
      src/anki/physics/PhysicsCollisionShape.cpp
  43. 29 69
      src/anki/physics/PhysicsCollisionShape.h
  44. 4 101
      src/anki/physics/PhysicsDrawer.cpp
  45. 47 25
      src/anki/physics/PhysicsDrawer.h
  46. 72 0
      src/anki/physics/PhysicsJoint.cpp
  47. 82 0
      src/anki/physics/PhysicsJoint.h
  48. 17 0
      src/anki/physics/PhysicsObject.cpp
  49. 99 5
      src/anki/physics/PhysicsObject.h
  50. 36 51
      src/anki/physics/PhysicsPlayerController.cpp
  51. 14 57
      src/anki/physics/PhysicsPlayerController.h
  52. 69 0
      src/anki/physics/PhysicsTrigger.cpp
  53. 61 0
      src/anki/physics/PhysicsTrigger.h
  54. 164 109
      src/anki/physics/PhysicsWorld.cpp
  55. 67 84
      src/anki/physics/PhysicsWorld.h
  56. 0 1
      src/anki/renderer/Dbg.cpp
  57. 1 1
      src/anki/renderer/RenderQueue.h
  58. 2 2
      src/anki/renderer/Renderer.cpp
  59. 21 7
      src/anki/renderer/ShadowMapping.cpp
  60. 3 1
      src/anki/renderer/ShadowMapping.h
  61. 3 6
      src/anki/renderer/Ssao.cpp
  62. 3 5
      src/anki/resource/CollisionResource.cpp
  63. 1 0
      src/anki/resource/ShaderProgramResource.cpp
  64. 13 8
      src/anki/scene/BodyNode.cpp
  65. 13 13
      src/anki/scene/CameraNode.cpp
  66. 2 0
      src/anki/scene/Common.h
  67. 63 35
      src/anki/scene/DebugDrawer.cpp
  68. 9 11
      src/anki/scene/DebugDrawer.h
  69. 13 13
      src/anki/scene/DecalNode.cpp
  70. 1 4
      src/anki/scene/Forward.h
  71. 16 16
      src/anki/scene/LightNode.cpp
  72. 0 30
      src/anki/scene/Misc.h
  73. 30 29
      src/anki/scene/ModelNode.cpp
  74. 1 1
      src/anki/scene/ModelNode.h
  75. 7 7
      src/anki/scene/OccluderNode.cpp
  76. 21 18
      src/anki/scene/ParticleEmitterNode.cpp
  77. 2 3
      src/anki/scene/ParticleEmitterNode.h
  78. 82 0
      src/anki/scene/PhysicsDebugNode.cpp
  79. 30 0
      src/anki/scene/PhysicsDebugNode.h
  80. 15 14
      src/anki/scene/PlayerNode.cpp
  81. 9 9
      src/anki/scene/ReflectionProbeNode.cpp
  82. 4 4
      src/anki/scene/ReflectionProxyNode.cpp
  83. 9 14
      src/anki/scene/SceneGraph.cpp
  84. 14 9
      src/anki/scene/SceneGraph.h
  85. 1 16
      src/anki/scene/SceneNode.h
  86. 0 647
      src/anki/scene/SectorNode.cpp
  87. 0 212
      src/anki/scene/SectorNode.h
  88. 3 2
      src/anki/scene/StaticCollisionNode.cpp
  89. 5 8
      src/anki/scene/StaticGeometryNode.cpp
  90. 62 0
      src/anki/scene/TriggerNode.cpp
  91. 37 0
      src/anki/scene/TriggerNode.h
  92. 2 4
      src/anki/scene/Visibility.cpp
  93. 0 1
      src/anki/scene/VisibilityInternal.h
  94. 5 3
      src/anki/scene/components/BodyComponent.h
  95. 1 1
      src/anki/scene/components/DecalComponent.h
  96. 1 1
      src/anki/scene/components/FrustumComponent.cpp
  97. 1 1
      src/anki/scene/components/FrustumComponent.h
  98. 147 0
      src/anki/scene/components/JointComponent.cpp
  99. 57 0
      src/anki/scene/components/JointComponent.h
  100. 1 1
      src/anki/scene/components/LensFlareComponent.h

+ 3 - 0
.gitignore

@@ -11,5 +11,8 @@
 !*.md
 !*.txt
 !*.glslp
+!*.anki*
+!*.blend
+!*.lua
 !CMakeLists.txt
 build*/*

+ 13 - 18
CMakeLists.txt

@@ -137,8 +137,6 @@ set(CXX_FLAGS "")
 set(COMPILER_FLAGS "")
 set(LINKER_FLAGS "")
 
-add_definitions(-D_NEWTON_STATIC_LIB -D_CUSTOM_JOINTS_STATIC_LIB)
-
 if(NOT MSVC)
 	if(ARCH_64)
 		add_definitions(-D_POSIX_VER_64)
@@ -147,13 +145,6 @@ if(NOT MSVC)
 	endif()
 endif()
 
-# Newton wants that
-if(MINGW AND ARCH_64)
-	add_definitions(-D_MINGW_64_VER)
-elseif(MINGW AND ARCH_32)
-	add_definitions(-D_MINGW_32_VER)
-endif()
-
 if(MINGW)
 	set(COMPILER_FLAGS "${COMPILER_FLAGS} -mconsole ")
 endif()
@@ -235,7 +226,14 @@ endif()
 ################################################################################
 # Thirdparty                                                                   #
 ################################################################################
-set(ANKI_EXTERN_SUB_DIRS tinyxml2 lua z newton)
+set(ANKI_EXTERN_SUB_DIRS tinyxml2 lua z bullet)
+
+# Bullet config
+option(BUILD_BULLET2_DEMOS OFF)
+option(BUILD_BULLET3 OFF)
+option(BUILD_CPU_DEMOS OFF)
+option(BUILD_OPENGL3_DEMOS OFF)
+option(BUILD_EXTRAS OFF)
 
 if((LINUX OR MACOS OR WINDOWS) AND GL)
 	set(ANKI_EXTERN_SUB_DIRS ${ANKI_EXTERN_SUB_DIRS} GLEW)
@@ -355,15 +353,11 @@ include_directories("src"
 	"${SDL2_INCLUDE_DIRS}"
 	"thirdparty/freetype/include"
 	"${CMAKE_CURRENT_BINARY_DIR}/thirdparty/freetype/include/freetype2"
-	"thirdparty/newton/coreLibrary_300/source/newton"
-	"thirdparty/newton/packages/dCustomJoints"
-	"thirdparty/newton/packages/dContainers"
-	"thirdparty/newton/packages/dMath"
-	"thirdparty/newton/packages/thirdParty/timeTracker/"
 	"thirdparty/khronos"
 	"${CMAKE_CURRENT_BINARY_DIR}"
 	"${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/glslang"
 	"${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/tinyexpr"
+	"${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/bullet/src"
 	"thirdparty"
 	${CMAKE_CURRENT_SOURCE_DIR})
 
@@ -426,17 +420,18 @@ if(SDL)
 	set(THIRD_PARTY_LIBS ${THIRD_PARTY_LIBS} SDL2-static)
 endif()
 
-set(THIRD_PARTY_LIBS ${THIRD_PARTY_LIBS} ankispirvcross glslang SPIRV OGLCompiler OSDependent ankitinyexpr)
+set(THIRD_PARTY_LIBS ${THIRD_PARTY_LIBS} BulletSoftBody BulletDynamics BulletCollision LinearMath 
+	ankispirvcross ankitinyxml2 ankilua ankiz glslang SPIRV OGLCompiler OSDependent ankitinyexpr)
 
 # Add anki sub libraries
-set(ANKI_SUB_DIRS core script renderer scene ui event input physics resource misc gr collision math util)
+set(ANKI_SUB_DIRS core script renderer scene ui input physics resource misc gr collision math util)
 foreach(TMP ${ANKI_SUB_DIRS})
 	add_subdirectory(src/anki/${TMP})
 endforeach()
 
 separate_arguments(AK_SOURCES)
 add_library(anki ${AK_SOURCES})
-target_link_libraries(anki ankitinyxml2 ankilua ankiz ankinewton ${THIRD_PARTY_LIBS})
+target_link_libraries(anki ${THIRD_PARTY_LIBS})
 
 ################################################################################
 # AnKi extra                                                                   #

+ 1 - 0
samples/CMakeLists.txt

@@ -1,3 +1,4 @@
 add_definitions(-UANKI_BUILD)
 add_subdirectory(simple_scene)
 add_subdirectory(sponza)
+add_subdirectory(physics_playground)

+ 3 - 7
samples/common/Framework.cpp

@@ -89,10 +89,6 @@ Error SampleApp::userMainLoop(Bool& quit)
 	{
 		// renderer.getDbg().flipFlags(DbgFlag::SPATIAL_COMPONENT);
 	}
-	if(in.getKey(KeyCode::F6) == 1)
-	{
-		renderer.getDbg().switchDepthTestEnabled();
-	}
 
 	if(in.getKey(KeyCode::UP))
 	{
@@ -124,14 +120,14 @@ Error SampleApp::userMainLoop(Bool& quit)
 		mover->moveLocalX(MOVE_DISTANCE);
 	}
 
-	if(in.getKey(KeyCode::Z))
+	if(in.getKey(KeyCode::C))
 	{
-		mover->moveLocalY(MOVE_DISTANCE);
+		mover->moveLocalY(-MOVE_DISTANCE);
 	}
 
 	if(in.getKey(KeyCode::SPACE))
 	{
-		mover->moveLocalY(-MOVE_DISTANCE);
+		mover->moveLocalY(MOVE_DISTANCE);
 	}
 
 	if(in.getKey(KeyCode::W))

+ 4 - 0
samples/physics_playground/CMakeLists.txt

@@ -0,0 +1,4 @@
+add_executable(physics_playground Main.cpp ../common/Framework.cpp)
+target_link_libraries(physics_playground anki)
+
+installExecutable(physics_playground)

+ 249 - 0
samples/physics_playground/Main.cpp

@@ -0,0 +1,249 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <cstdio>
+#include "../common/Framework.h"
+
+using namespace anki;
+
+class RayCast : public PhysicsWorldRayCastCallback
+{
+public:
+	RayCast(Vec3 from, Vec3 to, PhysicsMaterialBit mtl)
+		: PhysicsWorldRayCastCallback(from, to, mtl)
+	{
+	}
+
+	void processResult(PhysicsFilteredObject& obj, const Vec3& worldNormal, const Vec3& worldPosition)
+	{
+		SceneNode* node = static_cast<SceneNode*>(obj.getUserData());
+		ANKI_ASSERT(node);
+		ANKI_LOGI("Ray hits %s", node->getName().cstr());
+	}
+};
+
+class MyApp : public SampleApp
+{
+public:
+	Error sampleExtraInit() override;
+	Error userMainLoop(Bool& quit) override;
+};
+
+Error MyApp::sampleExtraInit()
+{
+	ScriptResourcePtr script;
+	ANKI_CHECK(getResourceManager().loadResource("assets/scene.lua", script));
+	ANKI_CHECK(getScriptManager().evalString(script->getSource()));
+
+	// Create the player
+	if(1)
+	{
+		SceneNode& cam = getSceneGraph().getActiveCameraNode();
+		cam.getComponent<MoveComponent>().setLocalTransform(
+			Transform(Vec4(0.0, 0.0, 5.0, 0.0), Mat3x4::getIdentity(), 1.0));
+
+		PlayerNode* player;
+		ANKI_CHECK(getSceneGraph().newSceneNode("player", player, Vec4(0.0f, 2.5f, 0.0f, 0.0f)));
+		PlayerControllerComponent& pcomp = player->getComponent<PlayerControllerComponent>();
+		pcomp.getPhysicsPlayerController()->setMaterialMask(PhysicsMaterialBit::STATIC_GEOMETRY);
+
+		player->addChild(&cam);
+	}
+
+	// Create a body component with joint
+	{
+		ModelNode* monkey;
+		ANKI_CHECK(
+			getSceneGraph().newSceneNode<ModelNode>("monkey_p2p", monkey, "assets/SuzanneMaterial-material.ankimdl"));
+
+		BodyNode* body;
+		ANKI_CHECK(getSceneGraph().newSceneNode<BodyNode>("bmonkey_p2p", body, "assets/Suzanne.ankicl"));
+		body->getComponent<BodyComponent>().setTransform(
+			Transform(Vec4(-0.0f, 4.0f, -3.0f, 0.0f), Mat3x4::getIdentity(), 1.0f));
+
+		body->addChild(monkey);
+
+		body->getComponent<JointComponent>().newHingeJoint(Vec3(0.2f, 1.0f, 0.0f), Vec3(1, 0, 0));
+	}
+
+	// Create a chain
+	{
+		const U LINKS = 5;
+
+		BodyNode* prevBody = nullptr;
+		for(U i = 0; i < LINKS; ++i)
+		{
+			ModelNode* monkey;
+			ANKI_CHECK(getSceneGraph().newSceneNode<ModelNode>(
+				StringAuto(getAllocator()).sprintf("monkey_chain%u", i).toCString(),
+				monkey,
+				"assets/SuzanneMaterial-material.ankimdl"));
+
+			Transform trf(Vec4(-4.3f, 12.0f, -3.0f, 0.0f), Mat3x4::getIdentity(), 1.0f);
+			trf.getOrigin().y() -= i * 1.25f;
+			// trf.getOrigin().x() -= i * 0.25f;
+
+			// monkey->getComponent<MoveComponent>().setLocalTransform(trf);
+
+			BodyNode* body;
+			ANKI_CHECK(getSceneGraph().newSceneNode<BodyNode>(
+				StringAuto(getAllocator()).sprintf("bmonkey_chain%u", i).toCString(), body, "assets/Suzanne.ankicl"));
+			body->getComponent<BodyComponent>().setTransform(trf);
+
+			// Create joint
+			JointComponent& jointc = body->getComponent<JointComponent>();
+			if(prevBody == nullptr)
+			{
+				jointc.newPoint2PointJoint(Vec3(0, 1, 0));
+			}
+			else
+			{
+				prevBody->addChild(body);
+				jointc.newPoint2PointJoint2(Vec3(0, 1.0, 0), Vec3(0, -1.0, 0));
+			}
+
+			body->addChild(monkey);
+			prevBody = body;
+		}
+	}
+
+	// Trigger
+	{
+		TriggerNode* node;
+		ANKI_CHECK(getSceneGraph().newSceneNode("trigger", node, 1.8f));
+
+		node->getComponent<MoveComponent>().setLocalOrigin(Vec4(1.0f, 0.5f, 0.0f, 0.0f));
+	}
+
+	return Error::NONE;
+}
+
+Error MyApp::userMainLoop(Bool& quit)
+{
+	// ANKI_CHECK(SampleApp::userMainLoop(quit));
+
+	if(getInput().getKey(KeyCode::ESCAPE))
+	{
+		quit = true;
+	}
+
+	if(getInput().getKey(KeyCode::F1) == 1)
+	{
+		static U mode = 0;
+		mode = (mode + 1) % 3;
+		if(mode == 0)
+		{
+			getMainRenderer().getDbg().setEnabled(false);
+		}
+		else if(mode == 1)
+		{
+			getMainRenderer().getDbg().setEnabled(true);
+			getMainRenderer().getDbg().setDepthTestEnabled(true);
+			getMainRenderer().getDbg().setDitheredDepthTestEnabled(false);
+		}
+		else
+		{
+			getMainRenderer().getDbg().setEnabled(true);
+			getMainRenderer().getDbg().setDepthTestEnabled(false);
+			getMainRenderer().getDbg().setDitheredDepthTestEnabled(true);
+		}
+	}
+
+	if(getInput().getKey(KeyCode::R))
+	{
+		SceneNode& player = getSceneGraph().findSceneNode("player");
+		player.getComponent<PlayerControllerComponent>().moveToPosition(Vec4(0.0f, 2.0f, 0.0f, 0.0f));
+	}
+
+	if(getInput().getMouseButton(MouseButton::LEFT) == 1)
+	{
+		ANKI_LOGI("Firing a monkey");
+
+		static U instance = 0;
+
+		Transform camTrf = getSceneGraph().getActiveCameraNode().getComponent<MoveComponent>().getWorldTransform();
+
+		ModelNode* monkey;
+		ANKI_CHECK(getSceneGraph().newSceneNode<ModelNode>(
+			StringAuto(getAllocator()).sprintf("monkey%u", instance++).toCString(),
+			monkey,
+			"assets/SuzanneMaterial-material.ankimdl"));
+		// monkey->getComponent<MoveComponent>().setLocalTransform(camTrf);
+
+		BodyNode* body;
+		ANKI_CHECK(getSceneGraph().newSceneNode<BodyNode>(
+			StringAuto(getAllocator()).sprintf("bmonkey%u", instance++).toCString(), body, "assets/Suzanne.ankicl"));
+		body->getComponent<BodyComponent>().setTransform(camTrf);
+
+		PhysicsBodyPtr pbody = body->getComponent<BodyComponent>().getPhysicsBody();
+		pbody->applyForce(camTrf.getRotation().getZAxis().xyz() * -1500.0f, Vec3(0.0f, 0.0f, 0.0f));
+
+		body->addChild(monkey);
+
+		// Create the destruction event
+		CString script = R"(
+function update(event, prevTime, crntTime)
+	-- Do nothing
+	return 1
+end
+
+function onKilled(event, prevTime, crntTime)
+	logi("onKilled")
+	event:getAssociatedSceneNodes():getAt(0):setMarkedForDeletion()
+	return 1
+end
+		)";
+		ScriptEvent* event;
+		ANKI_CHECK(getSceneGraph().getEventManager().newEvent(event, -1, 10.0, script));
+		event->addAssociatedSceneNode(body);
+	}
+
+	if(getInput().getMouseButton(MouseButton::RIGHT) == 1)
+	{
+		Transform camTrf = getSceneGraph().getActiveCameraNode().getComponent<MoveComponent>().getWorldTransform();
+		Vec3 from = camTrf.getOrigin().xyz();
+		Vec3 to = from + -camTrf.getRotation().getZAxis() * 10.0f;
+
+		RayCast ray(from, to, PhysicsMaterialBit::ALL);
+
+		getPhysicsWorld().rayCast(ray);
+	}
+
+	{
+		SceneNode& node = getSceneGraph().findSceneNode("trigger");
+		TriggerComponent& comp = node.getComponent<TriggerComponent>();
+
+		for(U i = 0; i < comp.getContactSceneNodes().getSize(); ++i)
+		{
+			ANKI_LOGI("Touching %s", comp.getContactSceneNodes()[i]->getName().cstr());
+		}
+	}
+
+	return Error::NONE;
+}
+
+int main(int argc, char* argv[])
+{
+	Error err = Error::NONE;
+
+	MyApp* app = new MyApp;
+	err = app->init(argc, argv, argv[0]);
+	if(!err)
+	{
+		err = app->mainLoop();
+	}
+
+	if(err)
+	{
+		ANKI_LOGE("Error reported. Bye!");
+	}
+	else
+	{
+		delete app;
+		ANKI_LOGI("Bye!!");
+	}
+
+	return 0;
+}

+ 29 - 0
samples/physics_playground/assets/Material-material.ankimtl

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="shaders/GBufferGeneric.glslp">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="0"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="0"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="0"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="rotationMat" builtin="ROTATION_MATRIX"/>
+		
+	
+		<input shaderInput="diffColor" value="0.548460 0.640000 0.063144"/>
+		<input shaderInput="specColor" value="0.500000 0.500000 0.500000"/>
+		<input shaderInput="roughness" value="0.097847" />
+		<input shaderInput="metallic" value="0.000000"/>
+		
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 29 - 0
samples/physics_playground/assets/Material_001-material.ankimtl

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This file is auto generated by ExporterMaterial.cpp -->
+<material shaderProgram="shaders/GBufferGeneric.glslp">
+	
+	<mutators>
+		<mutator name="DIFFUSE_TEX" value="0"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="0"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="0"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutators>
+	
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="rotationMat" builtin="ROTATION_MATRIX"/>
+		
+	
+		<input shaderInput="diffColor" value="0.012172 0.367335 0.640000"/>
+		<input shaderInput="specColor" value="0.500000 0.500000 0.500000"/>
+		<input shaderInput="roughness" value="0.097847" />
+		<input shaderInput="metallic" value="0.000000"/>
+		
+		<input shaderInput="emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderInput="subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 5 - 0
samples/physics_playground/assets/Suzanne.ankicl

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<collisionShape>
+	<type>staticMesh</type>
+	<value>assets/Suzanne.ankimesh</value>
+</collisionShape>

二進制
samples/physics_playground/assets/Suzanne.ankimesh


+ 9 - 0
samples/physics_playground/assets/SuzanneMaterial-material.ankimdl

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<model>
+	<modelPatches>
+		<modelPatch>
+			<mesh>assets/Suzanne.ankimesh</mesh>
+			<material>assets/Material-material.ankimtl</material>
+		</modelPatch>
+	</modelPatches>
+</model>

+ 5 - 0
samples/physics_playground/assets/floor.ankicl

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<collisionShape>
+	<type>staticMesh</type>
+	<value>assets/floor.ankimesh</value>
+</collisionShape>

二進制
samples/physics_playground/assets/floor.ankimesh


+ 9 - 0
samples/physics_playground/assets/floorMaterial-material.ankimdl

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<model>
+	<modelPatches>
+		<modelPatch>
+			<mesh>assets/floor.ankimesh</mesh>
+			<material>assets/Material-material.ankimtl</material>
+		</modelPatch>
+	</modelPatches>
+</model>

二進制
samples/physics_playground/assets/physics_playground.blend


+ 68 - 0
samples/physics_playground/assets/scene.lua

@@ -0,0 +1,68 @@
+local scene = getSceneGraph()
+local events = getEventManager()
+local rot
+local node
+local inst
+local lcomp
+
+node = scene:newReflectionProbeNode("reflprobe0", Vec4.new(-13.4112, -13.4112, -13.4112, 0), Vec4.new(13.4112, 13.4112, 13.4112, 0))
+trf = Transform.new()
+trf:setOrigin(Vec4.new(0, 9.1132, 0, 0))
+rot = Mat3x4.new()
+rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
+trf:setRotation(rot)
+trf:setScale(1)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+
+node = scene:newModelNode("floorMaterial-materialnone0", "assets/floorMaterial-material.ankimdl")
+trf = Transform.new()
+trf:setOrigin(Vec4.new(0, 0, 0, 0))
+rot = Mat3x4.new()
+rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
+trf:setRotation(rot)
+trf:setScale(1)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+node = scene:newStaticCollisionNode("floorMaterial-materialnone0_cl", "assets/floor.ankicl", trf)
+
+node = scene:newModelNode("wallsMaterial_001-materialnone1", "assets/wallsMaterial_001-material.ankimdl")
+trf = Transform.new()
+trf:setOrigin(Vec4.new(0, 9.10935, 0, 0))
+rot = Mat3x4.new()
+rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
+trf:setRotation(rot)
+trf:setScale(1)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+
+node = scene:newModelNode("SuzanneMaterial-materialnone2", "assets/SuzanneMaterial-material.ankimdl")
+trf = Transform.new()
+trf:setOrigin(Vec4.new(-23.0149, 9.70113, 0, 0))
+rot = Mat3x4.new()
+rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
+trf:setRotation(rot)
+trf:setScale(1)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+node = scene:newStaticCollisionNode("SuzanneMaterial-materialnone2_cl", "assets/Suzanne.ankicl", trf)
+
+node = scene:newPointLightNode("Lamp")
+lcomp = node:getSceneNodeBase():getLightComponent()
+lcomp:setDiffuseColor(Vec4.new(100, 100, 100, 1))
+lcomp:setRadius(30.0003)
+trf = Transform.new()
+trf:setOrigin(Vec4.new(4.07624, 5.90386, -1.00545, 0))
+rot = Mat3x4.new()
+rot:setAll(-0.290865, -0.771075, 0.566429, 0, -0.0551891, 0.604562, 0.794644, 0, -0.955171, 0.199873, -0.2184, 0)
+trf:setRotation(rot)
+trf:setScale(1)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+lcomp:setShadowEnabled(1)
+
+node = scene:newPerspectiveCameraNode("Camera")
+scene:setActiveCameraNode(node:getSceneNodeBase())
+node:setAll(1.22173, 1.0 / getMainRenderer():getAspectRatio() * 1.22173, 0.1, 100)
+trf = Transform.new()
+trf:setOrigin(Vec4.new(7.48113, 5.34367, 6.50764, 0))
+rot = Mat3x4.new()
+rot:setAll(0.685921, -0.323983, 0.651573, 0, 0, 0.895416, 0.44523, 0, -0.727676, -0.305392, 0.614184, 0)
+trf:setRotation(rot)
+trf:setScale(1)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)

+ 5 - 0
samples/physics_playground/assets/walls.ankicl

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<collisionShape>
+	<type>staticMesh</type>
+	<value>assets/walls.ankimesh</value>
+</collisionShape>

二進制
samples/physics_playground/assets/walls.ankimesh


+ 9 - 0
samples/physics_playground/assets/wallsMaterial_001-material.ankimdl

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<model>
+	<modelPatches>
+		<modelPatch>
+			<mesh>assets/walls.ankimesh</mesh>
+			<material>assets/Material_001-material.ankimtl</material>
+		</modelPatch>
+	</modelPatches>
+</model>

+ 23 - 10
shaders/ExponentialShadowmappingResolve.glslp

@@ -5,6 +5,11 @@
 
 #pragma anki input const UVec2 INPUT_TEXTURE_SIZE
 
+const F32 OFFSET = 1.25;
+
+const Vec2 TEXEL_SIZE = 1.0 / Vec2(INPUT_TEXTURE_SIZE);
+const Vec2 HALF_TEXEL_SIZE = TEXEL_SIZE / 2.0;
+
 #pragma anki start vert
 #include <shaders/Common.glsl>
 
@@ -20,6 +25,8 @@ layout(ANKI_UBO_BINDING(0, 0)) uniform u_
 };
 
 layout(location = 0) out Vec2 out_uv;
+layout(location = 1) flat out Vec2 out_maxUv;
+layout(location = 2) flat out Vec2 out_minUv;
 
 void main()
 {
@@ -28,6 +35,10 @@ void main()
 
 	out_uv = fma(out_uv, u_uvScaleAndTranslation.zw, u_uvScaleAndTranslation.xy);
 	gl_Position = Vec4(pos, 0.0, 1.0);
+
+	// Compute the limits
+	out_maxUv = fma(Vec2(1.0), u_uvScaleAndTranslation.zw, u_uvScaleAndTranslation.xy) - HALF_TEXEL_SIZE;
+	out_minUv = fma(Vec2(0.0), u_uvScaleAndTranslation.zw, u_uvScaleAndTranslation.xy) + HALF_TEXEL_SIZE;
 }
 #pragma anki end
 
@@ -36,6 +47,8 @@ void main()
 #include <shaders/Functions.glsl>
 
 layout(location = 0) in Vec2 in_uv;
+layout(location = 1) flat in Vec2 in_maxUv;
+layout(location = 2) flat in Vec2 in_minUv;
 
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_inputTex;
 
@@ -52,22 +65,22 @@ layout(location = 0) out F32 out_color;
 
 F32 sampleLinearDepth(Vec2 uv)
 {
+	uv = clamp(uv, in_minUv, in_maxUv);
 	return linearizeDepth(textureLod(u_inputTex, uv, 0.0).r, u_near, u_far);
 }
 
 void main()
 {
-	const Vec2 TEXEL_SIZE = 1.0 / Vec2(INPUT_TEXTURE_SIZE);
-	Vec2 uvOffset = 1.1 * TEXEL_SIZE;
+	const Vec2 UV_OFFSET = OFFSET * TEXEL_SIZE;
 
 	out_color = sampleLinearDepth(in_uv) * BOX_WEIGHTS[0u];
-	out_color += sampleLinearDepth(in_uv + Vec2(uvOffset.x, 0.0)) * BOX_WEIGHTS[1u];
-	out_color += sampleLinearDepth(in_uv + Vec2(-uvOffset.x, 0.0)) * BOX_WEIGHTS[1u];
-	out_color += sampleLinearDepth(in_uv + Vec2(0.0, uvOffset.y)) * BOX_WEIGHTS[1u];
-	out_color += sampleLinearDepth(in_uv + Vec2(0.0, -uvOffset.y)) * BOX_WEIGHTS[1u];
-	out_color += sampleLinearDepth(in_uv + Vec2(uvOffset.x, uvOffset.y)) * BOX_WEIGHTS[2u];
-	out_color += sampleLinearDepth(in_uv + Vec2(-uvOffset.x, uvOffset.y)) * BOX_WEIGHTS[2u];
-	out_color += sampleLinearDepth(in_uv + Vec2(uvOffset.x, -uvOffset.y)) * BOX_WEIGHTS[2u];
-	out_color += sampleLinearDepth(in_uv + Vec2(-uvOffset.x, -uvOffset.y)) * BOX_WEIGHTS[2u];
+	out_color += sampleLinearDepth(in_uv + Vec2(UV_OFFSET.x, 0.0)) * BOX_WEIGHTS[1u];
+	out_color += sampleLinearDepth(in_uv + Vec2(-UV_OFFSET.x, 0.0)) * BOX_WEIGHTS[1u];
+	out_color += sampleLinearDepth(in_uv + Vec2(0.0, UV_OFFSET.y)) * BOX_WEIGHTS[1u];
+	out_color += sampleLinearDepth(in_uv + Vec2(0.0, -UV_OFFSET.y)) * BOX_WEIGHTS[1u];
+	out_color += sampleLinearDepth(in_uv + Vec2(UV_OFFSET.x, UV_OFFSET.y)) * BOX_WEIGHTS[2u];
+	out_color += sampleLinearDepth(in_uv + Vec2(-UV_OFFSET.x, UV_OFFSET.y)) * BOX_WEIGHTS[2u];
+	out_color += sampleLinearDepth(in_uv + Vec2(UV_OFFSET.x, -UV_OFFSET.y)) * BOX_WEIGHTS[2u];
+	out_color += sampleLinearDepth(in_uv + Vec2(-UV_OFFSET.x, -UV_OFFSET.y)) * BOX_WEIGHTS[2u];
 }
 #pragma anki end

+ 4 - 0
shaders/LightFunctions.glsl

@@ -140,6 +140,10 @@ F32 computeShadowFactorOmni(Vec3 frag2Light, F32 radius, UVec2 atlasTiles, F32 t
 		U32 faceIdxu;
 		Vec2 uv = convertCubeUvsu(dir, faceIdxu);
 
+		// Clamp uv to a small value to avoid reading from other tiles due to bilinear filtering. It's not a perfect
+		// solution but it works
+		uv = clamp(uv, Vec2(0.001), Vec2(1.0 - 0.001));
+
 		// Compute atlas tile
 		atlasTiles >>= UVec2(faceIdxu * 5u);
 		atlasTiles &= UVec2(31u);

+ 1 - 1
shaders/TemporalAAResolve.glslp

@@ -57,7 +57,7 @@ Vec3 sharpen()
 #endif
 	near *= 0.5;
 	F32 sharpness = 1.0;
-	return center + (center - near) * sharpness;
+	return center + max(Vec3(0.0), center - near) * sharpness;
 }
 
 void main()

+ 0 - 1
src/anki/AnKi.h

@@ -9,7 +9,6 @@
 #include <anki/Math.h>
 #include <anki/Collision.h>
 #include <anki/Scene.h>
-#include <anki/Event.h>
 #include <anki/Renderer.h>
 #include <anki/Script.h>
 #include <anki/Input.h>

+ 0 - 12
src/anki/Event.h

@@ -1,12 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <anki/event/EventManager.h>
-
-#include <anki/event/LightEvent.h>
-#include <anki/event/JitterMoveEvent.h>
-#include <anki/event/AnimationEvent.h>

+ 5 - 0
src/anki/Physics.h

@@ -6,5 +6,10 @@
 #pragma once
 
 #include <anki/physics/PhysicsWorld.h>
+#include <anki/physics/PhysicsBody.h>
+#include <anki/physics/PhysicsCollisionShape.h>
+#include <anki/physics/PhysicsJoint.h>
+#include <anki/physics/PhysicsPlayerController.h>
+#include <anki/physics/PhysicsTrigger.h>
 
 /// @defgroup physics Physics subsystem

+ 0 - 1
src/anki/Renderer.h

@@ -6,7 +6,6 @@
 #pragma once
 
 #include <anki/renderer/GBuffer.h>
-#include <anki/renderer/DebugDrawer.h>
 #include <anki/renderer/Common.h>
 #include <anki/renderer/LightShading.h>
 #include <anki/renderer/Volumetric.h>

+ 11 - 1
src/anki/Scene.h

@@ -6,7 +6,6 @@
 #pragma once
 
 #include <anki/scene/SceneGraph.h>
-#include <anki/scene/SectorNode.h>
 #include <anki/scene/ModelNode.h>
 #include <anki/scene/StaticGeometryNode.h>
 #include <anki/scene/ParticleEmitterNode.h>
@@ -20,6 +19,8 @@
 #include <anki/scene/OccluderNode.h>
 #include <anki/scene/DecalNode.h>
 #include <anki/scene/Octree.h>
+#include <anki/scene/PhysicsDebugNode.h>
+#include <anki/scene/TriggerNode.h>
 
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/RenderComponent.h>
@@ -35,3 +36,12 @@
 #include <anki/scene/components/SpatialComponent.h>
 #include <anki/scene/components/ReflectionProxyComponent.h>
 #include <anki/scene/components/FrustumComponent.h>
+#include <anki/scene/components/JointComponent.h>
+#include <anki/scene/components/TriggerComponent.h>
+
+#include <anki/scene/events/EventManager.h>
+#include <anki/scene/events/Event.h>
+#include <anki/scene/events/AnimationEvent.h>
+#include <anki/scene/events/JitterMoveEvent.h>
+#include <anki/scene/events/LightEvent.h>
+#include <anki/scene/events/ScriptEvent.h>

+ 15 - 80
src/anki/core/App.cpp

@@ -258,85 +258,20 @@ App::~App()
 
 void App::cleanup()
 {
-	if(m_script)
-	{
-		m_heapAlloc.deleteInstance(m_script);
-		m_script = nullptr;
-	}
-
-	if(m_scene)
-	{
-		m_heapAlloc.deleteInstance(m_scene);
-		m_scene = nullptr;
-	}
-
-	if(m_renderer)
-	{
-		m_heapAlloc.deleteInstance(m_renderer);
-		m_renderer = nullptr;
-	}
-
-	if(m_ui)
-	{
-		m_statsUi.reset(nullptr);
-
-		m_heapAlloc.deleteInstance(m_ui);
-		m_ui = nullptr;
-	}
-
-	if(m_resources)
-	{
-		m_heapAlloc.deleteInstance(m_resources);
-		m_resources = nullptr;
-	}
-
-	if(m_resourceFs)
-	{
-		m_heapAlloc.deleteInstance(m_resourceFs);
-		m_resourceFs = nullptr;
-	}
-
-	if(m_physics)
-	{
-		m_heapAlloc.deleteInstance(m_physics);
-		m_physics = nullptr;
-	}
-
-	if(m_stagingMem)
-	{
-		m_heapAlloc.deleteInstance(m_stagingMem);
-		m_stagingMem = nullptr;
-	}
-
-	if(m_threadpool)
-	{
-		m_heapAlloc.deleteInstance(m_threadpool);
-		m_threadpool = nullptr;
-	}
-
-	if(m_threadHive)
-	{
-		m_heapAlloc.deleteInstance(m_threadHive);
-		m_threadHive = nullptr;
-	}
-
-	if(m_gr)
-	{
-		GrManager::deleteInstance(m_gr);
-		m_gr = nullptr;
-	}
-
-	if(m_input)
-	{
-		m_heapAlloc.deleteInstance(m_input);
-		m_input = nullptr;
-	}
-
-	if(m_window)
-	{
-		m_heapAlloc.deleteInstance(m_window);
-		m_window = nullptr;
-	}
+	m_heapAlloc.deleteInstance(m_scene);
+	m_heapAlloc.deleteInstance(m_script);
+	m_heapAlloc.deleteInstance(m_renderer);
+	m_statsUi.reset(nullptr);
+	m_heapAlloc.deleteInstance(m_ui);
+	m_heapAlloc.deleteInstance(m_resources);
+	m_heapAlloc.deleteInstance(m_resourceFs);
+	m_heapAlloc.deleteInstance(m_physics);
+	m_heapAlloc.deleteInstance(m_stagingMem);
+	m_heapAlloc.deleteInstance(m_threadpool);
+	m_heapAlloc.deleteInstance(m_threadHive);
+	GrManager::deleteInstance(m_gr);
+	m_heapAlloc.deleteInstance(m_input);
+	m_heapAlloc.deleteInstance(m_window);
 
 #if ANKI_ENABLE_TRACE
 	if(CoreTracerSingleton::get().isInitialized())
@@ -717,7 +652,7 @@ void App::injectStatsUiElement(DynamicArrayAuto<UiQueueElement>& newUiElementArr
 			static_cast<StatsUi*>(userData)->build(canvas);
 		};
 
-		rqueue.m_uis = newUiElementArr;
+		rqueue.m_uis = WeakArray<UiQueueElement>(newUiElementArr);
 	}
 }
 

+ 1 - 1
src/anki/core/Config.cpp

@@ -70,7 +70,7 @@ Config::Config()
 	newOption("core.storagePerFrameMemorySize", 16_MB);
 	newOption("core.vertexPerFrameMemorySize", 10_MB);
 	newOption("core.textureBufferPerFrameMemorySize", 1_MB);
-	newOption("core.mainThreadCount", getCpuCoresCount() / 2 - 1);
+	newOption("core.mainThreadCount", max(2u, getCpuCoresCount() / 2u - 1u));
 	newOption("core.displayStats", false);
 	newOption("core.clearCaches", false);
 }

+ 0 - 2
src/anki/event/CMakeLists.txt

@@ -1,2 +0,0 @@
-file(GLOB SOURCES *.cpp)
-addAnkiSourceFiles(${SOURCES})

+ 0 - 153
src/anki/event/EventManager.cpp

@@ -1,153 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <anki/event/EventManager.h>
-#include <anki/scene/SceneGraph.h>
-
-namespace anki
-{
-
-EventManager::EventManager()
-{
-}
-
-EventManager::~EventManager()
-{
-	Error err = iterateEvents([&](Event& event) -> Error {
-		event.setMarkedForDeletion();
-		return Error::NONE;
-	});
-	(void)err;
-
-	deleteEventsMarkedForDeletion();
-}
-
-Error EventManager::create(SceneGraph* scene)
-{
-	ANKI_ASSERT(scene);
-	m_scene = scene;
-	return Error::NONE;
-}
-
-SceneAllocator<U8> EventManager::getSceneAllocator() const
-{
-	return m_scene->getAllocator();
-}
-
-SceneFrameAllocator<U8> EventManager::getFrameAllocator() const
-{
-	return m_scene->getFrameAllocator();
-}
-
-void EventManager::registerEvent(Event* event)
-{
-	ANKI_ASSERT(event);
-	m_events.pushBack(getSceneAllocator(), event);
-}
-
-void EventManager::unregisterEvent(List<Event*>::Iterator it)
-{
-	ANKI_ASSERT(it != m_events.getEnd());
-	m_events.erase(getSceneAllocator(), it);
-}
-
-Error EventManager::updateAllEvents(F32 prevUpdateTime, F32 crntTime)
-{
-	Error err = Error::NONE;
-	m_prevUpdateTime = prevUpdateTime;
-	m_crntTime = crntTime;
-
-	auto it = m_events.getBegin();
-	auto end = m_events.getEnd();
-	for(; it != end && !err; ++it)
-	{
-		Event* event = *it;
-
-		// If event or the node's event is marked for deletion then dont do anything else for that event
-		if(event->getMarkedForDeletion())
-		{
-			continue;
-		}
-
-		if(event->getSceneNode() != nullptr && event->getSceneNode()->getMarkedForDeletion())
-		{
-			event->setMarkedForDeletion();
-			continue;
-		}
-
-		// Audjust starting time
-		if(event->m_startTime < 0.0)
-		{
-			event->m_startTime = crntTime;
-		}
-
-		// Check if dead
-		if(!event->isDead(crntTime))
-		{
-			// If not dead update it
-
-			if(event->getStartTime() <= crntTime)
-			{
-				err = event->update(prevUpdateTime, crntTime);
-			}
-		}
-		else
-		{
-			// Dead
-
-			if(event->getReanimate())
-			{
-				event->m_startTime = prevUpdateTime;
-				err = event->update(prevUpdateTime, crntTime);
-			}
-			else
-			{
-				Bool kill;
-				err = event->onKilled(prevUpdateTime, crntTime, kill);
-				if(!err && kill)
-				{
-					event->setMarkedForDeletion();
-				}
-			}
-		}
-	}
-
-	return err;
-}
-
-void EventManager::deleteEventsMarkedForDeletion()
-{
-	SceneAllocator<U8> alloc = getSceneAllocator();
-
-	// Check if nodes are marked for deletion
-	if(true)
-	{
-		auto it = m_events.getBegin();
-		auto end = m_events.getEnd();
-		for(; it != end; ++it)
-		{
-		}
-	}
-
-	// Gather events for deletion
-	while(m_markedForDeletionCount != 0)
-	{
-		auto it = m_events.getBegin();
-		auto end = m_events.getEnd();
-		for(; it != end; ++it)
-		{
-			Event* event = *it;
-			if(event->getMarkedForDeletion())
-			{
-				unregisterEvent(it);
-				alloc.deleteInstance<Event>(event);
-				--m_markedForDeletionCount;
-				break;
-			}
-		}
-	}
-}
-
-} // end namespace anki

+ 0 - 71
src/anki/event/MainRendererPpsHdrEvent.cpp

@@ -1,71 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <anki/event/MainRendererPpsHdrEvent.h>
-#include <anki/renderer/MainRenderer.h>
-
-namespace anki
-{
-
-#if 0
-
-
-MainRendererPpsHdrEvent::MainRendererPpsHdrEvent(float startTime,
-	float duration,
-	float exposure_,
-	uint blurringIterationsNum_,
-	float blurringDist_)
-	: Event(ET_MAIN_RENDERER_PPS_HDR, startTime, duration)
-{
-	finalData.exposure = exposure_;
-	finalData.blurringIterationsNum = blurringIterationsNum_;
-	finalData.blurringDist = blurringDist_;
-
-	const Hdr& hdr =
-		MainRendererSingleton::get().getPps().getHdr();
-	originalData.exposure = hdr.getExposure();
-	originalData.blurringIterationsNum = hdr.getBlurringIterationsCount();
-	//originalData.blurringDist = hdr.getBlurringDistance();
-}
-
-
-MainRendererPpsHdrEvent::MainRendererPpsHdrEvent(
-	const MainRendererPpsHdrEvent& b)
-	: Event(ET_MAIN_RENDERER_PPS_HDR, 0.0, 0.0)
-{
-	*this = b;
-}
-
-
-MainRendererPpsHdrEvent& MainRendererPpsHdrEvent::operator=(
-	const MainRendererPpsHdrEvent& b)
-{
-	Event::operator=(b);
-	finalData = b.finalData;
-	originalData = b.originalData;
-	return *this;
-}
-
-
-void MainRendererPpsHdrEvent::updateSp(float /*prevUpdateTime*/, float crntTime)
-{
-	float d = crntTime - getStartTime(); // delta
-	float dp = d / getDuration(); // delta as percentage
-
-	Hdr& hdr = MainRendererSingleton::get().getPps().getHdr();
-
-	hdr.setExposure(interpolate(originalData.exposure, finalData.exposure, dp));
-
-	hdr.setBlurringIterationsCount(
-		interpolate(originalData.blurringIterationsNum,
-		finalData.blurringIterationsNum, dp));
-
-	/*hdr.setBlurringDistance(interpolate(originalData.blurringDist,
-		finalData.blurringDist, dp));*/
-}
-
-#endif
-
-} // end namespace

+ 0 - 47
src/anki/event/MainRendererPpsHdrEvent.h

@@ -1,47 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <anki/event/Event.h>
-#include <cstdint>
-
-namespace anki
-{
-
-#if 0
-
-/// Change the HDR properties
-class MainRendererPpsHdrEvent: public Event
-{
-public:
-	/// Constructor
-	MainRendererPpsHdrEvent(float startTime, float duration,
-		float exposure, uint32_t blurringIterationsNum, float blurringDist);
-
-	/// Copy constructor
-	MainRendererPpsHdrEvent(const MainRendererPpsHdrEvent& b);
-
-	/// Copy
-	MainRendererPpsHdrEvent& operator=(const MainRendererPpsHdrEvent& b);
-
-private:
-	struct Data
-	{
-		float exposure; ///< @see Hdr::exposure
-		uint32_t blurringIterationsNum; ///< @see Hdr::blurringIterationsNum
-		float blurringDist; ///< @see Hdr::blurringDist
-	};
-
-	Data originalData; ///< From where do we start
-	Data finalData; ///< Where do we want to go
-
-	/// Implements Event::updateSp
-	void updateSp(float prevUpdateTime, float crntTime);
-};
-
-#endif
-
-} // end namespace

+ 4 - 1
src/anki/gr/gl/GrManager.cpp

@@ -56,7 +56,10 @@ Error GrManager::newInstance(GrManagerInitInfo& init, GrManager*& gr)
 
 void GrManager::deleteInstance(GrManager* gr)
 {
-	ANKI_ASSERT(gr);
+	if(gr == nullptr)
+	{
+		return;
+	}
 
 	auto alloc = gr->m_alloc;
 	gr->~GrManager();

+ 5 - 1
src/anki/gr/vulkan/GrManager.cpp

@@ -56,7 +56,11 @@ Error GrManager::newInstance(GrManagerInitInfo& init, GrManager*& gr)
 
 void GrManager::deleteInstance(GrManager* gr)
 {
-	ANKI_ASSERT(gr);
+	if(gr == nullptr)
+	{
+		return;
+	}
+
 	auto alloc = gr->m_alloc;
 	gr->~GrManager();
 	alloc.deallocate(gr, 1);

+ 1 - 1
src/anki/gr/vulkan/GrManagerImpl.cpp

@@ -333,7 +333,7 @@ Error GrManagerImpl::initInstance(const GrManagerInitInfo& init)
 		}
 	}
 
-#if ANKI_GR_MANAGER_DEBUG_MEMMORY
+#if 0 && ANKI_GR_MANAGER_DEBUG_MEMMORY
 	m_debugAllocCbs = {};
 	m_debugAllocCbs.pUserData = this;
 	m_debugAllocCbs.pfnAllocation = allocateCallback;

+ 24 - 0
src/anki/math/Transform.h

@@ -47,6 +47,30 @@ public:
 	{
 		checkW();
 	}
+
+	explicit TTransform(const TVec4<T>& origin)
+		: m_origin(origin)
+		, m_rotation(Mat3x4::getIdentity())
+		, m_scale(1.0f)
+	{
+		checkW();
+	}
+
+	explicit TTransform(const TMat3x4<T>& rotation)
+		: m_origin(Vec4(0.0f))
+		, m_rotation(rotation)
+		, m_scale(1.0f)
+	{
+		checkW();
+	}
+
+	TTransform(const T scale)
+		: m_origin(Vec4(0.0f))
+		, m_rotation(Mat3x4::getIdentity())
+		, m_scale(scale)
+	{
+		checkW();
+	}
 	/// @}
 
 	/// @name Accessors

+ 7 - 1
src/anki/physics/Common.cpp

@@ -12,7 +12,13 @@ namespace anki
 
 void PhysicsPtrDeleter::operator()(PhysicsObject* ptr)
 {
-	ptr->getWorld().deleteObjectDeferred(ptr);
+	if(ptr == nullptr)
+	{
+		return;
+	}
+
+	PhysicsWorld& world = ptr->getWorld();
+	world.destroyObject(ptr);
 }
 
 } // end namespace anki

+ 44 - 19
src/anki/physics/Common.h

@@ -10,11 +10,15 @@
 #include <anki/util/Ptr.h>
 #include <anki/Math.h>
 
-// Have all the newton headers here because they polute the global namespace
-#include <Newton.h>
-#include <dLinearAlgebra.h>
-#include <dCustomPlayerControllerManager.h>
-#include <anki/util/CleanupWindows.h>
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wall"
+#define BT_THREADSAFE 0
+#define BT_NO_PROFILE 1
+#include <btBulletCollisionCommon.h>
+#include <btBulletDynamicsCommon.h>
+#include <BulletCollision/CollisionDispatch/btGhostObject.h>
+#include <BulletDynamics/Character/btKinematicCharacterController.h>
+#pragma GCC diagnostic pop
 
 namespace anki
 {
@@ -26,10 +30,13 @@ namespace anki
 
 // Forward
 class PhysicsObject;
+class PhysicsFilteredObject;
 class PhysicsWorld;
 class PhysicsCollisionShape;
 class PhysicsBody;
 class PhysicsPlayerController;
+class PhysicsJoint;
+class PhysicsTrigger;
 
 /// @addtogroup physics
 /// @{
@@ -45,42 +52,60 @@ public:
 template<typename T>
 using PhysicsPtr = IntrusivePtr<T, PhysicsPtrDeleter>;
 
+using PhysicsObjectPtr = PhysicsPtr<PhysicsObject>;
+using PhysicsFilteredObjectPtr = PhysicsPtr<PhysicsFilteredObject>;
 using PhysicsCollisionShapePtr = PhysicsPtr<PhysicsCollisionShape>;
 using PhysicsBodyPtr = PhysicsPtr<PhysicsBody>;
 using PhysicsPlayerControllerPtr = PhysicsPtr<PhysicsPlayerController>;
+using PhysicsJointPtr = PhysicsPtr<PhysicsJoint>;
+using PhysicsTriggerPtr = PhysicsPtr<PhysicsTrigger>;
 
 /// Material types.
-enum class PhysicsMaterialBit : U16
+enum class PhysicsMaterialBit : U64
 {
 	NONE = 0,
 	STATIC_GEOMETRY = 1 << 0,
 	DYNAMIC_GEOMETRY = 1 << 1,
-	RAGDOLL = 1 << 2,
-	PARTICLES = 1 << 3,
-	ALL = MAX_U16
+	TRIGGER = 1 << 2,
+	PLAYER = 1 << 3,
+
+	ALL = MAX_U64
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(PhysicsMaterialBit, inline)
 
-ANKI_USE_RESULT inline Vec4 toAnki(const dVector& v)
+ANKI_USE_RESULT inline Vec3 toAnki(const btVector3& v)
+{
+	return Vec3(v.getX(), v.getY(), v.getZ());
+}
+
+ANKI_USE_RESULT inline btVector3 toBt(const Vec3& v)
 {
-	return Vec4(v.m_x, v.m_y, v.m_z, v.m_w);
+	return btVector3(v.x(), v.y(), v.z());
 }
 
-ANKI_USE_RESULT inline dVector toNewton(const Vec4& v)
+ANKI_USE_RESULT inline btTransform toBt(const Transform& a)
 {
-	return dVector(v.x(), v.y(), v.z(), v.w());
+	Mat4 mat(a);
+	mat.transpose();
+	btTransform out;
+	out.setFromOpenGLMatrix(&mat(0, 0));
+	return out;
 }
 
-ANKI_USE_RESULT inline Mat4 toAnki(const dMatrix& m)
+ANKI_USE_RESULT inline Mat3x4 toAnki(const btMatrix3x3& m)
 {
-	Mat4 ak(*reinterpret_cast<const Mat4*>(&m));
-	return ak.getTransposed();
+	Mat3x4 m3;
+	m3.setRows(Vec4(toAnki(m[0]), 0.0f), Vec4(toAnki(m[1]), 0.0f), Vec4(toAnki(m[2]), 0.0f));
+	return m3;
 }
 
-ANKI_USE_RESULT inline dMatrix toNewton(const Mat4& m)
+ANKI_USE_RESULT inline Transform toAnki(const btTransform& t)
 {
-	Mat4 transp = m.getTransposed();
-	return dMatrix(&transp(0, 0));
+	Transform out;
+	out.setRotation(toAnki(t.getBasis()));
+	out.setOrigin(Vec4(toAnki(t.getOrigin()), 0.0f));
+	out.setScale(1.0f);
+	return out;
 }
 /// @}
 

+ 36 - 98
src/anki/physics/PhysicsBody.cpp

@@ -10,119 +10,57 @@
 namespace anki
 {
 
-PhysicsBody::PhysicsBody(PhysicsWorld* world)
-	: PhysicsObject(PhysicsObjectType::BODY, world)
+PhysicsBody::PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init)
+	: PhysicsFilteredObject(CLASS_TYPE, world)
 {
-}
+	const Bool dynamic = init.m_mass > 0.0f;
+	m_shape = init.m_shape;
+	m_mass = init.m_mass;
 
-PhysicsBody::~PhysicsBody()
-{
-	if(m_sceneCollisionProxy)
-	{
-		NewtonCollision* scene = m_world->getNewtonScene();
-		NewtonSceneCollisionBeginAddRemove(scene);
-		NewtonSceneCollisionRemoveSubCollision(scene, m_sceneCollisionProxy);
-		NewtonSceneCollisionEndAddRemove(scene);
-	}
+	// Create motion state
+	m_motionState.m_body = this;
 
-	if(m_body)
+	// Compute inertia
+	btCollisionShape* shape = m_shape->getBtShape(dynamic);
+	btVector3 localInertia(0, 0, 0);
+	if(dynamic)
 	{
-		NewtonDestroyBody(m_body);
+		shape->calculateLocalInertia(init.m_mass, localInertia);
 	}
-}
-
-Error PhysicsBody::create(const PhysicsBodyInitInfo& init)
-{
-	// Create
-	dMatrix trf = toNewton(Mat4(init.m_startTrf));
 
-	if(init.m_static)
-	{
-		// Create static collision
-		NewtonCollision* scene = m_world->getNewtonScene();
+	// Create body
+	btRigidBody& body = *getBtBody();
+	btRigidBody::btRigidBodyConstructionInfo cInfo(init.m_mass, &m_motionState, shape, localInertia);
+	cInfo.m_friction = init.m_friction;
+	::new(&body) btRigidBody(cInfo);
 
-		NewtonSceneCollisionBeginAddRemove(scene);
-		m_sceneCollisionProxy = NewtonSceneCollisionAddSubCollision(scene, init.m_shape->getNewtonShape());
-		NewtonSceneCollisionEndAddRemove(scene);
+	// User pointer
+	body.setUserPointer(static_cast<PhysicsObject*>(this));
 
-		NewtonSceneCollisionSetSubCollisionMatrix(scene, m_sceneCollisionProxy, &trf[0][0]);
+	// Other
+	setMaterialGroup((dynamic) ? PhysicsMaterialBit::DYNAMIC_GEOMETRY : PhysicsMaterialBit::STATIC_GEOMETRY);
+	setMaterialMask(PhysicsMaterialBit::ALL);
+	setTransform(init.m_transform);
 
-		return Error::NONE;
-	}
-	else if(init.m_kinematic)
-	{
-		ANKI_ASSERT(0 && "TODO");
-	}
-	else
-	{
-		m_body = NewtonCreateDynamicBody(m_world->getNewtonWorld(), init.m_shape->getNewtonShape(), &trf[0][0]);
-	}
-
-	if(!m_body)
-	{
-		ANKI_PHYS_LOGE("NewtonCreateXXBody() failed");
-		return Error::FUNCTION_FAILED;
-	}
-
-	// Material
-	NewtonBodySetMaterialGroupID(m_body, NewtonMaterialGetDefaultGroupID(m_world->getNewtonWorld()));
-
-	// User data & callbacks
-	NewtonBodySetUserData(m_body, this);
-	NewtonBodySetTransformCallback(m_body, onTransformCallback);
-
-	// Set mass
-	NewtonCollision* shape = NewtonBodyGetCollision(m_body);
-	NewtonBodySetMassProperties(m_body, init.m_mass, shape);
-
-	// Set gravity
-	if(init.m_gravity)
-	{
-		NewtonBodySetForceAndTorqueCallback(m_body, applyGravityForce);
-	}
-
-	// Activate
-	NewtonBodySetSimulationState(m_body, true);
-
-	return Error::NONE;
+	// Add to world
+	auto lock = getWorld().lockBtWorld();
+	getWorld().getBtWorld()->addRigidBody(&body);
 }
 
-void PhysicsBody::setTransform(const Transform& trf)
-{
-	Mat4 mat(trf);
-	mat.transpose();
-	NewtonBodySetMatrix(m_body, &mat(0, 0));
-}
-
-void PhysicsBody::onTransformCallback(const NewtonBody* const body, const dFloat* const matrix, int threadIndex)
+PhysicsBody::~PhysicsBody()
 {
-	ANKI_ASSERT(body);
-	ANKI_ASSERT(matrix);
-
-	void* ud = NewtonBodyGetUserData(body);
-	ANKI_ASSERT(ud);
-	PhysicsBody* self = reinterpret_cast<PhysicsBody*>(ud);
-
-	Mat4 trf;
-	memcpy(&trf, matrix, sizeof(Mat4));
-	trf.transpose();
-	trf(3, 3) = 0.0;
-	self->m_trf = Transform(trf);
-	self->m_updated = true;
+	auto lock = getWorld().lockBtWorld();
+	getWorld().getBtWorld()->removeRigidBody(getBtBody());
 }
 
-void PhysicsBody::applyGravityForce(const NewtonBody* body, dFloat timestep, int threadIndex)
+void PhysicsBody::setMass(F32 mass)
 {
-	dFloat Ixx;
-	dFloat Iyy;
-	dFloat Izz;
-	dFloat mass;
-
-	NewtonBodyGetMass(body, &mass, &Ixx, &Iyy, &Izz);
-
-	const F32 GRAVITY = -9.8f;
-	Vec4 force(0.0f, mass * GRAVITY, 0.0f, 0.0f);
-	NewtonBodySetForce(body, &force[0]);
+	ANKI_ASSERT(m_mass > 0.0f && "Only relevant for dynamic bodies");
+	ANKI_ASSERT(mass > 0.0f);
+	btVector3 inertia;
+	m_shape->getBtShape(true)->calculateLocalInertia(mass, inertia);
+	getBtBody()->setMassProps(mass, inertia);
+	m_mass = mass;
 }
 
 } // end namespace anki

+ 48 - 36
src/anki/physics/PhysicsBody.h

@@ -18,71 +18,83 @@ class PhysicsBodyInitInfo
 {
 public:
 	PhysicsCollisionShapePtr m_shape;
-	F32 m_mass = 0.0;
-	Transform m_startTrf = Transform::getIdentity();
-	Bool m_kinematic = false;
-	Bool m_gravity = true;
-	Bool m_static = false;
+	F32 m_mass = 0.0f;
+	Transform m_transform = Transform::getIdentity();
+	F32 m_friction = 0.5f;
 };
 
 /// Rigid body.
-class PhysicsBody : public PhysicsObject
+class PhysicsBody : public PhysicsFilteredObject
 {
-public:
-	PhysicsBody(PhysicsWorld* world);
-
-	~PhysicsBody();
+	ANKI_PHYSICS_OBJECT
 
-	ANKI_USE_RESULT Error create(const PhysicsBodyInitInfo& init);
+public:
+	static const PhysicsObjectType CLASS_TYPE = PhysicsObjectType::BODY;
 
-	const Transform& getTransform(Bool& updated)
+	const Transform& getTransform() const
 	{
-		updated = m_updated;
-		m_updated = false;
 		return m_trf;
 	}
 
-	void setTransform(const Transform& trf);
-
-	F32 getFriction() const
+	void setTransform(const Transform& trf)
 	{
-		return m_friction;
+		m_trf = trf;
+		getBtBody()->setWorldTransform(toBt(trf));
 	}
 
-	void setFriction(F32 friction)
+	void applyForce(const Vec3& force, const Vec3& relPos)
 	{
-		m_friction = friction;
+		getBtBody()->applyForce(toBt(force), toBt(relPos));
 	}
 
-	F32 getElasticity() const
+	void setMass(F32 mass);
+
+	F32 getMass() const
 	{
-		return m_elasticity;
+		return m_mass;
 	}
 
-	void setElasticity(F32 elasticity)
+anki_internal:
+	const btRigidBody* getBtBody() const
 	{
-		m_elasticity = elasticity;
+		return reinterpret_cast<const btRigidBody*>(&m_bodyMem[0]);
 	}
 
-	void setMaterialsThatCollide(PhysicsMaterialBit bits)
+	btRigidBody* getBtBody()
 	{
-		m_materialBits = bits;
+		return reinterpret_cast<btRigidBody*>(&m_bodyMem[0]);
 	}
 
 private:
-	NewtonBody* m_body = nullptr;
-	void* m_sceneCollisionProxy = nullptr;
+	class MotionState : public btMotionState
+	{
+	public:
+		PhysicsBody* m_body = nullptr;
+
+		void getWorldTransform(btTransform& worldTrans) const override
+		{
+			worldTrans = toBt(m_body->m_trf);
+		}
+
+		void setWorldTransform(const btTransform& worldTrans) override
+		{
+			m_body->m_trf = toAnki(worldTrans);
+		}
+	};
+
+	/// Store the data of the btRigidBody in place to avoid additional allocations.
+	alignas(alignof(btRigidBody)) Array<U8, sizeof(btRigidBody)> m_bodyMem;
+
 	Transform m_trf = Transform::getIdentity();
-	F32 m_friction = 0.03f;
-	F32 m_elasticity = 0.1f;
-	PhysicsMaterialBit m_materialBits = PhysicsMaterialBit::ALL;
-	Bool8 m_updated = true;
+	MotionState m_motionState;
 
-	/// Newton callback.
-	static void onTransformCallback(const NewtonBody* const body, const dFloat* const matrix, int threadIndex);
+	PhysicsCollisionShapePtr m_shape;
 
-	/// Newton callback
-	static void applyGravityForce(const NewtonBody* body, dFloat timestep, int threadIndex);
+	F32 m_mass = 1.0f;
+
+	PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init);
+
+	~PhysicsBody();
 };
 /// @}
 

+ 42 - 56
src/anki/physics/PhysicsCollisionShape.cpp

@@ -5,84 +5,70 @@
 
 #include <anki/physics/PhysicsCollisionShape.h>
 #include <anki/physics/PhysicsWorld.h>
+#include <BulletCollision/Gimpact/btGImpactShape.h>
 
 namespace anki
 {
 
-I32 PhysicsCollisionShape::m_gid = 1;
-
 PhysicsCollisionShape::~PhysicsCollisionShape()
 {
-	if(m_shape)
-	{
-		NewtonDestroyCollision(m_shape);
-	}
+	getAllocator().deleteInstance(m_shape);
 }
 
-Error PhysicsSphere::create(PhysicsCollisionShapeInitInfo& init, F32 radius)
+PhysicsSphere::PhysicsSphere(PhysicsWorld* world, F32 radius)
+	: PhysicsCollisionShape(world)
 {
-	Error err = Error::NONE;
-
-	m_shape = NewtonCreateSphere(m_world->getNewtonWorld(), radius, m_gid++, nullptr);
-	if(!m_shape)
-	{
-		ANKI_PHYS_LOGE("NewtonCreateSphere() failed");
-		err = Error::FUNCTION_FAILED;
-	}
-
-	return err;
+	m_shape = getAllocator().newInstance<btSphereShape>(radius);
+	m_shape->setMargin(getWorld().getCollisionMargin());
+	m_shape->setUserPointer(static_cast<PhysicsObject*>(this));
 }
 
-Error PhysicsBox::create(PhysicsCollisionShapeInitInfo& init, const Vec3& extend)
+PhysicsBox::PhysicsBox(PhysicsWorld* world, const Vec3& extend)
+	: PhysicsCollisionShape(world)
 {
-	Error err = Error::NONE;
-
-	m_shape = NewtonCreateBox(m_world->getNewtonWorld(), extend.x(), extend.y(), extend.z(), m_gid++, nullptr);
-	if(!m_shape)
-	{
-		ANKI_PHYS_LOGE("NewtonCreateBox() failed");
-		err = Error::FUNCTION_FAILED;
-	}
-
-	return err;
+	m_shape = getAllocator().newInstance<btBoxShape>(toBt(extend));
+	m_shape->setMargin(getWorld().getCollisionMargin());
+	m_shape->setUserPointer(static_cast<PhysicsObject*>(this));
 }
 
-Error PhysicsTriangleSoup::create(PhysicsCollisionShapeInitInfo& init,
-	const Vec3* positions,
-	U32 positionsStride,
-	const U32* indices,
-	U32 indicesCount)
+PhysicsTriangleSoup::PhysicsTriangleSoup(
+	PhysicsWorld* world, ConstWeakArray<Vec3> positions, ConstWeakArray<U32> indices)
+	: PhysicsCollisionShape(world)
 {
-	m_shape = NewtonCreateTreeCollision(m_world->getNewtonWorld(), 0);
-	if(!m_shape)
-	{
-		ANKI_PHYS_LOGE("NewtonCreateBox() failed");
-		return Error::FUNCTION_FAILED;
-	}
+	ANKI_ASSERT((indices.getSize() % 3) == 0);
 
-	NewtonTreeCollisionBeginBuild(m_shape);
-
-	// Iterate index array
-	const U32* indicesEnd = indices + indicesCount;
-	for(; indices != indicesEnd; indices += 3)
+	m_mesh = getAllocator().newInstance<btTriangleMesh>();
+	for(U i = 0; i < indices.getSize(); i += 3)
 	{
-		Array<Vec3, 3> facePos;
+		m_mesh->addTriangle(
+			toBt(positions[indices[i]]), toBt(positions[indices[i + 1]]), toBt(positions[indices[i + 2]]));
+	}
 
-		for(U i = 0; i < 3; ++i)
-		{
-			U idx = indices[i];
-			const U8* ptr = reinterpret_cast<const U8*>(positions) + positionsStride * idx;
+	// Create the dynamic shape
+	btGImpactMeshShape* shape = getAllocator().newInstance<btGImpactMeshShape>(m_mesh);
+	shape->setMargin(getWorld().getCollisionMargin());
+	shape->updateBound();
+	m_shape = shape;
 
-			facePos[i] = *reinterpret_cast<const Vec3*>(ptr);
-		}
+	// And the static one
+	btBvhTriangleMeshShape* triShape = getAllocator().newInstance<btBvhTriangleMeshShape>(m_mesh, true);
+	m_staticShape = triShape;
+	m_staticShape->setMargin(getWorld().getCollisionMargin());
 
-		NewtonTreeCollisionAddFace(m_shape, 3, &facePos[0][0], sizeof(Vec3), 0);
-	}
+	m_shape->setUserPointer(static_cast<PhysicsObject*>(this));
+}
 
-	const I optimize = 1;
-	NewtonTreeCollisionEndBuild(m_shape, optimize);
+PhysicsTriangleSoup::~PhysicsTriangleSoup()
+{
+	getAllocator().deleteInstance(m_staticShape);
+	getAllocator().deleteInstance(m_shape);
+	m_shape = nullptr;
+	getAllocator().deleteInstance(m_mesh);
+}
 
-	return Error::NONE;
+btCollisionShape* PhysicsTriangleSoup::getBtShape(Bool forDynamicBodies) const
+{
+	return (forDynamicBodies) ? m_shape : m_staticShape;
 }
 
 } // end namespace anki

+ 29 - 69
src/anki/physics/PhysicsCollisionShape.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/physics/PhysicsObject.h>
+#include <anki/util/WeakArray.h>
 
 namespace anki
 {
@@ -13,112 +14,71 @@ namespace anki
 /// @addtogroup physics
 /// @{
 
-/// Standard initializer for all collision shapes.
-struct PhysicsCollisionShapeInitInfo
-{
-	// Empty for now
-};
-
-/// Type of supported physics collision shapes.
-enum class PhysicsCollisionShapeType : U8
-{
-	NONE,
-	SPHERE,
-	BOX,
-	STATIC_TRIANGLE_SOUP
-};
-
 /// The base of all collision shapes.
 class PhysicsCollisionShape : public PhysicsObject
 {
 public:
-	PhysicsCollisionShape(PhysicsWorld* world)
-		: PhysicsObject(PhysicsObjectType::COLLISION_SHAPE, world)
-	{
-	}
-
-	~PhysicsCollisionShape();
+	static const PhysicsObjectType CLASS_TYPE = PhysicsObjectType::COLLISION_SHAPE;
 
 anki_internal:
-	NewtonCollision* getNewtonShape() const
+	virtual btCollisionShape* getBtShape(Bool forDynamicBodies = false) const
 	{
 		ANKI_ASSERT(m_shape);
 		return m_shape;
 	}
 
 protected:
-	NewtonCollision* m_shape = nullptr;
-	void* m_sceneCollisionProxy = nullptr;
-	static I32 m_gid;
+	btCollisionShape* m_shape = nullptr;
+
+	PhysicsCollisionShape(PhysicsWorld* world)
+		: PhysicsObject(CLASS_TYPE, world)
+	{
+	}
+
+	~PhysicsCollisionShape();
 };
 
 /// Sphere collision shape.
 class PhysicsSphere final : public PhysicsCollisionShape
 {
-public:
-	PhysicsSphere(PhysicsWorld* world)
-		: PhysicsCollisionShape(world)
-	{
-	}
-
-	~PhysicsSphere()
-	{
-	}
+	ANKI_PHYSICS_OBJECT
 
-	ANKI_USE_RESULT Error create(PhysicsCollisionShapeInitInfo& init, F32 radius);
+private:
+	PhysicsSphere(PhysicsWorld* world, F32 radius);
 };
 
 /// Box collision shape.
 class PhysicsBox final : public PhysicsCollisionShape
 {
-public:
-	PhysicsBox(PhysicsWorld* world)
-		: PhysicsCollisionShape(world)
-	{
-	}
+	ANKI_PHYSICS_OBJECT
 
-	~PhysicsBox()
-	{
-	}
-
-	ANKI_USE_RESULT Error create(PhysicsCollisionShapeInitInfo& init, const Vec3& extend);
+private:
+	PhysicsBox(PhysicsWorld* world, const Vec3& extend);
 };
 
 /// Convex hull collision shape.
 class PhysicsConvexHull final : public PhysicsCollisionShape
 {
-public:
-	PhysicsConvexHull(PhysicsWorld* world)
-		: PhysicsCollisionShape(world)
-	{
-	}
+	ANKI_PHYSICS_OBJECT
 
-	~PhysicsConvexHull()
-	{
-	}
-
-	ANKI_USE_RESULT Error create(
-		PhysicsCollisionShapeInitInfo& init, const Vec3* positions, U32 positionsCount, U32 positionsStride);
+private:
+	PhysicsConvexHull(PhysicsWorld* world, const Vec3* positions, U32 positionsCount, U32 positionsStride);
 };
 
 /// Static triangle mesh shape.
 class PhysicsTriangleSoup final : public PhysicsCollisionShape
 {
-public:
-	PhysicsTriangleSoup(PhysicsWorld* world)
-		: PhysicsCollisionShape(world)
-	{
-	}
+	ANKI_PHYSICS_OBJECT
 
-	~PhysicsTriangleSoup()
-	{
-	}
+private:
+	btTriangleMesh* m_mesh = nullptr;
+	btCollisionShape* m_staticShape = nullptr;
+
+	PhysicsTriangleSoup(PhysicsWorld* world, ConstWeakArray<Vec3> positions, ConstWeakArray<U32> indices);
+
+	~PhysicsTriangleSoup();
 
-	ANKI_USE_RESULT Error create(PhysicsCollisionShapeInitInfo& init,
-		const Vec3* positions,
-		U32 positionsStride,
-		const U32* indices,
-		U32 indicesCount);
+	btCollisionShape* getBtShape(Bool forDynamicBodies = false) const override;
 };
 /// @}
 

+ 4 - 101
src/anki/physics/PhysicsDrawer.cpp

@@ -9,111 +9,14 @@
 namespace anki
 {
 
-struct CallbackData
-{
-	const NewtonBody* m_body;
-	PhysicsDrawer* m_drawer;
-};
-
 void PhysicsDrawer::drawWorld(const PhysicsWorld& world)
 {
-	NewtonWorld* nworld = world.getNewtonWorld();
-	for(NewtonBody* body = NewtonWorldGetFirstBody(nworld); body != nullptr;
-		body = NewtonWorldGetNextBody(nworld, body))
-	{
-		if(m_drawAabbs)
-		{
-			drawAabb(body);
-		}
-
-		if(m_drawCollision)
-		{
-			drawCollision(body);
-		}
-	}
-}
-
-void PhysicsDrawer::drawAabb(const NewtonBody* body)
-{
-	Vec4 p0;
-	Vec4 p1;
-	Mat4 matrix;
-
-	NewtonCollision* collision = NewtonBodyGetCollision(body);
-	NewtonBodyGetMatrix(body, &matrix[0]);
-	NewtonCollisionCalculateAABB(collision, &matrix[0], &p0[0], &p1[0]);
-
-	Vec3 lines[] = {Vec3(p0.x(), p0.y(), p0.z()),
-		Vec3(p1.x(), p0.y(), p0.z()),
-		Vec3(p0.x(), p1.y(), p0.z()),
-		Vec3(p1.x(), p1.y(), p0.z()),
-		Vec3(p0.x(), p1.y(), p1.z()),
-		Vec3(p1.x(), p1.y(), p1.z()),
-		Vec3(p0.x(), p0.y(), p1.z()),
-		Vec3(p1.x(), p0.y(), p1.z()),
-		Vec3(p0.x(), p0.y(), p0.z()),
-		Vec3(p0.x(), p1.y(), p0.z()),
-		Vec3(p1.x(), p0.y(), p0.z()),
-		Vec3(p1.x(), p1.y(), p0.z()),
-		Vec3(p0.x(), p0.y(), p1.z()),
-		Vec3(p0.x(), p1.y(), p1.z()),
-		Vec3(p1.x(), p0.y(), p1.z()),
-		Vec3(p1.x(), p1.y(), p1.z()),
-		Vec3(p0.x(), p0.y(), p0.z()),
-		Vec3(p0.x(), p0.y(), p1.z()),
-		Vec3(p1.x(), p0.y(), p0.z()),
-		Vec3(p1.x(), p0.y(), p1.z()),
-		Vec3(p0.x(), p1.y(), p0.z()),
-		Vec3(p0.x(), p1.y(), p1.z()),
-		Vec3(p1.x(), p1.y(), p0.z()),
-		Vec3(p1.x(), p1.y(), p1.z())};
-
-	const U32 linesCount = sizeof(lines) / sizeof(Vec3) / 2;
-	drawLines(lines, linesCount, Vec4(0.0, 0.0, 1.0, 0.5));
-}
-
-void PhysicsDrawer::drawCollision(const NewtonBody* body)
-{
-	Mat4 matrix;
-	NewtonBodyGetMatrix(body, &matrix[0]);
-
-	CallbackData data;
-	data.m_body = body;
-	data.m_drawer = this;
-
-	NewtonCollisionForEachPolygonDo(
-		NewtonBodyGetCollision(body), &matrix[0], drawGeometryCallback, static_cast<void*>(&data));
-}
-
-void PhysicsDrawer::drawGeometryCallback(void* userData, int vertexCount, const dFloat* const faceVertec, int id)
-{
-	CallbackData* data = static_cast<CallbackData*>(userData);
-	const NewtonBody* body = data->m_body;
-
-	Vec4 color(1.0);
-	if(NewtonBodyGetType(body) == NEWTON_DYNAMIC_BODY)
-	{
-		I sleepState = NewtonBodyGetSleepState(body);
-		if(sleepState == 1)
-		{
-			// Sleeping
-			color = Vec4(0.3, 0.3, 0.3, 1.0);
-		}
-	}
-
-	U i = vertexCount - 1;
-
-	Array<Vec3, 2> points;
-	points[0] = Vec3(faceVertec[i * 3 + 0], faceVertec[i * 3 + 1], faceVertec[i * 3 + 2]);
-
-	for(I i = 0; i < vertexCount; i++)
-	{
-		points[1] = Vec3(faceVertec[i * 3 + 0], faceVertec[i * 3 + 1], faceVertec[i * 3 + 2]);
+	auto lock = world.lockBtWorld();
 
-		data->m_drawer->drawLines(&points[0], 1, color);
+	btDynamicsWorld& btWorld = *world.getBtWorld();
 
-		points[0] = points[1];
-	}
+	btWorld.setDebugDrawer(&m_debugDraw);
+	btWorld.debugDrawWorld();
 }
 
 } // end namespace anki

+ 47 - 25
src/anki/physics/PhysicsDrawer.h

@@ -7,9 +7,6 @@
 
 #include <anki/physics/Common.h>
 
-// Forward
-class NewtonBody;
-
 namespace anki
 {
 
@@ -20,39 +17,64 @@ namespace anki
 class PhysicsDrawer
 {
 public:
+	PhysicsDrawer()
+		: m_debugDraw(this)
+	{
+	}
+
 	/// Draw a line.
-	virtual void drawLines(const Vec3* lines, const U32 linesCount, const Vec4& color) = 0;
+	virtual void drawLines(const Vec3* lines, const U32 vertCount, const Vec4& color) = 0;
 
 	void drawWorld(const PhysicsWorld& world);
 
-	void setDrawAabbs(Bool draw)
+private:
+	class DebugDraw : public btIDebugDraw
 	{
-		m_drawAabbs = draw;
-	}
+	public:
+		PhysicsDrawer* m_drawer = nullptr;
 
-	Bool getDrawAabbs() const
-	{
-		return m_drawAabbs;
-	}
+		DebugDraw(PhysicsDrawer* drawer)
+			: m_drawer(drawer)
+		{
+		}
 
-	void setDrawCollision(Bool draw)
-	{
-		m_drawCollision = draw;
-	}
+		void drawLine(const btVector3& from, const btVector3& to, const btVector3& color) override
+		{
+			Array<Vec3, 2> lines = {{toAnki(from), toAnki(to)}};
+			m_drawer->drawLines(&lines[0], 2, Vec4(toAnki(color), 1.0f));
+		}
 
-	Bool getDrawCollision() const
-	{
-		return m_drawCollision;
-	}
+		void drawContactPoint(const btVector3& PointOnB,
+			const btVector3& normalOnB,
+			btScalar distance,
+			int lifeTime,
+			const btVector3& color) override
+		{
+			// TODO
+		}
 
-private:
-	Bool8 m_drawAabbs = true;
-	Bool8 m_drawCollision = true;
+		void reportErrorWarning(const char* warningString) override
+		{
+			ANKI_PHYS_LOGW(warningString);
+		}
+
+		void draw3dText(const btVector3& location, const char* textString) override
+		{
+			// TODO
+		}
+
+		void setDebugMode(int debugMode) override
+		{
+			// TODO
+		}
 
-	void drawAabb(const NewtonBody* body);
-	void drawCollision(const NewtonBody* body);
+		int getDebugMode() const override
+		{
+			return btIDebugDraw::DBG_DrawWireframe | btIDebugDraw::DBG_DrawAabb;
+		}
+	};
 
-	static void drawGeometryCallback(void* userData, int vertexCount, const dFloat* const faceVertec, int id);
+	DebugDraw m_debugDraw;
 };
 /// @}
 

+ 72 - 0
src/anki/physics/PhysicsJoint.cpp

@@ -0,0 +1,72 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/physics/PhysicsJoint.h>
+#include <anki/physics/PhysicsBody.h>
+#include <anki/physics/PhysicsWorld.h>
+
+namespace anki
+{
+
+PhysicsJoint::PhysicsJoint(PhysicsWorld* world)
+	: PhysicsObject(CLASS_TYPE, world)
+{
+}
+
+PhysicsJoint::~PhysicsJoint()
+{
+	if(m_joint)
+	{
+		auto lock = getWorld().lockBtWorld();
+		getWorld().getBtWorld()->removeConstraint(m_joint);
+	}
+
+	getAllocator().deleteInstance(m_joint);
+}
+
+void PhysicsJoint::addToWorld()
+{
+	ANKI_ASSERT(m_joint);
+	m_joint->setUserConstraintPtr(static_cast<PhysicsObject*>(this));
+
+	auto lock = getWorld().lockBtWorld();
+	getWorld().getBtWorld()->addConstraint(m_joint);
+}
+
+PhysicsPoint2PointJoint::PhysicsPoint2PointJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPos)
+	: PhysicsJoint(world)
+{
+	m_bodyA = bodyA;
+	m_joint = getAllocator().newInstance<btPoint2PointConstraint>(*m_bodyA->getBtBody(), toBt(relPos));
+	m_joint->setUserConstraintPtr(static_cast<PhysicsJoint*>(this));
+
+	addToWorld();
+}
+
+PhysicsPoint2PointJoint::PhysicsPoint2PointJoint(
+	PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPosA, PhysicsBodyPtr bodyB, const Vec3& relPosB)
+	: PhysicsJoint(world)
+{
+	ANKI_ASSERT(bodyA != bodyB);
+	m_bodyA = bodyA;
+	m_bodyB = bodyB;
+
+	m_joint = getAllocator().newInstance<btPoint2PointConstraint>(
+		*m_bodyA->getBtBody(), *m_bodyB->getBtBody(), toBt(relPosA), toBt(relPosB));
+
+	addToWorld();
+}
+
+PhysicsHingeJoint::PhysicsHingeJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPos, const Vec3& axis)
+	: PhysicsJoint(world)
+{
+	m_bodyA = bodyA;
+	m_joint = getAllocator().newInstance<btHingeConstraint>(*m_bodyA->getBtBody(), toBt(relPos), toBt(axis));
+	m_joint->setUserConstraintPtr(static_cast<PhysicsJoint*>(this));
+
+	addToWorld();
+}
+
+} // end namespace anki

+ 82 - 0
src/anki/physics/PhysicsJoint.h

@@ -0,0 +1,82 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/physics/PhysicsObject.h>
+
+namespace anki
+{
+
+/// @addtogroup physics
+/// @{
+
+/// Joint base class. Joints connect two (or a single one) rigid bodies together.
+class PhysicsJoint : public PhysicsObject
+{
+public:
+	static const PhysicsObjectType CLASS_TYPE = PhysicsObjectType::JOINT;
+
+	/// Set the breaking impulse.
+	void setBreakingImpulseThreshold(F32 impulse)
+	{
+		m_joint->setBreakingImpulseThreshold(impulse);
+	}
+
+	/// Return true if the joint broke.
+	Bool isBroken() const
+	{
+		return !m_joint->isEnabled();
+	}
+
+	/// Break the joint.
+	void brake()
+	{
+		m_joint->setEnabled(false);
+	}
+
+protected:
+	btTypedConstraint* m_joint = nullptr;
+	PhysicsBodyPtr m_bodyA;
+	PhysicsBodyPtr m_bodyB;
+
+	PhysicsJoint(PhysicsWorld* world);
+
+	~PhysicsJoint();
+
+	void addToWorld();
+};
+
+/// Point to point joint.
+class PhysicsPoint2PointJoint : public PhysicsJoint
+{
+	ANKI_PHYSICS_OBJECT
+
+private:
+	PhysicsPoint2PointJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPos);
+
+	PhysicsPoint2PointJoint(
+		PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPosA, PhysicsBodyPtr bodyB, const Vec3& relPosB);
+};
+
+/// Hinge joint.
+class PhysicsHingeJoint : public PhysicsJoint
+{
+	ANKI_PHYSICS_OBJECT
+
+private:
+	PhysicsHingeJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPos, const Vec3& axis);
+
+	PhysicsHingeJoint(PhysicsWorld* world,
+		PhysicsBodyPtr bodyA,
+		const Vec3& relPosA,
+		const Vec3& axisA,
+		PhysicsBodyPtr bodyB,
+		const Vec3& relPosB,
+		const Vec3& axisB);
+};
+/// @}
+
+} // end namespace anki

+ 17 - 0
src/anki/physics/PhysicsObject.cpp

@@ -0,0 +1,17 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/physics/PhysicsObject.h>
+#include <anki/physics/PhysicsWorld.h>
+
+namespace anki
+{
+
+HeapAllocator<U8> PhysicsObject::getAllocator() const
+{
+	return m_world->getAllocator();
+}
+
+} // end namespace anki

+ 99 - 5
src/anki/physics/PhysicsObject.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/physics/Common.h>
+#include <anki/util/List.h>
 
 namespace anki
 {
@@ -17,20 +18,26 @@ namespace anki
 enum class PhysicsObjectType : U8
 {
 	COLLISION_SHAPE,
-	BODY,
 	JOINT,
+	BODY,
 	PLAYER_CONTROLLER,
-	COUNT
+	TRIGGER,
+
+	COUNT,
+	FIRST = 0,
+	LAST = COUNT - 1,
+	FIRST_FILTERED = BODY,
+	LAST_FILTERED = TRIGGER,
 };
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(PhysicsObjectType, inline)
 
 /// Base of all physics objects.
-class PhysicsObject
+class PhysicsObject : public IntrusiveListEnabled<PhysicsObject>
 {
 public:
 	PhysicsObject(PhysicsObjectType type, PhysicsWorld* world)
 		: m_world(world)
 		, m_type(type)
-		, m_refcount(0)
 	{
 		ANKI_ASSERT(m_world);
 	}
@@ -59,12 +66,99 @@ public:
 		return m_refcount;
 	}
 
+	HeapAllocator<U8> getAllocator() const;
+
+	void setUserData(void* ud)
+	{
+		m_userData = ud;
+	}
+
+	void* getUserData() const
+	{
+		return m_userData;
+	}
+
 protected:
 	PhysicsWorld* m_world = nullptr;
 
 private:
+	Atomic<I32> m_refcount = {0};
 	PhysicsObjectType m_type;
-	Atomic<I32> m_refcount;
+	void* m_userData = nullptr;
+};
+
+#define ANKI_PHYSICS_OBJECT \
+	friend class PhysicsWorld; \
+	friend class PhysicsPtrDeleter;
+
+/// This is a factor that will decide if two filtered objects will be checked for collision.
+/// @memberof PhysicsFilteredObject
+class PhysicsBroadPhaseFilterCallback
+{
+public:
+	virtual Bool needsCollision(const PhysicsFilteredObject& a, const PhysicsFilteredObject& b) = 0;
+};
+
+/// A PhysicsObject that takes part into collision detection. Has functionality to filter the broad phase detection.
+class PhysicsFilteredObject : public PhysicsObject
+{
+public:
+	PhysicsFilteredObject(PhysicsObjectType type, PhysicsWorld* world)
+		: PhysicsObject(type, world)
+	{
+	}
+
+	~PhysicsFilteredObject()
+	{
+	}
+
+	static Bool classof(const PhysicsObject* obj)
+	{
+		return obj->getType() >= PhysicsObjectType::FIRST_FILTERED
+			   && obj->getType() <= PhysicsObjectType::LAST_FILTERED;
+	}
+
+	/// Get the material(s) this object belongs.
+	PhysicsMaterialBit getMaterialGroup() const
+	{
+		return m_materialGroup;
+	}
+
+	/// Set the material(s) this object belongs.
+	void setMaterialGroup(PhysicsMaterialBit bits)
+	{
+		m_materialGroup = bits;
+	}
+
+	/// Get the materials this object collides.
+	PhysicsMaterialBit getMaterialMask() const
+	{
+		return m_materialMask;
+	}
+
+	/// Set the materials this object collides.
+	void setMaterialMask(PhysicsMaterialBit bit)
+	{
+		m_materialMask = bit;
+	}
+
+	/// Get the broadphase callback.
+	PhysicsBroadPhaseFilterCallback* getPhysicsBroadPhaseFilterCallback() const
+	{
+		return m_filter;
+	}
+
+	/// Set the broadphase callback.
+	void setPhysicsBroadPhaseFilterCallback(PhysicsBroadPhaseFilterCallback* f)
+	{
+		m_filter = f;
+	}
+
+private:
+	PhysicsMaterialBit m_materialGroup = PhysicsMaterialBit::ALL;
+	PhysicsMaterialBit m_materialMask = PhysicsMaterialBit::ALL;
+
+	PhysicsBroadPhaseFilterCallback* m_filter = nullptr;
 };
 /// @}
 

+ 36 - 51
src/anki/physics/PhysicsPlayerController.cpp

@@ -9,74 +9,59 @@
 namespace anki
 {
 
-CharacterControllerManager::CharacterControllerManager(PhysicsWorld* world)
-	: dCustomPlayerControllerManager(world->getNewtonWorld())
-	, m_world(world)
+PhysicsPlayerController::PhysicsPlayerController(PhysicsWorld* world, const PhysicsPlayerControllerInitInfo& init)
+	: PhysicsFilteredObject(CLASS_TYPE, world)
 {
-}
-
-void CharacterControllerManager::ApplyPlayerMove(dCustomPlayerController* const controller, dFloat timestep)
-{
-	ANKI_ASSERT(controller);
-
-	NewtonBody* body = controller->GetBody();
-	PhysicsPlayerController* player = static_cast<PhysicsPlayerController*>(NewtonBodyGetUserData(body));
-	ANKI_ASSERT(player);
+	const btTransform trf = toBt(Transform(init.m_position.xyz0(), Mat3x4::getIdentity(), 1.0f));
 
-	dVector gravity = toNewton(player->getWorld().getGravity());
+	m_convexShape = getAllocator().newInstance<btCapsuleShape>(init.m_outerRadius, init.m_height);
 
-	// Compute the angle the way newton wants it
-	Vec4 forwardDir{player->m_forwardDir.x(), 0.0, player->m_forwardDir.z(), 0.0f};
-	F32 cosTheta = clamp(Vec4(0.0, 0.0, -1.0, 0.0).dot(forwardDir), -1.0f, 1.0f);
-	F32 sign = Vec4(0.0, 0.0, -1.0, 0.0).cross(forwardDir).y();
-	sign = (!isZero(sign)) ? (absolute(sign) / sign) : 1.0f;
-	F32 angle = acos(cosTheta) * sign;
+	m_ghostObject = getAllocator().newInstance<btPairCachingGhostObject>();
+	m_ghostObject->setWorldTransform(trf);
+	m_ghostObject->setCollisionShape(m_convexShape);
+	m_ghostObject->setUserPointer(static_cast<PhysicsObject*>(this));
+	setMaterialGroup(PhysicsMaterialBit::PLAYER);
+	setMaterialMask(PhysicsMaterialBit::ALL);
 
-	controller->SetPlayerVelocity(
-		player->m_forwardSpeed, player->m_strafeSpeed, player->m_jumpSpeed, angle, gravity, timestep);
-}
+	m_controller = getAllocator().newInstance<btKinematicCharacterController>(
+		m_ghostObject, m_convexShape, init.m_stepHeight, btVector3(0, 1, 0));
 
-PhysicsPlayerController::~PhysicsPlayerController()
-{
-	if(m_newtonPlayer)
 	{
-		getWorld().getCharacterControllerManager().DestroyController(m_newtonPlayer);
+		auto lock = getWorld().lockBtWorld();
+		btDynamicsWorld* btworld = getWorld().getBtWorld();
+
+		btworld->addCollisionObject(m_ghostObject,
+			btBroadphaseProxy::CharacterFilter,
+			btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter);
+		btworld->addAction(m_controller);
 	}
+
+	// Need to call this else the player is upside down
+	moveToPosition(init.m_position);
 }
 
-Error PhysicsPlayerController::create(const PhysicsPlayerControllerInitInfo& init)
+PhysicsPlayerController::~PhysicsPlayerController()
 {
-	dMatrix playerAxis;
-	playerAxis[0] = dVector(0.0f, 1.0f, 0.0f, 0.0f); // the y axis is the character up vector
-	playerAxis[1] = dVector(0.0f, 0.0f, -1.0f, 0.0f); // the x axis is the character front direction
-	playerAxis[2] = playerAxis[0].CrossProduct(playerAxis[1]);
-	playerAxis[3] = dVector(0.0f, 0.0f, 0.0f, 1.0f);
-
-	m_newtonPlayer = getWorld().getCharacterControllerManager().CreatePlayer(
-		init.m_mass, init.m_outerRadius, init.m_innerRadius, init.m_height, init.m_stepHeight, playerAxis);
-
-	if(m_newtonPlayer == nullptr)
 	{
-		return Error::FUNCTION_FAILED;
+		auto lock = getWorld().lockBtWorld();
+		getWorld().getBtWorld()->removeAction(m_controller);
+		getWorld().getBtWorld()->removeCollisionObject(m_ghostObject);
 	}
 
-	// Set some data
-	NewtonBody* body = m_newtonPlayer->GetBody();
-	NewtonBodySetUserData(body, this);
-	NewtonBodySetTransformCallback(body, onTransformUpdateCallback);
-
-	dMatrix location(dGetIdentityMatrix());
-	location.m_posit.m_x = init.m_position.x();
-	location.m_posit.m_y = init.m_position.y();
-	location.m_posit.m_z = init.m_position.z();
-	NewtonBodySetMatrix(body, &location[0][0]);
-
-	return Error::NONE;
+	getAllocator().deleteInstance(m_controller);
+	getAllocator().deleteInstance(m_ghostObject);
+	getAllocator().deleteInstance(m_convexShape);
 }
 
 void PhysicsPlayerController::moveToPosition(const Vec4& position)
 {
-	ANKI_ASSERT(!"TODO");
+	auto lock = getWorld().lockBtWorld();
+
+	getWorld().getBtWorld()->getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(
+		m_ghostObject->getBroadphaseHandle(), getWorld().getBtWorld()->getDispatcher());
+
+	m_controller->reset(getWorld().getBtWorld());
+	m_controller->warp(toBt(position.xyz()));
 }
 
 } // end namespace anki

+ 14 - 57
src/anki/physics/PhysicsPlayerController.h

@@ -13,21 +13,6 @@ namespace anki
 /// @addtogroup physics
 /// @{
 
-/// The implementation of the Newton manager.
-class CharacterControllerManager : public dCustomPlayerControllerManager
-{
-public:
-	PhysicsWorld* m_world;
-
-	CharacterControllerManager(PhysicsWorld* world);
-
-	~CharacterControllerManager()
-	{
-	}
-
-	void ApplyPlayerMove(dCustomPlayerController* const controller, dFloat timestep);
-};
-
 /// Init info for PhysicsPlayerController.
 class PhysicsPlayerControllerInitInfo
 {
@@ -41,66 +26,38 @@ public:
 };
 
 /// A player controller that walks the world.
-class PhysicsPlayerController final : public PhysicsObject
+class PhysicsPlayerController final : public PhysicsFilteredObject
 {
-	friend class CharacterControllerManager;
+	ANKI_PHYSICS_OBJECT
 
 public:
-	PhysicsPlayerController(PhysicsWorld* world)
-		: PhysicsObject(PhysicsObjectType::PLAYER_CONTROLLER, world)
-	{
-	}
-
-	~PhysicsPlayerController();
-
-	ANKI_USE_RESULT Error create(const PhysicsPlayerControllerInitInfo& init);
+	static const PhysicsObjectType CLASS_TYPE = PhysicsObjectType::PLAYER_CONTROLLER;
 
 	// Update the state machine
 	void setVelocity(F32 forwardSpeed, F32 strafeSpeed, F32 jumpSpeed, const Vec4& forwardDir)
 	{
-		m_forwardSpeed = forwardSpeed;
-		m_strafeSpeed = strafeSpeed;
-		m_jumpSpeed = jumpSpeed;
-		m_forwardDir = forwardDir;
+		m_controller->setWalkDirection(toBt((forwardDir * forwardSpeed).xyz()));
 	}
 
 	void moveToPosition(const Vec4& position);
 
-	const Transform& getTransform(Bool& updated)
+	Transform getTransform(Bool& updated)
 	{
-		updated = m_updated;
-		m_updated = false;
-		return m_trf;
+		Transform out = toAnki(m_ghostObject->getWorldTransform());
+		updated = m_prevTrf != out;
+		return out;
 	}
 
 private:
-	dCustomPlayerController* m_newtonPlayer = nullptr;
-	Transform m_trf = Transform::getIdentity();
-	Bool m_updated = true;
+	btPairCachingGhostObject* m_ghostObject = nullptr;
+	btCapsuleShape* m_convexShape = nullptr;
+	btKinematicCharacterController* m_controller = nullptr;
 
-	// State
-	F32 m_forwardSpeed = 0.0;
-	F32 m_strafeSpeed = 0.0;
-	F32 m_jumpSpeed = 0.0;
-	Vec4 m_forwardDir = Vec4(0.0, 0.0, -1.0, 0.0);
+	Transform m_prevTrf = Transform::getIdentity();
 
-	static void onTransformUpdateCallback(const NewtonBody* body, const dFloat* matrix, int threadIndex)
-	{
-		ANKI_ASSERT(body && matrix);
-		Transform trf = Transform(toAnki(dMatrix(matrix)));
-		void* ud = NewtonBodyGetUserData(body);
-		ANKI_ASSERT(ud);
-		static_cast<PhysicsPlayerController*>(ud)->onTransformUpdate(trf);
-	}
+	PhysicsPlayerController(PhysicsWorld* world, const PhysicsPlayerControllerInitInfo& init);
 
-	void onTransformUpdate(const Transform& trf)
-	{
-		if(trf != m_trf)
-		{
-			m_updated = true;
-			m_trf = trf;
-		}
-	}
+	~PhysicsPlayerController();
 };
 /// @}
 

+ 69 - 0
src/anki/physics/PhysicsTrigger.cpp

@@ -0,0 +1,69 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/physics/PhysicsTrigger.h>
+#include <anki/physics/PhysicsCollisionShape.h>
+#include <anki/physics/PhysicsWorld.h>
+#include <anki/util/Rtti.h>
+
+namespace anki
+{
+
+PhysicsTrigger::PhysicsTrigger(PhysicsWorld* world, PhysicsCollisionShapePtr shape)
+	: PhysicsFilteredObject(CLASS_TYPE, world)
+{
+	m_shape = shape;
+
+	m_ghostShape = getAllocator().newInstance<btGhostObject>();
+	m_ghostShape->setWorldTransform(btTransform::getIdentity());
+	m_ghostShape->setCollisionShape(shape->getBtShape(true));
+
+	m_ghostShape->setUserPointer(static_cast<PhysicsObject*>(this));
+
+	setMaterialGroup(PhysicsMaterialBit::TRIGGER);
+	setMaterialMask(PhysicsMaterialBit::ALL);
+
+	auto lock = getWorld().lockBtWorld();
+	getWorld().getBtWorld()->addCollisionObject(m_ghostShape);
+}
+
+PhysicsTrigger::~PhysicsTrigger()
+{
+	{
+		auto lock = getWorld().lockBtWorld();
+		getWorld().getBtWorld()->removeCollisionObject(m_ghostShape);
+	}
+
+	getAllocator().deleteInstance(m_ghostShape);
+}
+
+void PhysicsTrigger::processContacts()
+{
+	if(m_contactCallback == nullptr)
+	{
+		return;
+	}
+
+	if(m_ghostShape->getOverlappingPairs().size() < 0)
+	{
+		return;
+	}
+
+	// Process contacts
+	for(U i = 0; i < U(m_ghostShape->getOverlappingPairs().size()); ++i)
+	{
+		btCollisionObject* obj = m_ghostShape->getOverlappingPairs()[i];
+
+		ANKI_ASSERT(obj);
+
+		PhysicsObject* aobj = static_cast<PhysicsObject*>(obj->getUserPointer());
+		ANKI_ASSERT(aobj);
+
+		PhysicsFilteredObject* fobj = dcast<PhysicsFilteredObject*>(aobj);
+		m_contactCallback->processContact(*this, *fobj);
+	}
+}
+
+} // end namespace anki

+ 61 - 0
src/anki/physics/PhysicsTrigger.h

@@ -0,0 +1,61 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/physics/PhysicsObject.h>
+#include <anki/util/WeakArray.h>
+
+namespace anki
+{
+
+/// @addtogroup physics
+/// @{
+
+/// An interface to process contacts.
+/// @memberof PhysicsTrigger
+class PhysicsTriggerProcessContactCallback
+{
+public:
+	virtual ~PhysicsTriggerProcessContactCallback()
+	{
+	}
+
+	virtual void processContact(PhysicsTrigger& trigger, PhysicsFilteredObject& obj) = 0;
+};
+
+/// A trigger that uses a PhysicsShape and its purpose is to collect collision events.
+class PhysicsTrigger : public PhysicsFilteredObject
+{
+	ANKI_PHYSICS_OBJECT
+
+public:
+	static const PhysicsObjectType CLASS_TYPE = PhysicsObjectType::TRIGGER;
+
+	void setTransform(const Transform& trf)
+	{
+		m_ghostShape->setWorldTransform(toBt(trf));
+	}
+
+	void setContactProcessCallback(PhysicsTriggerProcessContactCallback* cb)
+	{
+		m_contactCallback = cb;
+	}
+
+private:
+	PhysicsCollisionShapePtr m_shape;
+	btGhostObject* m_ghostShape = nullptr;
+
+	PhysicsTriggerProcessContactCallback* m_contactCallback = nullptr;
+
+	PhysicsTrigger(PhysicsWorld* world, PhysicsCollisionShapePtr shape);
+
+	~PhysicsTrigger();
+
+	void processContacts();
+};
+/// @}
+
+} // end namespace anki

+ 164 - 109
src/anki/physics/PhysicsWorld.cpp

@@ -4,9 +4,11 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/physics/PhysicsWorld.h>
-#include <anki/physics/PhysicsPlayerController.h>
 #include <anki/physics/PhysicsCollisionShape.h>
 #include <anki/physics/PhysicsBody.h>
+#include <anki/physics/PhysicsTrigger.h>
+#include <anki/util/Rtti.h>
+#include <BulletCollision/Gimpact/btGImpactCollisionAlgorithm.h>
 
 namespace anki
 {
@@ -14,166 +16,219 @@ namespace anki
 // Ugly but there is no other way
 static HeapAllocator<U8>* gAlloc = nullptr;
 
-static void* newtonAlloc(int size)
+static void* btAlloc(size_t size)
 {
-	return gAlloc->allocate(size + 16);
+	ANKI_ASSERT(gAlloc);
+	return gAlloc->getMemoryPool().allocate(size, 16);
 }
 
-static void newtonFree(void* const ptr, int size)
+static void btFree(void* ptr)
 {
-	gAlloc->deallocate(ptr, size + 16);
+	ANKI_ASSERT(gAlloc);
+	gAlloc->getMemoryPool().free(ptr);
 }
 
-PhysicsWorld::PhysicsWorld()
+/// Broad phase collision callback.
+class PhysicsWorld::MyOverlapFilterCallback : public btOverlapFilterCallback
 {
-}
+public:
+	bool needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) const override
+	{
+		ANKI_ASSERT(proxy0 && proxy1);
+
+		const btCollisionObject* btObj0 = static_cast<const btCollisionObject*>(proxy0->m_clientObject);
+		const btCollisionObject* btObj1 = static_cast<const btCollisionObject*>(proxy1->m_clientObject);
+		ANKI_ASSERT(btObj0 && btObj1);
+
+		const PhysicsObject* aobj0 = static_cast<const PhysicsObject*>(btObj0->getUserPointer());
+		const PhysicsObject* aobj1 = static_cast<const PhysicsObject*>(btObj1->getUserPointer());
+
+		if(aobj0 == nullptr || aobj1 == nullptr)
+		{
+			return false;
+		}
+
+		const PhysicsFilteredObject* fobj0 = dcast<const PhysicsFilteredObject*>(aobj0);
+		const PhysicsFilteredObject* fobj1 = dcast<const PhysicsFilteredObject*>(aobj1);
+
+		// First check the masks
+		Bool collide = !!(fobj0->getMaterialGroup() & fobj1->getMaterialMask());
+		collide = collide && !!(fobj1->getMaterialGroup() & fobj0->getMaterialMask());
+		if(!collide)
+		{
+			return false;
+		}
+
+		// Detailed tests using callbacks
+		if(fobj0->getPhysicsBroadPhaseFilterCallback())
+		{
+			collide = fobj0->getPhysicsBroadPhaseFilterCallback()->needsCollision(*fobj0, *fobj1);
+			if(!collide)
+			{
+				return false;
+			}
+		}
+
+		if(fobj1->getPhysicsBroadPhaseFilterCallback())
+		{
+			collide = fobj1->getPhysicsBroadPhaseFilterCallback()->needsCollision(*fobj1, *fobj0);
+			if(!collide)
+			{
+				return false;
+			}
+		}
+
+		return true;
+	}
+};
 
-PhysicsWorld::~PhysicsWorld()
+class PhysicsWorld::MyRaycastCallback : public btCollisionWorld::RayResultCallback
 {
-	cleanupMarkedForDeletion();
+public:
+	PhysicsWorldRayCastCallback* m_raycast = nullptr;
 
-	if(m_sceneBody)
+	bool needsCollision(btBroadphaseProxy* proxy) const override
 	{
-		NewtonDestroyBody(m_sceneBody);
-		m_sceneBody = nullptr;
+		ANKI_ASSERT(proxy);
+
+		const btCollisionObject* cobj = static_cast<const btCollisionObject*>(proxy->m_clientObject);
+		ANKI_ASSERT(cobj);
+
+		const PhysicsObject* pobj = static_cast<const PhysicsObject*>(cobj->getUserPointer());
+		ANKI_ASSERT(pobj);
+
+		const PhysicsFilteredObject* fobj = dcast<const PhysicsFilteredObject*>(pobj);
+
+		return !!(fobj->getMaterialGroup() & m_raycast->m_materialMask);
 	}
 
-	if(m_sceneCollision)
+	btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) final
 	{
-		NewtonDestroyCollision(m_sceneCollision);
-		m_sceneCollision = nullptr;
+		// No idea why
+		if(m_raycast->m_firstHit)
+		{
+			m_closestHitFraction = rayResult.m_hitFraction;
+		}
+
+		m_collisionObject = rayResult.m_collisionObject;
+		Vec3 worldNormal;
+		if(normalInWorldSpace)
+		{
+			worldNormal = toAnki(rayResult.m_hitNormalLocal);
+		}
+		else
+		{
+			worldNormal = toAnki(m_collisionObject->getWorldTransform().getBasis() * rayResult.m_hitNormalLocal);
+		}
+
+		Vec3 hitPointWorld = mix(m_raycast->m_from, m_raycast->m_to, rayResult.m_hitFraction);
+
+		// Call the callback
+		PhysicsObject* pobj = static_cast<PhysicsObject*>(m_collisionObject->getUserPointer());
+		ANKI_ASSERT(pobj);
+		m_raycast->processResult(dcast<PhysicsFilteredObject&>(*pobj), worldNormal, hitPointWorld);
+
+		return m_closestHitFraction;
 	}
+};
 
-	if(m_world)
+PhysicsWorld::PhysicsWorld()
+{
+}
+
+PhysicsWorld::~PhysicsWorld()
+{
+#if ANKI_ASSERTS_ENABLED
+	for(PhysicsObjectType type = PhysicsObjectType::FIRST; type < PhysicsObjectType::COUNT; ++type)
 	{
-		NewtonDestroy(m_world);
-		m_world = nullptr;
+		ANKI_ASSERT(m_objectLists[type].isEmpty() && "Someone is holding refs to some physics objects");
 	}
+#endif
+
+	m_alloc.deleteInstance(m_world);
+	m_alloc.deleteInstance(m_solver);
+	m_alloc.deleteInstance(m_dispatcher);
+	m_alloc.deleteInstance(m_collisionConfig);
+	m_alloc.deleteInstance(m_broadphase);
+	m_alloc.deleteInstance(m_gpc);
+	m_alloc.deleteInstance(m_filterCallback);
 
 	gAlloc = nullptr;
 }
 
 Error PhysicsWorld::create(AllocAlignedCallback allocCb, void* allocCbData)
 {
-	Error err = Error::NONE;
-
 	m_alloc = HeapAllocator<U8>(allocCb, allocCbData);
+	m_tmpAlloc = StackAllocator<U8>(allocCb, allocCbData, 1_KB, 2.0f);
 
 	// Set allocators
 	gAlloc = &m_alloc;
-	NewtonSetMemorySystem(newtonAlloc, newtonFree);
+	btAlignedAllocSetCustom(btAlloc, btFree);
 
-	// Initialize world
-	m_world = NewtonCreate();
-	if(!m_world)
-	{
-		ANKI_PHYS_LOGE("NewtonCreate() failed");
-		return Error::FUNCTION_FAILED;
-	}
-
-	// Set the simplified solver mode (faster but less accurate)
-	NewtonSetSolverModel(m_world, 1);
+	// Create objects
+	m_broadphase = m_alloc.newInstance<btDbvtBroadphase>();
+	m_gpc = m_alloc.newInstance<btGhostPairCallback>();
+	m_broadphase->getOverlappingPairCache()->setInternalGhostPairCallback(m_gpc);
+	m_filterCallback = m_alloc.newInstance<MyOverlapFilterCallback>();
+	m_broadphase->getOverlappingPairCache()->setOverlapFilterCallback(m_filterCallback);
 
-	// Create the character controller manager. Newton needs it's own allocators
-	m_playerManager = new CharacterControllerManager(this);
+	m_collisionConfig = m_alloc.newInstance<btDefaultCollisionConfiguration>();
 
-	// Create scene collision
-	m_sceneCollision = NewtonCreateSceneCollision(m_world, 0);
-	Mat4 trf = Mat4::getIdentity();
-	m_sceneBody = NewtonCreateDynamicBody(m_world, m_sceneCollision, &trf[0]);
-	NewtonBodySetMaterialGroupID(m_sceneBody, NewtonMaterialGetDefaultGroupID(m_world));
+	m_dispatcher = m_alloc.newInstance<btCollisionDispatcher>(m_collisionConfig);
+	btGImpactCollisionAlgorithm::registerAlgorithm(m_dispatcher);
 
-	NewtonDestroyCollision(m_sceneCollision); // destroy old scene
-	m_sceneCollision = NewtonBodyGetCollision(m_sceneBody);
+	m_solver = m_alloc.newInstance<btSequentialImpulseConstraintSolver>();
 
-	// Set callbacks
-	NewtonMaterialSetCollisionCallback(m_world,
-		NewtonMaterialGetDefaultGroupID(m_world),
-		NewtonMaterialGetDefaultGroupID(m_world),
-		onAabbOverlapCallback,
-		onContactCallback);
-
-	return err;
-}
-
-Error PhysicsWorld::updateAsync(Second dt)
-{
-	m_dt = dt;
-
-	// Do cleanup of marked for deletion
-	cleanupMarkedForDeletion();
-
-	// Update
-	NewtonUpdateAsync(m_world, dt);
+	m_world = m_alloc.newInstance<btDiscreteDynamicsWorld>(m_dispatcher, m_broadphase, m_solver, m_collisionConfig);
+	m_world->setGravity(btVector3(0.0f, -9.8f, 0.0f));
 
 	return Error::NONE;
 }
 
-void PhysicsWorld::waitUpdate()
-{
-	NewtonWaitForUpdateToFinish(m_world);
-}
-
-void PhysicsWorld::cleanupMarkedForDeletion()
+Error PhysicsWorld::update(Second dt)
 {
-	LockGuard<Mutex> lock(m_mtx);
-
-	while(!m_forDeletion.isEmpty())
+	// Update world
 	{
-		auto it = m_forDeletion.getBegin();
-		PhysicsObject* obj = *it;
+		auto lock = lockBtWorld();
+		m_world->stepSimulation(dt, 2, 1.0 / 60.0);
+	}
 
-		// Remove from objects marked for deletion
-		m_forDeletion.erase(m_alloc, it);
+	// Process trigger contacts
+	{
+		LockGuard<Mutex> lock(m_objectListsMtx);
 
-		// Finaly, delete it
-		m_alloc.deleteInstance(obj);
+		for(PhysicsObject& trigger : m_objectLists[PhysicsObjectType::TRIGGER])
+		{
+			static_cast<PhysicsTrigger&>(trigger).processContacts();
+		}
 	}
-}
 
-void PhysicsWorld::registerObject(PhysicsObject* ptr)
-{
-	// TODO Remove
+	// Reset the pool
+	m_tmpAlloc.getMemoryPool().reset();
+
+	return Error::NONE;
 }
 
-void PhysicsWorld::onContactCallback(const NewtonJoint* contactJoint, F32 timestep, int threadIndex)
+void PhysicsWorld::destroyObject(PhysicsObject* obj)
 {
-	const NewtonBody* body0 = NewtonJointGetBody0(contactJoint);
-	const NewtonBody* body1 = NewtonJointGetBody1(contactJoint);
-
-	F32 friction0 = 0.01f;
-	F32 elasticity0 = 0.001f;
-	F32 friction1 = friction0;
-	F32 elasticity1 = elasticity0;
-
-	void* userData = NewtonBodyGetUserData(body0);
-	if(userData)
-	{
-		friction0 = static_cast<PhysicsBody*>(userData)->getFriction();
-		elasticity0 = static_cast<PhysicsBody*>(userData)->getElasticity();
-	}
+	ANKI_ASSERT(obj);
 
-	userData = NewtonBodyGetUserData(body1);
-	if(userData)
 	{
-		friction1 = static_cast<PhysicsBody*>(userData)->getFriction();
-		elasticity1 = static_cast<PhysicsBody*>(userData)->getElasticity();
+		LockGuard<Mutex> lock(m_objectListsMtx);
+		m_objectLists[obj->getType()].erase(obj);
 	}
 
-	F32 friction = friction0 + friction1;
-	F32 elasticity = elasticity0 + elasticity1;
+	obj->~PhysicsObject();
+	m_alloc.getMemoryPool().free(obj);
+}
 
-	void* contact = NewtonContactJointGetFirstContact(contactJoint);
-	while(contact)
+void PhysicsWorld::rayCast(WeakArray<PhysicsWorldRayCastCallback*> rayCasts)
+{
+	MyRaycastCallback callback;
+	for(PhysicsWorldRayCastCallback* cb : rayCasts)
 	{
-		NewtonMaterial* material = NewtonContactGetMaterial(contact);
-
-		NewtonMaterialSetContactFrictionCoef(material, friction + 0.1, friction, 0);
-		NewtonMaterialSetContactFrictionCoef(material, friction + 0.1, friction, 1);
-
-		NewtonMaterialSetContactElasticity(material, elasticity);
-
-		contact = NewtonContactJointGetNextContact(contactJoint, contact);
+		callback.m_raycast = cb;
+		m_world->rayTest(toBt(cb->m_from), toBt(cb->m_to), callback);
 	}
 }
 

+ 67 - 84
src/anki/physics/PhysicsWorld.h

@@ -6,17 +6,36 @@
 #pragma once
 
 #include <anki/physics/Common.h>
+#include <anki/physics/PhysicsObject.h>
 #include <anki/util/List.h>
+#include <anki/util/WeakArray.h>
 
 namespace anki
 {
 
-// Forward
-class CharacterControllerManager;
-
 /// @addtogroup physics
 /// @{
 
+/// Raycast callback (interface).
+class PhysicsWorldRayCastCallback
+{
+public:
+	Vec3 m_from;
+	Vec3 m_to;
+	PhysicsMaterialBit m_materialMask; ///< Materials to check
+	Bool8 m_firstHit = true;
+
+	PhysicsWorldRayCastCallback(const Vec3& from, const Vec3& to, PhysicsMaterialBit materialMask)
+		: m_from(from)
+		, m_to(to)
+		, m_materialMask(materialMask)
+	{
+	}
+
+	/// Process a raycast result.
+	virtual void processResult(PhysicsFilteredObject& obj, const Vec3& worldNormal, const Vec3& worldPosition) = 0;
+};
+
 /// The master container for all physics related stuff.
 class PhysicsWorld
 {
@@ -27,115 +46,79 @@ public:
 	ANKI_USE_RESULT Error create(AllocAlignedCallback allocCb, void* allocCbData);
 
 	template<typename T, typename... TArgs>
-	PhysicsPtr<T> newInstance(TArgs&&... args);
-
-	/// Start asynchronous update.
-	Error updateAsync(Second dt);
+	PhysicsPtr<T> newInstance(TArgs&&... args)
+	{
+		void* mem = m_alloc.getMemoryPool().allocate(sizeof(T), alignof(T));
+		::new(mem) T(this, std::forward<TArgs>(args)...);
 
-	/// End asynchronous update.
-	void waitUpdate();
+		T* obj = static_cast<T*>(mem);
+		LockGuard<Mutex> lock(m_objectListsMtx);
+		m_objectLists[obj->getType()].pushBack(obj);
 
-	const Vec4& getGravity() const
-	{
-		return m_gravity;
+		return PhysicsPtr<T>(obj);
 	}
 
-anki_internal:
-	NewtonWorld* getNewtonWorld() const
-	{
-		ANKI_ASSERT(m_world);
-		return m_world;
-	}
+	/// Do the update.
+	Error update(Second dt);
 
-	/// Used for static collision.
-	NewtonCollision* getNewtonScene() const
+	HeapAllocator<U8> getAllocator() const
 	{
-		return m_sceneCollision;
+		return m_alloc;
 	}
 
-	Second getDeltaTime() const
+	HeapAllocator<U8>& getAllocator()
 	{
-		return m_dt;
+		return m_alloc;
 	}
 
-	void deleteObjectDeferred(PhysicsObject* obj)
+	void rayCast(WeakArray<PhysicsWorldRayCastCallback*> rayCasts);
+
+	void rayCast(PhysicsWorldRayCastCallback& raycast)
 	{
-		LockGuard<Mutex> lock(m_mtx);
-		m_forDeletion.pushBack(m_alloc, obj);
+		PhysicsWorldRayCastCallback* ptr = &raycast;
+		WeakArray<PhysicsWorldRayCastCallback*> arr(&ptr, 1);
+		rayCast(arr);
 	}
 
-	CharacterControllerManager& getCharacterControllerManager()
+anki_internal:
+	btDynamicsWorld* getBtWorld() const
 	{
-		ANKI_ASSERT(m_playerManager);
-		return *m_playerManager;
+		ANKI_ASSERT(m_world);
+		return m_world;
 	}
 
-private:
-	HeapAllocator<U8> m_alloc;
-	mutable NewtonWorld* m_world = nullptr;
-	NewtonCollision* m_sceneCollision = nullptr;
-	NewtonBody* m_sceneBody = nullptr;
-	Vec4 m_gravity = Vec4(0.0f, -9.8f, 0.0f, 0.0f);
-	Second m_dt = 0.0;
-
-	/// @note Don't delete it. Newton will
-	CharacterControllerManager* m_playerManager = nullptr;
-
-	Mutex m_mtx;
-	List<PhysicsObject*> m_forDeletion;
-
-	template<typename T, typename... TArgs>
-	PhysicsPtr<T> newObjectInternal(TArgs&&... args);
-
-	void cleanupMarkedForDeletion();
-
-	static void destroyCallback(const NewtonWorld* const world, void* const listenerUserData)
+	F32 getCollisionMargin() const
 	{
+		return 0.04f;
 	}
 
-	void registerObject(PhysicsObject* ptr);
-
-	static int onAabbOverlapCallback(const NewtonMaterial* const material,
-		const NewtonBody* const body0,
-		const NewtonBody* const body1,
-		int threadIndex)
+	ANKI_USE_RESULT LockGuard<Mutex> lockBtWorld() const
 	{
-		(void)material;
-		(void)body0;
-		(void)body1;
-		(void)threadIndex;
-		return 1;
+		return LockGuard<Mutex>(m_btWorldMtx);
 	}
 
-	static void onContactCallback(const NewtonJoint* contactJoint, F32 timestep, int threadIndex);
-};
+	void destroyObject(PhysicsObject* obj);
 
-template<typename T, typename... TArgs>
-inline PhysicsPtr<T> PhysicsWorld::newInstance(TArgs&&... args)
-{
-	Error err = Error::NONE;
-	PhysicsPtr<T> out;
+private:
+	class MyOverlapFilterCallback;
+	class MyRaycastCallback;
 
-	T* ptr = m_alloc.template newInstance<T>(this);
-	err = ptr->create(std::forward<TArgs>(args)...);
+	HeapAllocator<U8> m_alloc;
+	StackAllocator<U8> m_tmpAlloc;
 
-	if(!err)
-	{
-		registerObject(ptr);
-		out.reset(ptr);
-	}
-	else
-	{
-		ANKI_PHYS_LOGE("Failed to create physics object");
+	btBroadphaseInterface* m_broadphase = nullptr;
+	btGhostPairCallback* m_gpc = nullptr;
+	MyOverlapFilterCallback* m_filterCallback = nullptr;
 
-		if(ptr)
-		{
-			m_alloc.deleteInstance(ptr);
-		}
-	}
+	btDefaultCollisionConfiguration* m_collisionConfig = nullptr;
+	btCollisionDispatcher* m_dispatcher = nullptr;
+	btSequentialImpulseConstraintSolver* m_solver = nullptr;
+	btDiscreteDynamicsWorld* m_world = nullptr;
+	mutable Mutex m_btWorldMtx;
 
-	return out;
-}
+	Array<IntrusiveList<PhysicsObject>, U(PhysicsObjectType::COUNT)> m_objectLists;
+	mutable Mutex m_objectListsMtx;
+};
 /// @}
 
 } // end namespace anki

+ 0 - 1
src/anki/renderer/Dbg.cpp

@@ -8,7 +8,6 @@
 #include <anki/renderer/GBuffer.h>
 #include <anki/renderer/LightShading.h>
 #include <anki/renderer/FinalComposite.h>
-#include <anki/renderer/DebugDrawer.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/Scene.h>
 #include <anki/util/Logger.h>

+ 1 - 1
src/anki/renderer/RenderQueue.h

@@ -53,7 +53,7 @@ public:
 	RenderQueueDrawCallback m_callback;
 	const void* m_userData;
 	U64 m_mergeKey;
-	F32 m_distanceFromCamera;
+	F32 m_distanceFromCamera; ///< Don't set this
 };
 
 static_assert(

+ 2 - 2
src/anki/renderer/Renderer.cpp

@@ -213,7 +213,7 @@ void Renderer::initJitteredMats()
 		subSample *= 0.5f; // In [-texSize / 2, texSize / 2]
 
 		m_jitteredMats16x[i] = Mat4::getIdentity();
-		// m_jitteredMats16x[i].setTranslationPart(Vec4(subSample, 0.0, 1.0));
+		m_jitteredMats16x[i].setTranslationPart(Vec4(subSample, 0.0, 1.0));
 	}
 
 	static const Array<Vec2, 8> SAMPLE_LOCS_8 = {{Vec2(-7.0, 1.0),
@@ -236,7 +236,7 @@ void Renderer::initJitteredMats()
 		subSample *= 0.5f; // In [-texSize / 2, texSize / 2]
 
 		m_jitteredMats8x[i] = Mat4::getIdentity();
-		// m_jitteredMats8x[i].setTranslationPart(Vec4(subSample, 0.0, 1.0));
+		m_jitteredMats8x[i].setTranslationPart(Vec4(subSample, 0.0, 1.0));
 	}
 }
 

+ 21 - 7
src/anki/renderer/ShadowMapping.cpp

@@ -292,6 +292,7 @@ void ShadowMapping::processLights(RenderingContext& ctx, U32& threadCountForScra
 		Array<U32, 6> scratchTiles;
 		Array<U64, 6> timestamps;
 		Array<U32, 6> faceIndices;
+		Array<U32, 6> drawcallCounts;
 		U numOfFacesThatHaveDrawcalls = 0;
 		for(U face = 0; face < 6; ++face)
 		{
@@ -304,6 +305,9 @@ void ShadowMapping::processLights(RenderingContext& ctx, U32& threadCountForScra
 				timestamps[numOfFacesThatHaveDrawcalls] =
 					light->m_shadowRenderQueues[face]->m_shadowRenderablesLastUpdateTimestamp;
 
+				drawcallCounts[numOfFacesThatHaveDrawcalls] =
+					light->m_shadowRenderQueues[face]->m_renderables.getSize();
+
 				++numOfFacesThatHaveDrawcalls;
 			}
 		}
@@ -312,6 +316,7 @@ void ShadowMapping::processLights(RenderingContext& ctx, U32& threadCountForScra
 											 numOfFacesThatHaveDrawcalls,
 											 &timestamps[0],
 											 &faceIndices[0],
+											 &drawcallCounts[0],
 											 &tiles[0],
 											 &scratchTiles[0]);
 
@@ -370,11 +375,13 @@ void ShadowMapping::processLights(RenderingContext& ctx, U32& threadCountForScra
 
 		// Allocate tiles
 		U32 tileIdx, scratchTileIdx, faceIdx = 0;
-		const Bool allocationFailed = light->m_shadowRenderQueue->m_renderables.getSize() == 0
+		U32 drawcallCount = light->m_shadowRenderQueue->m_renderables.getSize();
+		const Bool allocationFailed = drawcallCount == 0
 									  || allocateTilesAndScratchTiles(light->m_uuid,
 											 1,
 											 &light->m_shadowRenderQueue->m_shadowRenderablesLastUpdateTimestamp,
 											 &faceIdx,
+											 &drawcallCount,
 											 &tileIdx,
 											 &scratchTileIdx);
 
@@ -514,13 +521,14 @@ Bool ShadowMapping::allocateTilesAndScratchTiles(U64 lightUuid,
 	U32 faceCount,
 	const U64* faceTimestamps,
 	const U32* faceIndices,
+	const U32* drawcallsCount,
 	U32* tileIndices,
 	U32* scratchTileIndices)
 {
 	ANKI_ASSERT(faceTimestamps);
 	ANKI_ASSERT(lightUuid > 0);
 	ANKI_ASSERT(faceCount > 0 && faceCount <= 6);
-	ANKI_ASSERT(faceIndices && tileIndices && scratchTileIndices);
+	ANKI_ASSERT(faceIndices && tileIndices && scratchTileIndices && drawcallsCount);
 
 	Bool failed = false;
 	Array<Bool, 6> inTheCache;
@@ -549,8 +557,8 @@ Bool ShadowMapping::allocateTilesAndScratchTiles(U64 lightUuid,
 		for(U i = 0; i < faceCount && !failed; ++i)
 		{
 			scratchTileIndices[i] = MAX_U32;
-			const Bool shouldRender =
-				shouldRenderTile(faceTimestamps[i], lightUuid, faceIndices[i], m_tiles[tileIndices[i]]);
+			const Bool shouldRender = shouldRenderTile(
+				faceTimestamps[i], lightUuid, faceIndices[i], m_tiles[tileIndices[i]], drawcallsCount[i]);
 			const Bool scratchTileFailed = shouldRender && freeScratchTiles == 0;
 
 			if(scratchTileFailed)
@@ -582,6 +590,7 @@ Bool ShadowMapping::allocateTilesAndScratchTiles(U64 lightUuid,
 			tile.m_face = faceIndices[i];
 			tile.m_lightUuid = lightUuid;
 			tile.m_lastUsedTimestamp = m_r->getGlobalTimestamp();
+			tile.m_drawcallCount = drawcallsCount[i];
 
 			// Update the cache
 			if(!inTheCache[i])
@@ -596,13 +605,18 @@ Bool ShadowMapping::allocateTilesAndScratchTiles(U64 lightUuid,
 	return failed;
 }
 
-Bool ShadowMapping::shouldRenderTile(U64 lightTimestamp, U64 lightUuid, U32 face, const Tile& tileIdx)
+Bool ShadowMapping::shouldRenderTile(
+	U64 lightTimestamp, U64 lightUuid, U32 face, const Tile& tileIdx, U32 drawcallCount)
 {
-	if(tileIdx.m_face == face && tileIdx.m_lightUuid == lightUuid && tileIdx.m_lastUsedTimestamp >= lightTimestamp)
+	if(tileIdx.m_face == face && tileIdx.m_lightUuid == lightUuid && tileIdx.m_lastUsedTimestamp >= lightTimestamp
+		&& tileIdx.m_drawcallCount == drawcallCount)
 	{
 		return false;
 	}
-	return true;
+	else
+	{
+		return true;
+	}
 }
 
 Bool ShadowMapping::allocateTile(U64 lightTimestamp, U64 lightUuid, U32 face, U32& tileAllocated, Bool& inTheCache)

+ 3 - 1
src/anki/renderer/ShadowMapping.h

@@ -46,6 +46,7 @@ private:
 	public:
 		U64 m_lastUsedTimestamp = 0;
 		U64 m_lightUuid = 0;
+		U32 m_drawcallCount = 0;
 		U8 m_face = 0;
 		Bool8 m_pinned = false; ///< If true we cannot allocate from it.
 
@@ -81,7 +82,7 @@ private:
 	HashMap<TileKey, U32> m_lightUuidToTileIdx;
 
 	Bool allocateTile(U64 lightTimestamp, U64 lightUuid, U32 face, U32& tileAllocated, Bool& inTheCache);
-	static Bool shouldRenderTile(U64 lightTimestamp, U64 lightUuid, U32 face, const Tile& tileIdx);
+	static Bool shouldRenderTile(U64 lightTimestamp, U64 lightUuid, U32 face, const Tile& tileIdx, U32 drawcallCount);
 
 	class EsmResolveWorkItem
 	{
@@ -149,6 +150,7 @@ private:
 		U32 faceCount,
 		const U64* faceTimestamps,
 		const U32* faceIndices,
+		const U32* drawcallsCount,
 		U32* tileIndices,
 		U32* scratchTileIndices);
 

+ 3 - 6
src/anki/renderer/Ssao.cpp

@@ -127,8 +127,7 @@ void Ssao::runMain(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx
 
 	cmdb->bindShaderProgram(m_main.m_grProg);
 
-	rgraphCtx.bindTextureAndSampler(
-		0, 0, m_r->getDepthDownscale().getHiZRt(), HIZ_QUARTER_DEPTH, m_r->getLinearSampler());
+	rgraphCtx.bindTextureAndSampler(0, 0, m_r->getDepthDownscale().getHiZRt(), HIZ_HALF_DEPTH, m_r->getLinearSampler());
 	cmdb->bindTextureAndSampler(0,
 		1,
 		m_main.m_noiseTex->getGrTextureView(),
@@ -207,8 +206,7 @@ void Ssao::populateRenderGraph(RenderingContext& ctx)
 				pass.newConsumer({m_r->getGBuffer().getColorRt(2), TextureUsageBit::SAMPLED_COMPUTE});
 			}
 
-			pass.newConsumer(
-				{m_r->getDepthDownscale().getHiZRt(), TextureUsageBit::SAMPLED_COMPUTE, HIZ_QUARTER_DEPTH});
+			pass.newConsumer({m_r->getDepthDownscale().getHiZRt(), TextureUsageBit::SAMPLED_COMPUTE, HIZ_HALF_DEPTH});
 			pass.newConsumer({m_runCtx.m_rts[0], TextureUsageBit::IMAGE_COMPUTE_WRITE});
 			pass.newProducer({m_runCtx.m_rts[0], TextureUsageBit::IMAGE_COMPUTE_WRITE});
 
@@ -225,8 +223,7 @@ void Ssao::populateRenderGraph(RenderingContext& ctx)
 				pass.newConsumer({m_r->getGBuffer().getColorRt(2), TextureUsageBit::SAMPLED_FRAGMENT});
 			}
 
-			pass.newConsumer(
-				{m_r->getDepthDownscale().getHiZRt(), TextureUsageBit::SAMPLED_FRAGMENT, HIZ_QUARTER_DEPTH});
+			pass.newConsumer({m_r->getDepthDownscale().getHiZRt(), TextureUsageBit::SAMPLED_FRAGMENT, HIZ_HALF_DEPTH});
 			pass.newConsumer({m_runCtx.m_rts[0], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 			pass.newProducer({m_runCtx.m_rts[0], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 

+ 3 - 5
src/anki/resource/CollisionResource.cpp

@@ -29,19 +29,18 @@ Error CollisionResource::load(const ResourceFilename& filename, Bool async)
 	ANKI_CHECK(collEl.getChildElement("value", valEl));
 
 	PhysicsWorld& physics = getManager().getPhysicsWorld();
-	PhysicsCollisionShapeInitInfo csInit;
 
 	if(type == "sphere")
 	{
 		F64 tmp;
 		ANKI_CHECK(valEl.getNumber(tmp));
-		m_physicsShape = physics.newInstance<PhysicsSphere>(csInit, tmp);
+		m_physicsShape = physics.newInstance<PhysicsSphere>(tmp);
 	}
 	else if(type == "box")
 	{
 		Vec3 extend;
 		ANKI_CHECK(valEl.getVec3(extend));
-		m_physicsShape = physics.newInstance<PhysicsBox>(csInit, extend);
+		m_physicsShape = physics.newInstance<PhysicsBox>(extend);
 	}
 	else if(type == "staticMesh")
 	{
@@ -55,8 +54,7 @@ Error CollisionResource::load(const ResourceFilename& filename, Bool async)
 		DynamicArrayAuto<Vec3> positions(getTempAllocator());
 		ANKI_CHECK(loader.storeIndicesAndPosition(indices, positions));
 
-		m_physicsShape = physics.newInstance<PhysicsTriangleSoup>(
-			csInit, &positions[0], sizeof(Vec3), &indices[0], indices.getSize());
+		m_physicsShape = physics.newInstance<PhysicsTriangleSoup>(positions, indices);
 	}
 	else
 	{

+ 1 - 0
src/anki/resource/ShaderProgramResource.cpp

@@ -30,6 +30,7 @@ Bool ShaderProgramResourceInputVariable::evalPreproc(ConstWeakArray<te_variable>
 		ANKI_RESOURCE_LOGF("Wrong result of the expression: %s", m_preprocExpr.cstr());
 	}
 
+	te_free(n);
 	return evalOut != 0.0;
 }
 

+ 13 - 8
src/anki/scene/BodyNode.cpp

@@ -6,6 +6,7 @@
 #include <anki/scene/BodyNode.h>
 #include <anki/scene/components/BodyComponent.h>
 #include <anki/scene/components/MoveComponent.h>
+#include <anki/scene/components/JointComponent.h>
 #include <anki/scene/SceneGraph.h>
 #include <anki/physics/PhysicsWorld.h>
 #include <anki/resource/ResourceManager.h>
@@ -22,15 +23,15 @@ public:
 	{
 	}
 
-	ANKI_USE_RESULT Error update(SceneNode& node, Second, Second, Bool& updated) override
+	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) override
 	{
 		updated = false;
 
-		BodyComponent& bodyc = node.getComponent<BodyComponent>();
+		BodyComponent& bodyc = m_node->getComponent<BodyComponent>();
 
-		if(bodyc.getTimestamp() == node.getGlobalTimestamp())
+		if(bodyc.getTimestamp() == m_node->getGlobalTimestamp())
 		{
-			MoveComponent& move = node.getComponent<MoveComponent>();
+			MoveComponent& move = m_node->getComponent<MoveComponent>();
 			move.setLocalTransform(bodyc.getTransform());
 		}
 
@@ -54,18 +55,22 @@ Error BodyNode::init(const CString& resourceFname)
 
 	// Create body
 	PhysicsBodyInitInfo init;
-	init.m_mass = 1.0;
+	init.m_mass = 1.0f;
 	init.m_shape = m_rsrc->getShape();
 	m_body = getSceneGraph().getPhysicsWorld().newInstance<PhysicsBody>(init);
+	m_body->setUserData(this);
+
+	// Joint component
+	newComponent<JointComponent>();
 
 	// Body component
-	newComponent<BodyComponent>(this, m_body);
+	newComponent<BodyComponent>(m_body);
 
 	// Feedback component
-	newComponent<BodyFeedbackComponent>(this);
+	newComponent<BodyFeedbackComponent>();
 
 	// Move component
-	newComponent<MoveComponent>(this);
+	newComponent<MoveComponent>(MoveComponentFlag::IGNORE_PARENT_TRANSFORM);
 
 	return Error::NONE;
 }

+ 13 - 13
src/anki/scene/CameraNode.cpp

@@ -14,19 +14,19 @@ namespace anki
 class CameraMoveFeedbackComponent : public SceneComponent
 {
 public:
-	CameraMoveFeedbackComponent(CameraNode* node)
+	CameraMoveFeedbackComponent(SceneNode* node)
 		: SceneComponent(SceneComponentType::NONE, node)
 	{
 	}
 
-	ANKI_USE_RESULT Error update(SceneNode& node, Second, Second, Bool& updated) override
+	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) override
 	{
 		updated = false;
 
-		MoveComponent& move = node.getComponent<MoveComponent>();
+		MoveComponent& move = m_node->getComponent<MoveComponent>();
 		if(move.getTimestamp() == getGlobalTimestamp())
 		{
-			CameraNode& cam = static_cast<CameraNode&>(node);
+			CameraNode& cam = *static_cast<CameraNode*>(m_node);
 			cam.onMoveComponentUpdate(move);
 		}
 
@@ -38,19 +38,19 @@ public:
 class CameraFrustumFeedbackComponent : public SceneComponent
 {
 public:
-	CameraFrustumFeedbackComponent(CameraNode* node)
+	CameraFrustumFeedbackComponent(SceneNode* node)
 		: SceneComponent(SceneComponentType::NONE, node)
 	{
 	}
 
-	ANKI_USE_RESULT Error update(SceneNode& node, Second, Second, Bool& updated) override
+	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) override
 	{
 		updated = false;
 
-		FrustumComponent& fr = node.getComponent<FrustumComponent>();
+		FrustumComponent& fr = m_node->getComponent<FrustumComponent>();
 		if(fr.getTimestamp() == getGlobalTimestamp())
 		{
-			CameraNode& cam = static_cast<CameraNode&>(node);
+			CameraNode& cam = *static_cast<CameraNode*>(m_node);
 			cam.onFrustumComponentUpdate(fr);
 		}
 
@@ -67,13 +67,13 @@ CameraNode::CameraNode(SceneGraph* scene, Type type, CString name)
 Error CameraNode::init(Frustum* frustum)
 {
 	// Move component
-	newComponent<MoveComponent>(this);
+	newComponent<MoveComponent>();
 
 	// Feedback component
-	newComponent<CameraMoveFeedbackComponent>(this);
+	newComponent<CameraMoveFeedbackComponent>();
 
 	// Frustum component
-	FrustumComponent* frc = newComponent<FrustumComponent>(this, frustum);
+	FrustumComponent* frc = newComponent<FrustumComponent>(frustum);
 	frc->setEnabledVisibilityTests(
 		FrustumComponentVisibilityTestFlag::RENDER_COMPONENTS | FrustumComponentVisibilityTestFlag::LIGHT_COMPONENTS
 		| FrustumComponentVisibilityTestFlag::LENS_FLARE_COMPONENTS
@@ -82,10 +82,10 @@ Error CameraNode::init(Frustum* frustum)
 		| FrustumComponentVisibilityTestFlag::EARLY_Z);
 
 	// Feedback component #2
-	newComponent<CameraFrustumFeedbackComponent>(this);
+	newComponent<CameraFrustumFeedbackComponent>();
 
 	// Spatial component
-	newComponent<SpatialComponent>(this, frustum);
+	newComponent<SpatialComponent>(frustum);
 
 	return Error::NONE;
 }

+ 2 - 0
src/anki/scene/Common.h

@@ -22,6 +22,8 @@ class SceneGraph;
 class SceneNode;
 class MoveComponent;
 class DecalComponent;
+class EventManager;
+class Event;
 
 /// @addtogroup Scene
 /// @{

+ 63 - 35
src/anki/renderer/DebugDrawer.cpp → src/anki/scene/DebugDrawer.cpp

@@ -3,75 +3,103 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#include <anki/renderer/DebugDrawer.h>
-#include <anki/renderer/Renderer.h>
-#include <anki/resource/TextureResource.h>
-#include <anki/renderer/Renderer.h>
-#include <anki/renderer/FinalComposite.h>
-#include <anki/renderer/GBuffer.h>
-#include <anki/util/Logger.h>
+#include <anki/scene/DebugDrawer.h>
+#include <anki/resource/ResourceManager.h>
+#include <anki/renderer/RenderQueue.h>
+#include <anki/core/StagingGpuMemoryManager.h>
 #include <anki/physics/PhysicsWorld.h>
 #include <anki/Collision.h>
-#include <anki/Scene.h>
 
 namespace anki
 {
 
-Error DebugDrawer::init(Renderer* r)
+Error DebugDrawer::init(ResourceManager* rsrcManager)
 {
-	ANKI_ASSERT(r);
-	m_r = r;
+	ANKI_ASSERT(rsrcManager);
 
 	// Create the prog and shaders
-	ANKI_CHECK(r->getResourceManager().loadResource("shaders/SceneDebug.glslp", m_prog));
-	ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
-	consts.add("INSTANCE_COUNT", 1u);
-	ShaderProgramResourceMutationInitList<2> mutations(m_prog);
-	mutations.add("COLOR_TEXTURE", 0).add("DITHERED_DEPTH_TEST", 0);
-
-	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(mutations.get(), consts.get(), variant);
-	m_grProg = variant->getProgram();
+	ANKI_CHECK(rsrcManager->loadResource("shaders/SceneDebug.glslp", m_prog));
 
 	return Error::NONE;
 }
 
+void DebugDrawer::prepareFrame(RenderQueueDrawContext* ctx)
+{
+	m_ctx = ctx;
+}
+
 void DebugDrawer::flush()
 {
-	if(m_cachedPositionCount > 0)
+	if(m_cachedPositionCount == 0)
 	{
-		m_cmdb->bindShaderProgram(m_grProg);
+		return;
+	}
+
+	CommandBufferPtr& cmdb = m_ctx->m_commandBuffer;
 
-		// Set vertex state
+	// Bind program
+	{
+		ShaderProgramResourceMutationInitList<2> mutators(m_prog);
+		mutators.add("COLOR_TEXTURE", 0);
+		mutators.add(
+			"DITHERED_DEPTH_TEST", m_ctx->m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON));
+		ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
+		consts.add("INSTANCE_COUNT", 1u);
+		const ShaderProgramResourceVariant* variant;
+		m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
+		cmdb->bindShaderProgram(variant->getProgram());
+	}
+
+	// Set vertex state
+	{
 		const U32 size = m_cachedPositionCount * sizeof(Vec3);
 
 		StagingGpuMemoryToken token;
-		void* mem = m_r->getStagingGpuMemoryManager().allocateFrame(size, StagingGpuMemoryType::VERTEX, token);
+		void* mem = m_ctx->m_stagingGpuAllocator->allocateFrame(size, StagingGpuMemoryType::VERTEX, token);
 		memcpy(mem, &m_cachedPositions[0], size);
 
-		m_cmdb->bindVertexBuffer(0, token.m_buffer, token.m_offset, sizeof(Vec3));
-		m_cmdb->setVertexAttribute(0, 0, Format::R32G32B32_SFLOAT, 0);
+		cmdb->bindVertexBuffer(0, token.m_buffer, token.m_offset, sizeof(Vec3));
+		cmdb->setVertexAttribute(0, 0, Format::R32G32B32_SFLOAT, 0);
+	}
 
-		// Set uniform state
+	// Set uniform state
+	{
 		struct Uniforms
 		{
 			Mat4 m_mvp;
 			Vec4 m_color;
 		};
 
+		StagingGpuMemoryToken token;
 		Uniforms* uniforms = static_cast<Uniforms*>(
-			m_r->getStagingGpuMemoryManager().allocateFrame(sizeof(Uniforms), StagingGpuMemoryType::UNIFORM, token));
+			m_ctx->m_stagingGpuAllocator->allocateFrame(sizeof(Uniforms), StagingGpuMemoryType::UNIFORM, token));
 		uniforms->m_mvp = m_mvpMat;
 		uniforms->m_color = m_crntCol;
 
-		m_cmdb->bindUniformBuffer(1, 0, token.m_buffer, token.m_offset, token.m_range);
+		cmdb->bindUniformBuffer(1, 0, token.m_buffer, token.m_offset, token.m_range);
+	}
+
+	const Bool enableDepthTest = m_ctx->m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
+	if(enableDepthTest)
+	{
+		cmdb->setDepthCompareOperation(CompareOperation::LESS);
+	}
+	else
+	{
+		cmdb->setDepthCompareOperation(CompareOperation::ALWAYS);
+	}
 
-		// Draw
-		m_cmdb->drawArrays(m_topology, m_cachedPositionCount);
+	// Draw
+	cmdb->drawArrays(m_topology, m_cachedPositionCount);
 
-		// Other
-		m_cachedPositionCount = 0;
+	// Restore state
+	if(!enableDepthTest)
+	{
+		cmdb->setDepthCompareOperation(CompareOperation::LESS);
 	}
+
+	// Other
+	m_cachedPositionCount = 0;
 }
 
 void DebugDrawer::drawLine(const Vec3& from, const Vec3& to, const Vec4& color)
@@ -318,11 +346,11 @@ void CollisionDebugDrawer::visit(const ConvexHullShape& hull)
 	}
 }
 
-void PhysicsDebugDrawer::drawLines(const Vec3* lines, const U32 linesCount, const Vec4& color)
+void PhysicsDebugDrawer::drawLines(const Vec3* lines, const U32 vertCount, const Vec4& color)
 {
 	m_dbg->setTopology(PrimitiveTopology::LINES);
 	m_dbg->setColor(color);
-	for(U i = 0; i < linesCount * 2; ++i)
+	for(U i = 0; i < vertCount; ++i)
 	{
 		m_dbg->pushBackVertex(lines[i]);
 	}

+ 9 - 11
src/anki/renderer/DebugDrawer.h → src/anki/scene/DebugDrawer.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/renderer/Common.h>
+#include <anki/scene/Common.h>
 #include <anki/Math.h>
 #include <anki/Gr.h>
 #include <anki/collision/CollisionShape.h>
@@ -16,6 +16,9 @@
 namespace anki
 {
 
+// Forward
+class RenderQueueDrawContext;
+
 /// @addtogroup renderer
 /// @{
 
@@ -23,17 +26,14 @@ namespace anki
 class DebugDrawer
 {
 public:
-	ANKI_USE_RESULT Error init(Renderer* r);
+	ANKI_USE_RESULT Error init(ResourceManager* rsrcManager);
 
-	void prepareFrame(CommandBufferPtr& cmdb)
-	{
-		m_cmdb = cmdb;
-	}
+	void prepareFrame(RenderQueueDrawContext* ctx);
 
 	void finishFrame()
 	{
 		flush();
-		m_cmdb.reset(nullptr);
+		m_ctx = nullptr;
 	}
 
 	void drawGrid();
@@ -85,11 +85,9 @@ public:
 	}
 
 private:
-	Renderer* m_r;
 	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
 
-	CommandBufferPtr m_cmdb;
+	RenderQueueDrawContext* m_ctx = nullptr;
 
 	// State
 	Mat4 m_mMat = Mat4::getIdentity();
@@ -146,7 +144,7 @@ public:
 	{
 	}
 
-	void drawLines(const Vec3* lines, const U32 linesCount, const Vec4& color) final;
+	void drawLines(const Vec3* lines, const U32 vertCount, const Vec4& color) final;
 
 private:
 	DebugDrawer* m_dbg; ///< The debug drawer

+ 13 - 13
src/anki/scene/DecalNode.cpp

@@ -20,15 +20,15 @@ public:
 	{
 	}
 
-	ANKI_USE_RESULT Error update(SceneNode& node, Second, Second, Bool& updated) override
+	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) override
 	{
 		updated = false;
 
-		MoveComponent& movec = node.getComponent<MoveComponent>();
+		MoveComponent& movec = m_node->getComponent<MoveComponent>();
 
-		if(movec.getTimestamp() == node.getGlobalTimestamp())
+		if(movec.getTimestamp() == m_node->getGlobalTimestamp())
 		{
-			static_cast<DecalNode&>(node).onMove(movec);
+			static_cast<DecalNode*>(m_node)->onMove(movec);
 		}
 
 		return Error::NONE;
@@ -44,15 +44,15 @@ public:
 	{
 	}
 
-	ANKI_USE_RESULT Error update(SceneNode& node, Second, Second, Bool& updated) override
+	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) override
 	{
 		updated = false;
 
-		DecalComponent& decalc = node.getComponent<DecalComponent>();
+		DecalComponent& decalc = m_node->getComponent<DecalComponent>();
 
-		if(decalc.getTimestamp() == node.getGlobalTimestamp())
+		if(decalc.getTimestamp() == m_node->getGlobalTimestamp())
 		{
-			static_cast<DecalNode&>(node).onDecalUpdated();
+			static_cast<DecalNode*>(m_node)->onDecalUpdated();
 		}
 
 		return Error::NONE;
@@ -65,11 +65,11 @@ DecalNode::~DecalNode()
 
 Error DecalNode::init()
 {
-	newComponent<MoveComponent>(this);
-	newComponent<DecalMoveFeedbackComponent>(this);
-	DecalComponent* decalc = newComponent<DecalComponent>(this);
-	newComponent<DecalShapeFeedbackComponent>(this);
-	newComponent<SpatialComponent>(this, &decalc->getBoundingVolume());
+	newComponent<MoveComponent>();
+	newComponent<DecalMoveFeedbackComponent>();
+	DecalComponent* decalc = newComponent<DecalComponent>();
+	newComponent<DecalShapeFeedbackComponent>();
+	newComponent<SpatialComponent>(&decalc->getBoundingVolume());
 
 	return Error::NONE;
 }

+ 1 - 4
src/anki/scene/Forward.h

@@ -14,10 +14,9 @@ class FrustumComponent;
 class InstanceComponent;
 class MoveComponent;
 class RenderComponent;
+class MaterialRenderComponent;
 class SpatialComponent;
 class DecalComponent;
-class PortalComponent;
-class SectorComponent;
 class ReflectionProxyComponent;
 class ReflectionProbeComponent;
 
@@ -27,8 +26,6 @@ class LightNode;
 class PointLightNode;
 class SpotLightNode;
 class CameraNode;
-class SectorNode;
-class PortalNode;
 
 // Other
 class SceneGraph;

+ 16 - 16
src/anki/scene/LightNode.cpp

@@ -21,13 +21,13 @@ public:
 	{
 	}
 
-	Error update(SceneNode& node, Second, Second, Bool& updated) override
+	Error update(Second, Second, Bool& updated) override
 	{
 		updated = false;
-		LightNode& lnode = static_cast<LightNode&>(node);
+		LightNode& lnode = *static_cast<LightNode*>(m_node);
 
-		const MoveComponent& move = node.getComponentAt<MoveComponent>(0);
-		if(move.getTimestamp() == node.getGlobalTimestamp())
+		const MoveComponent& move = m_node->getComponentAt<MoveComponent>(0);
+		if(move.getTimestamp() == m_node->getGlobalTimestamp())
 		{
 			// Move updated
 			lnode.onMoveUpdate(move);
@@ -46,13 +46,13 @@ public:
 	{
 	}
 
-	Error update(SceneNode& node, Second, Second, Bool& updated) override
+	Error update(Second, Second, Bool& updated) override
 	{
 		updated = false;
-		LightNode& lnode = static_cast<LightNode&>(node);
+		LightNode& lnode = *static_cast<LightNode*>(m_node);
 
-		LightComponent& light = node.getComponentAt<LightComponent>(getIndex() - 1);
-		if(light.getTimestamp() == node.getGlobalTimestamp())
+		LightComponent& light = m_node->getComponentAt<LightComponent>(getIndex() - 1);
+		if(light.getTimestamp() == m_node->getGlobalTimestamp())
 		{
 			// Shape updated
 			lnode.onShapeUpdate(light);
@@ -74,19 +74,19 @@ LightNode::~LightNode()
 Error LightNode::init(LightComponentType type, CollisionShape* shape)
 {
 	// Move component
-	newComponent<MoveComponent>(this);
+	newComponent<MoveComponent>();
 
 	// Feedback component
-	newComponent<MovedFeedbackComponent>(this);
+	newComponent<MovedFeedbackComponent>();
 
 	// Light component
-	newComponent<LightComponent>(this, type);
+	newComponent<LightComponent>(type);
 
 	// Feedback component
-	newComponent<LightChangedFeedbackComponent>(this);
+	newComponent<LightChangedFeedbackComponent>();
 
 	// Spatial component
-	newComponent<SpatialComponent>(this, shape);
+	newComponent<SpatialComponent>(shape);
 
 	return Error::NONE;
 }
@@ -149,7 +149,7 @@ Error LightNode::loadLensFlare(const CString& filename)
 {
 	ANKI_ASSERT(tryGetComponent<LensFlareComponent>() == nullptr);
 
-	LensFlareComponent* flareComp = newComponent<LensFlareComponent>(this);
+	LensFlareComponent* flareComp = newComponent<LensFlareComponent>();
 
 	Error err = flareComp->init(filename);
 	if(err)
@@ -244,7 +244,7 @@ Error PointLightNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 			trf.setOrigin(origin);
 			m_shadowData[i].m_frustum.resetTransform(trf);
 
-			newComponent<FrustumComponent>(this, &m_shadowData[i].m_frustum);
+			newComponent<FrustumComponent>(&m_shadowData[i].m_frustum);
 		}
 	}
 
@@ -262,7 +262,7 @@ Error SpotLightNode::init()
 {
 	ANKI_CHECK(LightNode::init(LightComponentType::SPOT, &m_frustum));
 
-	FrustumComponent* fr = newComponent<FrustumComponent>(this, &m_frustum);
+	FrustumComponent* fr = newComponent<FrustumComponent>(&m_frustum);
 	fr->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE);
 
 	return Error::NONE;

+ 0 - 30
src/anki/scene/Misc.h

@@ -1,30 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <anki/scene/components/SpatialComponent.h>
-
-namespace anki
-{
-
-/// @addtogroup scene
-/// @{
-
-/// A class that holds spatial information and implements the SpatialComponent virtuals. You just need to update the
-/// OBB manually
-class ObbSpatialComponent : public SpatialComponent
-{
-public:
-	Obb m_obb;
-
-	ObbSpatialComponent(SceneNode* node)
-		: SpatialComponent(node, &m_obb)
-	{
-	}
-};
-/// @}
-
-} // end namespace anki

+ 30 - 29
src/anki/scene/ModelNode.cpp

@@ -7,7 +7,7 @@
 #include <anki/scene/SceneGraph.h>
 #include <anki/scene/components/BodyComponent.h>
 #include <anki/scene/components/SkinComponent.h>
-#include <anki/scene/Misc.h>
+#include <anki/scene/components/RenderComponent.h>
 #include <anki/resource/ModelResource.h>
 #include <anki/resource/ResourceManager.h>
 #include <anki/resource/SkeletonResource.h>
@@ -17,7 +17,7 @@ namespace anki
 {
 
 /// Render component implementation.
-class ModelPatchNode::MRenderComponent : public RenderComponent
+class ModelPatchNode::MRenderComponent : public MaterialRenderComponent
 {
 public:
 	const ModelPatchNode& getNode() const
@@ -25,8 +25,8 @@ public:
 		return static_cast<const ModelPatchNode&>(getSceneNode());
 	}
 
-	MRenderComponent(ModelPatchNode* node)
-		: RenderComponent(node, node->m_modelPatch->getMaterial())
+	MRenderComponent(SceneNode* node)
+		: MaterialRenderComponent(node, static_cast<ModelPatchNode*>(node)->m_modelPatch->getMaterial())
 	{
 	}
 
@@ -52,10 +52,10 @@ Error ModelPatchNode::init(const ModelPatch* modelPatch, U idx, const ModelNode&
 	m_modelPatch = modelPatch;
 
 	// Spatial component
-	newComponent<SpatialComponent>(this, &m_obb);
+	newComponent<SpatialComponent>(&m_obb);
 
 	// Render component
-	newComponent<MRenderComponent>(this);
+	newComponent<MRenderComponent>();
 
 	// Merge key
 	Array<U64, 2> toHash;
@@ -111,11 +111,11 @@ void ModelPatchNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<vo
 		trfs[i] = Mat4(self2.getParent()->getComponentAt<MoveComponent>(0).getWorldTransform());
 	}
 
-	self.getComponentAt<RenderComponent>(1).allocateAndSetupUniforms(
-		self.m_modelPatch->getMaterial()->getDescriptorSetIndex(),
-		ctx,
-		ConstWeakArray<Mat4>(&trfs[0], userData.getSize()),
-		*ctx.m_stagingGpuAllocator);
+	static_cast<const MaterialRenderComponent&>(self.getComponentAt<RenderComponent>(1))
+		.allocateAndSetupUniforms(self.m_modelPatch->getMaterial()->getDescriptorSetIndex(),
+			ctx,
+			ConstWeakArray<Mat4>(&trfs[0], userData.getSize()),
+			*ctx.m_stagingGpuAllocator);
 
 	// Draw
 	cmdb->drawElements(PrimitiveTopology::TRIANGLES,
@@ -135,14 +135,14 @@ public:
 	{
 	}
 
-	ANKI_USE_RESULT Error update(SceneNode& node, Second, Second, Bool& updated) override
+	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) override
 	{
 		updated = false;
 
-		const MoveComponent& move = node.getComponent<MoveComponent>();
-		if(move.getTimestamp() == node.getGlobalTimestamp())
+		const MoveComponent& move = m_node->getComponent<MoveComponent>();
+		if(move.getTimestamp() == m_node->getGlobalTimestamp())
 		{
-			ModelNode& mnode = static_cast<ModelNode&>(node);
+			ModelNode& mnode = *static_cast<ModelNode*>(m_node);
 			mnode.onMoveComponentUpdate(move);
 		}
 
@@ -150,11 +150,11 @@ public:
 	}
 };
 
-class ModelNode::MRenderComponent : public RenderComponent
+class ModelNode::MRenderComponent : public MaterialRenderComponent
 {
 public:
-	MRenderComponent(ModelNode* node)
-		: RenderComponent(node, node->m_model->getModelPatches()[0]->getMaterial())
+	MRenderComponent(SceneNode* node)
+		: MaterialRenderComponent(node, static_cast<ModelNode*>(node)->m_model->getModelPatches()[0]->getMaterial())
 	{
 	}
 
@@ -198,10 +198,10 @@ Error ModelNode::init(const CString& modelFname)
 		}
 
 		// Move component
-		newComponent<MoveComponent>(this);
+		newComponent<MoveComponent>();
 
 		// Feedback component
-		newComponent<MoveFeedbackComponent>(this);
+		newComponent<MoveFeedbackComponent>();
 	}
 	else
 	{
@@ -211,12 +211,12 @@ Error ModelNode::init(const CString& modelFname)
 
 		if(m_model->getSkeleton().isCreated())
 		{
-			newComponent<SkinComponent>(this, m_model->getSkeleton());
+			newComponent<SkinComponent>(m_model->getSkeleton());
 		}
-		newComponent<MoveComponent>(this);
-		newComponent<MoveFeedbackComponent>(this);
-		newComponent<SpatialComponent>(this, &m_obb);
-		newComponent<MRenderComponent>(this);
+		newComponent<MoveComponent>();
+		newComponent<MoveFeedbackComponent>();
+		newComponent<SpatialComponent>(&m_obb);
+		newComponent<MRenderComponent>();
 	}
 
 	ANKI_CHECK(getResourceManager().loadResource("shaders/SceneDebug.glslp", m_dbgProg));
@@ -300,10 +300,11 @@ void ModelNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*>
 			trfs[i] = Mat4(self2.getComponent<MoveComponent>().getWorldTransform());
 		}
 
-		self.getComponent<RenderComponent>().allocateAndSetupUniforms(patch->getMaterial()->getDescriptorSetIndex(),
-			ctx,
-			ConstWeakArray<Mat4>(&trfs[0], userData.getSize()),
-			*ctx.m_stagingGpuAllocator);
+		static_cast<const MaterialRenderComponent&>(self.getComponent<RenderComponent>())
+			.allocateAndSetupUniforms(patch->getMaterial()->getDescriptorSetIndex(),
+				ctx,
+				ConstWeakArray<Mat4>(&trfs[0], userData.getSize()),
+				*ctx.m_stagingGpuAllocator);
 
 		// Bones storage
 		if(self.m_model->getSkeleton())

+ 1 - 1
src/anki/scene/ModelNode.h

@@ -6,11 +6,11 @@
 #pragma once
 
 #include <anki/scene/SceneNode.h>
-#include <anki/scene/components/RenderComponent.h>
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
 #include <anki/resource/ModelResource.h>
 #include <anki/collision/Obb.h>
+#include <anki/renderer/RenderQueue.h>
 
 namespace anki
 {

+ 7 - 7
src/anki/scene/OccluderNode.cpp

@@ -21,14 +21,14 @@ public:
 	{
 	}
 
-	ANKI_USE_RESULT Error update(SceneNode& node, Second, Second, Bool& updated) override
+	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) override
 	{
 		updated = false;
 
-		MoveComponent& move = node.getComponent<MoveComponent>();
-		if(move.getTimestamp() == node.getGlobalTimestamp())
+		MoveComponent& move = m_node->getComponent<MoveComponent>();
+		if(move.getTimestamp() == m_node->getGlobalTimestamp())
 		{
-			OccluderNode& mnode = static_cast<OccluderNode&>(node);
+			OccluderNode& mnode = *static_cast<OccluderNode*>(m_node);
 			mnode.onMoveComponentUpdate(move);
 		}
 
@@ -62,9 +62,9 @@ Error OccluderNode::init(const CString& meshFname)
 	}
 
 	// Create the components
-	newComponent<MoveComponent>(this);
-	newComponent<OccluderMoveFeedbackComponent>(this);
-	newComponent<OccluderComponent>(this);
+	newComponent<MoveComponent>();
+	newComponent<OccluderMoveFeedbackComponent>();
+	newComponent<OccluderComponent>();
 
 	return Error::NONE;
 }

+ 21 - 18
src/anki/scene/ParticleEmitterNode.cpp

@@ -5,7 +5,9 @@
 
 #include <anki/scene/ParticleEmitterNode.h>
 #include <anki/scene/SceneGraph.h>
-#include <anki/scene/Misc.h>
+#include <anki/scene/components/MoveComponent.h>
+#include <anki/scene/components/SpatialComponent.h>
+#include <anki/scene/components/RenderComponent.h>
 #include <anki/resource/ModelResource.h>
 #include <anki/resource/ResourceManager.h>
 #include <anki/util/Functions.h>
@@ -162,7 +164,7 @@ void Particle::revive(const ParticleEmitterNode& pe,
 #endif
 
 /// The derived render component for particle emitters.
-class ParticleEmitterRenderComponent : public RenderComponent
+class ParticleEmitterRenderComponent : public MaterialRenderComponent
 {
 public:
 	const ParticleEmitterNode& getNode() const
@@ -170,8 +172,9 @@ public:
 		return static_cast<const ParticleEmitterNode&>(getSceneNode());
 	}
 
-	ParticleEmitterRenderComponent(ParticleEmitterNode* node)
-		: RenderComponent(node, node->m_particleEmitterResource->getMaterial())
+	ParticleEmitterRenderComponent(SceneNode* node)
+		: MaterialRenderComponent(
+			  node, static_cast<ParticleEmitterNode*>(node)->m_particleEmitterResource->getMaterial())
 	{
 	}
 
@@ -185,19 +188,19 @@ public:
 class MoveFeedbackComponent : public SceneComponent
 {
 public:
-	MoveFeedbackComponent(ParticleEmitterNode* node)
+	MoveFeedbackComponent(SceneNode* node)
 		: SceneComponent(SceneComponentType::NONE, node)
 	{
 	}
 
-	ANKI_USE_RESULT Error update(SceneNode& node, Second, Second, Bool& updated) override
+	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) override
 	{
 		updated = false; // Don't care about updates for this component
 
-		MoveComponent& move = node.getComponent<MoveComponent>();
-		if(move.getTimestamp() == node.getGlobalTimestamp())
+		MoveComponent& move = m_node->getComponent<MoveComponent>();
+		if(move.getTimestamp() == m_node->getGlobalTimestamp())
 		{
-			static_cast<ParticleEmitterNode&>(node).onMoveComponentUpdate(move);
+			static_cast<ParticleEmitterNode*>(m_node)->onMoveComponentUpdate(move);
 		}
 
 		return Error::NONE;
@@ -229,16 +232,16 @@ Error ParticleEmitterNode::init(const CString& filename)
 	ANKI_CHECK(getResourceManager().loadResource(filename, m_particleEmitterResource));
 
 	// Move component
-	newComponent<MoveComponent>(this);
+	newComponent<MoveComponent>();
 
 	// Move component feedback
-	newComponent<MoveFeedbackComponent>(this);
+	newComponent<MoveFeedbackComponent>();
 
 	// Spatial component
-	newComponent<SpatialComponent>(this, &m_obb);
+	newComponent<SpatialComponent>(&m_obb);
 
 	// Render component
-	newComponent<ParticleEmitterRenderComponent>(this);
+	newComponent<ParticleEmitterRenderComponent>();
 
 	// Other
 	m_obb.setCenter(Vec4(0.0));
@@ -304,11 +307,11 @@ void ParticleEmitterNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArr
 
 		// Uniforms
 		Array<Mat4, 1> trf = {{Mat4::getIdentity()}};
-		self.getComponent<RenderComponent>().allocateAndSetupUniforms(
-			self.m_particleEmitterResource->getMaterial()->getDescriptorSetIndex(),
-			ctx,
-			trf,
-			*ctx.m_stagingGpuAllocator);
+		static_cast<const MaterialRenderComponent&>(self.getComponent<RenderComponent>())
+			.allocateAndSetupUniforms(self.m_particleEmitterResource->getMaterial()->getDescriptorSetIndex(),
+				ctx,
+				trf,
+				*ctx.m_stagingGpuAllocator);
 
 		// Draw
 		cmdb->drawArrays(PrimitiveTopology::TRIANGLE_STRIP, 4, self.m_aliveParticlesCount, 0, 0);

+ 2 - 3
src/anki/scene/ParticleEmitterNode.h

@@ -6,10 +6,9 @@
 #pragma once
 
 #include <anki/scene/SceneNode.h>
-#include <anki/scene/components/MoveComponent.h>
-#include <anki/scene/components/SpatialComponent.h>
-#include <anki/scene/components/RenderComponent.h>
 #include <anki/resource/ParticleEmitterResource.h>
+#include <anki/renderer/RenderQueue.h>
+#include <anki/collision/Obb.h>
 
 namespace anki
 {

+ 82 - 0
src/anki/scene/PhysicsDebugNode.cpp

@@ -0,0 +1,82 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/scene/PhysicsDebugNode.h>
+#include <anki/resource/ResourceManager.h>
+#include <anki/scene/components/RenderComponent.h>
+#include <anki/scene/components/SpatialComponent.h>
+#include <anki/scene/SceneGraph.h>
+#include <anki/scene/DebugDrawer.h>
+
+namespace anki
+{
+
+/// Render component implementation.
+class PhysicsDebugNode::MyRenderComponent : public RenderComponent
+{
+public:
+	DebugDrawer m_dbgDrawer;
+	PhysicsDebugDrawer m_physDbgDrawer;
+
+	MyRenderComponent(SceneNode* node)
+		: RenderComponent(node)
+		, m_physDbgDrawer(&m_dbgDrawer)
+	{
+		m_castsShadow = false;
+		m_isForwardShading = false;
+	}
+
+	ANKI_USE_RESULT Error init()
+	{
+		return m_dbgDrawer.init(&getSceneGraph().getResourceManager());
+	}
+
+	void setupRenderableQueueElement(RenderableQueueElement& el) const override
+	{
+		el.m_callback = drawCallback;
+		el.m_mergeKey = 1;
+		el.m_userData = this;
+	}
+
+	/// RenderableQueueElement draw callback.
+	static void drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData)
+	{
+		static_cast<MyRenderComponent*>(userData[0])->draw(ctx);
+	}
+
+	/// Draw the world.
+	void draw(RenderQueueDrawContext& ctx)
+	{
+		if(ctx.m_debugDraw)
+		{
+			m_dbgDrawer.prepareFrame(&ctx);
+			m_dbgDrawer.setViewProjectionMatrix(ctx.m_viewProjectionMatrix);
+			m_dbgDrawer.setModelMatrix(Mat4::getIdentity());
+			m_physDbgDrawer.drawWorld(getSceneGraph().getPhysicsWorld());
+			m_dbgDrawer.finishFrame();
+		}
+	}
+};
+
+PhysicsDebugNode::~PhysicsDebugNode()
+{
+}
+
+Error PhysicsDebugNode::init()
+{
+	MyRenderComponent* rcomp = newComponent<MyRenderComponent>();
+	ANKI_CHECK(rcomp->init());
+
+	ObbSpatialComponent* scomp = newComponent<ObbSpatialComponent>();
+	Vec3 center = (getSceneGraph().getSceneMax() + getSceneGraph().getSceneMin()) / 2.0f;
+	scomp->m_obb.setCenter(center.xyz0());
+	scomp->m_obb.setExtend((getSceneGraph().getSceneMax() - center).xyz0());
+	scomp->m_obb.setRotation(Mat3x4::getIdentity());
+	scomp->setSpatialOrigin(Vec4(0.0f));
+
+	return Error::NONE;
+}
+
+} // end namespace anki

+ 30 - 0
src/anki/scene/PhysicsDebugNode.h

@@ -0,0 +1,30 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/scene/SceneNode.h>
+
+namespace anki
+{
+
+/// An always visible node that debug draws primitives of the physics world.
+class PhysicsDebugNode : public SceneNode
+{
+public:
+	PhysicsDebugNode(SceneGraph* scene, CString name)
+		: SceneNode(scene, name)
+	{
+	}
+
+	~PhysicsDebugNode();
+
+	ANKI_USE_RESULT Error init();
+
+private:
+	class MyRenderComponent;
+};
+
+} // end namespace anki

+ 15 - 14
src/anki/scene/PlayerNode.cpp

@@ -23,19 +23,19 @@ public:
 	{
 	}
 
-	Error update(SceneNode& node, Second, Second, Bool& updated) override
+	Error update(Second, Second, Bool& updated) override
 	{
 		updated = false;
 
-		PlayerControllerComponent& playerc = node.getComponent<PlayerControllerComponent>();
-		const Input& in = node.getSceneGraph().getInput();
+		PlayerControllerComponent& playerc = m_node->getComponent<PlayerControllerComponent>();
+		const Input& in = m_node->getSceneGraph().getInput();
 		const F32 ang = toRad(7.0);
 
 		F32 y = in.getMousePosition().y();
 		F32 x = in.getMousePosition().x();
-		if(playerc.getTimestamp() == node.getGlobalTimestamp() || y != 0.0 || x != 0.0)
+		if(playerc.getTimestamp() == m_node->getGlobalTimestamp() || y != 0.0 || x != 0.0)
 		{
-			MoveComponent& move = node.getComponent<MoveComponent>();
+			MoveComponent& move = m_node->getComponent<MoveComponent>();
 
 			// Set origin
 			Vec4 origin = playerc.getTransform().getOrigin();
@@ -69,15 +69,15 @@ public:
 	{
 	}
 
-	Error update(SceneNode& node, Second, Second, Bool& updated) override
+	Error update(Second, Second, Bool& updated) override
 	{
 		updated = false;
 
-		PlayerControllerComponent& playerc = node.getComponent<PlayerControllerComponent>();
-		MoveComponent& move = node.getComponent<MoveComponent>();
-		const Input& in = node.getSceneGraph().getInput();
+		PlayerControllerComponent& playerc = m_node->getComponent<PlayerControllerComponent>();
+		MoveComponent& move = m_node->getComponent<MoveComponent>();
+		const Input& in = m_node->getSceneGraph().getInput();
 
-		const F32 speed = 3.5;
+		const F32 speed = 0.5;
 
 		Vec4 moveVec(0.0);
 		if(in.getKey(KeyCode::W))
@@ -125,18 +125,19 @@ Error PlayerNode::init(const Vec4& position)
 	PhysicsPlayerControllerInitInfo init;
 	init.m_position = position;
 	m_player = getSceneGraph().getPhysicsWorld().newInstance<PhysicsPlayerController>(init);
+	m_player->setUserData(this);
 
 	// Player controller component
-	newComponent<PlayerControllerComponent>(this, m_player);
+	newComponent<PlayerControllerComponent>(m_player);
 
 	// Feedback component
-	newComponent<PlayerNodeFeedbackComponent>(this);
+	newComponent<PlayerNodeFeedbackComponent>();
 
 	// Move component
-	newComponent<MoveComponent>(this);
+	newComponent<MoveComponent>();
 
 	// Feedback component #2
-	newComponent<PlayerNodeFeedbackComponent2>(this);
+	newComponent<PlayerNodeFeedbackComponent2>();
 
 	return Error::NONE;
 }

+ 9 - 9
src/anki/scene/ReflectionProbeNode.cpp

@@ -26,15 +26,15 @@ public:
 	{
 	}
 
-	Error update(SceneNode& node, Second, Second, Bool& updated) override
+	Error update(Second, Second, Bool& updated) override
 	{
 		updated = false;
 
-		MoveComponent& move = node.getComponent<MoveComponent>();
-		if(move.getTimestamp() == node.getGlobalTimestamp())
+		MoveComponent& move = m_node->getComponent<MoveComponent>();
+		if(move.getTimestamp() == m_node->getGlobalTimestamp())
 		{
 			// Move updated
-			ReflectionProbeNode& dnode = static_cast<ReflectionProbeNode&>(node);
+			ReflectionProbeNode& dnode = *static_cast<ReflectionProbeNode*>(m_node);
 			dnode.onMoveUpdate(move);
 		}
 
@@ -49,10 +49,10 @@ ReflectionProbeNode::~ReflectionProbeNode()
 Error ReflectionProbeNode::init(const Vec4& aabbMinLSpace, const Vec4& aabbMaxLSpace)
 {
 	// Move component first
-	newComponent<MoveComponent>(this);
+	newComponent<MoveComponent>();
 
 	// Feedback component
-	newComponent<ReflectionProbeMoveFeedbackComponent>(this);
+	newComponent<ReflectionProbeMoveFeedbackComponent>();
 
 	// The frustum components
 	const F32 ang = toRad(90.0f);
@@ -81,7 +81,7 @@ Error ReflectionProbeNode::init(const Vec4& aabbMinLSpace, const Vec4& aabbMaxLS
 		m_cubeSides[i].m_frustum.setAll(ang, ang, zNear, EFFECTIVE_DISTANCE);
 		m_cubeSides[i].m_frustum.resetTransform(m_cubeSides[i].m_localTrf);
 
-		FrustumComponent* frc = newComponent<FrustumComponent>(this, &m_cubeSides[i].m_frustum);
+		FrustumComponent* frc = newComponent<FrustumComponent>(&m_cubeSides[i].m_frustum);
 
 		frc->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE);
 	}
@@ -91,10 +91,10 @@ Error ReflectionProbeNode::init(const Vec4& aabbMinLSpace, const Vec4& aabbMaxLS
 	m_aabbMaxLSpace = aabbMaxLSpace.xyz();
 	m_spatialAabb.setMin(aabbMinLSpace);
 	m_spatialAabb.setMax(aabbMaxLSpace);
-	newComponent<SpatialComponent>(this, &m_spatialAabb);
+	newComponent<SpatialComponent>(&m_spatialAabb);
 
 	// Reflection probe comp
-	ReflectionProbeComponent* reflc = newComponent<ReflectionProbeComponent>(this);
+	ReflectionProbeComponent* reflc = newComponent<ReflectionProbeComponent>();
 	reflc->setPosition(Vec4(0.0f));
 	reflc->setBoundingBox(aabbMinLSpace, aabbMaxLSpace);
 

+ 4 - 4
src/anki/scene/ReflectionProxyNode.cpp

@@ -22,15 +22,15 @@ public:
 	{
 	}
 
-	Error update(SceneNode& node, Second, Second, Bool& updated) final
+	Error update(Second, Second, Bool& updated) final
 	{
 		updated = false;
 
-		MoveComponent& move = node.getComponent<MoveComponent>();
-		if(move.getTimestamp() == node.getGlobalTimestamp())
+		MoveComponent& move = m_node->getComponent<MoveComponent>();
+		if(move.getTimestamp() == m_node->getGlobalTimestamp())
 		{
 			// Move updated
-			ReflectionProxyNode& dnode = static_cast<ReflectionProxyNode&>(node);
+			ReflectionProxyNode& dnode = *static_cast<ReflectionProxyNode*>(m_node);
 			dnode.onMoveUpdate(move);
 		}
 

+ 9 - 14
src/anki/scene/SceneGraph.cpp

@@ -5,8 +5,8 @@
 
 #include <anki/scene/SceneGraph.h>
 #include <anki/scene/CameraNode.h>
+#include <anki/scene/PhysicsDebugNode.h>
 #include <anki/scene/ModelNode.h>
-#include <anki/scene/SectorNode.h>
 #include <anki/scene/Octree.h>
 #include <anki/core/Trace.h>
 #include <anki/physics/PhysicsWorld.h>
@@ -57,12 +57,6 @@ SceneGraph::~SceneGraph()
 
 	deleteNodesMarkedForDeletion();
 
-	if(m_sectors)
-	{
-		m_alloc.deleteInstance(m_sectors);
-		m_sectors = nullptr;
-	}
-
 	if(m_octree)
 	{
 		m_alloc.deleteInstance(m_octree);
@@ -94,20 +88,22 @@ Error SceneGraph::init(AllocAlignedCallback allocCb,
 
 	m_earlyZDist = config.getNumber("scene.earlyZDistance");
 
-	ANKI_CHECK(m_events.create(this));
-
-	m_sectors = m_alloc.newInstance<SectorGroup>(this);
+	ANKI_CHECK(m_events.init(this));
 
 	m_maxReflectionProxyDistance = config.getNumber("scene.imageReflectionMaxDistance");
 
 	m_octree = m_alloc.newInstance<Octree>(m_alloc);
-	m_octree->init(Vec3(-1000.0f), Vec3(1000.0f), 5); // TODO
+	m_octree->init(m_sceneMin, m_sceneMax, 5); // TODO
 
 	// Init the default main camera
 	ANKI_CHECK(newSceneNode<PerspectiveCameraNode>("mainCamera", m_defaultMainCam));
 	m_defaultMainCam->setAll(toRad(60.0f), toRad(60.0f), 0.1f, 1000.0f);
 	m_mainCam = m_defaultMainCam;
 
+	// Create a special node for debugging the physics world
+	PhysicsDebugNode* pnode;
+	ANKI_CHECK(newSceneNode<PhysicsDebugNode>("_physicsDebugNode", pnode));
+
 	return Error::NONE;
 }
 
@@ -221,8 +217,7 @@ Error SceneGraph::update(Second prevUpdateTime, Second crntTime)
 	// Update
 	{
 		ANKI_TRACE_SCOPED_EVENT(SCENE_PHYSICS_UPDATE);
-		m_physics->updateAsync(crntTime - prevUpdateTime);
-		m_physics->waitUpdate();
+		m_physics->update(crntTime - prevUpdateTime);
 	}
 
 	{
@@ -268,7 +263,7 @@ Error SceneGraph::updateNode(Second prevTime, Second crntTime, SceneNode& node)
 	Timestamp componentTimestam = 0;
 	err = node.iterateComponents([&](SceneComponent& comp) -> Error {
 		Bool updated = false;
-		Error e = comp.updateReal(node, prevTime, crntTime, updated);
+		Error e = comp.updateReal(prevTime, crntTime, updated);
 		componentTimestam = max(componentTimestam, comp.getTimestamp());
 
 		return e;

+ 14 - 9
src/anki/scene/SceneGraph.h

@@ -12,7 +12,7 @@
 #include <anki/util/HighRezTimer.h>
 #include <anki/util/HashMap.h>
 #include <anki/core/App.h>
-#include <anki/event/EventManager.h>
+#include <anki/scene/events/EventManager.h>
 
 namespace anki
 {
@@ -22,7 +22,6 @@ class MainRenderer;
 class ResourceManager;
 class CameraNode;
 class Input;
-class SectorGroup;
 class ConfigSet;
 class PerspectiveCameraNode;
 class UpdateSceneNodesCtx;
@@ -167,6 +166,16 @@ public:
 		return m_stats;
 	}
 
+	const Vec3& getSceneMin() const
+	{
+		return m_sceneMin;
+	}
+
+	const Vec3& getSceneMax() const
+	{
+		return m_sceneMax;
+	}
+
 anki_internal:
 	ResourceManager& getResourceManager()
 	{
@@ -200,12 +209,6 @@ anki_internal:
 		return *m_input;
 	}
 
-	SectorGroup& getSectorGroup()
-	{
-		ANKI_ASSERT(m_sectors);
-		return *m_sectors;
-	}
-
 	F32 getMaxReflectionProxyDistance() const
 	{
 		ANKI_ASSERT(m_maxReflectionProxyDistance > 0.0);
@@ -258,10 +261,12 @@ private:
 	PerspectiveCameraNode* m_defaultMainCam = nullptr;
 
 	EventManager m_events;
-	SectorGroup* m_sectors;
 
 	Octree* m_octree = nullptr;
 
+	Vec3 m_sceneMin = {-1000.0f, -200.0f, -1000.0f};
+	Vec3 m_sceneMax = {1000.0f, 200.0f, 1000.0f};
+
 	Atomic<U32> m_objectsMarkedForDeletionCount;
 
 	F32 m_maxReflectionProxyDistance = 0.0;

+ 1 - 16
src/anki/scene/SceneNode.h

@@ -93,7 +93,6 @@ public:
 
 	ANKI_USE_RESULT Error frameUpdateComplete(Second prevUpdateTime, Second crntTime, Timestamp maxComponentTimestamp)
 	{
-		m_sectorVisitedBitset.unsetAll();
 		m_maxComponentTimestamp = maxComponentTimestamp;
 		if(m_components.getSize() > 0)
 		{
@@ -102,16 +101,6 @@ public:
 		return frameUpdate(prevUpdateTime, crntTime);
 	}
 
-	/// Inform if a sector has visited this node.
-	/// @return The previous value.
-	Bool fetchSetSectorVisited(U testId, Bool visited)
-	{
-		LockGuard<SpinLock> lock(m_sectorVisitedBitsetLock);
-		Bool prevValue = m_sectorVisitedBitset.get(testId);
-		m_sectorVisitedBitset.set(testId, visited);
-		return prevValue;
-	}
-
 	/// Iterate all components
 	template<typename Func>
 	ANKI_USE_RESULT Error iterateComponents(Func func) const
@@ -222,7 +211,7 @@ protected:
 	template<typename TComponent, typename... TArgs>
 	TComponent* newComponent(TArgs&&... args)
 	{
-		TComponent* comp = getSceneAllocator().newInstance<TComponent>(std::forward<TArgs>(args)...);
+		TComponent* comp = getSceneAllocator().newInstance<TComponent>(this, std::forward<TArgs>(args)...);
 		m_components.emplaceBack(getSceneAllocator(), comp);
 		return comp;
 	}
@@ -243,10 +232,6 @@ private:
 	String m_name; ///< A unique name
 	BitMask<Flag> m_flags;
 
-	/// A mask of bits for each test. If bit set then the node was visited by a sector.
-	BitSet<256> m_sectorVisitedBitset = {false};
-	SpinLock m_sectorVisitedBitsetLock;
-
 	U64 m_uuid;
 
 	Timestamp m_maxComponentTimestamp = 0;

+ 0 - 647
src/anki/scene/SectorNode.cpp

@@ -1,647 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <anki/scene/SectorNode.h>
-#include <anki/scene/components/SectorComponent.h>
-#include <anki/scene/components/SpatialComponent.h>
-#include <anki/scene/components/FrustumComponent.h>
-#include <anki/scene/components/MoveComponent.h>
-#include <anki/scene/SceneGraph.h>
-#include <anki/scene/SoftwareRasterizer.h>
-#include <anki/util/Logger.h>
-#include <anki/resource/ResourceManager.h>
-#include <anki/resource/MeshLoader.h>
-
-namespace anki
-{
-
-template<typename TFunc>
-static void iterateSceneSectors(SceneGraph& scene, TFunc func)
-{
-	scene.getSceneComponentLists().iterateComponents<SectorComponent>([&](SectorComponent& comp) {
-		SectorNode& s = static_cast<SectorNode&>(comp.getSceneNode());
-		if(func(s))
-		{
-			return;
-		}
-	});
-}
-
-template<typename TFunc>
-static void iterateScenePortals(SceneGraph& scene, TFunc func)
-{
-	scene.getSceneComponentLists().iterateComponents<PortalComponent>([&](PortalComponent& comp) {
-		PortalNode& s = static_cast<PortalNode&>(comp.getSceneNode());
-		if(func(s))
-		{
-			return;
-		}
-	});
-}
-
-PortalSectorBase::~PortalSectorBase()
-{
-	auto alloc = getSceneAllocator();
-
-	if(m_shape)
-	{
-		alloc.deleteInstance(m_shape);
-		m_shape = nullptr;
-	}
-
-	m_shapeStorageLSpace.destroy(alloc);
-	m_shapeStorageWSpace.destroy(alloc);
-	m_vertIndices.destroy(alloc);
-}
-
-Error PortalSectorBase::init(const CString& meshFname, Bool isSector)
-{
-	// Create move component
-	newComponent<MoveComponent>(this);
-
-	// Create portal or sector component
-	if(isSector)
-	{
-		newComponent<SectorComponent>(this);
-	}
-	else
-	{
-		newComponent<PortalComponent>(this);
-	}
-
-	// Load mesh
-	MeshLoader loader(&getSceneGraph().getResourceManager());
-	ANKI_CHECK(loader.load(meshFname));
-
-	DynamicArrayAuto<U32> indices(getSceneAllocator());
-	DynamicArrayAuto<Vec3> positions(getSceneAllocator());
-
-	ANKI_CHECK(loader.storeIndicesAndPosition(indices, positions));
-
-	// Convert Vec3 positions to Vec4
-	m_shapeStorageLSpace.create(getSceneAllocator(), positions.getSize());
-	m_shapeStorageWSpace.create(getSceneAllocator(), positions.getSize());
-
-	for(U i = 0; i < positions.getSize(); ++i)
-	{
-		m_shapeStorageLSpace[i] = Vec4(positions[i], 0.0f);
-	}
-
-	// Create shape
-	ConvexHullShape* hull = getSceneAllocator().newInstance<ConvexHullShape>();
-	m_shape = hull;
-	hull->initStorage(&m_shapeStorageWSpace[0], m_shapeStorageWSpace.getSize());
-	updateTransform(Transform::getIdentity());
-
-	// Store indices
-	m_vertIndices.create(getSceneAllocator(), indices.getSize());
-	for(U i = 0; i < indices.getSize(); ++i)
-	{
-		m_vertIndices[i] = indices[i];
-	}
-
-	return Error::NONE;
-}
-
-void PortalSectorBase::updateTransform(const Transform& trf)
-{
-	const U count = m_shapeStorageWSpace.getSize();
-	for(U i = 0; i < count; ++i)
-	{
-		m_shapeStorageWSpace[i] = trf.transform(m_shapeStorageLSpace[i]);
-	}
-
-	m_shape->computeAabb(m_aabb);
-}
-
-PortalNode::~PortalNode()
-{
-	auto alloc = getSceneAllocator();
-
-	// Remove from sectors
-	for(SectorNode* s : m_sectors)
-	{
-		s->tryRemovePortal(this);
-	}
-
-	m_sectors.destroy(getSceneAllocator());
-}
-
-Error PortalNode::init(const CString& meshFname)
-{
-	ANKI_CHECK(Base::init(meshFname, false));
-	return Error::NONE;
-}
-
-Error PortalNode::frameUpdate(Second prevUpdateTime, Second crntTime)
-{
-	MoveComponent& move = getComponent<MoveComponent>();
-	if(move.getTimestamp() == getGlobalTimestamp())
-	{
-		// Move comp updated
-		updateTransform(move.getWorldTransform());
-		getSceneGraph().getSectorGroup().portalUpdated(this);
-	}
-
-	return Error::NONE;
-}
-
-void PortalNode::deferredUpdate()
-{
-	// Gather the sectors it collides
-	iterateSceneSectors(getSceneGraph(), [&](SectorNode& sector) -> Bool {
-		Bool collide = testCollisionShapes(m_aabb, sector.m_aabb);
-
-		// Perform a more detailed test
-		if(collide)
-		{
-			collide = testCollisionShapes(*m_shape, sector.getBoundingShape());
-		}
-
-		// Update graphs
-		if(collide)
-		{
-			tryAddSector(&sector);
-			sector.tryAddPortal(this);
-		}
-		else
-		{
-			tryRemoveSector(&sector);
-			sector.tryRemovePortal(this);
-		}
-
-		return false;
-	});
-}
-
-void PortalNode::tryAddSector(SectorNode* sector)
-{
-	ANKI_ASSERT(sector);
-
-	auto it = m_sectors.getBegin();
-	auto end = m_sectors.getEnd();
-	for(; it != end; ++it)
-	{
-		if(*it == sector)
-		{
-			// Already there, return
-			return;
-		}
-	}
-
-	m_sectors.pushBack(getSceneAllocator(), sector);
-}
-
-void PortalNode::tryRemoveSector(SectorNode* sector)
-{
-	ANKI_ASSERT(sector);
-
-	auto it = m_sectors.getBegin();
-	auto end = m_sectors.getEnd();
-	for(; it != end; ++it)
-	{
-		if(*it == sector)
-		{
-			m_sectors.erase(getSceneAllocator(), it);
-			break;
-		}
-	}
-}
-
-SectorNode::~SectorNode()
-{
-	auto alloc = getSceneAllocator();
-
-	// Remove portals
-	for(PortalNode* p : m_portals)
-	{
-		p->tryRemoveSector(this);
-	}
-
-	m_portals.destroy(alloc);
-
-	// Remove spatials
-	U spatialsCount = m_spatials.getSize();
-	while(spatialsCount-- != 0) // Use counter
-	{
-		tryRemoveSpatialComponent(m_spatials.getFront());
-	}
-}
-
-Error SectorNode::init(const CString& meshFname)
-{
-	ANKI_CHECK(PortalSectorBase::init(meshFname, true));
-	return Error::NONE;
-}
-
-void SectorNode::tryAddPortal(PortalNode* portal)
-{
-	ANKI_ASSERT(portal);
-
-	auto it = m_portals.getBegin();
-	auto end = m_portals.getEnd();
-	for(; it != end; ++it)
-	{
-		if(*it == portal)
-		{
-			// Already there, return
-			return;
-		}
-	}
-
-	m_portals.pushBack(getSceneAllocator(), portal);
-}
-
-void SectorNode::tryRemovePortal(PortalNode* portal)
-{
-	ANKI_ASSERT(portal);
-
-	auto it = m_portals.getBegin();
-	auto end = m_portals.getEnd();
-	for(; it != end; ++it)
-	{
-		if(*it == portal)
-		{
-			m_portals.erase(getSceneAllocator(), it);
-			break;
-		}
-	}
-}
-
-void SectorNode::tryAddSpatialComponent(SpatialComponent* sp)
-{
-	ANKI_ASSERT(sp);
-
-	// Find sector in spatial
-	auto itsp = sp->getSectorInfo().getBegin();
-	auto endsp = sp->getSectorInfo().getEnd();
-	for(; itsp != endsp; ++itsp)
-	{
-		if(*itsp == this)
-		{
-			// Found, return
-			ANKI_ASSERT(
-				findSpatialComponent(sp) != m_spatials.getEnd() && "Spatial has reference to sector but sector not");
-			return;
-		}
-	}
-
-	ANKI_ASSERT(findSpatialComponent(sp) == m_spatials.getEnd());
-
-	m_spatials.pushBack(getSceneAllocator(), sp);
-	sp->getSectorInfo().pushBack(getSceneAllocator(), this);
-}
-
-void SectorNode::tryRemoveSpatialComponent(SpatialComponent* sp)
-{
-	ANKI_ASSERT(sp);
-
-	// Find sector in spatial
-	auto itsp = sp->getSectorInfo().getBegin();
-	auto endsp = sp->getSectorInfo().getEnd();
-	for(; itsp != endsp; ++itsp)
-	{
-		if(*itsp == this)
-		{
-			break;
-		}
-	}
-
-	if(itsp != endsp)
-	{
-		// Found, remove
-
-		sp->getSectorInfo().erase(getSceneAllocator(), itsp);
-
-		auto it = findSpatialComponent(sp);
-		ANKI_ASSERT(it != m_spatials.getEnd());
-		m_spatials.erase(getSceneAllocator(), it);
-	}
-	else
-	{
-#if ANKI_EXTRA_CHECKS
-		ANKI_ASSERT(findSpatialComponent(sp) == m_spatials.getEnd());
-#endif
-	}
-}
-
-List<SpatialComponent*>::Iterator SectorNode::findSpatialComponent(SpatialComponent* sp)
-{
-	ANKI_ASSERT(sp);
-	auto it = m_spatials.getBegin();
-	auto end = m_spatials.getEnd();
-	for(; it != end; ++it)
-	{
-		if(*it == sp)
-		{
-			break;
-		}
-	}
-
-	return it;
-}
-
-Error SectorNode::frameUpdate(Second prevUpdateTime, Second crntTime)
-{
-	MoveComponent& move = getComponent<MoveComponent>();
-	if(move.getTimestamp() == getGlobalTimestamp())
-	{
-		// Move comp updated.
-		updateTransform(move.getWorldTransform());
-		getSceneGraph().getSectorGroup().sectorUpdated(this);
-	}
-
-	return Error::NONE;
-}
-
-void SectorNode::deferredUpdate()
-{
-	// Spatials should get updated
-	for(SpatialComponent* sp : m_spatials)
-	{
-		sp->markForUpdate();
-	}
-
-	iterateScenePortals(getSceneGraph(), [&](PortalNode& portal) -> Bool {
-		Bool collide = testCollisionShapes(m_aabb, portal.m_aabb);
-
-		if(collide)
-		{
-			collide = testCollisionShapes(*m_shape, portal.getBoundingShape());
-		}
-
-		if(collide)
-		{
-			portal.tryAddSector(this);
-			tryAddPortal(&portal);
-		}
-		else
-		{
-			portal.tryRemoveSector(this);
-			tryRemovePortal(&portal);
-		}
-
-		return false;
-	});
-}
-
-SectorGroup::~SectorGroup()
-{
-}
-
-void SectorGroup::spatialUpdated(SpatialComponent* sp)
-{
-	ANKI_ASSERT(sp);
-	LockGuard<SpinLock> lock(m_mtx);
-	m_spatialsDeferredBinning.pushBack(m_scene->getFrameAllocator(), sp);
-}
-
-void SectorGroup::portalUpdated(PortalNode* portal)
-{
-	ANKI_ASSERT(portal);
-	LockGuard<SpinLock> lock(m_portalsUpdatedLock);
-	m_portalsUpdated.pushBack(m_scene->getFrameAllocator(), portal);
-}
-
-void SectorGroup::sectorUpdated(SectorNode* sector)
-{
-	ANKI_ASSERT(sector);
-	LockGuard<SpinLock> lock(m_sectorsUpdatedLock);
-	m_sectorsUpdated.pushBack(m_scene->getFrameAllocator(), sector);
-}
-
-void SectorGroup::binSpatial(SpatialComponent* sp)
-{
-	ANKI_ASSERT(sp);
-
-	// Iterate all sectors and bin the spatial
-	iterateSceneSectors(*m_scene, [&](SectorNode& sector) -> Bool {
-		Bool collide = false;
-		if(!sp->getSingleSector())
-		{
-			// Spatial can belong to multiple sectors
-
-			// Fast test
-			collide = testCollisionShapes(sector.m_aabb, sp->getAabb());
-
-			// Detailed test
-			if(collide)
-			{
-				collide = testCollisionShapes(sector.getBoundingShape(), sp->getSpatialCollisionShape());
-			}
-		}
-		else
-		{
-			// Spatial can belong to one sector
-
-			// Make sure the origin of the spatial is inside the sector
-			const Vec4& center = sp->getSpatialOrigin();
-
-			if(center >= sector.m_aabb.getMin() && center <= sector.m_aabb.getMax())
-			{
-				collide = true;
-			}
-
-			// Detailed test
-			const F32 smallf = EPSILON * 10.0;
-			Aabb smallBox(center, center + Vec4(smallf, smallf, smallf, 0.0));
-			if(collide)
-			{
-				collide = testCollisionShapes(sector.getBoundingShape(), smallBox);
-			}
-		}
-
-		if(collide)
-		{
-			sector.tryAddSpatialComponent(sp);
-		}
-		else
-		{
-			sector.tryRemoveSpatialComponent(sp);
-		}
-
-		return false;
-	});
-}
-
-void SectorGroup::spatialDeleted(SpatialComponent* sp)
-{
-	auto it = sp->getSectorInfo().getBegin();
-	auto end = sp->getSectorInfo().getEnd();
-	while(it != end)
-	{
-		(*it)->tryRemoveSpatialComponent(sp);
-		++it;
-	}
-}
-
-void SectorGroup::findVisibleSectors(const FrustumComponent& frc,
-	const SoftwareRasterizer* r,
-	List<const SectorNode*>& visibleSectors,
-	U& spatialsCount) const
-{
-	// Find the sector the eye is in
-	Sphere eye(frc.getFrustumOrigin(), frc.getFrustum().getNear());
-	Bool eyeInsideASector = false;
-	SectorNode* sectorEyeIsInside = nullptr;
-
-	iterateSceneSectors(*m_scene, [&](SectorNode& sector) -> Bool {
-		Bool collide = testCollisionShapes(eye, sector.m_aabb);
-
-		if(collide)
-		{
-			collide = testCollisionShapes(eye, sector.getBoundingShape());
-		}
-
-		if(collide)
-		{
-			eyeInsideASector = true;
-			sectorEyeIsInside = &sector;
-			return true;
-		}
-
-		return false;
-	});
-
-	if(!eyeInsideASector)
-	{
-		// eye outside all sectors, find those the frustum collides
-
-		iterateSceneSectors(*m_scene, [&](SectorNode& sector) -> Bool {
-			if(frc.insideFrustum(sector.getBoundingShape()))
-			{
-				findVisibleSectorsInternal(frc, sector, r, visibleSectors, spatialsCount);
-			}
-
-			return false;
-		});
-	}
-	else
-	{
-		// eye inside a sector
-		findVisibleSectorsInternal(frc, *sectorEyeIsInside, r, visibleSectors, spatialsCount);
-	}
-}
-
-void SectorGroup::findVisibleSectorsInternal(const FrustumComponent& frc,
-	const SectorNode& s,
-	const SoftwareRasterizer* r,
-	List<const SectorNode*>& visibleSectors,
-	U& spatialsCount) const
-{
-	auto alloc = m_scene->getFrameAllocator();
-
-	// Check if "s" is already there
-	auto it = visibleSectors.getBegin();
-	auto end = visibleSectors.getEnd();
-	for(; it != end; ++it)
-	{
-		if(*it == &s)
-		{
-			// SectorNode already there, skip
-			return;
-		}
-	}
-
-	// SectorNode not in the list, push it
-	visibleSectors.pushBack(alloc, &s);
-	spatialsCount += s.m_spatials.getSize();
-
-	// Check visible portals
-	auto itp = s.m_portals.getBegin();
-	auto itend = s.m_portals.getEnd();
-	for(; itp != itend; ++itp)
-	{
-		const PortalNode& p = *(*itp);
-
-		if(frc.insideFrustum(p.getBoundingShape())
-			&& (r == nullptr || r->visibilityTest(p.getBoundingShape(), p.m_aabb)))
-		{
-			auto it = p.m_sectors.getBegin();
-			auto end = p.m_sectors.getEnd();
-			for(; it != end; ++it)
-			{
-				if(*it != &s)
-				{
-					findVisibleSectorsInternal(frc, *(*it), r, visibleSectors, spatialsCount);
-				}
-			}
-		}
-	}
-}
-
-void SectorGroup::prepareForVisibilityTests()
-{
-	// Update portals
-	Error err = m_portalsUpdated.iterateForward([](PortalNode* portal) {
-		portal->deferredUpdate();
-		return Error::NONE;
-	});
-	(void)err;
-	m_portalsUpdated.destroy(m_scene->getFrameAllocator());
-
-	// Update sectors
-	err = m_sectorsUpdated.iterateForward([](SectorNode* portal) {
-		portal->deferredUpdate();
-		return Error::NONE;
-	});
-	(void)err;
-	m_sectorsUpdated.destroy(m_scene->getFrameAllocator());
-
-	// Bin spatials
-	err = m_spatialsDeferredBinning.iterateForward([this](SpatialComponent* spc) {
-		binSpatial(spc);
-		return Error::NONE;
-	});
-	(void)err;
-	m_spatialsDeferredBinning.destroy(m_scene->getFrameAllocator());
-}
-
-void SectorGroup::findVisibleNodes(
-	const FrustumComponent& frc, U testId, const SoftwareRasterizer* r, SectorGroupVisibilityTestsContext& ctx) const
-{
-	auto alloc = m_scene->getFrameAllocator();
-
-	// Find visible sectors
-	ListAuto<const SectorNode*> visSectors(alloc);
-	U spatialsCount = 0;
-	findVisibleSectors(frc, r, visSectors, spatialsCount);
-
-	if(ANKI_UNLIKELY(spatialsCount == 0))
-	{
-		return;
-	}
-
-	// Initiate storage of nodes
-	SceneNode** visibleNodesMem = reinterpret_cast<SceneNode**>(alloc.allocate(spatialsCount * sizeof(void*)));
-
-	WeakArray<SceneNode*> visibleNodes(visibleNodesMem, spatialsCount);
-
-	// Iterate visible sectors and get the scene nodes. The array will contain
-	// duplicates
-	U nodesCount = 0;
-	auto it = visSectors.getBegin();
-	auto end = visSectors.getEnd();
-	for(; it != end; ++it)
-	{
-		const SectorNode& s = *(*it);
-		for(auto itsp : s.m_spatials)
-		{
-			const SpatialComponent& spc = *itsp;
-			SceneNode& node = const_cast<SceneNode&>(spc.getSceneNode());
-
-			// Check if alrady visited
-			if(!node.fetchSetSectorVisited(testId, true))
-			{
-				visibleNodes[nodesCount++] = &node;
-			}
-		}
-	}
-
-	// Update the context
-	ctx.m_visibleNodes = WeakArray<SceneNode*>(visibleNodesMem, nodesCount);
-}
-
-} // end namespace anki

+ 0 - 212
src/anki/scene/SectorNode.h

@@ -1,212 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <anki/scene/SceneNode.h>
-#include <anki/scene/components/SceneComponent.h>
-#include <anki/Collision.h>
-#include <anki/util/WeakArray.h>
-
-namespace anki
-{
-
-// Forward
-class SectorGroup;
-class Renderer;
-class SoftwareRasterizer;
-
-/// @addtogroup scene
-/// @{
-
-/// The base for portals and sectors.
-class PortalSectorBase : public SceneNode
-{
-	friend class PortalNode;
-	friend class SectorNode;
-
-public:
-	PortalSectorBase(SceneGraph* scene, CString name)
-		: SceneNode(scene, name)
-	{
-	}
-
-	~PortalSectorBase();
-
-	ANKI_USE_RESULT Error init(const CString& modelFname, Bool isSector);
-
-	const CollisionShape& getBoundingShape() const
-	{
-		return *m_shape;
-	}
-
-	SectorGroup& getSectorGroup();
-
-	const DynamicArray<Vec4>& getVertices() const
-	{
-		return m_shapeStorageLSpace;
-	}
-
-	const DynamicArray<U16>& getVertexIndices() const
-	{
-		return m_vertIndices;
-	}
-
-protected:
-	DynamicArray<Vec4> m_shapeStorageLSpace;
-	DynamicArray<Vec4> m_shapeStorageWSpace;
-	CollisionShape* m_shape = nullptr;
-	Aabb m_aabb;
-	DynamicArray<U16> m_vertIndices; ///< Used in debug draw
-
-	void updateTransform(const Transform& trf);
-};
-
-/// 2 way portal.
-class PortalNode : public PortalSectorBase
-{
-	friend class SectorGroup;
-
-public:
-	using Base = PortalSectorBase;
-
-	PortalNode(SceneGraph* scene, CString name)
-		: PortalSectorBase(scene, name)
-	{
-	}
-
-	~PortalNode();
-
-	ANKI_USE_RESULT Error init(const CString& modelFname);
-
-	ANKI_USE_RESULT Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
-
-	/// Add reference to sector.
-	void tryAddSector(SectorNode* sector);
-
-	/// Remove reference from sector.
-	void tryRemoveSector(SectorNode* sector);
-
-	void deferredUpdate();
-
-private:
-	List<SectorNode*> m_sectors;
-};
-
-/// A sector. It consists of an octree and some portals
-class SectorNode : public PortalSectorBase
-{
-	friend class SectorGroup;
-
-public:
-	using Base = PortalSectorBase;
-
-	/// Default constructor
-	SectorNode(SceneGraph* scene, CString name)
-		: Base(scene, name)
-	{
-	}
-
-	~SectorNode();
-
-	ANKI_USE_RESULT Error init(const CString& modelFname);
-
-	void tryAddPortal(PortalNode* portal);
-	void tryRemovePortal(PortalNode* portal);
-
-	void tryAddSpatialComponent(SpatialComponent* sp);
-	void tryRemoveSpatialComponent(SpatialComponent* sp);
-
-	ANKI_USE_RESULT Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
-
-	void deferredUpdate();
-
-private:
-	List<PortalNode*> m_portals;
-	List<SpatialComponent*> m_spatials;
-
-	List<SpatialComponent*>::Iterator findSpatialComponent(SpatialComponent* sp);
-};
-
-/// The context for visibility tests from a single FrustumComponent (not for all of them).
-class SectorGroupVisibilityTestsContext
-{
-	friend class SectorGroup;
-
-public:
-	U getVisibleSceneNodeCount() const
-	{
-		return m_visibleNodes.getSize();
-	}
-
-	template<typename Func>
-	void iterateVisibleSceneNodes(PtrSize begin, PtrSize end, Func func)
-	{
-		ANKI_ASSERT(begin <= end);
-
-		for(U i = begin; i < end; ++i)
-		{
-			func(*m_visibleNodes[i]);
-		}
-	}
-
-private:
-	WeakArray<SceneNode*> m_visibleNodes;
-};
-
-/// Sector group. This is supposed to represent the whole scene
-class SectorGroup
-{
-public:
-	/// Default constructor
-	SectorGroup(SceneGraph* scene)
-		: m_scene(scene)
-	{
-	}
-
-	/// Destructor
-	~SectorGroup();
-
-	void spatialUpdated(SpatialComponent* sp);
-	void spatialDeleted(SpatialComponent* sp);
-
-	void portalUpdated(PortalNode* portal);
-	void sectorUpdated(SectorNode* sector);
-
-	void prepareForVisibilityTests();
-
-	void findVisibleNodes(const FrustumComponent& frc,
-		U threadId,
-		const SoftwareRasterizer* r,
-		SectorGroupVisibilityTestsContext& ctx) const;
-
-private:
-	SceneGraph* m_scene; ///< Keep it here to access various allocators
-
-	List<SpatialComponent*> m_spatialsDeferredBinning;
-	SpinLock m_mtx;
-
-	List<PortalNode*> m_portalsUpdated;
-	SpinLock m_portalsUpdatedLock;
-	List<SectorNode*> m_sectorsUpdated;
-	SpinLock m_sectorsUpdatedLock;
-
-	void findVisibleSectors(const FrustumComponent& frc,
-		const SoftwareRasterizer* r,
-		List<const SectorNode*>& visibleSectors,
-		U& spatialsCount) const;
-
-	/// Recursive method
-	void findVisibleSectorsInternal(const FrustumComponent& frc,
-		const SectorNode& s,
-		const SoftwareRasterizer* r,
-		List<const SectorNode*>& visibleSectors,
-		U& spatialsCount) const;
-
-	void binSpatial(SpatialComponent* sp);
-};
-/// @}
-
-} // end namespace anki

+ 3 - 2
src/anki/scene/StaticCollisionNode.cpp

@@ -30,10 +30,11 @@ Error StaticCollisionNode::init(const CString& resourceFname, const Transform& t
 	// Create body
 	PhysicsBodyInitInfo init;
 	init.m_shape = m_rsrc->getShape();
-	init.m_static = true;
-	init.m_startTrf = transform;
+	init.m_mass = 0.0f;
+	init.m_transform = transform;
 
 	m_body = getSceneGraph().getPhysicsWorld().newInstance<PhysicsBody>(init);
+	m_body->setUserData(this);
 
 	return Error::NONE;
 }

+ 5 - 8
src/anki/scene/StaticGeometryNode.cpp

@@ -12,14 +12,11 @@ namespace anki
 {
 
 /// The implementation of static geometry node renderable component.
-class StaticGeometryRenderComponent : public RenderComponent
+class StaticGeometryRenderComponent : public MaterialRenderComponent
 {
 public:
-	StaticGeometryPatchNode* m_node;
-
-	StaticGeometryRenderComponent(StaticGeometryPatchNode* node)
-		: RenderComponent(node, node->m_modelPatch->getMaterial())
-		, m_node(node)
+	StaticGeometryRenderComponent(SceneNode* node)
+		: MaterialRenderComponent(node, static_cast<StaticGeometryPatchNode*>(node)->m_modelPatch->getMaterial())
 	{
 	}
 
@@ -47,13 +44,13 @@ Error StaticGeometryPatchNode::init(const ModelPatch* modelPatch)
 	// Create spatial components
 	for(U i = 1; i < m_modelPatch->getSubMeshCount(); i++)
 	{
-		SpatialComponent* spatial = newComponent<SpatialComponent>(this, &m_modelPatch->getBoundingShapeSub(i));
+		SpatialComponent* spatial = newComponent<SpatialComponent>(&m_modelPatch->getBoundingShapeSub(i));
 
 		spatial->setSpatialOrigin(m_modelPatch->getBoundingShapeSub(i).getCenter());
 	}
 
 	// Create render component
-	newComponent<StaticGeometryRenderComponent>(this);
+	newComponent<StaticGeometryRenderComponent>();
 
 	return Error::NONE;
 }

+ 62 - 0
src/anki/scene/TriggerNode.cpp

@@ -0,0 +1,62 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/scene/TriggerNode.h>
+#include <anki/scene/SceneGraph.h>
+#include <anki/scene/components/MoveComponent.h>
+#include <anki/scene/components/TriggerComponent.h>
+#include <anki/physics/PhysicsWorld.h>
+#include <anki/physics/PhysicsCollisionShape.h>
+#include <anki/physics/PhysicsTrigger.h>
+
+namespace anki
+{
+
+class TriggerNode::MoveFeedbackComponent : public SceneComponent
+{
+public:
+	MoveFeedbackComponent(SceneNode* node)
+		: SceneComponent(SceneComponentType::NONE, node)
+	{
+	}
+
+	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) final
+	{
+		updated = false;
+
+		const MoveComponent& move = m_node->getComponent<MoveComponent>();
+		if(move.getTimestamp() == m_node->getGlobalTimestamp())
+		{
+			TriggerNode* node = static_cast<TriggerNode*>(m_node);
+			node->m_trigger->setTransform(move.getWorldTransform());
+		}
+
+		return Error::NONE;
+	}
+};
+
+TriggerNode::TriggerNode(SceneGraph* scene, CString name)
+	: SceneNode(scene, name)
+{
+}
+
+TriggerNode::~TriggerNode()
+{
+}
+
+Error TriggerNode::init(F32 sphereRadius)
+{
+	m_shape = getSceneGraph().getPhysicsWorld().newInstance<PhysicsSphere>(sphereRadius);
+	m_trigger = getSceneGraph().getPhysicsWorld().newInstance<PhysicsTrigger>(m_shape);
+	m_trigger->setUserData(this);
+
+	newComponent<MoveComponent>();
+	newComponent<MoveFeedbackComponent>();
+	newComponent<TriggerComponent>(m_trigger);
+
+	return Error::NONE;
+}
+
+} // end namespace anki

+ 37 - 0
src/anki/scene/TriggerNode.h

@@ -0,0 +1,37 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/scene/SceneNode.h>
+#include <anki/physics/Forward.h>
+
+namespace anki
+{
+
+/// @addtogroup scene
+/// @{
+
+/// Trigger node.
+class TriggerNode : public SceneNode
+{
+public:
+	TriggerNode(SceneGraph* scene, CString name);
+
+	~TriggerNode();
+
+	ANKI_USE_RESULT Error init(const CString& collisionShapeFilename);
+
+	ANKI_USE_RESULT Error init(F32 sphereRadius);
+
+private:
+	class MoveFeedbackComponent;
+
+	PhysicsCollisionShapePtr m_shape;
+	PhysicsTriggerPtr m_trigger;
+};
+/// @}
+
+} // end namespace anki

+ 2 - 4
src/anki/scene/Visibility.cpp

@@ -5,7 +5,6 @@
 
 #include <anki/scene/VisibilityInternal.h>
 #include <anki/scene/SceneGraph.h>
-#include <anki/scene/SectorNode.h>
 #include <anki/scene/components/FrustumComponent.h>
 #include <anki/scene/components/LensFlareComponent.h>
 #include <anki/scene/components/RenderComponent.h>
@@ -350,7 +349,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 			if(wantsRenderComponents || (wantsShadowCasters && rc->getCastsShadow()))
 			{
 				RenderableQueueElement* el;
-				if(rc->getMaterial().isForwardShading())
+				if(rc->isForwardShading())
 				{
 					el = result.m_forwardShadingRenderables.newElement(alloc);
 				}
@@ -366,7 +365,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 				el->m_distanceFromCamera = max(0.0f, sps[0].m_sp->getAabb().testPlane(nearPlane));
 
 				if(wantsEarlyZ && el->m_distanceFromCamera < m_frcCtx->m_visCtx->m_earlyZDist
-					&& !rc->getMaterial().isForwardShading())
+					&& !rc->isForwardShading())
 				{
 					RenderableQueueElement* el2 = result.m_earlyZRenderables.newElement(alloc);
 					*el2 = *el;
@@ -678,7 +677,6 @@ void SceneGraph::doVisibilityTests(SceneNode& fsn, SceneGraph& scene, RenderQueu
 	ANKI_TRACE_SCOPED_EVENT(SCENE_VIS_TESTS);
 
 	ThreadHive& hive = scene.getThreadHive();
-	scene.getSectorGroup().prepareForVisibilityTests();
 
 	VisibilityContext ctx;
 	ctx.m_scene = &scene;

+ 0 - 1
src/anki/scene/VisibilityInternal.h

@@ -5,7 +5,6 @@
 
 #pragma once
 
-#include <anki/scene/SectorNode.h>
 #include <anki/scene/SceneGraph.h>
 #include <anki/scene/SoftwareRasterizer.h>
 #include <anki/scene/components/FrustumComponent.h>

+ 5 - 3
src/anki/scene/components/BodyComponent.h

@@ -43,15 +43,17 @@ public:
 		return m_body;
 	}
 
-	ANKI_USE_RESULT Error update(SceneNode&, Second, Second, Bool& updated) override
+	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) override
 	{
-		m_trf = m_body->getTransform(updated);
+		Transform newTrf = m_body->getTransform();
+		updated = newTrf != m_trf;
+		m_trf = newTrf;
 		return Error::NONE;
 	}
 
 private:
 	PhysicsBodyPtr m_body;
-	Transform m_trf;
+	Transform m_trf = Transform::getIdentity();
 };
 /// @}
 

+ 1 - 1
src/anki/scene/components/DecalComponent.h

@@ -74,7 +74,7 @@ public:
 	}
 
 	/// Implements SceneComponent::update.
-	ANKI_USE_RESULT Error update(SceneNode&, Second, Second, Bool& updated) override
+	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) override
 	{
 		updated = m_markedForUpdate;
 

+ 1 - 1
src/anki/scene/components/FrustumComponent.cpp

@@ -26,7 +26,7 @@ FrustumComponent::~FrustumComponent()
 	m_coverageBuff.m_depthMap.destroy(getAllocator());
 }
 
-Error FrustumComponent::update(SceneNode& node, Second, Second, Bool& updated)
+Error FrustumComponent::update(Second, Second, Bool& updated)
 {
 	updated = false;
 

+ 1 - 1
src/anki/scene/components/FrustumComponent.h

@@ -111,7 +111,7 @@ public:
 
 	/// @name SceneComponent overrides
 	/// @{
-	ANKI_USE_RESULT Error update(SceneNode&, Second, Second, Bool& updated) override;
+	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) override;
 	/// @}
 
 	void setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag bits)

+ 147 - 0
src/anki/scene/components/JointComponent.cpp

@@ -0,0 +1,147 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/scene/components/JointComponent.h>
+#include <anki/scene/components/BodyComponent.h>
+#include <anki/scene/SceneGraph.h>
+#include <anki/physics/PhysicsWorld.h>
+
+namespace anki
+{
+
+class JointComponent::JointNode : public IntrusiveListEnabled<JointNode>
+{
+public:
+	PhysicsJointPtr m_joint;
+	SceneNode* m_parentNode = nullptr; ///< Keep track the node that is connected with this node through a joint.
+};
+
+JointComponent::~JointComponent()
+{
+	removeAllJoints();
+}
+
+void JointComponent::removeAllJoints()
+{
+	while(!m_jointList.isEmpty())
+	{
+		JointNode* node = &m_jointList.getFront();
+		m_jointList.popFront();
+
+		getAllocator().deleteInstance(node);
+	}
+}
+
+Vec3 JointComponent::computeLocalPivotFromFactors(const PhysicsBodyPtr& body, const Vec3& factors)
+{
+	btTransform identityTrf;
+	identityTrf.setIdentity();
+	btVector3 aabbmin, aabbmax, center;
+	body->getBtBody()->getCollisionShape()->getAabb(identityTrf, aabbmin, aabbmax);
+
+	center = (aabbmin + aabbmax) * 0.5f;
+
+	Vec3 out;
+	for(U i = 0; i < 3; ++i)
+	{
+		const F32 factor = factors[i];
+		ANKI_ASSERT(factor >= -1.0f || factor <= 1.0f);
+		const F32 dist = aabbmax[i] - center[i];
+		out[i] = dist * factor + center[i];
+	}
+
+	return out;
+}
+
+template<typename TJoint, typename... TArgs>
+void JointComponent::newJoint(const Vec3& relPosFactor, F32 breakingImpulse, TArgs&&... args)
+{
+	BodyComponent* bodyc = m_node->tryGetComponent<BodyComponent>();
+
+	if(bodyc)
+	{
+		Vec3 relPos = computeLocalPivotFromFactors(bodyc->getPhysicsBody(), relPosFactor);
+
+		PhysicsJointPtr joint = getSceneGraph().getPhysicsWorld().newInstance<TJoint>(
+			bodyc->getPhysicsBody(), relPos, std::forward<TArgs>(args)...);
+		joint->setBreakingImpulseThreshold(breakingImpulse);
+
+		JointNode* newNode = getAllocator().newInstance<JointNode>();
+		newNode->m_joint = joint;
+		m_jointList.pushBack(newNode);
+	}
+	else
+	{
+		ANKI_SCENE_LOGW("Can't create new joint. The node is missing a BodyComponent");
+	}
+}
+
+void JointComponent::newPoint2PointJoint(const Vec3& relPosFactor, F32 breakingImpulse)
+{
+	newJoint<PhysicsPoint2PointJoint>(relPosFactor, breakingImpulse);
+}
+
+void JointComponent::newPoint2PointJoint2(const Vec3& relPosFactorA, const Vec3& relPosFactorB, F32 breakingImpulse)
+{
+	BodyComponent* bodycA = m_node->tryGetComponent<BodyComponent>();
+	BodyComponent* bodycB = nullptr;
+	if(m_node->getParent())
+	{
+		bodycB = m_node->getParent()->tryGetComponent<BodyComponent>();
+	}
+
+	if(bodycA && bodycB)
+	{
+		Vec3 relPosA = computeLocalPivotFromFactors(bodycA->getPhysicsBody(), relPosFactorA);
+		Vec3 relPosB = computeLocalPivotFromFactors(bodycB->getPhysicsBody(), relPosFactorB);
+
+		PhysicsJointPtr joint = getSceneGraph().getPhysicsWorld().newInstance<PhysicsPoint2PointJoint>(
+			bodycA->getPhysicsBody(), relPosA, bodycB->getPhysicsBody(), relPosB);
+		joint->setBreakingImpulseThreshold(breakingImpulse);
+
+		JointNode* newNode = getAllocator().newInstance<JointNode>();
+		newNode->m_joint = joint;
+		newNode->m_parentNode = m_node->getParent();
+		m_jointList.pushBack(newNode);
+	}
+	else
+	{
+		ANKI_SCENE_LOGW("Can't create new joint. The node or its parent are missing a BodyComponent");
+	}
+}
+
+void JointComponent::newHingeJoint(const Vec3& relPosFactor, const Vec3& axis, F32 breakingImpulse)
+{
+	newJoint<PhysicsHingeJoint>(relPosFactor, breakingImpulse, axis);
+}
+
+Error JointComponent::update(Second prevTime, Second crntTime, Bool& updated)
+{
+	// Iterate the joints and check if the connected scene node is not the parent of this node anymore.
+	while(true)
+	{
+		Bool erasedOne = false;
+		for(auto node : m_jointList)
+		{
+			if(node.m_parentNode != m_node->getParent() || node.m_joint->isBroken())
+			{
+				m_jointList.erase(&node);
+				getAllocator().deleteInstance(&node);
+				erasedOne = true;
+				updated = true;
+				break;
+			}
+		}
+
+		if(!erasedOne)
+		{
+			break;
+		}
+	}
+
+	return Error::NONE;
+}
+
+} // end namespace anki

+ 57 - 0
src/anki/scene/components/JointComponent.h

@@ -0,0 +1,57 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/scene/components/SceneComponent.h>
+#include <anki/physics/PhysicsJoint.h>
+
+namespace anki
+{
+
+/// @addtogroup scene
+/// @{
+
+/// Contains a list of joints.
+class JointComponent : public SceneComponent
+{
+public:
+	static const SceneComponentType CLASS_TYPE = SceneComponentType::JOINT;
+
+	JointComponent(SceneNode* node)
+		: SceneComponent(SceneComponentType::JOINT, node)
+	{
+	}
+
+	~JointComponent();
+
+	/// Create a point 2 point joint on the BodyComponent of the SceneNode.
+	void newPoint2PointJoint(const Vec3& relPosFactor, F32 brakingImpulse = MAX_F32);
+
+	/// Create a point 2 point joint on the BodyComponents of the SceneNode and its child node.
+	void newPoint2PointJoint2(const Vec3& relPosFactorA, const Vec3& relPosFactorB, F32 brakingImpulse = MAX_F32);
+
+	/// Create a hinge joint on the BodyComponent of the SceneNode.
+	void newHingeJoint(const Vec3& relPosFactor, const Vec3& axis, F32 brakingImpulse = MAX_F32);
+
+	ANKI_USE_RESULT Error update(Second prevTime, Second crntTime, Bool& updated) override;
+
+private:
+	class JointNode;
+
+	IntrusiveList<JointNode> m_jointList;
+
+	/// Given a 3 coodrinates that lie in [-1.0, +1.0] compute a pivot point that lies into the AABB of the collision
+	/// shape of the body.
+	static Vec3 computeLocalPivotFromFactors(const PhysicsBodyPtr& body, const Vec3& factors);
+
+	void removeAllJoints();
+
+	template<typename TJoint, typename... TArgs>
+	void newJoint(const Vec3& relPosFactor, F32 brakingImpulse, TArgs&&... args);
+};
+/// @}
+
+} // end namespace anki

+ 1 - 1
src/anki/scene/components/LensFlareComponent.h

@@ -74,7 +74,7 @@ public:
 	}
 
 	/// Implements SceneComponent::update.
-	Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
+	Error update(Second prevTime, Second crntTime, Bool& updated) override
 	{
 		updated = false;
 		return Error::NONE;

Some files were not shown because too many files changed in this diff