Sfoglia il codice sorgente

Merge branch 'world' of https://github.com/taylor001/crown into world

mikymod 12 anni fa
parent
commit
d86209f609

+ 2 - 2
engine/gui/GuiImage.h

@@ -50,8 +50,8 @@ struct GuiImage
 		MaterialResource* mat = (MaterialResource*) device()->resource_manager()->data(material);
 		m_material = m_render_world.create_material(mat);
 
-		m_vb = m_r.create_vertex_buffer(4, VertexFormat::P2_T2, m_vertices);
-		m_ib = m_r.create_index_buffer(6, m_indices);
+		m_vb = m_r.create_vertex_buffer(4 * Vertex::bytes_per_vertex(VertexFormat::P2_T2), m_vertices, VertexFormat::P2_T2);
+		m_ib = m_r.create_index_buffer(6 * sizeof(uint16_t), m_indices);
 	}
 
 	//-------------------------------------------------------------------------

+ 2 - 2
engine/gui/GuiRect.h

@@ -45,8 +45,8 @@ struct GuiRect
 	{
 		update(pos, size, color);
 
-		m_vb = m_r.create_vertex_buffer(4, VertexFormat::P2_C4, m_vertices);
-		m_ib = m_r.create_index_buffer(8, m_indices);
+		m_vb = m_r.create_vertex_buffer(4 * Vertex::bytes_per_vertex(VertexFormat::P2_C4), m_vertices, VertexFormat::P2_C4);
+		m_ib = m_r.create_index_buffer(8 * sizeof(uint16_t), m_indices);
 	}
 
 	//-------------------------------------------------------------------------

+ 2 - 2
engine/gui/GuiText.h

@@ -86,8 +86,8 @@ struct GuiText
 		m_indices[2] = 2; m_indices[3] = 0;
 		m_indices[4] = 2; m_indices[5] = 3;
 
-		m_vb = m_r.create_vertex_buffer(4, VertexFormat::P2_T2, m_vertices);
-		m_ib = m_r.create_index_buffer(6, m_indices);
+		m_vb = m_r.create_vertex_buffer(4 * Vertex::bytes_per_vertex(VertexFormat::P2_T2), m_vertices, VertexFormat::P2_T2);
+		m_ib = m_r.create_index_buffer(6 * sizeof(uint16_t), m_indices);
 	}
 
 	//-------------------------------------------------------------------------

+ 2 - 2
engine/gui/GuiTriangle.h

@@ -40,8 +40,8 @@ struct GuiTriangle
 	{
 		update(p1, p2, p3, color);
 
-		m_vb = m_r.create_vertex_buffer(3, VertexFormat::P2_C4, m_vertices);
-		m_ib = m_r.create_index_buffer(6, m_indices);
+		m_vb = m_r.create_vertex_buffer(3 * Vertex::bytes_per_vertex(VertexFormat::P2_C4), m_vertices, VertexFormat::P2_C4);
+		m_ib = m_r.create_index_buffer(6 * sizeof(uint16_t), m_indices);
 	}
 
 	//-------------------------------------------------------------------------

+ 10 - 2
engine/physics/Actor.h

@@ -66,8 +66,13 @@ struct Actor
 	void				set_kinematic();
 	void				clear_kinematic();
 
+	/// Returns whether the actor is static (i.e. immovable).
 	bool				is_static() const;
+
+	/// Returns whether the actor is dynamic (i.e. driven dy physics).
 	bool				is_dynamic() const;
+
+	/// Returns whether the actor is kinematic (i.e. driven by the user).
 	bool				is_kinematic() const;
 
 	float				linear_damping() const;
@@ -91,10 +96,9 @@ struct Actor
 
 	StringId32			name();
 
-	void				update(const Matrix4x4& pose);
-
 private:
 
+	void				update(const Matrix4x4& pose);
 	void				create_shapes(const PhysicsResource* res, const PhysicsConfigResource* config, PxPhysics* physics);
 	
 public:
@@ -109,6 +113,10 @@ public:
 	PxRigidActor* 			m_actor;
 	uint32_t				m_group;
 	uint32_t				m_mask;
+
+private:
+
+	friend class PhysicsWorld;
 };
 
 } // namespace crown

+ 10 - 8
engine/physics/Controller.cpp

