Sprites and billboards

    Create rectangle and rotate it using view-invert matrix

    
    bool slD3D11ShaderSprite::init(){
    	const char* text =
    		"Texture2D tex2d_1;\n"
    		"SamplerState tex2D_sampler_1;\n"
    		"struct VSIn{\n"
    		"   float3 position : POSITION;\n"
    		"   float2 uv : TEXCOORD;\n"
    		"};\n"
    		"cbuffer cbElement{\n"
    		"	double4x4 W;\n"
    		"	double4x4 V;\n"
    		"	double4x4 P;\n"
    		"	double4x4 Vi;\n"
    		"	double4 Corners;\n"	
    		"	float4 Color1;\n"
    		"	float4 UVs;\n"
    		"	float alphaDiscard;\n"
    		"	float padding[3];\n"
    		"};\n"
    		"struct VSOut{\n"
    		"   float4 pos : SV_POSITION;\n"
    		"   float2 uv : TEXCOORD0;\n"
    		"	float4 color : COLOR0;\n"
    		"};\n"
    		"struct PSOut{\n"
    		"    float4 color : SV_Target;\n"
    		"};\n"
    		"VSOut VSMain(VSIn input){\n"
    		"   VSOut output;\n"
    		"	output.pos.xyz   = input.position.xyz;\n"
    		"	output.pos.w   = 1.f;\n"
    		"	output.uv   = input.uv;\n"
    		"	return output;\n"
    		"}\n"
    		"PSOut PSMain(VSOut input){\n"
    		"   PSOut output;\n"
    		"   output.color = saturate(tex2d_1.Sample(tex2D_sampler_1, input.uv) * input.color);\n"
    		"	if(output.color.w < alphaDiscard) discard;\n"
    		"   return output;\n"
    		"}\n"
    		"[maxvertexcount(4)]\n"
    		"void GSMain(point VSOut input[1], inout TriangleStream<VSOut> TriStream ){\n"
    		"	VSOut Out;\n"
    
    		"	double4x4 V2 = Vi;\n"
    		"	V2._41 = 0.0;\n"
    		"	V2._42 = 0.0;\n"
    		"	V2._43 = 0.0;\n"
    		"	V2._44 = 1.0;\n"
    		"	V2._14 = 0.0;\n"
    		"	V2._24 = 0.0;\n"
    		"	V2._34 = 0.0;\n"
    
    		"   double4 v1 = double4(Corners.x, Corners.y, 0.0, 1.0);\n"
    		"   double4 v2 = double4(Corners.z, Corners.w, 0.0, 1.0);\n"
    		
    		"	Out.pos   =  mul(W, mul(V2,double4(v2.x, v2.y,  0.0, 1.0)));\n"
    		"	Out.pos   =  mul(V, Out.pos);\n"
    		"	Out.pos   =  mul(P, Out.pos);\n"
    		"	Out.uv = float2(UVs.x,UVs.y);\n" // 2
    		"	Out.color = Color1;\n"
    		"	TriStream.Append(Out);\n"
    
    		"	Out.pos   =  mul(W, mul(V2,double4(v1.x, v2.y, 0.0,1.0)));\n" 
    		"	Out.pos   =  mul(V, Out.pos);\n"
    		"	Out.pos   =  mul(P, Out.pos);\n"
    		"	Out.uv = float2(UVs.z,UVs.y);\n" // 1
    		"	Out.color = Color1;\n"
    		"	TriStream.Append(Out);\n"
    
    		"	Out.pos   =  mul(W, mul(V2,double4(v2.x, v1.y, 0.0, 1.0)));\n"
    		"	Out.pos   =  mul(V, Out.pos);\n"
    		"	Out.pos   =  mul(P, Out.pos);\n"
    		"	Out.uv = float2(UVs.x,UVs.w);\n"// 4
    		"	Out.color = Color1;\n"
    		"	TriStream.Append(Out);\n"
    
    		"	Out.pos   =  mul(W, mul(V2,double4(v1.x, v1.y, 0.0, 1.0)));\n" 
    		"	Out.pos   =  mul(V, Out.pos);\n"
    		"	Out.pos   =  mul(P, Out.pos);\n"
    		"	Out.uv = float2(UVs.z,UVs.w);\n" // 3
    		"	Out.color = Color1;\n"
    		"	TriStream.Append(Out);\n"
    		
    		"	TriStream.RestartStrip();\n"
    		"}\n";
    	if (!m_gs->createShaders(
    		"vs_5_0",
    		"ps_5_0",
    		text,
    		text,
    		"VSMain",
    		"PSMain",
    		slMeshVertexType::Null,
    		&this->m_vShader,
    		&this->m_pShader,
    		&this->m_vLayout))
    		return false;
    	if (!m_gs->createGeometryShaders("gs_5_0", text, "GSMain", &m_gShader))
    		return false;
    
    	if (!m_gs->createConstantBuffer(sizeof(cbElement), &m_cbElement))
    		return false;
    
    	return true;
    }
    

    Sprite

    
    class slSpriteState;
    
    // just rectangle with texture
    // it can rotate using matrix
    // this is not GUI element
    class slSprite : public slSceneObject
    {
    	slTexture* m_texture = 0;
    	slVec4f m_rect;
    	slArray<slSpriteState*> m_states;
    	slSpriteState* m_activeState = 0;
    	slColor m_color = ColorWhite;
    public:
    	slSprite();
    	virtual ~slSprite();
    
    	slColor& GetColor() { return m_color; }
    
    	slTexture* GetTexture() { return m_texture; }
    	void SetTexture(slTexture* t) { m_texture = t; }
    
    	void SetSize(float x, float y);
    
    	enum class TransparentType
    	{
    		Discard,
    		Blending
    	}
    	m_transparentType = TransparentType::Discard;
    
    	float m_alphaDiscard = 0.5f;
    
    	slVec4f& GetRect() { return m_rect; }
    	slSpriteState* GetActiveState() { return m_activeState; }
    	void SetActiveState(slSpriteState* s) { m_activeState = s; }
    	slSpriteState* CreateNewState();
    
    	void AnimationUpdate(float dt);
    };
    
    class slSpriteState
    {
    	friend class slSprite;
    	slSprite* m_sprite = 0;
    public:
    	slSpriteState() {}
    	~slSpriteState(){}
    
    	slString m_name;
    
    	slArray<slVec4f> m_frames; // UVs
    	uint32_t m_frameCurrent = 0;
    	uint32_t m_frameNum = 0;
    
    	bool m_invertX = false;
    	bool m_invertY = false;
    
    	bool m_isAnimation = false;
    	float m_fps = 1.f;
    	float m_fpsTime = 1.f;
    
    	bool m_play = true;
    	bool m_loop = true;
    	float m_updateTimer = 0.f;
    
    	void SetFPS(float newFPS) {
    		if (newFPS < 1.f)
    			newFPS = 1.f;
    		m_fps = newFPS;
    		m_fpsTime = 1.f / newFPS;
    	}
    
    	void AddFrame(const slVec4f& rect);
    	void AnimationUpdate(float dt);
    	void CreateAnimation(uint32_t numFrames, const slVec2f& frameSize, const slVec2f& startPosition);
    };
    

    Implementation

    
    slSprite::slSprite()
    {
    }
    
    slSprite::~slSprite()
    {
    	for (size_t i = 0; i < m_states.m_size; ++i)
    	{
    		slDestroy(m_states.m_data[i]);
    	}
    }
    
    void slSprite::SetSize(float x, float y)
    {
    	float hx = x * 0.5f;
    	float hy = y * 0.5f;
    
    	m_rect.set(-hx, -hy, hx, hy);
    }
    
    slSpriteState* slSprite::CreateNewState()
    {
    	slSpriteState* state = slCreate<slSpriteState>();
    	state->m_sprite = this;
    	m_states.push_back(state);
    	return state;
    }
    
    void slSprite::AnimationUpdate(float dt)
    {
    	SL_ASSERT_ST(m_activeState);
    	m_activeState->AnimationUpdate(dt);
    }
    
    void slSpriteState::AddFrame(const slVec4f& rect)
    {
    	slVec2f textureSize((float)m_sprite->GetTexture()->GetInfo().m_imageInfo.m_width,
    		(float)m_sprite->GetTexture()->GetInfo().m_imageInfo.m_height);
    
    	float xMulFactor = 1.f / (float)textureSize.x;
    	float yMulFactor = 1.f / (float)textureSize.y;
    
    	slVec4f newFrame;
    	newFrame.x = rect.x * xMulFactor;
    	newFrame.y = (rect.y * yMulFactor);
    	newFrame.z = (rect.z + 1.f) * xMulFactor;
    	newFrame.w = (rect.w + 1.f) * yMulFactor;
    	this->m_frames.push_back(newFrame);
    	m_frameNum = m_frames.size();
    }
    
    void slSpriteState::AnimationUpdate(float dt)
    {
    	if (m_isAnimation && m_play)
    	{
    		m_updateTimer += dt;
    		if (m_updateTimer >= m_fpsTime)
    		{
    			m_updateTimer = 0.f;
    			++m_frameCurrent;
    			if (m_frameCurrent == m_frameNum)
    			{
    				if (m_loop)
    				{
    					m_frameCurrent = 0;
    				}
    				else
    				{
    					m_play = false;
    				}
    			}
    		}
    	}
    }
    
    void slSpriteState::CreateAnimation(uint32_t numFrames, const slVec2f& frameSize, const slVec2f& startPosition)
    {
    	slVec2f textureSize((float)m_sprite->GetTexture()->GetInfo().m_imageInfo.m_width,
    		(float)m_sprite->GetTexture()->GetInfo().m_imageInfo.m_height);
    
    	slVec2f sp = startPosition;
    	for (uint32_t i = 0; i < numFrames; ++i)
    	{
    		slVec4f f;
    
    		f.x = sp.x;
    		f.y = sp.y;
    		f.z = f.x + frameSize.x - 1.f;
    		f.w = f.y + frameSize.y - 1.f;
    
    		if (f.z > textureSize.x)
    		{
    			sp.x = 0.f;
    			sp.y += frameSize.y;
    
    			f.x = sp.x;
    			f.y = sp.y;
    			f.z = f.x + frameSize.x - 1.f;
    			f.w = f.y + frameSize.y - 1.f;
    		}
    
    		AddFrame(f);
    		m_isAnimation = true;
    	
    		sp.x += frameSize.x;
    	}
    }
    

    Draw it

    
    void slGSD3D11::DrawSprite(slSprite* s)
    {
    	SL_ASSERT_ST(s);
    	SL_ASSERT_ST(s->GetTexture());
    	SL_ASSERT_ST(slFramework::GetMatrix(slMatrixType::ViewInvert));
    	SL_ASSERT_ST(slFramework::GetMatrix(slMatrixType::World));
    	SL_ASSERT_ST(slFramework::GetMatrix(slMatrixType::View));
    	SL_ASSERT_ST(slFramework::GetMatrix(slMatrixType::Projection));
    
    	m_shaderSprite->m_cbDataElement.Color1 = s->GetColor();
    
    	m_shaderSprite->m_cbDataElement.Corners = s->GetRect();
    
    	auto state = s->GetActiveState();
    	if (state)
    	{
    		if (state->m_frames.m_size)
    			m_shaderSprite->m_cbDataElement.UVs = state->m_frames.m_data[state->m_frameCurrent];
    		else
    			goto default_uv;
    	}
    	else
    	{
    	default_uv:;
    		m_shaderSprite->m_cbDataElement.UVs.x = 0.f;
    		m_shaderSprite->m_cbDataElement.UVs.y = 0.f;
    		m_shaderSprite->m_cbDataElement.UVs.z = 1.f;
    		m_shaderSprite->m_cbDataElement.UVs.w = 1.f;
    	}
    
    
    	m_shaderSprite->m_cbDataElement.alphaDiscard = s->m_alphaDiscard;
    
    	m_shaderSprite->m_cbDataElement.Vi = *slFramework::GetMatrix(slMatrixType::ViewInvert);
    	m_shaderSprite->m_cbDataElement.W = *slFramework::GetMatrix(slMatrixType::World);
    	m_shaderSprite->m_cbDataElement.V = *slFramework::GetMatrix(slMatrixType::View);
    	m_shaderSprite->m_cbDataElement.P = *slFramework::GetMatrix(slMatrixType::Projection);
    
    	m_shaderSprite->SetOnElement(dynamic_cast<slGSD3D11Texture*>(s->GetTexture()));
    
    	m_d3d11DevCon->Draw(1, 0);
    }
    
    

    Download