From e430ca5fd6e9ff1a44a8eafdf5d8cc821cc3d6d9 Mon Sep 17 00:00:00 2001
From: D-AIRY <admin@ds-servers.com>
Date: Thu, 30 Apr 2020 12:34:29 +0300
Subject: [PATCH] Emissive materials

---
 build/engine/shaders/default/default.ps | 11 +++--
 source/anim/DynamicModel.cpp            | 10 ++++-
 source/anim/DynamicModel.h              |  2 +
 source/anim/DynamicModelProvider.cpp    | 34 +++++++++++++++
 source/anim/DynamicModelProvider.h      |  2 +
 source/anim/DynamicModelShared.cpp      | 57 +++++++++++++++++++++++--
 source/anim/DynamicModelShared.h        |  5 ++-
 source/anim/Renderable.cpp              |  3 +-
 source/anim/RenderableVisibility.cpp    |  1 +
 source/anim/RenderableVisibility.h      |  1 +
 source/game/GameData.cpp                |  2 +-
 source/mtrl/IXMaterial.h                |  3 ++
 source/mtrl/MaterialSystem.cpp          | 12 ++++++
 source/mtrl/MaterialSystem.h            |  4 ++
 source/render/RenderPipeline.cpp        | 10 ++++-
 source/xcommon/XEvents.h                |  1 +
 16 files changed, 145 insertions(+), 13 deletions(-)

diff --git a/build/engine/shaders/default/default.ps b/build/engine/shaders/default/default.ps
index 420e1d047..0d851c06e 100644
--- a/build/engine/shaders/default/default.ps
+++ b/build/engine/shaders/default/default.ps
@@ -47,13 +47,15 @@ XMaterial MainGBuffer(PSI_XMaterial IN)
 XMaterial MainIllimination(PSI_XMaterial IN)
 {
 	XMaterial OUT = XMATERIAL_LOAD_DEFAULTS();
+#ifdef HAS_EMISSION
 	
+#ifdef HAS_EMISSIVE_MAP
+	float4 fColor = g_txEmissive.Sample(g_sScene, IN.vTexUV);
+#else
 	float4 fColor = g_txBase.Sample(g_sScene, IN.vTexUV);
-	// float4 fColor = IN.vPos;
-#ifdef HAS_ALPHATEST
-	clip(fColor.a - g_xMaterialConstants.pbr_alphatest_threshold);
 #endif
-	OUT.vEmissiveColor = fColor.xyz;
+
+	OUT.vEmissiveColor = fColor.xyz * fColor.w * g_xMaterialConstants.em_multiplier;
 	
 #ifdef HAS_NORMALMAP
 	OUT.vNormal = MixNormalMicro(IN.vNormal, 
@@ -63,6 +65,7 @@ XMaterial MainIllimination(PSI_XMaterial IN)
 	OUT.vNormal = IN.vNormal;
 #endif
 
+#endif
 	
 	return(OUT);
 }
diff --git a/source/anim/DynamicModel.cpp b/source/anim/DynamicModel.cpp
index 2819bd742..31bd43fd0 100644
--- a/source/anim/DynamicModel.cpp
+++ b/source/anim/DynamicModel.cpp
@@ -208,6 +208,10 @@ const IXResourceModel * XMETHODCALLTYPE CDynamicModel::getResource(UINT uIndex)
 }
 
 void XMETHODCALLTYPE CDynamicModel::render(UINT uLod, bool isTransparent)