@@ -46,11 +46,10 @@ namespace crown
 {
 
 //-----------------------------------------------------------------------------
-Controller::Controller(const PhysicsResource* pr, SceneGraph& sg, int32_t node, PxPhysics* physics, PxScene* scene, PxControllerManager* manager)
+Controller::Controller(const PhysicsResource* pr, SceneGraph& sg, int32_t node, PxPhysics* physics, PxControllerManager* manager)
 	: m_resource(pr)
 	, m_scene_graph(sg)
 	, m_node(node)
-	, m_scene(scene)
 	, m_manager(manager)
 	, m_controller(NULL)
 {
@@ -67,29 +66,32 @@ Controller::Controller(const PhysicsResource* pr, SceneGraph& sg, int32_t node,
 	desc.upDirection = PxVec3(0.0, 1.0, 0.0);
 	desc.material = physics->createMaterial(0.5f, 0.5f, 0.5f);
 	desc.position = PxExtendedVec3(0, 0, 0);
-
+	desc.reportCallback = &m_callback;
 	CE_ASSERT(desc.isValid(), "Capsule is not valid");
-	m_callback = CE_NEW(default_allocator(), PhysicsControllerCallback)();
-	desc.callback = m_callback;
 
-	m_controller = manager->createController(*physics, scene, desc);
+	m_controller = manager->createController(desc);
 	CE_ASSERT(m_controller, "Failed to create controller");
 }
 
 //-----------------------------------------------------------------------------
 Controller::~Controller()
 {
-	CE_DELETE(default_allocator(), m_callback);
 	m_controller->release();
 }
 
 //-----------------------------------------------------------------------------
 void Controller::move(const Vector3& pos)
 {
-	PxVec3 disp(pos.x, pos.y, pos.z);
+	const PxVec3 disp(pos.x, pos.y, pos.z);
 	m_flags = m_controller->move(disp, 0.001, 1.0 / 60.0, PxControllerFilters());
 }
 
+//-----------------------------------------------------------------------------
+void Controller::set_height(float height)
+{
+	m_controller->resize(height);
+}
+
 //-----------------------------------------------------------------------------
 Vector3 Controller::position() const
 {

+ 4 - 3
engine/physics/Controller.h

@@ -28,6 +28,7 @@ OTHER DEALINGS IN THE SOFTWARE.
 
 #include "PxController.h"
 #include "PxControllerManager.h"
+#include "PhysicsCallback.h"
 
 using physx::PxController;
 using physx::PxControllerManager;
@@ -45,10 +46,11 @@ class PhysicsControllerCallback;
 
 struct Controller
 {
-							Controller(const PhysicsResource* pr, SceneGraph& sg, int32_t node, PxPhysics* physics, PxScene* scene, PxControllerManager* manager);
+							Controller(const PhysicsResource* pr, SceneGraph& sg, int32_t node, PxPhysics* physics, PxControllerManager* manager);
 							~Controller();
 
 	void					move(const Vector3& pos);
+	void					set_height(float height);
 
 	bool					collides_up() const;
 	bool					collides_down() const;
@@ -63,12 +65,11 @@ private:
 
 	SceneGraph&				m_scene_graph;
 	int32_t					m_node;
-	PxScene*				m_scene;
 	PxControllerManager*	m_manager;
 	PxController*			m_controller;
 	PxU32					m_flags;
 
-	PhysicsControllerCallback* m_callback;
+	PhysicsControllerCallback m_callback;
 };
 
 } // namespace crown

+ 6 - 6
engine/physics/Joint.cpp

@@ -44,6 +44,7 @@ OTHER DEALINGS IN THE SOFTWARE.
 #include "PxPrismaticJoint.h"
 #include "PxDistanceJoint.h"
 #include "PxJointLimit.h"
+#include "PxTolerancesScale.h"
 #include "Actor.h"
 
 using physx::PxPhysics;
@@ -61,7 +62,6 @@ using physx::PxConstraintFlag;
 using physx::PxJoint;
 using physx::PxFixedJoint;
 using physx::PxFixedJointCreate;
-using physx::PxJointLimitPair;
 
 using physx::PxSphericalJoint;
 using physx::PxSphericalJointCreate;
@@ -79,6 +79,9 @@ using physx::PxPrismaticJointFlag;
 using physx::PxDistanceJoint;
 using physx::PxDistanceJointCreate;
 using physx::PxDistanceJointFlag;
+using physx::PxJointLinearLimitPair;
+using physx::PxTolerancesScale;
+using physx::PxJointAngularLimitPair;
 
 namespace crown
 {
@@ -105,7 +108,6 @@ Joint::Joint(PxPhysics* physics, const PhysicsResource* pr, const uint32_t index
 		{
 			PxJointLimitCone limit_cone(joint.y_limit_angle, joint.z_limit_angle, joint.contact_dist);
 			limit_cone.restitution = joint.restitution;
-			limit_cone.spring = joint.spring;
 			limit_cone.damping = joint.damping;
 			limit_cone.contactDistance = joint.distance;
 
@@ -117,8 +119,7 @@ Joint::Joint(PxPhysics* physics, const PhysicsResource* pr, const uint32_t index
 		}
 		case JointType::REVOLUTE:
 		{
-			PxJointLimitPair limit_pair(joint.lower_limit, joint.upper_limit, joint.contact_dist);
-			limit_pair.spring = joint.spring;
+			PxJointAngularLimitPair limit_pair(joint.lower_limit, joint.upper_limit, joint.contact_dist);
 			limit_pair.damping = joint.damping;
 
 			m_joint = PxRevoluteJointCreate(*physics, actor_0.m_actor, PxTransform(anchor_0), actor_1.m_actor, PxTransform(anchor_1));
@@ -132,8 +133,7 @@ Joint::Joint(PxPhysics* physics, const PhysicsResource* pr, const uint32_t index
 		}
 		case JointType::PRISMATIC:
 		{
-			PxJointLimitPair limit_pair(joint.lower_limit, joint.upper_limit, joint.contact_dist);
-			limit_pair.spring = joint.spring;
+			PxJointLinearLimitPair limit_pair(PxTolerancesScale(), joint.lower_limit, joint.upper_limit, joint.contact_dist);
 			limit_pair.damping = joint.damping;
 
 			m_joint = PxPrismaticJointCreate(*physics, actor_0.m_actor, PxTransform(anchor_0), actor_1.m_actor, PxTransform(anchor_1));

+ 27 - 13
engine/physics/PhysicsCallback.h

@@ -29,24 +29,26 @@ OTHER DEALINGS IN THE SOFTWARE.
 #include "EventStream.h"
 #include "PhysicsTypes.h"
 #include "PxActor.h"
+#include "PxRigidActor.h"
 #include "PxController.h"
 #include "PxSimulationEventCallback.h"
 
-using physx::PxSimulationEventCallback;
-using physx::PxContactPairHeader;
-using physx::PxContactPair;
-using physx::PxConstraintInfo;
-using physx::PxTriggerPair;
-using physx::PxActor;
-using physx::PxU32;
 
-using physx::PxUserControllerHitReport;
-using physx::PxControllerShapeHit;
-using physx::PxControllersHit;
-using physx::PxControllerObstacleHit;
+using physx::PxActor;
+using physx::PxConstraintInfo;
+using physx::PxContactPair;
+using physx::PxContactPairHeader;
 using physx::PxContactPairHeader;
 using physx::PxContactPairHeaderFlag;
 using physx::PxContactPairPoint;
+using physx::PxControllerObstacleHit;
+using physx::PxControllerShapeHit;
+using physx::PxControllersHit;
+using physx::PxSimulationEventCallback;
+using physx::PxTriggerPair;
+using physx::PxTriggerPairFlag;
+using physx::PxU32;
+using physx::PxUserControllerHitReport;
 using physx::PxVec3;
 
 namespace crown
@@ -106,9 +108,21 @@ public:
 	}
 
 	//-----------------------------------------------------------------------------
-	void onTrigger(PxTriggerPair* /*pairs*/, PxU32 /*count*/)
+	void onTrigger(PxTriggerPair* pairs, PxU32 count)
 	{
-		Log::i("TRIGGER");
+		// printf("TRIGGER\n");
+		// printf("Num pairs = %d\n", count);
+
+		for (PxU32 pp = 0; pp < count; pp++)
+		{
+			const PxTriggerPair& pair = pairs[pp];
+			// Do not report event if either trigger ot other shape has been deleted
+			if (pair.flags & PxTriggerPairFlag::eDELETED_SHAPE_TRIGGER || pair.flags & PxTriggerPairFlag::eDELETED_SHAPE_OTHER) continue;
+
+			// TODO
+			physics_world::TriggerEvent ev;
+			event_stream::write(m_events, physics_world::EventType::TRIGGER, ev);
+		}
 	}
 
 	//-----------------------------------------------------------------------------

+ 7 - 1
engine/physics/PhysicsTypes.h

@@ -120,7 +120,8 @@ namespace physics_world
 	{
 		enum Enum
 		{
-			COLLISION
+			COLLISION,
+			TRIGGER
 		};
 	};
 
@@ -129,6 +130,11 @@ namespace physics_world
 		Actor* actors[2];
 		Vector3 where;
 	};
+
+	struct TriggerEvent
+	{
+		Actor* actor;
+	};
 }
 
 } // namespace crown

+ 3 - 3
engine/physics/PhysicsWorld.cpp

@@ -231,7 +231,7 @@ PhysicsWorld::PhysicsWorld()
 	m_scene = physics_system::s_physics->createScene(scene_desc);
 	
 	// Create controller manager
-	m_controller_manager = PxCreateControllerManager(*physics_system::s_foundation);
+	m_controller_manager = PxCreateControllerManager(*m_scene);
 	CE_ASSERT(m_controller_manager != NULL, "Failed to create PhysX controller manager");
 }
 
@@ -263,7 +263,7 @@ void PhysicsWorld::destroy_actor(ActorId id)
 //-----------------------------------------------------------------------------
 ControllerId PhysicsWorld::create_controller(const PhysicsResource* pr, SceneGraph& sg, int32_t node)
 {
-	Controller* controller = CE_NEW(m_controllers_pool, Controller)(pr, sg, node, physics_system::s_physics, m_scene, m_controller_manager);
+	Controller* controller = CE_NEW(m_controllers_pool, Controller)(pr, sg, node, physics_system::s_physics, m_controller_manager);
 	return m_controllers.create(controller);
 }
 
@@ -379,7 +379,7 @@ void PhysicsWorld::update(float dt)
 
 	// Update transforms
 	PxU32 num_active_transforms;
-	PxActiveTransform* active_transforms = m_scene->getActiveTransforms(num_active_transforms);
+	const PxActiveTransform* active_transforms = m_scene->getActiveTransforms(num_active_transforms);
 
 	// Update each actor with its new transform
 	for (PxU32 i = 0; i < num_active_transforms; i++)

+ 67 - 1
engine/renderers/backend/RenderContext.h

@@ -175,6 +175,11 @@ struct RenderState
 		program.id = INVALID_ID;
 		vb.id = INVALID_ID;
 		ib.id = INVALID_ID;
+		start_vertex = 0;
+		num_vertices = 0xFFFFFFFF;
+		start_index = 0;
+		num_indices = 0xFFFFFFFF;
+		vertex_format = VertexFormat::COUNT;
 
 		for (uint32_t i = 0; i < STATE_MAX_TEXTURES; i++)
 		{
@@ -191,8 +196,11 @@ public:
 	GPUProgramId	program;
 	VertexBufferId	vb;
 	IndexBufferId	ib;
+	uint32_t		start_vertex;
+	uint32_t		num_vertices;
 	uint32_t		start_index;
 	uint32_t		num_indices;
+	VertexFormat::Enum vertex_format;
 	Sampler			samplers[STATE_MAX_TEXTURES];
 };
 
@@ -218,6 +226,25 @@ public:
 	uint8_t m_layer;
 };
 
+/// A vertex buffer valid for one frame only
+struct TransientVertexBuffer
+{
+	VertexBufferId vb;
+	VertexFormat::Enum format;
+	uint32_t start_vertex;
+	size_t size;
+	char* data;
+};
+
+/// An index buffer valid for one frame only
+struct TransientIndexBuffer
+{
+	IndexBufferId ib;
+	uint32_t start_index;
+	size_t size;
+	char* data;
+};
+
 struct RenderContext
 {
 	RenderContext()
@@ -225,6 +252,20 @@ struct RenderContext
 		clear();
 	}
 
+	uint32_t reserve_transient_vertex_buffer(uint32_t num, VertexFormat::Enum format)
+	{
+		const uint32_t offset = m_tvb_offset;
+		m_tvb_offset = offset + Vertex::bytes_per_vertex(format) * num;
+		return offset;
+	}
+
+	uint32_t reserve_transient_index_buffer(uint32_t num)
+	{
+		const uint32_t offset = m_tib_offset;
+		m_tib_offset = offset + sizeof(uint16_t) * num;
+		return offset;
+	}
+
 	void set_state(uint64_t flags)
 	{
 		m_state.m_flags = flags;
@@ -240,9 +281,19 @@ struct RenderContext
 		m_state.program = program;
 	}
 
-	void set_vertex_buffer(VertexBufferId vb)
+	void set_vertex_buffer(VertexBufferId vb, uint32_t num_vertices)
 	{
 		m_state.vb = vb;
+		m_state.start_vertex = 0;
+		m_state.num_vertices = num_vertices;
+	}
+
+	void set_vertex_buffer(const TransientVertexBuffer& tvb, uint32_t num_vertices)
+	{
+		m_state.vb = tvb.vb;
+		m_state.start_vertex = tvb.start_vertex;
+		m_state.num_vertices = math::min((uint32_t) tvb.size / (uint32_t) Vertex::bytes_per_vertex(tvb.format), num_vertices);
+		m_state.vertex_format = tvb.format;
 	}
 
 	void set_index_buffer(IndexBufferId ib, uint32_t start_index, uint32_t num_indices)
@@ -252,6 +303,13 @@ struct RenderContext
 		m_state.num_indices = num_indices;
 	}
 
