Browse Source

Added example-32-particles.

Branimir Karadžić 9 years ago
parent
commit
76ecde3d37

+ 428 - 0
examples/32-particles/particles.cpp

@@ -0,0 +1,428 @@
+/*
+ * Copyright 2011-2017 Branimir Karadzic. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#include "common.h"
+#include "bgfx_utils.h"
+#include <entry/cmd.h>
+#include <entry/input.h>
+
+#include <camera.h>
+#include <debugdraw/debugdraw.h>
+#include <imgui/imgui.h>
+
+#include <bx/rng.h>
+#include <bx/easing.h>
+
+#include <ps/particle_system.h>
+
+static const char* s_shapeNames[] =
+{
+	"Sphere",
+	"Hemisphere",
+	"Circle",
+	"Disc",
+	"Rect",
+};
+
+static const char* s_directionName[] =
+{
+	"Up",
+	"Outward",
+};
+
+static const char* s_easeFuncName[] =
+{
+	"Linear",
+	"InQuad",
+	"OutQuad",
+	"InOutQuad",
+	"OutInQuad",
+	"InCubic",
+	"OutCubic",
+	"InOutCubic",
+	"OutInCubic",
+	"InQuart",
+	"OutQuart",
+	"InOutQuart",
+	"OutInQuart",
+	"InQuint",
+	"OutQuint",
+	"InOutQuint",
+	"OutInQuint",
+	"InSine",
+	"OutSine",
+	"InOutSine",
+	"OutInSine",
+	"InExpo",
+	"OutExpo",
+	"InOutExpo",
+	"OutInExpo",
+	"InCirc",
+	"OutCirc",
+	"InOutCirc",
+	"OutInCirc",
+	"InElastic",
+	"OutElastic",
+	"InOutElastic",
+	"OutInElastic",
+	"InBack",
+	"OutBack",
+	"InOutBack",
+	"OutInBack",
+	"InBounce",
+	"OutBounce",
+	"InOutBounce",
+	"OutInBounce",
+};
+BX_STATIC_ASSERT(BX_COUNTOF(s_easeFuncName) == bx::Easing::Count);
+
+struct Emitter
+{
+	EmitterUniforms m_uniforms;
+	EmitterHandle   m_handle;
+
+	EmitterShape::Enum     m_shape;
+	EmitterDirection::Enum m_direction;
+
+	void create()
+	{
+		m_shape      = EmitterShape::Sphere;
+		m_direction  = EmitterDirection::Outward;
+
+		m_handle = psCreateEmitter(m_shape, m_direction, 1024);
+		m_uniforms.reset();
+	}
+
+	void destroy()
+	{
+		psDestroyEmitter(m_handle);
+	}
+
+	void update()
+	{
+		psUpdateEmitter(m_handle, &m_uniforms);
+	}
+
+	void imgui(const float* _view, const float* _proj)
+	{
+//		if (ImGui::CollapsingHeader("General") )
+		{
+			if (ImGui::Combo("Shape", (int*)&m_shape, s_shapeNames, BX_COUNTOF(s_shapeNames) )
+					||  ImGui::Combo("Direction", (int*)&m_direction, s_directionName, BX_COUNTOF(s_directionName) ) )
+			{
+				psDestroyEmitter(m_handle);
+				m_handle = psCreateEmitter(m_shape, m_direction, 1024);
+			}
+
+			ImGui::SliderInt("particles / s", (int*)&m_uniforms.m_particlesPerSecond, 0, 1024);
+
+			ImGui::SliderFloat("Gravity scale"
+					, &m_uniforms.m_gravityScale
+					, -2.0f
+					,  2.0f
+					);
+
+			ImGui::RangeSliderFloat("Life span"
+					, &m_uniforms.m_lifeSpan[0]
+					, &m_uniforms.m_lifeSpan[1]
+					, 0.1f
+					, 5.0f
+					);
+
+			if (ImGui::Button("Reset") )
+			{
+				psUpdateEmitter(m_handle);
+			}
+		}
+
+		if (ImGui::CollapsingHeader("Position and scale") )
+		{
+			ImGui::Combo("Position Ease", (int*)&m_uniforms.m_easePos, s_easeFuncName, BX_COUNTOF(s_easeFuncName) );
+
+			ImGui::RangeSliderFloat("Start offset"
+					, &m_uniforms.m_offsetStart[0]
+					, &m_uniforms.m_offsetStart[1]
+					, 0.0f
+					, 10.0f
+					);
+			ImGui::RangeSliderFloat("End offset"
+					, &m_uniforms.m_offsetEnd[0]
+					, &m_uniforms.m_offsetEnd[1]
+					, 0.0f
+					, 10.0f
+					);
+
+			ImGui::Text("Scale:");
+
+			ImGui::Combo("Scale Ease", (int*)&m_uniforms.m_easeScale, s_easeFuncName, BX_COUNTOF(s_easeFuncName) );
+
+			ImGui::RangeSliderFloat("Scale Start"
+					, &m_uniforms.m_scaleStart[0]
+					, &m_uniforms.m_scaleStart[1]
+					, 0.0f
+					, 3.0f
+					);
+			ImGui::RangeSliderFloat("Scale End"
+					, &m_uniforms.m_scaleEnd[0]
+					, &m_uniforms.m_scaleEnd[1]
+					, 0.0f
+					, 3.0f
+					);
+		}
+
+		if (ImGui::CollapsingHeader("Blending and color") )
+		{
+			ImGui::Combo("Blend Ease", (int*)&m_uniforms.m_easeBlend, s_easeFuncName, BX_COUNTOF(s_easeFuncName) );
+			ImGui::RangeSliderFloat("Blend Start"
+					, &m_uniforms.m_blendStart[0]
+					, &m_uniforms.m_blendStart[1]
+					, 0.0f
+					, 1.0f
+					);
+			ImGui::RangeSliderFloat("Blend End"
+					, &m_uniforms.m_blendEnd[0]
+					, &m_uniforms.m_blendEnd[1]
+					, 0.0f
+					, 1.0f
+					);
+
+			ImGui::Text("Color:");
+
+			ImGui::Combo("RGBA Ease", (int*)&m_uniforms.m_easeRgba, s_easeFuncName, BX_COUNTOF(s_easeFuncName) );
+			ImGui::ColorEdit4("RGBA0", &m_uniforms.m_rgba[0], true);
+			ImGui::ColorEdit4("RGBA1", &m_uniforms.m_rgba[1], true);
+			ImGui::ColorEdit4("RGBA2", &m_uniforms.m_rgba[2], true);
+			ImGui::ColorEdit4("RGBA3", &m_uniforms.m_rgba[3], true);
+			ImGui::ColorEdit4("RGBA4", &m_uniforms.m_rgba[4], true);
+		}
+
+		ImGui::End();
+
+		float mtx[16];
+		bx::mtxSRT(mtx
+				, 1.0f, 1.0f, 1.0f
+				, m_uniforms.m_angle[0],    m_uniforms.m_angle[1],    m_uniforms.m_angle[2]
+				, m_uniforms.m_position[0], m_uniforms.m_position[1], m_uniforms.m_position[2]
+				);
+
+		ImGuizmo::Manipulate(
+				_view
+				, _proj
+				, ImGuizmo::OPERATION::TRANSLATE
+				, ImGuizmo::MODE::LOCAL
+				, mtx
+				);
+
+		float scale[3];
+		ImGuizmo::DecomposeMatrixToComponents(mtx, m_uniforms.m_position, m_uniforms.m_angle, scale);
+	}
+};
+
+class Particles : public entry::AppI
+{
+	void init(int _argc, char** _argv) BX_OVERRIDE
+	{
+		Args args(_argc, _argv);
+
+		m_width  = 1280;
+		m_height = 720;
+		m_debug  = BGFX_DEBUG_TEXT;
+		m_reset  = BGFX_RESET_VSYNC;
+
+		bgfx::init(args.m_type, args.m_pciId);
+		bgfx::reset(m_width, m_height, m_reset);
+
+		// Enable m_debug text.
+		bgfx::setDebug(m_debug);
+
+		// Set view 0 clear state.
+		bgfx::setViewClear(0
+				, BGFX_CLEAR_COLOR|BGFX_CLEAR_DEPTH
+				, 0x202020ff
+				, 1.0f
+				, 0
+				);
+
+		ddInit();
+
+		psInit();
+
+		for (uint32_t ii = 0; ii < BX_COUNTOF(m_emitter); ++ii)
+		{
+			m_emitter[ii].create();
+		}
+
+		imguiCreate();
+
+		cameraCreate();
+
+		const float initialPos[3] = { 0.0f, 2.0f, -12.0f };
+		cameraSetPosition(initialPos);
+		cameraSetVerticalAngle(0.0f);
+
+		m_timeOffset = bx::getHPCounter();
+	}
+
+	virtual int shutdown() BX_OVERRIDE
+	{
+		for (uint32_t ii = 0; ii < BX_COUNTOF(m_emitter); ++ii)
+		{
+			m_emitter[ii].destroy();
+		}
+
+		psShutdown();
+
+		ddShutdown();
+
+		imguiDestroy();
+
+		cameraDestroy();
+
+		// Shutdown bgfx.
+		bgfx::shutdown();
+
+		return 0;
+	}
+
+	bool update() BX_OVERRIDE
+	{
+		if (!entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState) )
+		{
+			// Set view 0 default viewport.
+			bgfx::setViewRect(0, 0, 0, m_width, m_height);
+
+			bgfx::touch(0);
+
+			int64_t now = bx::getHPCounter() - m_timeOffset;
+			static int64_t last = now;
+			const int64_t frameTime = now - last;
+			last = now;
+			const double freq = double(bx::getHPFrequency() );
+			const double toMs = 1000.0/freq;
+			const float deltaTime = float(frameTime/freq);
+
+			// Use debug font to print information about this example.
+			bgfx::dbgTextClear();
+			bgfx::dbgTextPrintf(0, 1, 0x4f, "bgfx/examples/xx-particles");
+			bgfx::dbgTextPrintf(0, 2, 0x6f, "Description: Particles.");
+			bgfx::dbgTextPrintf(0, 3, 0x0f, "Frame: % 7.3f[ms]", double(frameTime)*toMs);
+
+			// Update camera.George RR Martin
+			cameraUpdate(deltaTime, m_mouseState);
+
+			float view[16];
+			cameraGetViewMtx(view);
+
+			float proj[16];
+
+			// Set view and projection matrix for view 0.
+			const bgfx::HMD* hmd = bgfx::getHMD();
+			if (NULL != hmd && 0 != (hmd->flags & BGFX_HMD_RENDERING) )
+			{
+				float eye[3];
+				cameraGetPosition(eye);
+				bx::mtxQuatTranslationHMD(view, hmd->eye[0].rotation, eye);
+				bgfx::setViewTransform(0, view, hmd->eye[0].projection, BGFX_VIEW_STEREO, hmd->eye[1].projection);
+				bgfx::setViewRect(0, 0, 0, hmd->width, hmd->height);
+			}
+			else
+			{
+				bx::mtxProj(proj, 60.0f, float(m_width)/float(m_height), 0.1f, 100.0f);
+
+				bgfx::setViewTransform(0, view, proj);
+				bgfx::setViewRect(0, 0, 0, m_width, m_height);
+			}
+
+			imguiBeginFrame(
+				   m_mouseState.m_mx
+				,  m_mouseState.m_my
+				, (m_mouseState.m_buttons[entry::MouseButton::Left  ] ? IMGUI_MBUT_LEFT   : 0)
+				| (m_mouseState.m_buttons[entry::MouseButton::Right ] ? IMGUI_MBUT_RIGHT  : 0)
+				| (m_mouseState.m_buttons[entry::MouseButton::Middle] ? IMGUI_MBUT_MIDDLE : 0)
+				,  m_mouseState.m_mz
+				, m_width
+				, m_height
+				);
+
+			ImGui::Begin("Properties"
+				, NULL
+				, ImVec2(400.0f, 600.0f)
+				, ImGuiWindowFlags_AlwaysAutoResize
+				);
+
+			static float timeScale = 1.0f;
+			ImGui::SliderFloat("Time scale"
+				, &timeScale
+				, 0.0f
+				, 1.0f
+				);
+
+			static bool showBounds;
+			ImGui::Checkbox("Show bounds", &showBounds);
+
+			ImGui::Text("Emitter:");
+			static int currentEmitter = 0;
+			for (uint32_t ii = 0; ii < BX_COUNTOF(m_emitter); ++ii)
+			{
+				ImGui::SameLine();
+
+				char name[16];
+				bx::snprintf(name, BX_COUNTOF(name), "%d", ii);
+
+				ImGui::RadioButton(name, &currentEmitter, ii);
+			}
+
+			m_emitter[currentEmitter].imgui(view, proj);
+
+			imguiEndFrame();
+
+			ddBegin(0);
+
+			float center[3] = { 0.0f, 0.0f, 0.0f };
+			ddDrawGrid(Axis::Y, center);
+
+			float eye[3];
+			cameraGetPosition(eye);
+
+			m_emitter[currentEmitter].update();
+
+			psUpdate(deltaTime * timeScale);
+			psRender(0, view, eye);
+
+			if (showBounds)
+			{
+//				Aabb aabb;
+//				toAabb(aabb, tvb.data, tvb.size/tvb.stride, tvb.stride);
+
+//				ddSetColor(0xff0000ff);
+//				ddDraw(aabb);
+			}
+
+
+			ddEnd();
+
+			// Advance to next frame. Rendering thread will be kicked to
+			// process submitted rendering primitives.
+			bgfx::frame();
+
+			return true;
+		}
+
+		return false;
+	}
+
+	entry::MouseState m_mouseState;
+
+	int64_t m_timeOffset;
+
+	uint32_t m_width;
+	uint32_t m_height;
+	uint32_t m_debug;
+	uint32_t m_reset;
+
+	Emitter m_emitter[4];
+};
+
+ENTRY_IMPLEMENT_MAIN(Particles);

+ 150 - 0
examples/common/ps/fs_particle.bin.h

@@ -0,0 +1,150 @@
+static const uint8_t fs_particle_glsl[403] =
+{
+	0x46, 0x53, 0x48, 0x04, 0x01, 0x83, 0xf2, 0xe1, 0x01, 0x00, 0x0a, 0x73, 0x5f, 0x74, 0x65, 0x78, // FSH........s_tex
+	0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x73, 0x01, 0x00, 0x00, 0x76, // Color......s...v
+	0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, // arying highp vec
+	0x34, 0x20, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x3b, 0x0a, 0x76, 0x61, 0x72, 0x79, // 4 v_color0;.vary
+	0x69, 0x6e, 0x67, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x76, // ing highp vec4 v
+	0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, // _texcoord0;.unif
+	0x6f, 0x72, 0x6d, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x20, 0x73, 0x5f, // orm sampler2D s_
+	0x74, 0x65, 0x78, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, // texColor;.void m
+	0x61, 0x69, 0x6e, 0x20, 0x28, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x6c, 0x6f, 0x77, 0x70, 0x20, // ain ().{.  lowp 
+	0x76, 0x65, 0x63, 0x34, 0x20, 0x72, 0x67, 0x62, 0x61, 0x5f, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x6c, // vec4 rgba_1;.  l
+	0x6f, 0x77, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, // owp vec4 tmpvar_
+	0x32, 0x3b, 0x0a, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x20, 0x3d, 0x20, // 2;.  tmpvar_2 = 
+	0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x44, 0x20, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, // texture2D (s_tex
+	0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, // Color, v_texcoor
+	0x64, 0x30, 0x2e, 0x78, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x72, 0x67, 0x62, 0x61, 0x5f, 0x31, // d0.xy);.  rgba_1
+	0x2e, 0x78, 0x79, 0x7a, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, // .xyz = ((tmpvar_
+	0x32, 0x2e, 0x78, 0x78, 0x78, 0x20, 0x2a, 0x20, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, // 2.xxx * v_color0
+	0x2e, 0x78, 0x79, 0x7a, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, // .xyz) * (tmpvar_
+	0x32, 0x2e, 0x78, 0x20, 0x2a, 0x20, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x2e, 0x77, // 2.x * v_color0.w
+	0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x72, 0x67, 0x62, 0x61, 0x5f, 0x31, 0x2e, 0x77, 0x20, 0x3d, // ));.  rgba_1.w =
+	0x20, 0x28, 0x28, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x2e, 0x78, 0x20, 0x2a, 0x20, //  ((tmpvar_2.x * 
+	0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x2e, 0x77, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, // v_color0.w) * (1
+	0x2e, 0x30, 0x20, 0x2d, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, // .0 - v_texcoord0
+	0x2e, 0x7a, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, // .z));.  gl_FragC
+	0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x72, 0x67, 0x62, 0x61, 0x5f, 0x31, 0x3b, 0x0a, 0x7d, // olor = rgba_1;.}
+	0x0a, 0x0a, 0x00,                                                                               // ...
+};
+static const uint8_t fs_particle_dx9[326] =
+{
+	0x46, 0x53, 0x48, 0x04, 0x01, 0x83, 0xf2, 0xe1, 0x01, 0x00, 0x0a, 0x73, 0x5f, 0x74, 0x65, 0x78, // FSH........s_tex
+	0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x01, 0x00, 0x00, 0x01, 0x00, 0x28, 0x01, 0x00, 0x03, 0xff, // Color0.....(....
+	0xff, 0xfe, 0xff, 0x20, 0x00, 0x43, 0x54, 0x41, 0x42, 0x1c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, // ... .CTAB....S..
+	0x00, 0x00, 0x03, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x91, 0x00, // ................
+	0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, // .L...0..........
+	0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x43, 0x6f, // .<.......s_texCo
+	0x6c, 0x6f, 0x72, 0x00, 0xab, 0x04, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, // lor.............
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x73, 0x5f, 0x33, 0x5f, 0x30, 0x00, 0x4d, 0x69, 0x63, 0x72, // .....ps_3_0.Micr
+	0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, // osoft (R) HLSL S
+	0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, // hader Compiler 1
+	0x30, 0x2e, 0x31, 0x00, 0xab, 0x51, 0x00, 0x00, 0x05, 0x00, 0x00, 0x0f, 0xa0, 0x00, 0x00, 0x80, // 0.1..Q..........
+	0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, // ?...............
+	0x02, 0x0a, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, // ................
+	0x80, 0x01, 0x00, 0x07, 0x90, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x90, 0x00, 0x08, 0x0f, // ................
+	0xa0, 0x42, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80, 0x01, 0x00, 0xe4, 0x90, 0x00, 0x08, 0xe4, // .B..............
+	0xa0, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, // ................
+	0x80, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x54, 0x80, 0x00, 0x00, 0x93, // ...........T....
+	0x90, 0x05, 0x00, 0x00, 0x03, 0x00, 0x08, 0x07, 0x80, 0x00, 0x00, 0xf9, 0x80, 0x00, 0x00, 0xff, // ................
+	0x90, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x00, 0xaa, // ................
+	0x91, 0x05, 0x00, 0x00, 0x03, 0x00, 0x08, 0x08, 0x80, 0x00, 0x00, 0x55, 0x80, 0x00, 0x00, 0x00, // ...........U....
+	0x80, 0xff, 0xff, 0x00, 0x00, 0x00,                                                             // ......
+};
+static const uint8_t fs_particle_dx11[517] =
+{
+	0x46, 0x53, 0x48, 0x04, 0x01, 0x83, 0xf2, 0xe1, 0x01, 0x00, 0x0a, 0x73, 0x5f, 0x74, 0x65, 0x78, // FSH........s_tex
+	0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x01, 0x00, 0x00, 0x01, 0x00, 0xe4, 0x01, 0x44, 0x58, 0x42, // Color0.......DXB
+	0x43, 0xdd, 0x04, 0xf1, 0x4a, 0xaa, 0xb0, 0xdf, 0xe0, 0xf5, 0x18, 0x2f, 0x3b, 0x6e, 0xa9, 0x0e, // C...J....../;n..
+	0x0a, 0x01, 0x00, 0x00, 0x00, 0xe4, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, // .............,..
+	0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x49, 0x53, 0x47, 0x4e, 0x6c, 0x00, 0x00, // .........ISGNl..
+	0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .........P......
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, // ................
+	0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // ................
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .........b......
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x07, 0x00, // ................
+	0x00, 0x53, 0x56, 0x5f, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x43, 0x4f, 0x4c, // .SV_POSITION.COL
+	0x4f, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x00, 0xab, 0x4f, 0x53, 0x47, // OR.TEXCOORD..OSG
+	0x4e, 0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, // N,........... ..
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
+	0x00, 0x0f, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5f, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x00, 0xab, // .....SV_TARGET..
+	0xab, 0x53, 0x48, 0x44, 0x52, 0x08, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, // [email protected]..
+	0x00, 0x5a, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x18, 0x00, // .Z....`......X..
+	0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x62, 0x10, 0x00, // ..p......UU..b..
+	0x03, 0xf2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0x72, 0x10, 0x10, // .........b...r..
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xf2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, // .....e.... .....
+	0x00, 0x68, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xf2, 0x00, 0x10, // .h.......E......
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7e, 0x10, // .....F.......F~.
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, // ......`......8..
+	0x07, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // ."..............
+	0x00, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0xf2, 0x00, 0x10, // .........8......
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x05, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x19, 0x10, // .....F.......6..
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0x72, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, // .....8...r .....
+	0x00, 0x96, 0x07, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x1f, 0x10, 0x00, 0x01, 0x00, 0x00, // ................
+	0x00, 0x00, 0x00, 0x00, 0x08, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x10, 0x10, // .....".......*..
+	0x80, 0x41, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x80, // .A........@.....
+	0x3f, 0x38, 0x00, 0x00, 0x07, 0x82, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x10, // ?8.... .........
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, // .............>..
+	0x01, 0x00, 0x00, 0x00, 0x00,                                                                   // .....
+};
+static const uint8_t fs_particle_mtl[882] =
+{
+	0x46, 0x53, 0x48, 0x04, 0x01, 0x83, 0xf2, 0xe1, 0x00, 0x00, 0x63, 0x03, 0x00, 0x00, 0x75, 0x73, // FSH.......c...us
+	0x69, 0x6e, 0x67, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x6d, 0x65, // ing namespace me
+	0x74, 0x61, 0x6c, 0x3b, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x78, 0x6c, 0x61, 0x74, // tal;.struct xlat
+	0x4d, 0x74, 0x6c, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x7b, // MtlShaderInput {
+	0x0a, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, // .  float4 v_colo
+	0x72, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x76, 0x5f, 0x74, // r0;.  float4 v_t
+	0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x3b, 0x0a, 0x7d, 0x3b, 0x0a, 0x73, 0x74, 0x72, // excoord0;.};.str
+	0x75, 0x63, 0x74, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x53, 0x68, 0x61, 0x64, 0x65, // uct xlatMtlShade
+	0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x68, 0x61, 0x6c, 0x66, // rOutput {.  half
+	0x34, 0x20, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0a, // 4 gl_FragColor;.
+	0x7d, 0x3b, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, // };.struct xlatMt
+	0x6c, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x7b, // lShaderUniform {
+	0x0a, 0x7d, 0x3b, 0x0a, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x78, 0x6c, 0x61, // .};.fragment xla
+	0x74, 0x4d, 0x74, 0x6c, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, // tMtlShaderOutput
+	0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, 0x20, 0x28, 0x78, 0x6c, //  xlatMtlMain (xl
+	0x61, 0x74, 0x4d, 0x74, 0x6c, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, // atMtlShaderInput
+	0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x69, 0x20, 0x5b, 0x5b, 0x73, 0x74, 0x61, 0x67, 0x65, 0x5f, //  _mtl_i [[stage_
+	0x69, 0x6e, 0x5d, 0x5d, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x78, // in]], constant x
+	0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x55, 0x6e, 0x69, 0x66, // latMtlShaderUnif
+	0x6f, 0x72, 0x6d, 0x26, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x20, 0x5b, 0x5b, 0x62, 0x75, // orm& _mtl_u [[bu
+	0x66, 0x66, 0x65, 0x72, 0x28, 0x30, 0x29, 0x5d, 0x5d, 0x0a, 0x20, 0x20, 0x2c, 0x20, 0x20, 0x20, // ffer(0)]].  ,   
+	0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, // texture2d<float>
+	0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x5b, 0x5b, 0x74, 0x65, //  s_texColor [[te
+	0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x30, 0x29, 0x5d, 0x5d, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, // xture(0)]], samp
+	0x6c, 0x65, 0x72, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x73, 0x6d, 0x70, 0x5f, 0x73, 0x5f, 0x74, 0x65, // ler _mtlsmp_s_te
+	0x78, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x5b, 0x5b, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, // xColor [[sampler
+	0x28, 0x30, 0x29, 0x5d, 0x5d, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, // (0)]]).{.  xlatM
+	0x74, 0x6c, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x5f, // tlShaderOutput _
+	0x6d, 0x74, 0x6c, 0x5f, 0x6f, 0x3b, 0x0a, 0x20, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x20, 0x72, // mtl_o;.  half4 r
+	0x67, 0x62, 0x61, 0x5f, 0x31, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x68, 0x61, 0x6c, // gba_1 = 0;.  hal
+	0x66, 0x34, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x20, 0x3d, 0x20, 0x30, 0x3b, // f4 tmpvar_2 = 0;
+	0x0a, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x20, 0x3d, 0x20, 0x68, 0x61, // .  tmpvar_2 = ha
+	0x6c, 0x66, 0x34, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x73, // lf4(s_texColor.s
+	0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x73, 0x6d, 0x70, 0x5f, 0x73, 0x5f, // ample(_mtlsmp_s_
+	0x74, 0x65, 0x78, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, // texColor, (float
+	0x32, 0x29, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x69, 0x2e, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, // 2)(_mtl_i.v_texc
+	0x6f, 0x6f, 0x72, 0x64, 0x30, 0x2e, 0x78, 0x79, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x72, // oord0.xy)));.  r
+	0x67, 0x62, 0x61, 0x5f, 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x28, 0x68, // gba_1.xyz = (((h
+	0x61, 0x6c, 0x66, 0x33, 0x29, 0x28, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x29, 0x28, 0x74, // alf3)((float3)(t
+	0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x2e, 0x78, 0x78, 0x78, 0x29, 0x20, 0x2a, 0x20, 0x5f, // mpvar_2.xxx) * _
+	0x6d, 0x74, 0x6c, 0x5f, 0x69, 0x2e, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x2e, 0x78, // mtl_i.v_color0.x
+	0x79, 0x7a, 0x29, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x28, 0x68, 0x61, 0x6c, 0x66, 0x29, 0x28, 0x28, // yz)) * ((half)((
+	0x66, 0x6c, 0x6f, 0x61, 0x74, 0x29, 0x28, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x2e, // float)(tmpvar_2.
+	0x78, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x69, 0x2e, 0x76, 0x5f, 0x63, 0x6f, // x) * _mtl_i.v_co
+	0x6c, 0x6f, 0x72, 0x30, 0x2e, 0x77, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x72, 0x67, 0x62, // lor0.w)));.  rgb
+	0x61, 0x5f, 0x31, 0x2e, 0x77, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x68, 0x61, 0x6c, 0x66, 0x29, 0x28, // a_1.w = ((half)(
+	0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x29, 0x28, 0x28, 0x28, 0x68, 0x61, 0x6c, 0x66, 0x29, 0x28, // (float)(((half)(
+	0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x29, 0x28, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, // (float)(tmpvar_2
+	0x2e, 0x78, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x69, 0x2e, 0x76, 0x5f, 0x63, // .x) * _mtl_i.v_c
+	0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x2e, 0x77, 0x29, 0x29, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, // olor0.w))) * (1.
+	0x30, 0x20, 0x2d, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x69, 0x2e, 0x76, 0x5f, 0x74, 0x65, 0x78, // 0 - _mtl_i.v_tex
+	0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x2e, 0x7a, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x5f, // coord0.z)));.  _
+	0x6d, 0x74, 0x6c, 0x5f, 0x6f, 0x2e, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, // mtl_o.gl_FragCol
+	0x6f, 0x72, 0x20, 0x3d, 0x20, 0x72, 0x67, 0x62, 0x61, 0x5f, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x72, // or = rgba_1;.  r
+	0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x6f, 0x3b, 0x0a, 0x7d, 0x0a, // eturn _mtl_o;.}.
+	0x0a, 0x00,                                                                                     // ..
+};
+extern const uint8_t* fs_particle_pssl;
+extern const uint32_t fs_particle_pssl_size;

+ 19 - 0
examples/common/ps/fs_particle.sc

@@ -0,0 +1,19 @@
+$input v_color0, v_texcoord0
+
+/*
+ * Copyright 2011-2017 Branimir Karadzic. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#include <bgfx_shader.sh>
+
+SAMPLER2D(s_texColor, 0);
+
+void main()
+{
+	vec4 rgba = texture2D(s_texColor, v_texcoord0.xy).xxxx;
+
+	rgba.xyz = rgba.xyz * v_color0.xyz * rgba.w * v_color0.w;
+	rgba.w   = rgba.w * v_color0.w * (1.0f - v_texcoord0.z);
+	gl_FragColor = rgba;
+}

+ 9 - 0
examples/common/ps/makefile

@@ -0,0 +1,9 @@
+#
+# Copyright 2011-2016 Branimir Karadzic. All rights reserved.
+# License: http://www.opensource.org/licenses/BSD-2-Clause
+#
+
+include ../../../../bgfx/scripts/shader-embeded.mk
+
+rebuild:
+	@make -s --no-print-directory clean all

+ 638 - 0
examples/common/ps/particle_system.cpp

@@ -0,0 +1,638 @@
+/*
+ * Copyright 2011-2017 Branimir Karadzic. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#include <bgfx/bgfx.h>
+#include <bgfx/embedded_shader.h>
+
+#include "particle_system.h"
+#include "../bgfx_utils.h"
+
+#include <bx/easing.h>
+#include <bx/crtimpl.h>
+#include <bx/handlealloc.h>
+
+#include "vs_particle.bin.h"
+#include "fs_particle.bin.h"
+
+static const bgfx::EmbeddedShader s_embeddedShaders[] =
+{
+	BGFX_EMBEDDED_SHADER(vs_particle),
+	BGFX_EMBEDDED_SHADER(fs_particle),
+
+	BGFX_EMBEDDED_SHADER_END()
+};
+
+static const bx::EaseFn s_easeFunc[] =
+{
+	bx::easeLinear,
+	bx::easeInQuad,
+	bx::easeOutQuad,
+	bx::easeInOutQuad,
+	bx::easeOutInQuad,
+	bx::easeInCubic,
+	bx::easeOutCubic,
+	bx::easeInOutCubic,
+	bx::easeOutInCubic,
+	bx::easeInQuart,
+	bx::easeOutQuart,
+	bx::easeInOutQuart,
+	bx::easeOutInQuart,
+	bx::easeInQuint,
+	bx::easeOutQuint,
+	bx::easeInOutQuint,
+	bx::easeOutInQuint,
+	bx::easeInSine,
+	bx::easeOutSine,
+	bx::easeInOutSine,
+	bx::easeOutInSine,
+	bx::easeInExpo,
+	bx::easeOutExpo,
+	bx::easeInOutExpo,
+	bx::easeOutInExpo,
+	bx::easeInCirc,
+	bx::easeOutCirc,
+	bx::easeInOutCirc,
+	bx::easeOutInCirc,
+	bx::easeInElastic,
+	bx::easeOutElastic,
+	bx::easeInOutElastic,
+	bx::easeOutInElastic,
+	bx::easeInBack,
+	bx::easeOutBack,
+	bx::easeInOutBack,
+	bx::easeOutInBack,
+	bx::easeInBounce,
+	bx::easeOutBounce,
+	bx::easeInOutBounce,
+	bx::easeOutInBounce,
+};
+BX_STATIC_ASSERT(BX_COUNTOF(s_easeFunc) == bx::Easing::Count);
+
+struct PosColorTexCoord0Vertex
+{
+	float m_x;
+	float m_y;
+	float m_z;
+	uint32_t m_abgr;
+	float m_u;
+	float m_v;
+	float m_blend;
+	float m_angle;
+
+	static void init()
+	{
+		ms_decl
+			.begin()
+			.add(bgfx::Attrib::Position,  3, bgfx::AttribType::Float)
+			.add(bgfx::Attrib::Color0,    4, bgfx::AttribType::Uint8, true)
+			.add(bgfx::Attrib::TexCoord0, 4, bgfx::AttribType::Float)
+			.end();
+	}
+
+	static bgfx::VertexDecl ms_decl;
+};
+
+bgfx::VertexDecl PosColorTexCoord0Vertex::ms_decl;
+
+struct Particle
+{
+	float start[3];
+	float end[2][3];
+	float blendStart;
+	float blendEnd;
+	float scaleStart;
+	float scaleEnd;
+
+	uint32_t rgba[5];
+
+	float life;
+	float lifeSpan;
+};
+
+struct ParticleSort
+{
+	float    dist;
+	uint32_t idx;
+};
+
+inline uint32_t toAbgr(const float* _rgba)
+{
+	return 0
+		| (uint8_t(_rgba[0]*255.0f)<< 0)
+		| (uint8_t(_rgba[1]*255.0f)<< 8)
+		| (uint8_t(_rgba[2]*255.0f)<<16)
+		| (uint8_t(_rgba[3]*255.0f)<<24)
+		;
+}
+
+inline uint32_t toAbgr(float _rr, float _gg, float _bb, float _aa)
+{
+	return 0
+		| (uint8_t(_rr*255.0f)<< 0)
+		| (uint8_t(_gg*255.0f)<< 8)
+		| (uint8_t(_bb*255.0f)<<16)
+		| (uint8_t(_aa*255.0f)<<24)
+		;
+}
+
+struct Emitter
+{
+	void create(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles);
+	void destroy();
+
+	void reset()
+	{
+		m_num = 0;
+	}
+
+	void update(float _dt)
+	{
+		uint32_t num = m_num;
+		for (uint32_t ii = 0; ii < num; ++ii)
+		{
+			Particle& particle = m_particles[ii];
+			particle.life += _dt * 1.0f/particle.lifeSpan;
+
+			if (particle.life > 1.0f)
+			{
+				if (ii != num-1)
+				{
+					memcpy(&particle, &m_particles[num-1], sizeof(Particle) );
+					--ii;
+				}
+
+				--num;
+			}
+		}
+
+		m_num = num;
+
+		if (0 < m_uniforms.m_particlesPerSecond)
+		{
+			spawn(_dt);
+		}
+	}
+
+	void spawn(float _dt)
+	{
+		float mtx[16];
+		bx::mtxSRT(mtx
+			, 1.0f, 1.0f, 1.0f
+			, m_uniforms.m_angle[0],    m_uniforms.m_angle[1],    m_uniforms.m_angle[2]
+			, m_uniforms.m_position[0], m_uniforms.m_position[1], m_uniforms.m_position[2]
+			);
+
+		const float timePerParticle = 1.0f/m_uniforms.m_particlesPerSecond;
+		m_dt += _dt;
+		const uint32_t numParticles = uint32_t(m_dt / timePerParticle);
+		m_dt -= numParticles * timePerParticle;
+
+		float time = 0.0f;
+		for (uint32_t ii = 0
+			; ii < numParticles && m_num < m_max
+			; ++ii
+			)
+		{
+			Particle& particle = m_particles[m_num];
+			m_num++;
+
+			const float up[3] = { 0.0f, 1.0f, 0.0f };
+
+			float pos[3];
+			switch (m_shape)
+			{
+				default:
+				case EmitterShape::Sphere:
+					bx::randUnitSphere(pos, &m_rng);
+					break;
+
+				case EmitterShape::Hemisphere:
+					bx::randUnitHemisphere(pos, &m_rng, up);
+					break;
+
+				case EmitterShape::Circle:
+					bx::randUnitCircle(pos, &m_rng);
+					break;
+
+				case EmitterShape::Disc:
+					{
+						float tmp[3];
+						bx::randUnitCircle(tmp, &m_rng);
+						bx::vec3Mul(pos, tmp, bx::frnd(&m_rng) );
+					}
+					break;
+
+				case EmitterShape::Rect:
+					pos[0] = bx::frndh(&m_rng);
+					pos[1] = 0.0f;
+					pos[2] = bx::frndh(&m_rng);
+					break;
+			}
+
+			float dir[3];
+			switch (m_direction)
+			{
+				default:
+				case EmitterDirection::Up:
+					bx::vec3Move(dir, up);
+					break;
+
+				case EmitterDirection::Outward:
+					bx::vec3Norm(dir, pos);
+					break;
+			}
+
+			float start[3];
+			float end[3];
+			const float startOffset = bx::flerp(m_uniforms.m_offsetStart[0], m_uniforms.m_offsetStart[1], bx::frnd(&m_rng) );
+			bx::vec3Mul(start, pos, startOffset);
+
+			const float endOffset = bx::flerp(m_uniforms.m_offsetEnd[0], m_uniforms.m_offsetEnd[1], bx::frnd(&m_rng) );
+			float tmp1[3];
+			bx::vec3Mul(tmp1, dir, endOffset);
+			bx::vec3Add(end, tmp1, start);
+
+			particle.life = time;
+			particle.lifeSpan = bx::flerp(m_uniforms.m_lifeSpan[0], m_uniforms.m_lifeSpan[1], bx::frnd(&m_rng) );
+
+			float gravity[3] = { 0.0f, -9.81f * m_uniforms.m_gravityScale * bx::fsq(particle.lifeSpan), 0.0f };
+
+			bx::vec3MulMtx(particle.start,  start, mtx);
+			bx::vec3MulMtx(particle.end[0], end,   mtx);
+			bx::vec3Add(particle.end[1], particle.end[0], gravity);
+
+			memcpy(particle.rgba, m_uniforms.m_rgba, BX_COUNTOF(m_uniforms.m_rgba)*sizeof(uint32_t) );
+
+			particle.blendStart = bx::flerp(m_uniforms.m_blendStart[0], m_uniforms.m_blendStart[1], bx::frnd(&m_rng) );
+			particle.blendStart = bx::flerp(m_uniforms.m_blendEnd[0],   m_uniforms.m_blendEnd[1],   bx::frnd(&m_rng) );
+
+			particle.scaleStart = bx::flerp(m_uniforms.m_scaleStart[0], m_uniforms.m_scaleStart[1], bx::frnd(&m_rng) );
+			particle.scaleEnd   = bx::flerp(m_uniforms.m_scaleEnd[0],   m_uniforms.m_scaleEnd[1],   bx::frnd(&m_rng) );
+
+			time += timePerParticle;
+		}
+	}
+
+	uint32_t render(const float* _mtxView, const float* _eye, uint32_t _first, ParticleSort* _outSort, PosColorTexCoord0Vertex* _outVertices) const
+	{
+		bx::EaseFn easeRgba  = s_easeFunc[m_uniforms.m_easeRgba];
+		bx::EaseFn easePos   = s_easeFunc[m_uniforms.m_easePos];
+		bx::EaseFn easeBlend = s_easeFunc[m_uniforms.m_easeBlend];
+		bx::EaseFn easeScale = s_easeFunc[m_uniforms.m_easeScale];
+
+		for (uint32_t jj = 0, num = m_num; jj < num; ++jj)
+		{
+			const Particle& particle = m_particles[jj];
+
+			const float ttPos   = easePos(particle.life);
+			const float ttScale = easeScale(particle.life);
+			const float ttBlend = bx::fsaturate(easeBlend(particle.life) );
+			const float ttRgba  = bx::fsaturate(easeRgba(particle.life) );
+
+			float p0[3];
+			bx::vec3Lerp(p0, particle.start, particle.end[0], ttPos);
+
+			float p1[3];
+			bx::vec3Lerp(p1, particle.end[0], particle.end[1], ttPos);
+
+			float pos[3];
+			bx::vec3Lerp(pos, p0, p1, ttPos);
+
+			const uint32_t current = _first + jj;
+			ParticleSort& sort = _outSort[current];
+			float tmp[3];
+			bx::vec3Sub(tmp, _eye, pos);
+			sort.dist = bx::fsqrt(bx::vec3Dot(tmp, tmp) );
+			sort.idx  = current;
+
+			uint32_t idx = uint32_t(ttRgba*4);
+			float ttmod = bx::fmod(ttRgba, 0.25f)/0.25f;
+			uint32_t rgbaStart = particle.rgba[idx];
+			uint32_t rgbaEnd   = particle.rgba[idx+1];
+
+			float rr = bx::flerp( ( (uint8_t*)&rgbaStart)[0], ( (uint8_t*)&rgbaEnd)[0], ttmod)/255.0f;
+			float gg = bx::flerp( ( (uint8_t*)&rgbaStart)[1], ( (uint8_t*)&rgbaEnd)[1], ttmod)/255.0f;
+			float bb = bx::flerp( ( (uint8_t*)&rgbaStart)[2], ( (uint8_t*)&rgbaEnd)[2], ttmod)/255.0f;
+			float aa = bx::flerp( ( (uint8_t*)&rgbaStart)[3], ( (uint8_t*)&rgbaEnd)[3], ttmod)/255.0f;
+
+			float blend = bx::flerp(particle.blendStart, particle.blendEnd, ttBlend);
+			float scale = bx::flerp(particle.scaleStart, particle.scaleEnd, ttScale);
+
+			uint32_t abgr = toAbgr(rr, gg, bb, aa);
+
+			float udir[3] = { _mtxView[0]*scale, _mtxView[4]*scale, _mtxView[8]*scale };
+			float vdir[3] = { _mtxView[1]*scale, _mtxView[5]*scale, _mtxView[9]*scale };
+
+			PosColorTexCoord0Vertex* vertex = &_outVertices[current*4];
+			bx::vec3Sub(tmp, pos, udir);
+			bx::vec3Sub(&vertex->m_x, tmp, vdir);
+			vertex->m_abgr  = abgr;
+			vertex->m_u     = 0.0f;
+			vertex->m_v     = 0.0f;
+			vertex->m_blend = blend;
+			++vertex;
+
+			bx::vec3Add(tmp, pos, udir);
+			bx::vec3Sub(&vertex->m_x, tmp, vdir);
+			vertex->m_abgr  = abgr;
+			vertex->m_u     = 1.0f;
+			vertex->m_v     = 0.0f;
+			vertex->m_blend = blend;
+			++vertex;
+
+			bx::vec3Add(tmp, pos, udir);
+			bx::vec3Add(&vertex->m_x, tmp, vdir);
+			vertex->m_abgr  = abgr;
+			vertex->m_u     = 1.0f;
+			vertex->m_v     = 1.0f;
+			vertex->m_blend = blend;
+			++vertex;
+
+			bx::vec3Sub(tmp, pos, udir);
+			bx::vec3Add(&vertex->m_x, tmp, vdir);
+			vertex->m_abgr  = abgr;
+			vertex->m_u     = 0.0f;
+			vertex->m_v     = 1.0f;
+			vertex->m_blend = blend;
+			++vertex;
+		}
+
+		return m_num;
+	}
+
+	EmitterShape::Enum     m_shape;
+	EmitterDirection::Enum m_direction;
+
+	float           m_dt;
+	bx::RngMwc      m_rng;
+	EmitterUniforms m_uniforms;
+
+	Particle* m_particles;
+	uint32_t m_num;
+	uint32_t m_max;
+};
+
+void EmitterUniforms::reset()
+{
+	m_position[0] = 0.0f;
+	m_position[1] = 0.0f;
+	m_position[2] = 0.0f;
+
+	m_angle[0] = 0.0f;
+	m_angle[1] = 0.0f;
+	m_angle[2] = 0.0f;
+
+	m_particlesPerSecond = 0;
+
+	m_offsetStart[0] = 0.0f;
+	m_offsetStart[1] = 1.0f;
+	m_offsetEnd[0]   = 2.0f;
+	m_offsetEnd[1]   = 3.0f;
+
+	m_rgba[0] = 0x00ffffff;
+	m_rgba[1] = UINT32_MAX;
+	m_rgba[2] = UINT32_MAX;
+	m_rgba[3] = UINT32_MAX;
+	m_rgba[4] = 0x00ffffff;
+
+	m_blendStart[0] = 0.8f;
+	m_blendStart[1] = 1.0f;
+	m_blendEnd[0]   = 0.0f;
+	m_blendEnd[1]   = 0.2f;
+
+	m_scaleStart[0] = 0.1f;
+	m_scaleStart[1] = 0.2f;
+	m_scaleEnd[0]   = 0.3f;
+	m_scaleEnd[1]   = 0.4f;
+
+	m_lifeSpan[0]   = 1.0f;
+	m_lifeSpan[1]   = 2.0f;
+
+	m_gravityScale  = 0.0f;
+
+	m_easePos   = bx::Easing::Linear;
+	m_easeRgba  = bx::Easing::Linear;
+	m_easeBlend = bx::Easing::Linear;
+	m_easeScale = bx::Easing::Linear;
+}
+
+static int32_t particleSortFn(const void* _lhs, const void* _rhs)
+{
+	const ParticleSort& lhs = *(const ParticleSort*)_lhs;
+	const ParticleSort& rhs = *(const ParticleSort*)_rhs;
+	return lhs.dist > rhs.dist ? -1 : 1;
+}
+
+struct ParticleSystem
+{
+	void init(bx::AllocatorI* _allocator)
+	{
+		m_allocator = _allocator;
+
+#if BX_CONFIG_ALLOCATOR_CRT
+		if (NULL == _allocator)
+		{
+			static bx::CrtAllocator allocator;
+			m_allocator = &allocator;
+		}
+#endif // BX_CONFIG_ALLOCATOR_CRT
+
+		PosColorTexCoord0Vertex::init();
+
+		m_num = 0;
+
+		s_texColor = bgfx::createUniform("s_texColor", bgfx::UniformType::Int1);
+		m_particleTexture = loadTexture("textures/particle.ktx");
+
+		bgfx::RendererType::Enum type = bgfx::getRendererType();
+		m_particleProgram = bgfx::createProgram(
+			  bgfx::createEmbeddedShader(s_embeddedShaders, type, "vs_particle")
+			, bgfx::createEmbeddedShader(s_embeddedShaders, type, "fs_particle")
+			, true
+			);
+	}
+
+	void shutdown()
+	{
+		bgfx::destroyProgram(m_particleProgram);
+		bgfx::destroyTexture(m_particleTexture);
+		bgfx::destroyUniform(s_texColor);
+
+		m_allocator = NULL;
+	}
+
+	void update(float _dt)
+	{
+		uint32_t numParticles = 0;
+		for (uint16_t ii = 0, num = m_emitterAlloc.getNumHandles(); ii < num; ++ii)
+		{
+			const uint16_t idx = m_emitterAlloc.getHandleAt(ii);
+			Emitter& emitter = m_emitter[idx];
+			emitter.update(_dt);
+			numParticles += emitter.m_num;
+		}
+
+		m_num = numParticles;
+	}
+
+	void render(uint8_t _view, const float* _mtxView, const float* _eye)
+	{
+		if (0 != m_num)
+		{
+			bgfx::TransientVertexBuffer tvb;
+			bgfx::TransientIndexBuffer tib;
+			bgfx::allocTransientBuffers(&tvb
+					, PosColorTexCoord0Vertex::ms_decl
+					, m_num*4
+					, &tib
+					, m_num*6
+					);
+			PosColorTexCoord0Vertex* vertices = (PosColorTexCoord0Vertex*)tvb.data;
+
+			ParticleSort* particleSort = (ParticleSort*)BX_ALLOC(m_allocator, m_num*sizeof(ParticleSort) );
+
+			uint32_t pos = 0;
+			for (uint16_t ii = 0, num = m_emitterAlloc.getNumHandles(); ii < num; ++ii)
+			{
+				const uint16_t idx = m_emitterAlloc.getHandleAt(ii);
+				const Emitter& emitter = m_emitter[idx];
+				pos += emitter.render(_mtxView, _eye, pos, particleSort, vertices);
+			}
+
+			qsort(particleSort
+				, m_num
+				, sizeof(ParticleSort)
+				, particleSortFn
+				);
+
+			uint16_t* indices = (uint16_t*)tib.data;
+			for (uint32_t ii = 0; ii < m_num; ++ii)
+			{
+				const ParticleSort& sort = particleSort[ii];
+				uint16_t* index = &indices[ii*6];
+				uint16_t idx = (uint16_t)sort.idx;
+				index[0] = idx*4+0;
+				index[1] = idx*4+1;
+				index[2] = idx*4+2;
+				index[3] = idx*4+2;
+				index[4] = idx*4+3;
+				index[5] = idx*4+0;
+			}
+
+			BX_FREE(m_allocator, particleSort);
+
+			bgfx::setState(0
+				| BGFX_STATE_RGB_WRITE
+				| BGFX_STATE_ALPHA_WRITE
+				| BGFX_STATE_DEPTH_TEST_LESS
+				| BGFX_STATE_CULL_CW
+				| BGFX_STATE_BLEND_NORMAL
+				);
+			bgfx::setVertexBuffer(&tvb);
+			bgfx::setIndexBuffer(&tib);
+			bgfx::setTexture(0, s_texColor, m_particleTexture);
+			bgfx::submit(_view, m_particleProgram);
+		}
+	}
+
+	EmitterHandle createEmitter(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles)
+	{
+		EmitterHandle handle = { m_emitterAlloc.alloc() };
+
+		if (UINT16_MAX != handle.idx)
+		{
+			m_emitter[handle.idx].create(_shape, _direction, _maxParticles);
+		}
+
+		return handle;
+	}
+
+	void updateEmitter(EmitterHandle _handle, const EmitterUniforms* _uniforms)
+	{
+		Emitter& emitter = m_emitter[_handle.idx];
+
+		if (NULL == _uniforms)
+		{
+			emitter.reset();
+		}
+		else
+		{
+			memcpy(&emitter.m_uniforms, _uniforms, sizeof(EmitterUniforms) );
+		}
+	}
+
+	void destroyEmitter(EmitterHandle _handle)
+	{
+		m_emitter[_handle.idx].destroy();
+		m_emitterAlloc.free(_handle.idx);
+	}
+
+	bx::AllocatorI* m_allocator;
+
+#define MAX_EMITTERS 64
+	bx::HandleAllocT<MAX_EMITTERS> m_emitterAlloc;
+	Emitter m_emitter[MAX_EMITTERS];
+
+	bgfx::UniformHandle s_texColor;
+	bgfx::TextureHandle m_particleTexture;
+	bgfx::ProgramHandle m_particleProgram;
+
+	uint32_t m_num;
+};
+
+static ParticleSystem s_ps;
+
+void Emitter::create(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles)
+{
+	m_dt = 0.0f;
+	m_uniforms.reset();
+	m_shape     = _shape;
+	m_direction = _direction;
+
+	m_num = 0;
+	m_max = _maxParticles;
+	m_particles = (Particle*)BX_ALLOC(s_ps.m_allocator, m_max*sizeof(Particle) );
+}
+
+void Emitter::destroy()
+{
+	BX_FREE(s_ps.m_allocator, m_particles);
+	m_particles = NULL;
+}
+
+void psInit(bx::AllocatorI* _allocator)
+{
+	s_ps.init(_allocator);
+}
+
+void psShutdown()
+{
+	s_ps.shutdown();
+}
+
+EmitterHandle psCreateEmitter(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles)
+{
+	return s_ps.createEmitter(_shape, _direction, _maxParticles);
+}
+
+void psUpdateEmitter(EmitterHandle _handle, const EmitterUniforms* _uniforms)
+{
+	s_ps.updateEmitter(_handle, _uniforms);
+}
+
+void psDestroyEmitter(EmitterHandle _handle)
+{
+	s_ps.destroyEmitter(_handle);
+}
+
+void psUpdate(float _dt)
+{
+	s_ps.update(_dt);
+}
+
+void psRender(uint8_t _view, const float* _mtxView, const float* _eye)
+{
+	s_ps.render(_view, _mtxView, _eye);
+}

+ 86 - 0
examples/common/ps/particle_system.h

@@ -0,0 +1,86 @@
+/*
+ * Copyright 2011-2017 Branimir Karadzic. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#ifndef PARTICLE_SYSTEM_H_HEADER_GUARD
+#define PARTICLE_SYSTEM_H_HEADER_GUARD
+
+#include <bx/allocator.h>
+#include <bx/easing.h>
+#include <bx/rng.h>
+
+struct EmitterShape
+{
+	enum Enum
+	{
+		Sphere,
+		Hemisphere,
+		Circle,
+		Disc,
+		Rect,
+
+		Count
+	};
+};
+
+struct EmitterDirection
+{
+	enum Enum
+	{
+		Up,
+		Outward,
+
+		Count
+	};
+};
+
+struct EmitterUniforms
+{
+	void reset();
+
+	float m_position[3];
+	float m_angle[3];
+
+	float m_blendStart[2];
+	float m_blendEnd[2];
+	float m_offsetStart[2];
+	float m_offsetEnd[2];
+	float m_scaleStart[2];
+	float m_scaleEnd[2];
+	float m_lifeSpan[2];
+	float m_gravityScale;
+
+	uint32_t m_rgba[5];
+	uint32_t m_particlesPerSecond;
+
+	bx::Easing::Enum m_easePos;
+	bx::Easing::Enum m_easeRgba;
+	bx::Easing::Enum m_easeBlend;
+	bx::Easing::Enum m_easeScale;
+};
+
+struct EmitterHandle { uint16_t idx; };
+
+///
+void psInit(bx::AllocatorI* _allocator = NULL);
+
+///
+void psShutdown();
+
+///
+EmitterHandle psCreateEmitter(EmitterShape::Enum _shape, EmitterDirection::Enum _direction, uint32_t _maxParticles);
+
+///
+void psUpdateEmitter(EmitterHandle _handle, const EmitterUniforms* _uniforms = NULL);
+
+///
+void psDestroyEmitter(EmitterHandle _handle);
+
+///
+void psUpdate(float _dt);
+
+///
+void psRender(uint8_t _view, const float* _mtxView, const float* _eye);
+
+#endif // PARTICLE_SYSTEM_H_HEADER_GUARD

+ 6 - 0
examples/common/ps/varying.def.sc

@@ -0,0 +1,6 @@
+vec4 v_color0    : COLOR0    = vec4(1.0, 0.0, 0.0, 1.0);
+vec4 v_texcoord0 : TEXCOORD0 = vec4(0.0, 0.0, 0.0, 0.0);
+
+vec3 a_position  : POSITION;
+vec4 a_color0    : COLOR0;
+vec4 a_texcoord0 : TEXCOORD0;

+ 151 - 0
examples/common/ps/vs_particle.bin.h

@@ -0,0 +1,151 @@
+static const uint8_t vs_particle_glsl[420] =
+{
+	0x56, 0x53, 0x48, 0x04, 0x01, 0x83, 0xf2, 0xe1, 0x01, 0x00, 0x0f, 0x75, 0x5f, 0x6d, 0x6f, 0x64, // VSH........u_mod
+	0x65, 0x6c, 0x56, 0x69, 0x65, 0x77, 0x50, 0x72, 0x6f, 0x6a, 0x04, 0x01, 0x00, 0x00, 0x01, 0x00, // elViewProj......
+	0x7f, 0x01, 0x00, 0x00, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x68, 0x69, // ....attribute hi
+	0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x61, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, // ghp vec4 a_color
+	0x30, 0x3b, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x68, 0x69, 0x67, // 0;.attribute hig
+	0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x61, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, // hp vec3 a_positi
+	0x6f, 0x6e, 0x3b, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x68, 0x69, // on;.attribute hi
+	0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x61, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, // ghp vec4 a_texco
+	0x6f, 0x72, 0x64, 0x30, 0x3b, 0x0a, 0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x69, // ord0;.varying hi
+	0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, // ghp vec4 v_color
+	0x30, 0x3b, 0x0a, 0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, // 0;.varying highp
+	0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, //  vec4 v_texcoord
+	0x30, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, // 0;.uniform highp
+	0x20, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x75, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x56, 0x69, 0x65, //  mat4 u_modelVie
+	0x77, 0x50, 0x72, 0x6f, 0x6a, 0x3b, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, // wProj;.void main
+	0x20, 0x28, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, //  ().{.  highp ve
+	0x63, 0x34, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x74, // c4 tmpvar_1;.  t
+	0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x2e, 0x77, 0x20, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x3b, // mpvar_1.w = 1.0;
+	0x0a, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x20, // .  tmpvar_1.xyz 
+	0x3d, 0x20, 0x61, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x0a, 0x20, 0x20, // = a_position;.  
+	0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x28, 0x75, // gl_Position = (u
+	0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x56, 0x69, 0x65, 0x77, 0x50, 0x72, 0x6f, 0x6a, 0x20, 0x2a, // _modelViewProj *
+	0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x76, 0x5f, //  tmpvar_1);.  v_
+	0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x20, 0x3d, 0x20, 0x61, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, // color0 = a_color
+	0x30, 0x3b, 0x0a, 0x20, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, // 0;.  v_texcoord0
+	0x20, 0x3d, 0x20, 0x61, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x3b, 0x0a, //  = a_texcoord0;.
+	0x7d, 0x0a, 0x0a, 0x00,                                                                         // }...
+};
+static const uint8_t vs_particle_dx9[347] =
+{
+	0x56, 0x53, 0x48, 0x04, 0x01, 0x83, 0xf2, 0xe1, 0x01, 0x00, 0x0f, 0x75, 0x5f, 0x6d, 0x6f, 0x64, // VSH........u_mod
+	0x65, 0x6c, 0x56, 0x69, 0x65, 0x77, 0x50, 0x72, 0x6f, 0x6a, 0x04, 0x01, 0x00, 0x00, 0x04, 0x00, // elViewProj......
+	0x38, 0x01, 0x00, 0x03, 0xfe, 0xff, 0xfe, 0xff, 0x21, 0x00, 0x43, 0x54, 0x41, 0x42, 0x1c, 0x00, // 8.......!.CTAB..
+	0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfe, 0xff, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, // ..W.............
+	0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, // ......P...0.....
+	0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x5f, // [email protected]_
+	0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x56, 0x69, 0x65, 0x77, 0x50, 0x72, 0x6f, 0x6a, 0x00, 0x03, 0x00, // modelViewProj...
+	0x03, 0x00, 0x04, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x73, // ..............vs
+	0x5f, 0x33, 0x5f, 0x30, 0x00, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, // _3_0.Microsoft (
+	0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, // R) HLSL Shader C
+	0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x00, 0xab, 0x1f, 0x00, // ompiler 10.1....
+	0x00, 0x02, 0x0a, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, // ................
+	0x00, 0x80, 0x01, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x80, 0x02, 0x00, // ................
+	0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0f, 0xe0, 0x1f, 0x00, // ................
+	0x00, 0x02, 0x0a, 0x00, 0x00, 0x80, 0x01, 0x00, 0x0f, 0xe0, 0x1f, 0x00, 0x00, 0x02, 0x05, 0x00, // ................
+	0x00, 0x80, 0x02, 0x00, 0x0f, 0xe0, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x80, 0x01, 0x00, // ................
+	0xe4, 0xa0, 0x01, 0x00, 0x55, 0x90, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, // ....U...........
+	0xe4, 0xa0, 0x01, 0x00, 0x00, 0x90, 0x00, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, // ................
+	0x0f, 0x80, 0x02, 0x00, 0xe4, 0xa0, 0x01, 0x00, 0xaa, 0x90, 0x00, 0x00, 0xe4, 0x80, 0x02, 0x00, // ................
+	0x00, 0x03, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0xe4, 0x80, 0x03, 0x00, 0xe4, 0xa0, 0x01, 0x00, // ................
+	0x00, 0x02, 0x01, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0xe4, 0x90, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, // ................
+	0x0f, 0xe0, 0x02, 0x00, 0xe4, 0x90, 0xff, 0xff, 0x00, 0x00, 0x00,                               // ...........
+};
+static const uint8_t vs_particle_dx11[620] =
+{
+	0x56, 0x53, 0x48, 0x04, 0x01, 0x83, 0xf2, 0xe1, 0x01, 0x00, 0x0f, 0x75, 0x5f, 0x6d, 0x6f, 0x64, // VSH........u_mod
+	0x65, 0x6c, 0x56, 0x69, 0x65, 0x77, 0x50, 0x72, 0x6f, 0x6a, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, // elViewProj......
+	0x40, 0x02, 0x44, 0x58, 0x42, 0x43, 0x94, 0x83, 0x4e, 0xd2, 0x28, 0xd5, 0xeb, 0x87, 0x3e, 0xf5, // @.DXBC..N.(...>.
+	0xa1, 0x65, 0x63, 0x87, 0x0e, 0xf7, 0x01, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x03, 0x00, // .ec.......@.....
+	0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x49, 0x53, // ..,...........IS
+	0x47, 0x4e, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00, // GNh...........P.
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
+	0x00, 0x00, 0x0f, 0x0f, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ......V.........
+	0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x07, 0x00, 0x00, 0x5f, 0x00, // .............._.
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, // ................
+	0x00, 0x00, 0x0f, 0x0f, 0x00, 0x00, 0x43, 0x4f, 0x4c, 0x4f, 0x52, 0x00, 0x50, 0x4f, 0x53, 0x49, // ......COLOR.POSI
+	0x54, 0x49, 0x4f, 0x4e, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x00, 0x4f, 0x53, // TION.TEXCOORD.OS
+	0x47, 0x4e, 0x6c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00, // GNl...........P.
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
+	0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
+	0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x62, 0x00, // ..............b.
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, // ................
+	0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5f, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, // ......SV_POSITIO
+	0x4e, 0x00, 0x43, 0x4f, 0x4c, 0x4f, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, // N.COLOR.TEXCOORD
+	0x00, 0xab, 0x53, 0x48, 0x44, 0x52, 0x28, 0x01, 0x00, 0x00, 0x40, 0x00, 0x01, 0x00, 0x4a, 0x00, // ..SHDR([email protected].
+	0x00, 0x00, 0x59, 0x00, 0x00, 0x04, 0x46, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, // ..Y...F. .......
+	0x00, 0x00, 0x5f, 0x00, 0x00, 0x03, 0xf2, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, // .._..........._.
+	0x00, 0x03, 0x72, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x03, 0xf2, 0x10, // ..r......._.....
+	0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04, 0xf2, 0x20, 0x10, 0x00, 0x00, 0x00, // ......g.... ....
+	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xf2, 0x20, 0x10, 0x00, 0x01, 0x00, // ......e.... ....
+	0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xf2, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x00, // ..e.... ......h.
+	0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x08, 0xf2, 0x00, 0x10, 0x00, 0x00, 0x00, // ......8.........
+	0x00, 0x00, 0x56, 0x15, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x8e, 0x20, 0x00, 0x00, 0x00, // ..V.......F. ...
+	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0a, 0xf2, 0x00, 0x10, 0x00, 0x00, 0x00, // ......2.........
+	0x00, 0x00, 0x46, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, // ..F. ...........
+	0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, // ......F.......2.
+	0x00, 0x0a, 0xf2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8e, 0x20, 0x00, 0x00, 0x00, // ..........F. ...
+	0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa6, 0x1a, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x0e, // ..............F.
+	0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf2, 0x20, 0x10, 0x00, 0x00, 0x00, // ........... ....
+	0x00, 0x00, 0x46, 0x0e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8e, 0x20, 0x00, 0x00, 0x00, // ..F.......F. ...
+	0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xf2, 0x20, 0x10, 0x00, 0x01, 0x00, // ......6.... ....
+	0x00, 0x00, 0x46, 0x1e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xf2, 0x20, // ..F.......6.... 
+	0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x1e, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3e, 0x00, // ......F.......>.
+	0x00, 0x01, 0x00, 0x03, 0x05, 0x00, 0x01, 0x00, 0x10, 0x00, 0x40, 0x00,                         // ..........@.
+};
+static const uint8_t vs_particle_mtl[777] =
+{
+	0x56, 0x53, 0x48, 0x04, 0x01, 0x83, 0xf2, 0xe1, 0x01, 0x00, 0x0f, 0x75, 0x5f, 0x6d, 0x6f, 0x64, // VSH........u_mod
+	0x65, 0x6c, 0x56, 0x69, 0x65, 0x77, 0x50, 0x72, 0x6f, 0x6a, 0x04, 0x01, 0x00, 0x00, 0x01, 0x00, // elViewProj......
+	0xe4, 0x02, 0x00, 0x00, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, // ....using namesp
+	0x61, 0x63, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x6c, 0x3b, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, // ace metal;.struc
+	0x74, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x49, // t xlatMtlShaderI
+	0x6e, 0x70, 0x75, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, // nput {.  float4 
+	0x61, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x20, 0x5b, 0x5b, 0x61, 0x74, 0x74, 0x72, 0x69, // a_color0 [[attri
+	0x62, 0x75, 0x74, 0x65, 0x28, 0x30, 0x29, 0x5d, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x66, 0x6c, 0x6f, // bute(0)]];.  flo
+	0x61, 0x74, 0x33, 0x20, 0x61, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5b, // at3 a_position [
+	0x5b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x31, 0x29, 0x5d, 0x5d, 0x3b, // [attribute(1)]];
+	0x0a, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x61, 0x5f, 0x74, 0x65, 0x78, 0x63, // .  float4 a_texc
+	0x6f, 0x6f, 0x72, 0x64, 0x30, 0x20, 0x5b, 0x5b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, // oord0 [[attribut
+	0x65, 0x28, 0x32, 0x29, 0x5d, 0x5d, 0x3b, 0x0a, 0x7d, 0x3b, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, // e(2)]];.};.struc
+	0x74, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x4f, // t xlatMtlShaderO
+	0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, // utput {.  float4
+	0x20, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5b, 0x5b, 0x70, //  gl_Position [[p
+	0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5d, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x66, 0x6c, 0x6f, // osition]];.  flo
+	0x61, 0x74, 0x34, 0x20, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x3b, 0x0a, 0x20, 0x20, // at4 v_color0;.  
+	0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, // float4 v_texcoor
+	0x64, 0x30, 0x3b, 0x0a, 0x7d, 0x3b, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x78, 0x6c, // d0;.};.struct xl
+	0x61, 0x74, 0x4d, 0x74, 0x6c, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x55, 0x6e, 0x69, 0x66, 0x6f, // atMtlShaderUnifo
+	0x72, 0x6d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x78, 0x34, 0x20, // rm {.  float4x4 
+	0x75, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x56, 0x69, 0x65, 0x77, 0x50, 0x72, 0x6f, 0x6a, 0x3b, // u_modelViewProj;
+	0x0a, 0x7d, 0x3b, 0x0a, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, // .};.vertex xlatM
+	0x74, 0x6c, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x78, // tlShaderOutput x
+	0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, 0x20, 0x28, 0x78, 0x6c, 0x61, 0x74, // latMtlMain (xlat
+	0x4d, 0x74, 0x6c, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x5f, // MtlShaderInput _
+	0x6d, 0x74, 0x6c, 0x5f, 0x69, 0x20, 0x5b, 0x5b, 0x73, 0x74, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x6e, // mtl_i [[stage_in
+	0x5d, 0x5d, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x78, 0x6c, 0x61, // ]], constant xla
+	0x74, 0x4d, 0x74, 0x6c, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, // tMtlShaderUnifor
+	0x6d, 0x26, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x20, 0x5b, 0x5b, 0x62, 0x75, 0x66, 0x66, // m& _mtl_u [[buff
+	0x65, 0x72, 0x28, 0x30, 0x29, 0x5d, 0x5d, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x78, 0x6c, 0x61, // er(0)]]).{.  xla
+	0x74, 0x4d, 0x74, 0x6c, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, // tMtlShaderOutput
+	0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x6f, 0x3b, 0x0a, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, //  _mtl_o;.  float
+	0x34, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, // 4 tmpvar_1 = 0;.
+	0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x2e, 0x77, 0x20, 0x3d, 0x20, 0x31, //   tmpvar_1.w = 1
+	0x2e, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x2e, 0x78, // .0;.  tmpvar_1.x
+	0x79, 0x7a, 0x20, 0x3d, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x69, 0x2e, 0x61, 0x5f, 0x70, 0x6f, // yz = _mtl_i.a_po
+	0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x6f, // sition;.  _mtl_o
+	0x2e, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x28, // .gl_Position = (
+	0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x56, 0x69, // _mtl_u.u_modelVi
+	0x65, 0x77, 0x50, 0x72, 0x6f, 0x6a, 0x20, 0x2a, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, // ewProj * tmpvar_
+	0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x6f, 0x2e, 0x76, 0x5f, 0x63, // 1);.  _mtl_o.v_c
+	0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x69, 0x2e, 0x61, // olor0 = _mtl_i.a
+	0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, // _color0;.  _mtl_
+	0x6f, 0x2e, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x20, 0x3d, 0x20, // o.v_texcoord0 = 
+	0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x69, 0x2e, 0x61, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, // _mtl_i.a_texcoor
+	0x64, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x6d, 0x74, // d0;.  return _mt
+	0x6c, 0x5f, 0x6f, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x00,                                           // l_o;.}...
+};
+extern const uint8_t* vs_particle_pssl;
+extern const uint32_t vs_particle_pssl_size;

+ 16 - 0
examples/common/ps/vs_particle.sc

@@ -0,0 +1,16 @@
+$input a_position, a_color0, a_texcoord0
+$output v_color0, v_texcoord0
+
+/*
+ * Copyright 2011-2017 Branimir Karadzic. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#include <bgfx_shader.sh>
+
+void main()
+{
+	gl_Position = mul(u_modelViewProj, vec4(a_position, 1.0) );
+	v_color0    = a_color0;
+	v_texcoord0 = a_texcoord0;
+}

BIN
examples/runtime/textures/particle.ktx


+ 1 - 0
scripts/genie.lua

@@ -401,6 +401,7 @@ if _OPTIONS["with-examples"] then
 	exampleProject("29-debugdraw")
 	exampleProject("30-picking")
 	exampleProject("31-rsm")
+	exampleProject("32-particles")
 
 	-- C99 source doesn't compile under WinRT settings
 	if not premake.vstudio.iswinrt() then