+{
+	render(uLod, isTransparent, false);
+}
+void CDynamicModel::render(UINT uLod, bool isTransparent, bool isEmissiveOnly)
 {
 	if(!m_pDevice || !m_isEnabled || !m_pWorldBuffer)
 	{
@@ -223,7 +227,7 @@ void XMETHODCALLTYPE CDynamicModel::render(UINT uLod, bool isTransparent)
 	m_pProvider->bindVertexFormat();
 
 	m_pDevice->getThreadContext()->setVSConstant(m_pWorldBuffer, 1 /* SCR_OBJECT */);
-	m_pShared->render(m_uSkin, uLod, m_vColor, isTransparent);
+	m_pShared->render(m_uSkin, uLod, m_vColor, isTransparent, isEmissiveOnly);
 }
 
 CDynamicModelShared* CDynamicModel::getShared()
@@ -251,6 +255,10 @@ bool CDynamicModel::hasTransparentSubsets(UINT uLod) const
 {
 	return(m_pShared->hasTransparentSubsets(m_uSkin, uLod));
 }
+bool CDynamicModel::hasEmissiveSubsets(UINT uLod) const
+{
+	return(m_pShared->hasEmissiveSubsets(m_uSkin, uLod));
+}
 
 IXMaterial* CDynamicModel::getTransparentMaterial(UINT uLod)
 {
diff --git a/source/anim/DynamicModel.h b/source/anim/DynamicModel.h
index 51b829412..2c2f2b56f 100644
--- a/source/anim/DynamicModel.h
+++ b/source/anim/DynamicModel.h
@@ -41,6 +41,7 @@ public:
 	const IXResourceModel* XMETHODCALLTYPE getResource(UINT uIndex = 0) override;
 
 	void XMETHODCALLTYPE render(UINT uLod, bool isTransparent) override;
+	void render(UINT uLod, bool isTransparent, bool isEmissiveOnly);
 
 	CDynamicModelShared* getShared();
 	void initGPUresources();
@@ -48,6 +49,7 @@ public:
 	UINT getPSPcount(UINT uLod) const;
 	SMPLANE getPSP(UINT uLod, UINT uPlane) const;
 	bool hasTransparentSubsets(UINT uLod) const;
+	bool hasEmissiveSubsets(UINT uLod) const;
 	IXMaterial* getTransparentMaterial(UINT uLod);
 protected:
 	CDynamicModelProvider *m_pProvider;
diff --git a/source/anim/DynamicModelProvider.cpp b/source/anim/DynamicModelProvider.cpp
index e83f4bf87..64a948497 100644
--- a/source/anim/DynamicModelProvider.cpp
+++ b/source/anim/DynamicModelProvider.cpp
@@ -23,6 +23,10 @@ void CMaterialChangedEventListener::onEvent(const XEventMaterialChanged *pData)
 	{
 		m_pProvider->onMaterialTransparencyChanged(pData->pMaterial);
 	}
+	if(pData->type == XEventMaterialChanged::TYPE_EMISSIVITY)
+	{
+		m_pProvider->onMaterialEmissivityChanged(pData->pMaterial);
+	}
 }
 /*
 //##########################################################################
@@ -113,6 +117,7 @@ public:
 
 				if(pItem->isVisible)
 				{
+					pItem->isEmissive = pMdl->hasEmissiveSubsets(pItem->uLod);
 					pItem->isTransparent = pMdl->hasTransparentSubsets(pItem->uLod);
 					if(pItem->isTransparent)
 					{
@@ -347,6 +352,23 @@ void CDynamicModelProvider::render(bool isTransparent, CRenderableVisibility *pV
 	m_pMaterialSystem->bindVS(NULL);
 }
 
+void CDynamicModelProvider::renderEmissive(CRenderableVisibility *pVisibility)
+{
+	bindVertexFormat();
+	for(UINT i = 0, l = m_apModels.size(); i < l; ++i)
+	{
+		auto pItem = pVisibility->getItemDynamic(i);
+		if(pItem->isVisible)
+		{
+			if(pItem->isEmissive)
+			{
+				m_apModels[i]->render(pItem->uLod, pItem->isTransparent, pItem->isEmissive);
+			}
+		}
+	}
+	m_pMaterialSystem->bindVS(NULL);
+}
+
 void CDynamicModelProvider::computeVisibility(const IFrustum *pFrustum, CRenderableVisibility *pVisibility, CRenderableVisibility *pReference)
 {
 	pVisibility->setItemCountDynamic(m_apModels.size());
@@ -370,6 +392,7 @@ void CDynamicModelProvider::computeVisibility(const IFrustum *pFrustum, CRendera
 			if(pItem->isVisible)
 			{
 				pItem->isTransparent = pMdl->hasTransparentSubsets(pItem->uLod);
+				pItem->isEmissive = pMdl->hasEmissiveSubsets(pItem->uLod);
 				if(pItem->isTransparent)
 				{
 					IXMaterial *pMaterial = pMdl->getTransparentMaterial(pItem->uLod);
@@ -475,6 +498,17 @@ void CDynamicModelProvider::onMaterialTransparencyChanged(const IXMaterial *pMat
 	}
 }
 
+void CDynamicModelProvider::onMaterialEmissivityChanged(const IXMaterial *pMaterial)
+{
+	for(auto i = m_mModels.begin(); i; i++)
+	{
+		if(*i.second)
+		{
+			(*i.second)->onMaterialEmissivityChanged(pMaterial);
+		}
+	}
+}
+
 UINT CDynamicModelProvider::getTransparentCount(CRenderableVisibility *pVisibility)
 {
 	return(pVisibility->getItemTransparentDynamicCount());
diff --git a/source/anim/DynamicModelProvider.h b/source/anim/DynamicModelProvider.h
index 7aab732a1..b83dab01e 100644
--- a/source/anim/DynamicModelProvider.h
+++ b/source/anim/DynamicModelProvider.h
@@ -41,6 +41,7 @@ public:
 	IGXVertexDeclaration *getVertexDeclaration();
 
 	void render(bool isTransparent, CRenderableVisibility *pVisibility = NULL);
+	void renderEmissive(CRenderableVisibility *pVisibility);
 	void computeVisibility(const IFrustum *pFrustum, CRenderableVisibility *pVisibility, CRenderableVisibility *pReference=NULL);
 
 	void getLevelSize(const XEventLevelSize *pData);
@@ -58,6 +59,7 @@ public:
 
 	void bindVertexFormat();
 protected:
+	void onMaterialEmissivityChanged(const IXMaterial *pMaterial);
 	void onMaterialTransparencyChanged(const IXMaterial *pMaterial);
 
 	CMaterialChangedEventListener *m_pMaterialChangedEventListener;
diff --git a/source/anim/DynamicModelShared.cpp b/source/anim/DynamicModelShared.cpp
index 0d549a0f3..0b1a2acb7 100644
--- a/source/anim/DynamicModelShared.cpp
+++ b/source/anim/DynamicModelShared.cpp
@@ -24,6 +24,7 @@ CDynamicModelShared::~CDynamicModelShared()
 	m_pppMaterials = NULL;
 
 	mem_delete_a(m_isTransparent);
+	mem_delete_a(m_isEmissive);
 
 	if(m_pDevice)
 	{
@@ -76,6 +77,7 @@ bool CDynamicModelShared::init(IXResourceModelStatic *pResource)
 		m_uSkinCount = pResource->getSkinCount();
 
 		m_isTransparent = new bool[m_uSkinCount];
+		m_isEmissive = new bool[m_uSkinCount];
 
 		m_ppMaterialsBlob = new void*[m_uMaterialCount * m_uSkinCount + m_uSkinCount];
 		m_pppMaterials = (IXMaterial***)m_ppMaterialsBlob;
@@ -85,6 +87,7 @@ bool CDynamicModelShared::init(IXResourceModelStatic *pResource)
 		{
 			m_pppMaterials[i] = (IXMaterial**)(m_ppMaterialsBlob + m_uSkinCount + m_uMaterialCount * i);
 			bool isTransparent = false;
+			bool isEmissive = false;
 
 			for(UINT j = 0; j < m_uMaterialCount; ++j)
 			{
@@ -103,13 +106,21 @@ bool CDynamicModelShared::init(IXResourceModelStatic *pResource)
 					m_pppMaterials[i][j]->AddRef();
 				}
 
-				if(m_pppMaterials[i][j] && m_pppMaterials[i][j]->isTransparent())
+				if(m_pppMaterials[i][j])
 				{
-					isTransparent = true;
+					if(m_pppMaterials[i][j]->isTransparent())
+					{
+						isTransparent = true;
+					}
+					if(m_pppMaterials[i][j]->isEmissive())
+					{
+						isEmissive = true;
+					}
 				}
 			}
 
 			m_isTransparent[i] = isTransparent;
+			m_isEmissive[i] = isEmissive;
 		}
 	}
 
@@ -262,6 +273,24 @@ void CDynamicModelShared::onMaterialTransparencyChanged(const IXMaterial *pMater
 	}
 }
 
+void CDynamicModelShared::onMaterialEmissivityChanged(const IXMaterial *pMaterial)
+{
+	for(UINT i = 0; i < m_uSkinCount; ++i)
+	{
+		bool isEmissive = false;
+
+		for(UINT j = 0; j < m_uMaterialCount; ++j)
+		{
+			if(m_pppMaterials[i][j] && m_pppMaterials[i][j]->isEmissive())
+			{
+				isEmissive = true;
+			}
+		}
+
+		m_isEmissive[i] = isEmissive;
+	}
+}
+
 void CDynamicModelShared::initGPUresources()
 {
 	if(!m_ppTempIndices)
@@ -317,7 +346,7 @@ float3 CDynamicModelShared::getLocalBoundMax() const
 	return(m_vLocalMax);
 }
 
-void CDynamicModelShared::render(UINT uSkin, UINT uLod, const float4_t &vColor, bool isTransparent)
+void CDynamicModelShared::render(UINT uSkin, UINT uLod, const float4_t &vColor, bool isTransparent, bool isEmissiveOnly)
 {
 	if(!m_pDevice)
 	{
@@ -334,6 +363,11 @@ void CDynamicModelShared::render(UINT uSkin, UINT uLod, const float4_t &vColor,
 		return;
 	}
 
+	if(isEmissiveOnly && !hasEmissiveSubsets(uSkin, uLod))
+	{
+		return;
+	}
+
 	if(uLod >= m_aLods.size())
 	{
 		return;
@@ -366,7 +400,10 @@ void CDynamicModelShared::render(UINT uSkin, UINT uLod, const float4_t &vColor,
 	{
 		pSubset = &m_aLods[uLod][i];
 		
-		if(pSubset->uIndexCount != 0 && m_pppMaterials[uSkin][i] && ((!m_pppMaterials[uSkin][i] && !isTransparent) || m_pppMaterials[uSkin][i]->isTransparent() == isTransparent))
+		if(pSubset->uIndexCount != 0 && m_pppMaterials[uSkin][i] 
+			&& (m_pppMaterials[uSkin][i]->isTransparent() == isTransparent)
+			&& (!isEmissiveOnly || m_pppMaterials[uSkin][i]->isEmissive())
+			)
 		{
 			m_pMaterialSystem->bindMaterial(m_pppMaterials[uSkin][i]);
 
@@ -397,6 +434,18 @@ bool CDynamicModelShared::hasTransparentSubsets(UINT uSkin, UINT uLod)
 	return(m_isTransparent[uSkin]);
 }
 
+bool CDynamicModelShared::hasEmissiveSubsets(UINT uSkin, UINT uLod)
+{
+	assert(uSkin < m_uSkinCount);
+	if(uSkin >= m_uSkinCount)
+	{
+		return(false);
+	}
+	//! @todo add uLod support
+
+	return(m_isEmissive[uSkin]);
+}
+
 IXMaterial* CDynamicModelShared::getTransparentMaterial(UINT uSkin, UINT uLod)
 {
 	assert(uSkin < m_uSkinCount);
diff --git a/source/anim/DynamicModelShared.h b/source/anim/DynamicModelShared.h
index a6177296d..0f2a5c1d0 100644
--- a/source/anim/DynamicModelShared.h
+++ b/source/anim/DynamicModelShared.h
@@ -26,15 +26,17 @@ public:
 	float3 getLocalBoundMin() const;
 	float3 getLocalBoundMax() const;
 
-	void render(UINT uSkin, UINT uLod, const float4_t &vColor, bool isTransparent);
+	void render(UINT uSkin, UINT uLod, const float4_t &vColor, bool isTransparent, bool isEmissiveOnly = false);
 
 	void initGPUresources();
 	bool isReady() const;
 
 	bool hasTransparentSubsets(UINT uSkin, UINT uLod);
+	bool hasEmissiveSubsets(UINT uSkin, UINT uLod);
 	IXMaterial* getTransparentMaterial(UINT uSkin, UINT uLod);
 
 	void onMaterialTransparencyChanged(const IXMaterial *pMaterial);
+	void onMaterialEmissivityChanged(const IXMaterial *pMaterial);
 	float3 getTransparentBoundMin(UINT uSkin, UINT uLod) const;
 	float3 getTransparentBoundMax(UINT uSkin, UINT uLod) const;
 
@@ -64,6 +66,7 @@ protected:
 	UINT m_uMaterialCount = 0;
 	UINT m_uSkinCount = 0;
 	bool *m_isTransparent = NULL; //!< По количеству скинов, истина если есть прозрачные материалы в любом сабсете
+	bool *m_isEmissive = NULL; //!< По количеству скинов, истина если есть светящиеся материалы в любом сабсете
 
 	XPT_TOPOLOGY m_topology = XPT_TRIANGLELIST;
 
diff --git a/source/anim/Renderable.cpp b/source/anim/Renderable.cpp
index 37eb7b005..d3a492191 100644
--- a/source/anim/Renderable.cpp
+++ b/source/anim/Renderable.cpp
@@ -10,7 +10,7 @@ CRenderable::CRenderable(ID idPlugin, CAnimatedModelProvider *pProviderAnimated,
 
 X_RENDER_STAGE XMETHODCALLTYPE CRenderable::getStages()
 {
-	return(XRS_GBUFFER | XRS_SHADOWS | XRS_EDITOR_2D | XRS_TRANSPARENT);
+	return(XRS_GBUFFER | XRS_SHADOWS | XRS_EDITOR_2D | XRS_TRANSPARENT | XRS_GI);
 }
 
 UINT XMETHODCALLTYPE CRenderable::getPriorityForStage(X_RENDER_STAGE stage)
@@ -41,6 +41,7 @@ void XMETHODCALLTYPE CRenderable::renderStage(X_RENDER_STAGE stage, IXRenderable
 		m_pDynamicModelProvider->render(false, pVis);
 		break;
 	case XRS_GI:
+		m_pDynamicModelProvider->renderEmissive(pVis);
 		break;
 	case XRS_POSTPROCESS_MAIN:
 		break;
diff --git a/source/anim/RenderableVisibility.cpp b/source/anim/RenderableVisibility.cpp
index 7f15e413e..2d8182f91 100644
--- a/source/anim/RenderableVisibility.cpp
+++ b/source/anim/RenderableVisibility.cpp
@@ -61,6 +61,7 @@ void CRenderableVisibility::append(const IXRenderableVisibility *pOther_)
 			else
 			{
 				pItem->isTransparent = pOtherItem->isTransparent;
+				pItem->isEmissive = pOtherItem->isEmissive;
 				pItem->isVisible = true;
 				pItem->uLod = pOtherItem->uLod;
 			}
diff --git a/source/anim/RenderableVisibility.h b/source/anim/RenderableVisibility.h
index d0d4112e1..737b25166 100644
--- a/source/anim/RenderableVisibility.h
+++ b/source/anim/RenderableVisibility.h
@@ -28,6 +28,7 @@ public:
 		bool isVisible = false;
 		UINT uLod = 0;
 		bool isTransparent = false;
+		bool isEmissive = false;
 	};
 
 	struct TransparentModel
diff --git a/source/game/GameData.cpp b/source/game/GameData.cpp
index ea289172e..a3f420e14 100644
--- a/source/game/GameData.cpp
+++ b/source/game/GameData.cpp
@@ -441,7 +441,7 @@ GameData::GameData(HWND hWnd, bool isGame):
 
 	m_pLightSystem = (IXLightSystem*)Core_GetIXCore()->getPluginManager()->getInterface(IXLIGHTSYSTEM_GUID);
 
-	if(m_pLightSystem)
+	if(m_pLightSystem && false)
 	{
 		//	252, 212, 64
 		IXLightSun *pSun = m_pLightSystem->newSun();
diff --git a/source/mtrl/IXMaterial.h b/source/mtrl/IXMaterial.h
index c843d48bc..e908ab004 100644
--- a/source/mtrl/IXMaterial.h
+++ b/source/mtrl/IXMaterial.h
@@ -112,6 +112,9 @@ public:
 	virtual const char* XMETHODCALLTYPE getName() const = 0;
 
 	virtual bool XMETHODCALLTYPE save() = 0;
+
+	virtual void XMETHODCALLTYPE setEmissive(bool bValue) = 0;
+	virtual bool XMETHODCALLTYPE isEmissive() const = 0;
 };
 
 #endif
diff --git a/source/mtrl/MaterialSystem.cpp b/source/mtrl/MaterialSystem.cpp
index b81431c1a..1a2f5a5a2 100644
--- a/source/mtrl/MaterialSystem.cpp
+++ b/source/mtrl/MaterialSystem.cpp
@@ -2197,6 +2197,7 @@ CMaterial::CMaterial(CMaterialSystem *pMaterialSystem, const char *szName):
 	m_pTransparent = createFlag("transparent", XEventMaterialChanged::TYPE_TRANSPARENCY);
 	m_pRefractive = createFlag("refractive", XEventMaterialChanged::TYPE_REFRACTIVITY);
 	m_pBlurred = createFlag("blurred", XEventMaterialChanged::TYPE_BLURRED);
+	m_pEmissive = createFlag("emissive", XEventMaterialChanged::TYPE_EMISSIVITY);
 
 	setShader("Default");
 }
@@ -2249,6 +2250,17 @@ bool XMETHODCALLTYPE CMaterial::isRefractive() const
 	return(m_pRefractive->get());
 }
 
+void XMETHODCALLTYPE CMaterial::setEmissive(bool bValue)
+{
+	m_pEmissive->set(bValue);
+
+	notifyChanged(XEventMaterialChanged::TYPE_EMISSIVITY);
+}
+bool XMETHODCALLTYPE CMaterial::isEmissive() const
+{
+	return(m_pEmissive->get());
+}
+
 void XMETHODCALLTYPE CMaterial::setBlurred(bool bValue)
 {
 	m_pBlurred->set(bValue);
diff --git a/source/mtrl/MaterialSystem.h b/source/mtrl/MaterialSystem.h
index 0eedd9341..860873670 100644
--- a/source/mtrl/MaterialSystem.h
+++ b/source/mtrl/MaterialSystem.h
@@ -150,6 +150,9 @@ public:
 	void XMETHODCALLTYPE setBlurred(bool bValue) override;
 	bool XMETHODCALLTYPE isBlurred() const override;
 
+	void XMETHODCALLTYPE setEmissive(bool bValue) override;
+	bool XMETHODCALLTYPE isEmissive() const override;
+
 	void XMETHODCALLTYPE setShader(const char *szShader) override;
 	const char* XMETHODCALLTYPE getShader() const override;
 	XMaterialShaderHandler* XMETHODCALLTYPE getShaderHandler() const override;
@@ -280,6 +283,7 @@ private:
 	CMaterialFlag *m_pTransparent = NULL;
 	CMaterialFlag *m_pRefractive = NULL;
 	CMaterialFlag *m_pBlurred = NULL;
+	CMaterialFlag *m_pEmissive = NULL;
 };
 
 class CMaterialSystem: public IXMaterialSystem
diff --git a/source/render/RenderPipeline.cpp b/source/render/RenderPipeline.cpp
index e5efd5784..5e1e33b6f 100644
--- a/source/render/RenderPipeline.cpp
+++ b/source/render/RenderPipeline.cpp
@@ -268,6 +268,12 @@ CRenderPipeline::CRenderPipeline(IGXDevice *pDevice):
 			XMATERIAL_PARAM_GROUP(NULL, "HAS_ALPHATEST"),
 				XMATERIAL_PARAM_RANGE("Alphatest threshold", "pbr_alphatest_threshold", 0.0f, 1.0f, 0.8f),
 			XMATERIAL_PARAM_GROUP_END(),
+
+			XMATERIAL_PARAM_FLAG("Emissive", "emissive", "HAS_EMISSION"),
+			XMATERIAL_PARAM_GROUP(NULL, "HAS_EMISSION"),
+				XMATERIAL_PARAM_TEXTURE_OPT("Emissive map", "txEmissive", "HAS_EMISSIVE_MAP"),
+				XMATERIAL_PARAM_RANGE("Emissive multiplier", "em_multiplier", 0.0f, 1000.0f, 16.0f),
+			XMATERIAL_PARAM_GROUP_END(),
 			XMATERIAL_PROPERTY_LIST_END()
 		};
 
@@ -881,9 +887,11 @@ void CRenderPipeline::renderShadows()
 }
 void CRenderPipeline::renderGI()
 {
+	IGXContext *pCtx = m_pDevice->getThreadContext();
 	rfunc::SetRenderSceneFilter();
 	m_pMaterialSystem->bindRenderPass(m_pRenderPassIllumination);
-	renderStage(XRS_GI);
+	//pCtx->setDepthStencilState(gdata::rstates::pDepthStencilStateNoZ);
+	renderStage(XRS_GI, m_pMainCameraVisibility);
 }
 void CRenderPipeline::renderPostprocessMain()
 {
diff --git a/source/xcommon/XEvents.h b/source/xcommon/XEvents.h
index 727c9f767..a3fbfc39d 100644
--- a/source/xcommon/XEvents.h
+++ b/source/xcommon/XEvents.h
@@ -177,6 +177,7 @@ struct XEventMaterialChanged
 		TYPE_PROPERTY,
 		TYPE_FLAG,
 		TYPE_SHADER,
+		TYPE_EMISSIVITY,
 		// ...
 	} type;
 	IXMaterial *pMaterial;
-- 
GitLab