+	void set_index_buffer(const TransientIndexBuffer& tib, uint32_t num_indices)
+	{
+		m_state.ib = tib.ib;
+		m_state.start_index = tib.start_index;
+		m_state.num_indices = math::min((uint32_t) tib.size / (uint32_t) sizeof(uint16_t), num_indices);
+	}
+
 	void set_uniform(UniformId id, UniformType::Enum type, const void* value, uint8_t num)
 	{
 		m_constants.write_constant(id, type, value, num);
@@ -339,6 +397,9 @@ struct RenderContext
 
 		m_num_states = 0;
 		m_state.clear();
+
+		m_tvb_offset = 0;
+		m_tib_offset = 0;
 	}
 
 	void push()
@@ -373,6 +434,11 @@ public:
 
 	CommandBuffer m_commands;
 	ConstantBuffer m_constants;
+
+	uint32_t m_tvb_offset;
+	uint32_t m_tib_offset;
+	TransientVertexBuffer* m_transient_vb;
+	TransientIndexBuffer* m_transient_ib;
 };
 
 } // namespace crown

+ 183 - 76
engine/renderers/backend/Renderer.h

@@ -53,14 +53,14 @@ public:
 	void shutdown_impl();
 	void render_impl();
 
-	void create_vertex_buffer_impl(VertexBufferId id, size_t count, VertexFormat::Enum format, const void* vertices);
-	void create_dynamic_vertex_buffer_impl(VertexBufferId id, size_t count, VertexFormat::Enum format);
-	void update_vertex_buffer_impl(VertexBufferId id, size_t offset, size_t count, const void* vertices);
+	void create_vertex_buffer_impl(VertexBufferId id, size_t size, const void* data, VertexFormat::Enum format);
+	void create_dynamic_vertex_buffer_impl(VertexBufferId id, size_t size);
+	void update_vertex_buffer_impl(VertexBufferId id, size_t offset, size_t size, const void* data);
 	void destroy_vertex_buffer_impl(VertexBufferId id);
 
-	void create_index_buffer_impl(IndexBufferId id, size_t count, const void* indices);
-	void create_dynamic_index_buffer_impl(IndexBufferId id, size_t count);
-	void update_index_buffer_impl(IndexBufferId id, size_t offset, size_t count, const void* indices);
+	void create_index_buffer_impl(IndexBufferId id, size_t size, const void* data);
+	void create_dynamic_index_buffer_impl(IndexBufferId id, size_t size);
+	void update_index_buffer_impl(IndexBufferId id, size_t offset, size_t size, const void* data);
 	void destroy_index_buffer_impl(IndexBufferId id);
 
 	void create_texture_impl(TextureId id, uint32_t width, uint32_t height, PixelFormat::Enum format, const void* data);
@@ -85,10 +85,17 @@ public:
 	void init()
 	{
 		m_should_run = true;
-		m_thread.start(render_thread, this);
+		// m_thread.start(render_thread, this);
 
 		m_submit->m_commands.write(CommandType::INIT_RENDERER);
 		frame();
+
+		m_submit->m_transient_vb = create_transient_vertex_buffer(2 * 1024);
+		m_submit->m_transient_ib = create_transient_index_buffer(2 * 1024);
+		frame();
+		m_submit->m_transient_vb = create_transient_vertex_buffer(2 * 1024);
+		m_submit->m_transient_ib = create_transient_index_buffer(2 * 1024);
+		frame();
 	}
 
 	/// Shutdowns the renderer.
@@ -97,57 +104,98 @@ public:
 	{
 		if (m_should_run)
 		{
+			destroy_transient_index_buffer(m_submit->m_transient_ib);
+			destroy_transient_vertex_buffer(m_submit->m_transient_vb);
+			frame();
+
+			destroy_transient_index_buffer(m_submit->m_transient_ib);
+			destroy_transient_vertex_buffer(m_submit->m_transient_vb);
+			frame();
+
 			m_submit->m_commands.write(CommandType::SHUTDOWN_RENDERER);
 			frame();
 
-			m_thread.stop();		
+			//m_thread.stop();		
 		}
 	}
 
 	/// Creates a new vertex buffer optimized for rendering static vertex data.
-	/// @a vertices is the array containig @a count vertex data elements, each of the given @a format.
-	VertexBufferId create_vertex_buffer(size_t count, VertexFormat::Enum format, const void* vertices)
+	/// @a data is the array containig @a size bytes of vertex data in the given @a format.
+	VertexBufferId create_vertex_buffer(size_t size, const void* data, VertexFormat::Enum format)
 	{
 		const VertexBufferId id = m_vertex_buffers.create();
 
 		m_submit->m_commands.write(CommandType::CREATE_VERTEX_BUFFER);
 		m_submit->m_commands.write(id);
-		m_submit->m_commands.write(count);
+		m_submit->m_commands.write(size);
+		m_submit->m_commands.write(data);
 		m_submit->m_commands.write(format);
-		m_submit->m_commands.write(vertices);
 
 		return id;
 	}
 
 	/// Creates a new vertex buffer optimized for renderering dynamic vertex data.
-	/// This function only allocates storage for @a count vertices, each of the given @a format;
+	/// This function only allocates storage for @a size bytes of vertex data.
 	/// use Renderer::update_vertex_buffer() to fill the buffer with actual data.
-	VertexBufferId create_dynamic_vertex_buffer(size_t count, VertexFormat::Enum format)
+	VertexBufferId create_dynamic_vertex_buffer(size_t size)
 	{
 		const VertexBufferId id = m_vertex_buffers.create();
 
 		m_submit->m_commands.write(CommandType::CREATE_DYNAMIC_VERTEX_BUFFER);
 		m_submit->m_commands.write(id);
-		m_submit->m_commands.write(count);
-		m_submit->m_commands.write(format);
+		m_submit->m_commands.write(size);
 
 		return id;
 	}
 
-	/// Updates the vertex buffer data of @a id with @a count @a vertices starting
-	/// at the given @a offset. The @a vertices have to match the format specified at creation time.
+	/// Creates a new transient vertex buffer with storage for exactly @a size bytes.
+	/// Transient vertex buffers are useful when you have to render highly dynamic vertex data, such as font glyphs.
+	/// @note
+	/// This call is tipically only performed by the backend to allocate a common large generic transient buffer
+	/// that can be used to carve out smaller transient buffers by calling Renderer::reserve_transient_vertex_buffer().
+	TransientVertexBuffer* create_transient_vertex_buffer(size_t size)
+	{
+		VertexBufferId vb = create_dynamic_vertex_buffer(size);
+		TransientVertexBuffer* tvb = NULL;
+
+		tvb = (TransientVertexBuffer*) default_allocator().allocate(sizeof(TransientVertexBuffer) + size);
+		tvb->vb = vb;
+		tvb->data = (char*) &tvb[1]; // Nice trick
+		tvb->size = size;
+
+		return tvb;
+	}
+
+	/// Reserves a portion of the common transient vertex buffer for exactly @a num vertices
+	/// of the given @a format.
 	/// @note
-	/// @a count and @a offset together do not have to exceed the number of elements
-	/// originally specified to Renderer::create_vertex_buffer() (or Renderer::create_dynamic_vertex_buffer())
-	void update_vertex_buffer(VertexBufferId id, size_t offset, size_t count, const void* vertices)
+	/// The returned @a tvb transient buffer is valid for one frame only!
+	void reserve_transient_vertex_buffer(TransientVertexBuffer* tvb, uint32_t num, VertexFormat::Enum format)
+	{
+		CE_ASSERT(tvb != NULL, "Transient buffer must be != NULL");
+
+		TransientVertexBuffer& tvb_shared = *m_submit->m_transient_vb;
+
+		const uint32_t offset = m_submit->reserve_transient_vertex_buffer(num, format);
+
+		tvb->vb = tvb_shared.vb;
+		tvb->data = &tvb_shared.data[offset];
+		tvb->start_vertex = offset / Vertex::bytes_per_vertex(format);
+		tvb->size = Vertex::bytes_per_vertex(format) * num;
+		tvb->format = format;
+	}
+
+	/// Updates the vertex buffer data of @a id with @a size bytes of vertex @a data starting
+	/// at the given @a offset.
+	void update_vertex_buffer(VertexBufferId id, size_t offset, size_t size, const void* data)
 	{
 		CE_ASSERT(m_vertex_buffers.has(id), "Vertex buffer does not exist");
 
 		m_submit->m_commands.write(CommandType::UPDATE_VERTEX_BUFFER);
 		m_submit->m_commands.write(id);
 		m_submit->m_commands.write(offset);
-		m_submit->m_commands.write(count);
-		m_submit->m_commands.write(vertices);			
+		m_submit->m_commands.write(size);
+		m_submit->m_commands.write(data);	
 	}
 
 	/// Destroys the given vertex buffer @a id.
@@ -159,48 +207,89 @@ public:
 		m_submit->m_commands.write(id);
 	}
 
+	/// Destroys the given @a tvb transient buffer
+	void destroy_transient_vertex_buffer(TransientVertexBuffer* tvb)
+	{
+		CE_ASSERT(tvb != NULL, "Transient buffer must be != NULL");
+
+		destroy_vertex_buffer(tvb->vb);
+		default_allocator().deallocate(tvb);
+	}
+
 	/// Creates a new index buffer optimized for rendering static index buffers.
-	/// @a indices is the array containing @a count index data elements.
-	IndexBufferId create_index_buffer(size_t count, const void* indices)
+	/// @a data is the array containing @a size bytes of index data.
+	IndexBufferId create_index_buffer(size_t size, const void* data)
 	{
 		const IndexBufferId id = m_index_buffers.create();
 
 		m_submit->m_commands.write(CommandType::CREATE_INDEX_BUFFER);
 		m_submit->m_commands.write(id);
-		m_submit->m_commands.write(count);
-		m_submit->m_commands.write(indices);
+		m_submit->m_commands.write(size);
+		m_submit->m_commands.write(data);
 
 		return id;
 	}
 
 	/// Creates a new index buffer optimized for rendering dynamic index buffers.
-	/// This function only allocates storage for @a count indices;
+	/// This function only allocates storage for @a size bytes of index data.
 	/// use Renderer::update_index_buffer() to fill the buffer with actual data.
-	IndexBufferId create_dynamic_index_buffer(size_t count)
+	IndexBufferId create_dynamic_index_buffer(size_t size)
 	{
 		const IndexBufferId id = m_index_buffers.create();
 
 		m_submit->m_commands.write(CommandType::CREATE_DYNAMIC_INDEX_BUFFER);
 		m_submit->m_commands.write(id);
-		m_submit->m_commands.write(count);
+		m_submit->m_commands.write(size);
 
 		return id;
 	}
 
-	/// Updates the index buffer data of @a id with @a count @a indices starting
-	/// at the given @a offset.
+	/// Creates a new transient index buffer with storage for exactly @a size bytes.
+	/// Transient index buffers are useful when you have to render highly dynamic index data, such as font glyphs.
 	/// @note
-	/// @a count and @a offset together do not have to exceed the number of elements
-	/// originally specified to Renderer::create_index_buffer() (or Renderer::create_dynamic_index_buffer())
-	void update_index_buffer(IndexBufferId id, size_t offset, size_t count, const void* indices)
+	/// This call is tipically only performed by the backend to allocate a common large generic transient buffer
+	/// that can be used to carve out smaller transient buffers by calling Renderer::reserve_transient_index_buffer().
+	TransientIndexBuffer* create_transient_index_buffer(size_t size)
+	{
+		IndexBufferId ib = create_dynamic_index_buffer(size);
+		TransientIndexBuffer* tib = NULL;
+
+		tib = (TransientIndexBuffer*) default_allocator().allocate(sizeof(TransientIndexBuffer) + size);
+		tib->ib = ib;
+		tib->data = (char*) &tib[1]; // Same as before
+		tib->size = size;
+
+		return tib;
+	}
+
+	/// Reserves a portion of the common transient index buffer for exactly @a num indices.
+	/// @note
+	/// The returned @a tvb transient buffer is valid for one frame only!
+	void reserve_transient_index_buffer(TransientIndexBuffer* tib, uint32_t num)
+	{
+		CE_ASSERT(tib != NULL, "Transient buffer must be != NULL");
+
+		TransientIndexBuffer& tib_shared = *m_submit->m_transient_ib;
+
+		const uint32_t offset = m_submit->reserve_transient_index_buffer(num);
+
+		tib->ib = tib_shared.ib;
+		tib->data = &tib_shared.data[offset];
+		tib->start_index = offset / sizeof(uint16_t);
+		tib->size = sizeof(uint16_t) * num;
+	}
+
+	/// Updates the index buffer data of @a id with @a size bytes of index @data starting
+	/// at the given @a offset.
+	void update_index_buffer(IndexBufferId id, size_t offset, size_t size, const void* data)
 	{
 		CE_ASSERT(m_index_buffers.has(id), "Index buffer does not exist");
 
 		m_submit->m_commands.write(CommandType::UPDATE_INDEX_BUFFER);
 		m_submit->m_commands.write(id);
 		m_submit->m_commands.write(offset);
-		m_submit->m_commands.write(count);
-		m_submit->m_commands.write(indices);
+		m_submit->m_commands.write(size);
+		m_submit->m_commands.write(data);
 	}
 
 	/// Destroys the @a id index buffer.
@@ -212,6 +301,15 @@ public:
 		m_submit->m_commands.write(id);
 	}
 
+	/// Destroys the given @a tvi transient buffer
+	void destroy_transient_index_buffer(TransientIndexBuffer* tib)
+	{
+		CE_ASSERT(tib != NULL, "Transient buffer must be != NULL");
+
+		destroy_vertex_buffer(tib->ib);
+		default_allocator().deallocate(tib);
+	}
+
 	/// Creates a new texture of size @a width and @height.
 	/// The array @a data should contain @a width * @a height elements of the given @a format.
 	TextureId create_texture(uint32_t width, uint32_t height, PixelFormat::Enum format, const void* data)
@@ -365,44 +463,42 @@ public:
 				case CommandType::CREATE_VERTEX_BUFFER:
 				{
 					VertexBufferId id;
-					size_t count;
+					size_t size;
+					const void* data;
 					VertexFormat::Enum format;
-					void* vertices;
 
 					cmds.read(id);
-					cmds.read(count);
+					cmds.read(size);
+					cmds.read(data);
 					cmds.read(format);
-					cmds.read(vertices);
 
-					create_vertex_buffer_impl(id, count, format, vertices);
+					create_vertex_buffer_impl(id, size, data, format);
 					break;
 				}
 				case CommandType::CREATE_DYNAMIC_VERTEX_BUFFER:
 				{
 					VertexBufferId id;
-					size_t count;
-					VertexFormat::Enum format;
+					size_t size;
 
 					cmds.read(id);
-					cmds.read(count);
-					cmds.read(format);
+					cmds.read(size);
 
-					create_dynamic_vertex_buffer_impl(id, count, format);
+					create_dynamic_vertex_buffer_impl(id, size);
 					break;
 				}
 				case CommandType::UPDATE_VERTEX_BUFFER:
 				{
 					VertexBufferId id;
 					size_t offset;
-					size_t count;
-					void* vertices;
+					size_t size;
+					void* data;
 
 					cmds.read(id);
 					cmds.read(offset);
-					cmds.read(count);
-					cmds.read(vertices);
+					cmds.read(size);
+					cmds.read(data);
 
-					update_vertex_buffer_impl(id, offset, count, vertices);			
+					update_vertex_buffer_impl(id, offset, size, data);			
 					break;
 				}
 				case CommandType::DESTROY_VERTEX_BUFFER:
@@ -416,40 +512,40 @@ public:
 				case CommandType::CREATE_INDEX_BUFFER:
 				{
 					IndexBufferId id;
-					size_t count;
-					void* indices;
+					size_t size;
+					void* data;
 
 					cmds.read(id);
-					cmds.read(count);
-					cmds.read(indices);
+					cmds.read(size);
+					cmds.read(data);
 
-					create_index_buffer_impl(id, count, indices);
+					create_index_buffer_impl(id, size, data);
 					break;
 				}
 				case CommandType::CREATE_DYNAMIC_INDEX_BUFFER:
 				{
 					IndexBufferId id;
-					size_t count;
+					size_t size;
 
 					cmds.read(id);
-					cmds.read(count);
+					cmds.read(size);
 
-					create_dynamic_index_buffer_impl(id, count);
+					create_dynamic_index_buffer_impl(id, size);
 					break;
 				}
 				case CommandType::UPDATE_INDEX_BUFFER:
 				{
 					IndexBufferId id;
 					size_t offset;
-					size_t count;
-					void* indices;
+					size_t size;
+					void* data;
 
 					cmds.read(id);
 					cmds.read(offset);
-					cmds.read(count);
-					cmds.read(indices);
+					cmds.read(size);
+					cmds.read(data);
 
-					update_index_buffer_impl(id, offset, count, indices);
+					update_index_buffer_impl(id, offset, size, data);
 					break;
 				}
 				case CommandType::DESTROY_INDEX_BUFFER:
@@ -626,10 +722,15 @@ public:
 		m_submit->set_program(id);
 	}
 
-	void set_vertex_buffer(VertexBufferId id)
+	void set_vertex_buffer(VertexBufferId id, uint32_t num_vertices = 0xFFFFFFFF)
 	{
 		CE_ASSERT(m_vertex_buffers.has(id), "Vertex buffer does not exist");
-		m_submit->set_vertex_buffer(id);
+		m_submit->set_vertex_buffer(id, num_vertices);
+	}
+
+	void set_vertex_buffer(const TransientVertexBuffer& tvb, uint32_t num_vertices = 0xFFFFFFFF)
+	{
+		m_submit->set_vertex_buffer(tvb, num_vertices);
 	}
 
 	void set_index_buffer(IndexBufferId id, uint32_t start_index = 0, uint32_t num_indices = 0xFFFFFFFF)
@@ -638,6 +739,11 @@ public:
 		m_submit->set_index_buffer(id, start_index, num_indices);
 	}
 
+	void set_index_buffer(const TransientIndexBuffer& tib, uint32_t num_indices = 0xFFFFFFFF)
+	{
+		m_submit->set_index_buffer(tib, num_indices);
+	}
+
 	void set_uniform(UniformId id, UniformType::Enum type, const void* value, uint8_t num)
 	{
 		CE_ASSERT(m_uniforms.has(id), "Uniform does not exist");
@@ -690,11 +796,11 @@ public:
 
 	static int32_t render_thread(void* thiz)
 	{
-		Renderer* renderer = (Renderer*)thiz;
-		while (renderer->m_should_run)
-		{
-			renderer->render_all();
-		}
+		// Renderer* renderer = (Renderer*)thiz;
+		// while (renderer->m_should_run)
+		// {
+		// 	renderer->render_all();
+		// }
 
 		return 0;
 	}
@@ -712,15 +818,16 @@ public:
 	void frame()
 	{
 		// Signal main thread finished updating
-		m_render_wait.post();
-		m_main_wait.wait();
+		// m_render_wait.post();
+		// m_main_wait.wait();
+		render_all();
 	}
 
 	// Do all the processing needed to render a frame
 	void render_all()
 	{
 		// Waits for main thread to finish update
-		m_render_wait.wait();
+		// m_render_wait.wait();
 
 		swap_contexts();
 
@@ -732,7 +839,7 @@ public:
 			render_impl();
 		}
 
-		m_main_wait.post();
+		// m_main_wait.post();
 	}
 
 protected:

+ 25 - 14
engine/renderers/backend/gl/GLRenderer.cpp

@@ -262,6 +262,16 @@ public:
 		// Sort render keys
 		context.sort();
 
+		// Update transient buffers
+		if (context.m_tvb_offset)
+		{
+			m_vertex_buffers[context.m_transient_vb->vb.index].update(0, context.m_tvb_offset, context.m_transient_vb->data);
+		}
+		if (context.m_tib_offset)
+		{
+			m_index_buffers[context.m_transient_ib->ib.index].update(0, context.m_tib_offset, context.m_transient_ib->data);
+		}
+
 		for (uint32_t s = 0; s < context.m_num_states; s++)
 		{
 			const uint64_t key_s = context.m_keys[s];
@@ -462,7 +472,8 @@ public:
 				GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer.m_id));
 
 				const GPUProgram& gpu_program = m_gpu_programs[cur_state.program.index];
-				gpu_program.bind_attributes(vertex_buffer.m_format);
+				const VertexFormat::Enum format = vertex_buffer.m_format == VertexFormat::COUNT ? cur_state.vertex_format : vertex_buffer.m_format;
+				gpu_program.bind_attributes(format, cur_state.start_vertex);
 			}
 			else
 			{
@@ -475,7 +486,7 @@ public:
 				const IndexBuffer& index_buffer = m_index_buffers[ib.index];
 				const uint32_t prim_type = (flags & STATE_PRIMITIVE_MASK) >> STATE_PRIMITIVE_SHIFT;
 				const GLenum gl_prim_type = PRIMITIVE_TYPE_TABLE[prim_type];
-				const uint32_t num_indices = cur_state.num_indices == 0xFFFFFFFF ? index_buffer.m_index_count : cur_state.num_indices;
+				const uint32_t num_indices = cur_state.num_indices == 0xFFFFFFFF ? index_buffer.m_size : cur_state.num_indices;
 
 				GL_CHECK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer.m_id));
 				GL_CHECK(glDrawElements(gl_prim_type, num_indices, GL_UNSIGNED_SHORT, (void*) (uintptr_t) (cur_state.start_index * sizeof(uint16_t))));
@@ -561,21 +572,21 @@ void Renderer::render_impl()
 }
 
 //-----------------------------------------------------------------------------
-void Renderer::create_vertex_buffer_impl(VertexBufferId id, size_t count, VertexFormat::Enum format, const void* vertices)
+void Renderer::create_vertex_buffer_impl(VertexBufferId id, size_t size, const void* data, VertexFormat::Enum format)
 {
-	m_impl->m_vertex_buffers[id.index].create(count, format, vertices);
+	m_impl->m_vertex_buffers[id.index].create(size, data, format);
 }
 
 //-----------------------------------------------------------------------------
-void Renderer::create_dynamic_vertex_buffer_impl(VertexBufferId id, size_t count, VertexFormat::Enum format)
+void Renderer::create_dynamic_vertex_buffer_impl(VertexBufferId id, size_t size)
 {
-	m_impl->m_vertex_buffers[id.index].create(count, format, NULL);
+	m_impl->m_vertex_buffers[id.index].create(size, NULL, VertexFormat::COUNT);
 }
 
 //-----------------------------------------------------------------------------
-void Renderer::update_vertex_buffer_impl(VertexBufferId id, size_t offset, size_t count, const void* vertices)
+void Renderer::update_vertex_buffer_impl(VertexBufferId id, size_t offset, size_t size, const void* data)
 {
-	m_impl->m_vertex_buffers[id.index].update(offset, count, vertices);
+	m_impl->m_vertex_buffers[id.index].update(offset, size, data);
 }
 
 //-----------------------------------------------------------------------------
@@ -586,21 +597,21 @@ void Renderer::destroy_vertex_buffer_impl(VertexBufferId id)
 }
 
 //-----------------------------------------------------------------------------
-void Renderer::create_index_buffer_impl(IndexBufferId id, size_t count, const void* indices)
+void Renderer::create_index_buffer_impl(IndexBufferId id, size_t size, const void* data)
 {
-	m_impl->m_index_buffers[id.index].create(count, indices);
+	m_impl->m_index_buffers[id.index].create(size, data);
 }
 
 //-----------------------------------------------------------------------------
-void Renderer::create_dynamic_index_buffer_impl(IndexBufferId id, size_t count)
+void Renderer::create_dynamic_index_buffer_impl(IndexBufferId id, size_t size)
 {
-	m_impl->m_index_buffers[id.index].create(count, NULL);
+	m_impl->m_index_buffers[id.index].create(size, NULL);
 }
 
 //-----------------------------------------------------------------------------
-void Renderer::update_index_buffer_impl(IndexBufferId id, size_t offset, size_t count, const void* indices)
+void Renderer::update_index_buffer_impl(IndexBufferId id, size_t offset, size_t size, const void* data)
 {
-	m_impl->m_index_buffers[id.index].update(offset, count, indices);	
+	m_impl->m_index_buffers[id.index].update(offset, size, data);
 }
 
 //-----------------------------------------------------------------------------

+ 19 - 20
engine/renderers/backend/gl/GLRenderer.h

@@ -90,24 +90,23 @@ static const char* gl_error_to_string(GLenum error)
 struct VertexBuffer
 {
 	//-----------------------------------------------------------------------------
-	void create(size_t count, VertexFormat::Enum format, const void* vertices)
+	void create(size_t size, const void* data, VertexFormat::Enum format)
 	{
 		GL_CHECK(glGenBuffers(1, &m_id));
+		CE_ASSERT(m_id != 0, "Failed to create buffer");
 		GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER, m_id));
-		GL_CHECK(glBufferData(GL_ARRAY_BUFFER, count * Vertex::bytes_per_vertex(format), vertices,
-			(vertices == NULL) ? GL_STREAM_DRAW : GL_STATIC_DRAW));
+		GL_CHECK(glBufferData(GL_ARRAY_BUFFER, size, data, (data == NULL) ? GL_STREAM_DRAW : GL_STATIC_DRAW));
 		GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER, 0));
 
-		m_count = count;
+		m_size = size;
 		m_format = format;
 	}
 
 	//-----------------------------------------------------------------------------
-	void update(size_t offset, size_t count, const void* vertices)
+	void update(size_t offset, size_t size, const void* data)
 	{
 		GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER, m_id));
-		GL_CHECK(glBufferSubData(GL_ARRAY_BUFFER, offset * Vertex::bytes_per_vertex(m_format),
-									count * Vertex::bytes_per_vertex(m_format), vertices));
+		GL_CHECK(glBufferSubData(GL_ARRAY_BUFFER, offset, size, data));
 		GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER, 0));
 	}
 
@@ -120,32 +119,31 @@ struct VertexBuffer
 
 public:
 
-	GLuint			m_id;
-	size_t			m_count;
-	VertexFormat::Enum	m_format;
+	GLuint m_id;
+	size_t m_size;
+	VertexFormat::Enum m_format;
 };
 
 //-----------------------------------------------------------------------------
 struct IndexBuffer
 {
 	//-----------------------------------------------------------------------------
-	void create(size_t count, const void* indices)
+	void create(size_t size, const void* data)
 	{
 		GL_CHECK(glGenBuffers(1, &m_id));
+		CE_ASSERT(m_id != 0, "Failed to create buffer");
 		GL_CHECK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_id));
-		GL_CHECK(glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(GLushort), indices,
-					(indices == NULL) ? GL_STREAM_DRAW : GL_STATIC_DRAW));
+		GL_CHECK(glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, (data == NULL) ? GL_STREAM_DRAW : GL_STATIC_DRAW));
 		GL_CHECK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
 
-		m_index_count = count;
+		m_size = size;
 	}
 
 	//-----------------------------------------------------------------------------
-	void update(size_t offset, size_t count, const void* indices)
+	void update(size_t offset, size_t size, const void* data)
 	{
 		GL_CHECK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_id));
-		GL_CHECK(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset * sizeof(uint16_t),
-									count * sizeof(uint16_t), indices));
+		GL_CHECK(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, size, data));
 		GL_CHECK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
 	}
 
@@ -159,7 +157,7 @@ struct IndexBuffer
 public:
 
 	GLuint		m_id;
-	uint32_t	m_index_count;
+	uint32_t	m_size;
 };
 
 //-----------------------------------------------------------------------------
@@ -421,7 +419,7 @@ struct GPUProgram
 	}
 
 	//-----------------------------------------------------------------------------
-	void bind_attributes(VertexFormat::Enum format) const
+	void bind_attributes(VertexFormat::Enum format, uint32_t start_vertex) const
 	{
 		// Bind all active attributes
 		for (uint8_t i = 0; i < m_num_active_attribs; i++)
@@ -434,8 +432,9 @@ struct GPUProgram
 			if (loc != -1 && info.has_attrib(attrib))
 			{
 				GL_CHECK(glEnableVertexAttribArray(loc));
+				uint32_t base_vertex = start_vertex * info.attrib_stride(attrib) + info.attrib_offset(attrib);
 				GL_CHECK(glVertexAttribPointer(loc, info.num_components(attrib), GL_FLOAT, GL_FALSE, info.attrib_stride(attrib),
-										(GLvoid*)(uintptr_t) info.attrib_offset(attrib)));
+										(GLvoid*)(uintptr_t) base_vertex));
 			}
 		}
 	}

+ 2 - 2
engine/resource/MeshResource.h

@@ -94,8 +94,8 @@ public:
 		MeshResource* m = (MeshResource*) resource;
 		MeshHeader* h = (MeshHeader*) m;
 
-		h->vbuffer = device()->renderer()->create_vertex_buffer(m->num_vertices(), m->vertex_format(), m->vertices());
-		h->ibuffer = device()->renderer()->create_index_buffer(m->num_indices(), m->indices());
+		h->vbuffer = device()->renderer()->create_vertex_buffer(m->num_vertices() * Vertex::bytes_per_vertex(m->vertex_format()), m->vertices(), m->vertex_format());
+		h->ibuffer = device()->renderer()->create_index_buffer(m->num_indices() * sizeof(uint16_t), m->indices());
 	}
 
 	//-----------------------------------------------------------------------------

+ 2 - 2
engine/resource/SpriteResource.h

@@ -87,8 +87,8 @@ struct SpriteResource
 		const float* vertices = (float*) (((char*) resource) + h->vertices_offset);
 		const uint16_t* indices = (uint16_t*) (((char*) resource) + h->indices_offset);
 
-		h->vb = device()->renderer()->create_vertex_buffer(h->num_vertices, VertexFormat::P2_T2, vertices);
-		h->ib = device()->renderer()->create_index_buffer(h->num_indices, indices);
+		h->vb = device()->renderer()->create_vertex_buffer(h->num_vertices * Vertex::bytes_per_vertex(VertexFormat::P2_T2), vertices, VertexFormat::P2_T2);
+		h->ib = device()->renderer()->create_index_buffer(h->num_indices * sizeof(uint16_t), indices);
 	}
 
 	//-----------------------------------------------------------------------------

+ 0 - 1
utils/crown-android.rb

@@ -229,7 +229,6 @@ def fill_android_project(build, target, res, path)
 	FileUtils.cp($physx + "/lib/libPhysX3Vehicle.a", engine_dest)
 	FileUtils.cp($physx + "/lib/libPhysX3Cooking.a", engine_dest)
 	FileUtils.cp($physx + "/lib/libPvdRuntime.a", engine_dest)
-	FileUtils.cp($physx + "/lib/libRepX3.a", engine_dest)
 
 	# Copy java files
 	FileUtils.cp_r(Dir.glob($android_src), android_dest, :remove_destination => true)