diff --git a/build/engine/shaders/base/static.vs b/build/engine/shaders/base/static.vs
index 7b0432111ab88bfef95bad40f25752e0f8c604b8..7519f8a490b02ee9ec8f401c5d6f3bcd0c43941b 100644
--- a/build/engine/shaders/base/static.vs
+++ b/build/engine/shaders/base/static.vs
@@ -9,13 +9,50 @@ mtrlgeom_base.vs
 
 //##########################################################################
 
-VSO_SceneCommon main(VSI_Geometry IN)
+float3 RotateVec(float4 q, float3 p)
+{
+	float xxzz = q.x*q.x - q.z*q.z;
+	float wwyy = q.w*q.w - q.y*q.y;
+	float xw2 = q.x*q.w*2.0;
+	float xy2 = q.x*q.y*2.0;
+	float xz2 = q.x*q.z*2.0;
+	float yw2 = q.y*q.w*2.0;
+	float yz2 = q.y*q.z*2.0;
+	float zw2 = q.z*q.w*2.0;
+	float3 oout = float3((xxzz + wwyy)*p.x		+ (xy2 + zw2)*p.y		+ (xz2 - yw2)*p.z,
+					(xy2 - zw2)*p.x			+ (q.y*q.y+q.w*q.w-q.x*q.x-q.z*q.z)*p.y	+ (yz2 + xw2)*p.z,
+					(xz2 + yw2)*p.x			+ (yz2 - xw2)*p.y		+ (wwyy - xxzz)*p.z);
+	return(oout);
+}
+
+//##########################################################################
+
+VSO_SceneCommon main(VSI_Geometry IN
+#ifdef USE_INSTANCING
+, uint uInstanceId: SV_InstanceId
+#endif
+)
 {
 	VSO_SceneCommon OUT = (VSO_SceneCommon)0;
 
+#ifdef USE_INSTANCING
+	
+	float4 vPosScale = g_instanceData[uInstanceId * 2];
+	float4 qRot = g_instanceData[uInstanceId * 2 + 1];
+	
+	OUT.vPosition = float4(RotateVec(qRot, IN.vPosition * vPosScale.w) + vPosScale.xyz, 1.0f);
+	// OUT.vPosition = float4(IN.vPosition + vPosScale.xyz, 1.0f);
+	// OUT.vPosition = float4(IN.vPosition, 1.0f);
+
+	OUT.vNormal = RotateVec(qRot, IN.vNormal);
+	// OUT.vNormal = IN.vNormal;
+#else
 	OUT.vPosition = mul(float4(IN.vPosition, 1.0f), g_mW);
-	OUT.vPosition = mul(OUT.vPosition, g_mVP);
 	OUT.vNormal = mul(IN.vNormal, (float3x3)g_mW);
+#endif
+
+	
+	OUT.vPosition = mul(OUT.vPosition, g_mVP);
 	OUT.vTexUV = IN.vTexUV;
 	OUT.vPos = OUT.vPosition;
 
diff --git a/build/engine/shaders/const.h b/build/engine/shaders/const.h
index 52e0dbc2fcf6eee1d83f2879715f9a033e6f40a1..e36d19b9dbdda132c96db23ce512a566230a12b8 100644
--- a/build/engine/shaders/const.h
+++ b/build/engine/shaders/const.h
@@ -42,12 +42,19 @@ cbuffer CDataCamera: register(b2)
 	float4 g_vNearFar;
 	float3 g_vParamProj;
 };
+#ifdef USE_INSTANCING
+cbuffer CInstanceData: register(b1)
+{
+	float4 g_instanceData[MAX_INSTANCES * 2];
+};
+#else
 cbuffer CDataObject: register(b1)
 {
 	float4x4 g_mW;
 	// float4x4 g_mWV;
 	// float4x4 g_mWVP;
 };
+#endif
 /* cbuffer CDataMaterial: register(b0)
 {
 	float4 g_vUserVS;
diff --git a/proj/SkyXEngine/vs2013/SkyXEngine.vcxproj b/proj/SkyXEngine/vs2013/SkyXEngine.vcxproj
index c146ec6b7bd6e52815dcd9a15d9c44c6d444efbd..6c46e8a3e130cc07f0fc925d5b3af8a0cee62981 100644
--- a/proj/SkyXEngine/vs2013/SkyXEngine.vcxproj
+++ b/proj/SkyXEngine/vs2013/SkyXEngine.vcxproj
@@ -134,6 +134,7 @@
       <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <Profile>true</Profile>
+      <StackReserveSize>10485760</StackReserveSize>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
diff --git a/proj/sxanim/vs2013/sxanim.vcxproj b/proj/sxanim/vs2013/sxanim.vcxproj
index 6883fc8b73263061edaf7fcbe659451e7b671777..9bff4c967f3692ae4e805591b8fd2ef89049a6ef 100644
--- a/proj/sxanim/vs2013/sxanim.vcxproj
+++ b/proj/sxanim/vs2013/sxanim.vcxproj
@@ -133,6 +133,8 @@
       <ImportLibrary>../../../libs64/$(TargetName).lib</ImportLibrary>
       <ProgramDatabaseFile>$(ProjectDir)../../../pdb64/$(TargetName).pdb</ProgramDatabaseFile>
       <Profile>true</Profile>
+      <StackReserveSize>
+      </StackReserveSize>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -201,6 +203,7 @@
     <ClInclude Include="..\..\..\source\anim\Renderable.h" />
     <ClInclude Include="..\..\..\source\anim\RenderableVisibility.h" />
     <ClInclude Include="..\..\..\source\anim\Updatable.h" />
+    <ClInclude Include="..\..\..\source\xcommon\IXScene.h" />
     <ClInclude Include="..\..\..\source\xcommon\resource\IXModelProvider.h" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
diff --git a/proj/sxanim/vs2013/sxanim.vcxproj.filters b/proj/sxanim/vs2013/sxanim.vcxproj.filters
index f2f40835fe3038a157b97e1440a14c430bbab9ac..54b74f26893854001719bc2841ccf10a4ee93e18 100644
--- a/proj/sxanim/vs2013/sxanim.vcxproj.filters
+++ b/proj/sxanim/vs2013/sxanim.vcxproj.filters
@@ -13,6 +13,9 @@
       <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
       <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
     </Filter>
+    <Filter Include="Header Files\xcommon">
+      <UniqueIdentifier>{5397cc6c-b886-417e-9703-f317a4ac05b0}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\..\source\common\string.cpp">
@@ -60,9 +63,6 @@
     <ClInclude Include="..\..\..\source\anim\Updatable.h">
       <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\..\source\xcommon\resource\IXModelProvider.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\..\source\anim\AnimatedModelProvider.h">
       <Filter>Header Files</Filter>
     </ClInclude>
@@ -81,5 +81,11 @@
     <ClInclude Include="..\..\..\source\anim\DynamicModelShared.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\source\xcommon\resource\IXModelProvider.h">
+      <Filter>Header Files\xcommon</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\source\xcommon\IXScene.h">
+      <Filter>Header Files\xcommon</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/proj/sxcore/vs2013/sxcore.vcxproj b/proj/sxcore/vs2013/sxcore.vcxproj
index 8b485282171e34222cf86c3c4c95037a843c856f..26dd37d300f149b0866fb599fd5481931408b5e5 100644
--- a/proj/sxcore/vs2013/sxcore.vcxproj
+++ b/proj/sxcore/vs2013/sxcore.vcxproj
@@ -31,6 +31,7 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
     </ClCompile>
     <ClCompile Include="..\..\..\source\core\Config.cpp" />
+    <ClCompile Include="..\..\..\source\core\Console.cpp" />
     <ClCompile Include="..\..\..\source\core\Core.cpp" />
     <ClCompile Include="..\..\..\source\core\cvars.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
@@ -77,6 +78,7 @@
     <ClInclude Include="..\..\..\source\core\AsyncFileReader.h" />
     <ClInclude Include="..\..\..\source\core\AsyncTaskRunner.h" />
     <ClInclude Include="..\..\..\source\core\Buffer.h" />
+    <ClInclude Include="..\..\..\source\core\Console.h" />
     <ClInclude Include="..\..\..\source\core\FileExtPathsIterator.h" />
     <ClInclude Include="..\..\..\source\core\FileExtsIterator.h" />
     <ClInclude Include="..\..\..\source\core\FileInMemory.h" />
@@ -111,6 +113,7 @@
     <ClInclude Include="..\..\..\source\xcommon\IXAudioCodec.h" />
     <ClInclude Include="..\..\..\source\xcommon\IXBuffer.h" />
     <ClInclude Include="..\..\..\source\xcommon\IXConfig.h" />
+    <ClInclude Include="..\..\..\source\xcommon\IXConsole.h" />
     <ClInclude Include="..\..\..\source\xcommon\IXCore.h" />
     <ClInclude Include="..\..\..\source\xcommon\IXModelLoader.h" />
     <ClInclude Include="..\..\..\source\xcommon\IXPlugin.h" />
diff --git a/proj/sxcore/vs2013/sxcore.vcxproj.filters b/proj/sxcore/vs2013/sxcore.vcxproj.filters
index b3b7f432b889c4b50913fa01a58a61a7b297b099..36bdcb6a99e68b18dea0ce6fe801a8c7c631436e 100644
--- a/proj/sxcore/vs2013/sxcore.vcxproj.filters
+++ b/proj/sxcore/vs2013/sxcore.vcxproj.filters
@@ -94,6 +94,9 @@
     <ClCompile Include="..\..\..\source\core\ResourceTexture.cpp">
       <Filter>Source\resource</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\source\core\Console.cpp">
+      <Filter>Source</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="Source">
@@ -284,5 +287,11 @@
     <ClInclude Include="..\..\..\source\core\Buffer.h">
       <Filter>Header</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\source\xcommon\IXConsole.h">
+      <Filter>Header\xcommon</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\source\core\Console.h">
+      <Filter>Header</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/proj/sxrender/vs2013/sxrender.vcxproj b/proj/sxrender/vs2013/sxrender.vcxproj
index 514922e26e93dc740459b88f7fc52a6c25b2f935..9ab241fd10f2f226d142424c70054564f4ad164d 100644
--- a/proj/sxrender/vs2013/sxrender.vcxproj
+++ b/proj/sxrender/vs2013/sxrender.vcxproj
@@ -187,7 +187,11 @@
     <ClInclude Include="..\..\..\source\render\RenderableVisibility.h" />
     <ClInclude Include="..\..\..\source\render\RenderPipeline.h" />
     <ClInclude Include="..\..\..\source\render\render_func.h" />
+    <ClInclude Include="..\..\..\source\render\Scene.h" />
     <ClInclude Include="..\..\..\source\render\sxrender.h" />
+    <ClInclude Include="..\..\..\source\render\Updatable.h" />
+    <ClInclude Include="..\..\..\source\xcommon\IXRenderable.h" />
+    <ClInclude Include="..\..\..\source\xcommon\IXRenderPipeline.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\..\source\common\file_utils.cpp" />
@@ -198,7 +202,9 @@
     <ClCompile Include="..\..\..\source\render\RenderableVisibility.cpp" />
     <ClCompile Include="..\..\..\source\render\RenderPipeline.cpp" />
     <ClCompile Include="..\..\..\source\render\render_func.cpp" />
+    <ClCompile Include="..\..\..\source\render\Scene.cpp" />
     <ClCompile Include="..\..\..\source\render\sxrender.cpp" />
+    <ClCompile Include="..\..\..\source\render\Updatable.cpp" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
diff --git a/proj/sxrender/vs2013/sxrender.vcxproj.filters b/proj/sxrender/vs2013/sxrender.vcxproj.filters
index 4a957e1884c5323daa8aacb2e54e3927de389d67..786da36b26d0d4973a73bba5ebfb99d2355fe912 100644
--- a/proj/sxrender/vs2013/sxrender.vcxproj.filters
+++ b/proj/sxrender/vs2013/sxrender.vcxproj.filters
@@ -13,6 +13,9 @@
       <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
       <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
     </Filter>
+    <Filter Include="Header Files\xcommon">
+      <UniqueIdentifier>{9cfb51b4-aa0f-4d59-8ff2-f38c438a7086}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\..\source\render\gdata.h">
@@ -42,6 +45,18 @@
     <ClInclude Include="..\..\..\source\render\OcclusionCuller.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\source\xcommon\IXRenderable.h">
+      <Filter>Header Files\xcommon</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\source\xcommon\IXRenderPipeline.h">
+      <Filter>Header Files\xcommon</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\source\render\Scene.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\source\render\Updatable.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\..\source\render\gdata.cpp">
@@ -71,5 +86,11 @@
     <ClCompile Include="..\..\..\source\render\OcclusionCuller.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\source\render\Scene.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\source\render\Updatable.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/sdks/bullet3 b/sdks/bullet3
index 903ebafb58c6e2acd51dd3edebc4a50f89ac9f41..8b2a70a5ec651d0941c2257a7b331ffacd172916 160000
--- a/sdks/bullet3
+++ b/sdks/bullet3
@@ -1 +1 @@
-Subproject commit 903ebafb58c6e2acd51dd3edebc4a50f89ac9f41
+Subproject commit 8b2a70a5ec651d0941c2257a7b331ffacd172916
diff --git a/sdks/graphix b/sdks/graphix
index ead23b338ee7f899588ff090454bb1a8168368c2..3e49f0b06d6e2779847a0f8234bee2dd83275f10 160000
--- a/sdks/graphix
+++ b/sdks/graphix
@@ -1 +1 @@
-Subproject commit ead23b338ee7f899588ff090454bb1a8168368c2
+Subproject commit 3e49f0b06d6e2779847a0f8234bee2dd83275f10
diff --git a/source/SkyXEngine_Build/SkyXEngine_Build.cpp b/source/SkyXEngine_Build/SkyXEngine_Build.cpp
index 79179c9f23abda194fc007d0d5063d373881bdb1..6bf1f0095269310a0706a4fd2182508795199cc9 100644
--- a/source/SkyXEngine_Build/SkyXEngine_Build.cpp
+++ b/source/SkyXEngine_Build/SkyXEngine_Build.cpp
@@ -151,8 +151,8 @@ int main(int argc, char **argv)
 	// pEngine->initServer();
 
 
-	pEngine->getCore()->execCmd("exec ../config_game.cfg");
-	pEngine->getCore()->execCmd("exec ../config_game_user.cfg");
+	pEngine->getCore()->getConsole()->execCommand("exec ../config_game.cfg");
+	pEngine->getCore()->getConsole()->execCommand("exec ../config_game_user.cfg");
 
 #if 0
 
diff --git a/source/ambient/SkyBox.cpp b/source/ambient/SkyBox.cpp
index 8fbb4cde4a4df7ad6cb232fc5ecfa1902435ffac..01535585c225ca47f912b367eb6d089603f4cc8c 100644
--- a/source/ambient/SkyBox.cpp
+++ b/source/ambient/SkyBox.cpp
@@ -17,7 +17,7 @@ public:
 
 	virtual void onEvent(const XEventCvarChanged *pEvent)
 	{
-		static const float *r_far = m_pCore->getPCVarFloat("r_far");
+		static const float *r_far = m_pCore->getConsole()->getPCVarFloat("r_far");
 		if(pEvent->pCvar == r_far)
 		{
 			m_pSkyBox->updateBuffers();
@@ -111,7 +111,7 @@ void CSkyBox::updateBuffers()
 {
 	mem_release(m_pRenderBuffer);
 
-	static const float * r_far = m_pCore->getPCVarFloat("r_far");
+	static const float * r_far = m_pCore->getConsole()->getPCVarFloat("r_far");
 
 	float fFar = *r_far * 0.57735f;
 
diff --git a/source/anim/AnimatedModel.cpp b/source/anim/AnimatedModel.cpp
index 7e65b10c62bb5aa9855bb638aae0e6d42cb6b7a5..c73e8d4c0823b96b078a851709c23fb3b30bed06 100644
--- a/source/anim/AnimatedModel.cpp
+++ b/source/anim/AnimatedModel.cpp
@@ -736,7 +736,7 @@ void CAnimatedModel::fillBoneMatrix()
 	m_isBoneMatrixReFilled = true;
 }
 
-void XMETHODCALLTYPE CAnimatedModel::render(UINT uLod, bool isTransparent)
+void XMETHODCALLTYPE CAnimatedModel::render(UINT uLod, XMODEL_FEATURE bmFeatures)
 {
 	if(!m_pDevice || !m_isEnabled || !m_pWorldBuffer)
 	{
diff --git a/source/anim/AnimatedModel.h b/source/anim/AnimatedModel.h
index 63d3e2fffcba4c76cda0c015fc8856af10d4a62d..50e5e15e62327025d527ff3468e8e7603e46b38e 100644
--- a/source/anim/AnimatedModel.h
+++ b/source/anim/AnimatedModel.h
@@ -83,7 +83,7 @@ public:
 
 	UINT addLayer();
 
-	void XMETHODCALLTYPE render(UINT uLod, bool isTransparent) override;
+	void XMETHODCALLTYPE render(UINT uLod, XMODEL_FEATURE bmFeatures) override;
 	void sync();
 
 	void initGPUresources();
diff --git a/source/anim/AnimatedModelProvider.cpp b/source/anim/AnimatedModelProvider.cpp
index 4089345a9002d7b91039509deae4f04c1f69f603..9a34a45b8c597ab70b455600df0d854736a268ab 100644
--- a/source/anim/AnimatedModelProvider.cpp
+++ b/source/anim/AnimatedModelProvider.cpp
@@ -190,12 +190,12 @@ void CAnimatedModelProvider::render(CRenderableVisibility *pVisibility)
 			auto pItem = pVisibility->getItem(i);
 			if(pItem->isVisible)
 			{
-				m_apModels[i]->render(pItem->uLod, false);
+				m_apModels[i]->render(pItem->uLod, MF_OPAQUE);
 			}
 		}
 		else
 		{
-			m_apModels[i]->render(0, false);
+			m_apModels[i]->render(0, MF_OPAQUE);
 		}
 	}
 	m_pMaterialSystem->bindVS(NULL);
diff --git a/source/anim/DynamicModel.cpp b/source/anim/DynamicModel.cpp
index 51105133790e1c8a346b8d3f551d9f4b3c7d9adb..f83a8ead9a6580097941f65cc73b3bdfebe4aa13 100644
--- a/source/anim/DynamicModel.cpp
+++ b/source/anim/DynamicModel.cpp
@@ -19,6 +19,9 @@ CDynamicModel::CDynamicModel(CDynamicModelProvider *pProvider, CDynamicModelShar
 			m_pProvider->scheduleModelGPUinit(this);
 		}
 	}
+
+	m_pSceneObject = m_pProvider->getSceneObjectType()->newObject(getLocalBound() + getPosition(), this);
+	onFeaturesChanged();
 }
 CDynamicModel::~CDynamicModel()
 {
@@ -26,6 +29,7 @@ CDynamicModel::~CDynamicModel()
 	m_pProvider->onModelRelease(this);
 	mem_release(m_pShared);
 	mem_release(m_pWorldBuffer);
+	mem_release(m_pSceneObject);
 }
 
 void CDynamicModel::initGPUresources()
@@ -51,6 +55,16 @@ void XMETHODCALLTYPE CDynamicModel::enable(bool yesNo)
 	}
 	m_isEnabled = yesNo;
 
+	if(m_isEnabled && !m_pSceneObject)
+	{
+		m_pSceneObject = m_pProvider->getSceneObjectType()->newObject(getLocalBound() + getPosition(), this);
+		onFeaturesChanged();
+	}
+	if(!m_isEnabled)
+	{
+		mem_release(m_pSceneObject);
+	}
+
 	m_pProvider->notifyModelChanged(this, XEventModelChanged::TYPE_VISIBILITY);
 }
 
@@ -81,6 +95,8 @@ void XMETHODCALLTYPE CDynamicModel::setPosition(const float3 &vPos)
 	m_isWorldDirty = true;
 
 	m_pProvider->notifyModelChanged(this, XEventModelChanged::TYPE_MOVED);
+
+	m_pSceneObject->update(getLocalBound() + getPosition());
 }
 
 SMQuaternion XMETHODCALLTYPE CDynamicModel::getOrientation() const
@@ -98,6 +114,8 @@ void XMETHODCALLTYPE CDynamicModel::setOrientation(const SMQuaternion &qRot)
 	m_isWorldDirty = true;
 
 	m_pProvider->notifyModelChanged(this, XEventModelChanged::TYPE_MOVED);
+
+	m_pSceneObject->update(getLocalBound() + getPosition());
 }
 
 float XMETHODCALLTYPE CDynamicModel::getScale() const
@@ -115,6 +133,8 @@ void XMETHODCALLTYPE CDynamicModel::setScale(float fScale)
 	m_isWorldDirty = true;
 
 	m_pProvider->notifyModelChanged(this, XEventModelChanged::TYPE_MOVED);
+
+	m_pSceneObject->update(getLocalBound() + getPosition());
 }
 
 UINT XMETHODCALLTYPE CDynamicModel::getSkin() const
@@ -131,6 +151,8 @@ void XMETHODCALLTYPE CDynamicModel::setSkin(UINT uSkin)
 	m_uSkin = uSkin;
 
 	m_pProvider->notifyModelChanged(this, XEventModelChanged::TYPE_SKIN);
+
+	onFeaturesChanged();
 }
 
 float3 XMETHODCALLTYPE CDynamicModel::getLocalBoundMin() const
@@ -200,40 +222,43 @@ UINT XMETHODCALLTYPE CDynamicModel::getPhysboxCount(UINT uPartIndex) const
 
 	return(m_pShared->getPhysboxCount());
 }
-const IModelPhysbox * XMETHODCALLTYPE CDynamicModel::getPhysBox(UINT id, UINT uPartIndex) const
+const IModelPhysbox* XMETHODCALLTYPE CDynamicModel::getPhysBox(UINT id, UINT uPartIndex) const
 {
 	assert(uPartIndex == 0);
 
 	return(m_pShared->getPhysBox(id));
 }
-const IXResourceModel * XMETHODCALLTYPE CDynamicModel::getResource(UINT uIndex)
+const IXResourceModel* XMETHODCALLTYPE CDynamicModel::getResource(UINT uIndex)
 {
 	assert(uIndex == 0);
 
 	return(m_pShared->getResource());
 }
 
-void XMETHODCALLTYPE CDynamicModel::render(UINT uLod, bool isTransparent)
-{
-	render(uLod, isTransparent, false);
-}
-void CDynamicModel::render(UINT uLod, bool isTransparent, bool isEmissiveOnly)
+void XMETHODCALLTYPE CDynamicModel::render(UINT uLod, XMODEL_FEATURE bmFeatures)
 {
 	if(!m_pDevice || !m_isEnabled || !m_pWorldBuffer)
 	{
 		return;
 	}
 
-	if(m_isWorldDirty)
+	if(m_pShared->isInstancing())
 	{
-		m_pWorldBuffer->update(&SMMatrixTranspose(SMMatrixScaling(m_fScale) * m_qRotation.GetMatrix() * SMMatrixTranslation(m_vPosition)));
-		m_isWorldDirty = false;
+		m_pShared->renderInstanced(m_vPosition, m_qRotation, m_fScale, m_vColor);
 	}
+	else
+	{
+		if(m_isWorldDirty)
+		{
+			m_pWorldBuffer->update(&SMMatrixTranspose(SMMatrixScaling(m_fScale) * m_qRotation.GetMatrix() * SMMatrixTranslation(m_vPosition)));
+			m_isWorldDirty = false;
+		}
 
-	m_pProvider->bindVertexFormat();
+		m_pProvider->bindVertexFormat();
 
-	m_pDevice->getThreadContext()->setVSConstant(m_pWorldBuffer, 1 /* SCR_OBJECT */);
-	m_pShared->render(m_uSkin, uLod, m_vColor, isTransparent, isEmissiveOnly);
+		m_pDevice->getThreadContext()->setVSConstant(m_pWorldBuffer, 1 /* SCR_OBJECT */);
+		m_pShared->render(m_uSkin, uLod, m_vColor, bmFeatures);
+	}
 }
 
 CDynamicModelShared* CDynamicModel::getShared()
@@ -257,16 +282,45 @@ SMPLANE CDynamicModel::getPSP(UINT uLod, UINT uPlane) const
 	return(plane);
 }
 
-bool CDynamicModel::hasTransparentSubsets(UINT uLod) const
-{
-	return(m_pShared->hasTransparentSubsets(m_uSkin, uLod));
-}
-bool CDynamicModel::hasEmissiveSubsets(UINT uLod) const
+XMODEL_FEATURE CDynamicModel::getFeatures(UINT uLod) const
 {
-	return(m_pShared->hasEmissiveSubsets(m_uSkin, uLod));
+	return(m_pShared->getFeatures(m_uSkin, uLod));
 }
 
 IXMaterial* CDynamicModel::getTransparentMaterial(UINT uLod)
 {
 	return(m_pShared->getTransparentMaterial(m_uSkin, uLod));
 }
+
+void CDynamicModel::onFeaturesChanged()
+{
+	if(m_pSceneObject)
+	{
+		XMODEL_FEATURE bmFeatures = getFeatures(0);
+
+		IXSceneFeature *ppFeatures[4] = {0};
+		int i = 0;
+
+		if(bmFeatures & MF_OPAQUE)
+		{
+			ppFeatures[i++] = m_pProvider->getFeature(MF_OPAQUE);
+		}
+		if(bmFeatures & MF_TRANSPARENT)
+		{
+			ppFeatures[i++] = m_pProvider->getFeature(MF_TRANSPARENT);
+		}
+		if(bmFeatures & MF_SELFILLUM)
+		{
+			ppFeatures[i++] = m_pProvider->getFeature(MF_SELFILLUM);
+		}
+
+		assert(i < ARRAYSIZE(ppFeatures));
+
+		m_pSceneObject->setFeatures(ppFeatures);
+	}
+}
+
+void XMETHODCALLTYPE CDynamicModel::FinalRelease()
+{
+	m_pProvider->enqueueModelDelete(this);
+}
diff --git a/source/anim/DynamicModel.h b/source/anim/DynamicModel.h
index c6e2419ca60c2fcdb2352aa75435a609c2512024..c2ccc562d628be9ff1af951822271db406d4f685 100644
--- a/source/anim/DynamicModel.h
+++ b/source/anim/DynamicModel.h
@@ -2,9 +2,10 @@
 #define __DYNAMICMODEL_H
 
 #include <xcommon/resource/IXModel.h>
+#include <xcommon/IXScene.h>
 #include "DynamicModelShared.h"
 
-class CDynamicModel: public IXUnknownImplementation<IXDynamicModel>
+class CDynamicModel final: public IXUnknownImplementation<IXDynamicModel>
 {
 public:
 	CDynamicModel(CDynamicModelProvider *pProvider, CDynamicModelShared *pShared);
@@ -41,21 +42,22 @@ public:
 	const IModelPhysbox* XMETHODCALLTYPE getPhysBox(UINT id, UINT uPartIndex = 0) const override;
 	const IXResourceModel* XMETHODCALLTYPE getResource(UINT uIndex = 0) override;
 
-	void XMETHODCALLTYPE render(UINT uLod, bool isTransparent) override;
-	void render(UINT uLod, bool isTransparent, bool isEmissiveOnly);
+	void XMETHODCALLTYPE render(UINT uLod, XMODEL_FEATURE bmFeatures) override;
 
 	CDynamicModelShared* getShared();
 	void initGPUresources();
 
 	UINT getPSPcount(UINT uLod) const;
 	SMPLANE getPSP(UINT uLod, UINT uPlane) const;
-	bool hasTransparentSubsets(UINT uLod) const;
-	bool hasEmissiveSubsets(UINT uLod) const;
+	XMODEL_FEATURE getFeatures(UINT uLod) const;
 	IXMaterial* getTransparentMaterial(UINT uLod);
+
+	void onFeaturesChanged();
 protected:
 	CDynamicModelProvider *m_pProvider;
 	CDynamicModelShared *m_pShared;
 	IGXDevice *m_pDevice;
+	IXSceneObject *m_pSceneObject = NULL;
 
 	IGXConstantBuffer *m_pWorldBuffer = NULL;
 	bool m_isWorldDirty = true;
@@ -72,6 +74,8 @@ protected:
 	mutable float3_t m_vLocalMax;
 
 	void _updateAABB() const;
+
+	void XMETHODCALLTYPE FinalRelease() override;
 };
 
 #endif
diff --git a/source/anim/DynamicModelProvider.cpp b/source/anim/DynamicModelProvider.cpp
index 5b38ed428e878b1a8badb421843944e01fdc3f53..3826ab7d5366efcc526e764a4db09207ef06bf25 100644
--- a/source/anim/DynamicModelProvider.cpp
+++ b/source/anim/DynamicModelProvider.cpp
@@ -91,6 +91,7 @@ protected:
 };
 */
 
+#if 0
 class CVisUpdate: public IParallelForBody
 {
 public:
@@ -152,6 +153,7 @@ protected:
 	Array<CDynamicModel*> &m_apModels;
 	mutable mutex m_transparency;
 };
+#endif 
 
 //##########################################################################
 
@@ -161,6 +163,21 @@ CDynamicModelProvider::CDynamicModelProvider(IXCore *pCore):
 	m_pMaterialChangedEventListener = new CMaterialChangedEventListener(this);
 	pCore->getEventChannel<XEventMaterialChanged>(EVENT_MATERIAL_CHANGED_GUID)->addListener(m_pMaterialChangedEventListener);
 	m_pModelChangedEventChannel = pCore->getEventChannel<XEventModelChanged>(EVENT_MODEL_CHANGED_GUID);
+
+	m_pScene = (IXScene*)pCore->getPluginManager()->getInterface(IXSCENE_GUID);
+	m_pObjectType = m_pScene->registerObjectType("xDynamic");
+	m_pFeatureOpaque = m_pScene->registerObjectFeature("xOpaque");
+	m_pFeatureTransparent = m_pScene->registerObjectFeature("xTransparent");
+	m_pFeatureSelfillum = m_pScene->registerObjectFeature("xSelfillum");
+
+	m_pOpaqueQuery = m_pObjectType->newQuery();
+	m_pOpaqueQuery->setFeature(m_pFeatureOpaque, SQF_SET);
+
+	m_pTransparentQuery = m_pObjectType->newQuery();
+	m_pTransparentQuery->setFeature(m_pFeatureTransparent, SQF_SET);
+
+	m_pSelfillumQuery = m_pObjectType->newQuery();
+	m_pSelfillumQuery->setFeature(m_pFeatureSelfillum, SQF_SET);
 }
 
 CDynamicModelProvider::~CDynamicModelProvider()
@@ -168,6 +185,11 @@ CDynamicModelProvider::~CDynamicModelProvider()
 	m_pCore->getEventChannel<XEventMaterialChanged>(EVENT_MATERIAL_CHANGED_GUID)->removeListener(m_pMaterialChangedEventListener);
 	mem_delete(m_pMaterialChangedEventListener);
 	mem_release(m_pVertexDeclaration);
+	mem_release(m_pOpaqueQuery);
+	mem_release(m_pTransparentQuery);
+	mem_release(m_pSelfillumQuery);
+	mem_release(m_pObjectType);
+	mem_release(m_pScene);
 }
 
 IGXVertexDeclaration *CDynamicModelProvider::getVertexDeclaration()
@@ -205,6 +227,14 @@ void CDynamicModelProvider::setDevice(IGXDevice *pDevice)
 	m_pMaterialSystem = (IXMaterialSystem*)m_pCore->getPluginManager()->getInterface(IXMATERIALSYSTEM_GUID);
 	XVertexFormatHandler *pFormat = m_pMaterialSystem->getVertexFormat("xSceneGeneric");
 	m_pVertexShaderHandler = m_pMaterialSystem->registerVertexShader(pFormat, "base/static.vs");
+
+	GXMacro macroInstanced[] = {
+		{"USE_INSTANCING", "1"},
+		{"MAX_INSTANCES", MACRO_TEXT(MAX_INSTANCES)},
+		GX_MACRO_END()
+	};
+
+	m_pVertexShaderInstancedHandler = m_pMaterialSystem->registerVertexShader(pFormat, "base/static.vs", macroInstanced);
 }
 
 bool XMETHODCALLTYPE CDynamicModelProvider::createModel(IXResourceModel *pResource, IXDynamicModel **ppModel)
@@ -262,6 +292,18 @@ void CDynamicModelProvider::onSharedModelRelease(CDynamicModelShared *pShared)
 {
 	m_mModels[pShared->getResource()] = NULL;
 }
+void CDynamicModelProvider::onSharedModelFeaturesChanged(CDynamicModelShared *pShared)
+{
+	CDynamicModel *pTmp;
+	for(UINT i = 0, l = m_apModels.size(); i < l; ++i)
+	{
+		pTmp = m_apModels[i];
+		if(pTmp->getShared() == pShared)
+		{
+			pTmp->onFeaturesChanged();
+		}
+	}
+}
 
 void CDynamicModelProvider::onModelRelease(CDynamicModel *pModel)
 {
@@ -282,14 +324,137 @@ IXMaterialSystem *CDynamicModelProvider::getMaterialSystem()
 	return((IXMaterialSystem*)m_pCore->getPluginManager()->getInterface(IXMATERIALSYSTEM_GUID));
 }
 
-void CDynamicModelProvider::bindVertexFormat()
+void CDynamicModelProvider::bindVertexFormat(bool forInstancing)
 {
-	m_pMaterialSystem->bindVS(m_pVertexShaderHandler);
+	m_pMaterialSystem->bindVS(forInstancing ? m_pVertexShaderInstancedHandler : m_pVertexShaderHandler);
+}
+
+void CDynamicModelProvider::render(Array<CDynamicModel*> &aRenderList, XMODEL_FEATURE bmWhat)
+{
+	bindVertexFormat();
+	auto &rl = aRenderList;
+
+	CDynamicModel *pFirstInGroup = NULL;
+	CDynamicModel *pCur = NULL;
+	bool isInstancingStarted = false;
+	for(UINT i = 0, l = rl.size(); i < l; ++i)
+	{
+		pCur = rl[i];
+		if(!pFirstInGroup)
+		{
+			pFirstInGroup = pCur;
+		}
+		else
+		{
+			if(pFirstInGroup->getShared() != pCur->getShared() || pFirstInGroup->getSkin() != pCur->getSkin())
+			{
+				// new group
+				if(isInstancingStarted)
+				{
+					isInstancingStarted = false;
+					pFirstInGroup->getShared()->endInstancing();
+				}
+				else
+				{
+					pFirstInGroup->render(0, bmWhat);
+				}
+				pFirstInGroup = pCur;
+			}
+			else
+			{
+				// start instancing
+				if(!isInstancingStarted)
+				{
+					isInstancingStarted = true;
+					pFirstInGroup->getShared()->beginInstancing(pFirstInGroup->getSkin(), 0, bmWhat);
+					pFirstInGroup->render(0, bmWhat);
+				}
+				pCur->render(0, bmWhat);
+			}
+		}
+	}
+	if(isInstancingStarted)
+	{
+		pFirstInGroup->getShared()->endInstancing();
+	}
+	else if(pFirstInGroup)
+	{
+		pFirstInGroup->render(0, bmWhat);
+	}
+
+	m_pMaterialSystem->bindVS(NULL);
 }
 
 void CDynamicModelProvider::render(bool isTransparent, CRenderableVisibility *pVisibility)
 {
+	if(!pVisibility/* || isTransparent*/)
+	{
+		return;
+	}
+
+	render(pVisibility->getRenderList(), MF_OPAQUE);
+
+	if(isTransparent)
+	{
+		render(pVisibility->getSelfillumList(), MF_TRANSPARENT);
+	}
+
+#if 0
+	return;
+
 	bindVertexFormat();
+
+	auto &rl = pVisibility->getRenderList();
+
+	CDynamicModel *pFirstInGroup = NULL;
+	CDynamicModel *pCur = NULL;
+	bool isInstancingStarted = false;
+	for(UINT i = 0, l = rl.size(); i < l; ++i)
+	{
+		pCur = rl[i];
+		if(!pFirstInGroup)
+		{
+			pFirstInGroup = pCur;
+		}
+		else
+		{
+			if(pFirstInGroup->getShared() != pCur->getShared() || pFirstInGroup->getSkin() != pCur->getSkin())
+			{
+				// new group
+				if(isInstancingStarted)
+				{
+					isInstancingStarted = false;
+					pFirstInGroup->getShared()->endInstancing();
+				}
+				else
+				{
+					pFirstInGroup->render(0, MF_OPAQUE);
+				}
+				pFirstInGroup = pCur;
+			}
+			else
+			{
+				// start instancing
+				if(!isInstancingStarted)
+				{
+					isInstancingStarted = true;
+					pFirstInGroup->getShared()->beginInstancing(pFirstInGroup->getSkin(), 0);
+					pFirstInGroup->render(0, MF_OPAQUE);
+				}
+				pCur->render(0, MF_OPAQUE);
+			}
+		}
+	}
+	if(isInstancingStarted)
+	{
+		pFirstInGroup->getShared()->endInstancing();
+	}
+	else if(pFirstInGroup)
+	{
+		pFirstInGroup->render(0, MF_OPAQUE);
+	}
+
+#if 0
 	//if(isTransparent/* || m_apModels.size() < 1000*/)
 	//{
 		for(UINT i = 0, l = m_apModels.size(); i < l; ++i)
@@ -312,6 +477,7 @@ void CDynamicModelProvider::render(bool isTransparent, CRenderableVisibility *pV
 			}
 		}
 	//}
+#endif
 	/*else
 	{
 		IGXContextState *pState = m_pRenderContext->getThreadContext()->getCurrentState();
@@ -350,29 +516,63 @@ void CDynamicModelProvider::render(bool isTransparent, CRenderableVisibility *pV
 		//printf("\n");
 	}*/
 	m_pMaterialSystem->bindVS(NULL);
+#endif
 }
 
 void CDynamicModelProvider::renderEmissive(CRenderableVisibility *pVisibility)
 {
-	bindVertexFormat();
-	for(UINT i = 0, l = m_apModels.size(); i < l; ++i)
+	if(!pVisibility/* || isTransparent*/)
 	{
-		auto pItem = pVisibility->getItemDynamic(i);
-		if(pItem->isVisible)
+		return;
+	}
+
+	render(pVisibility->getSelfillumList(), MF_SELFILLUM);
+}
+
+void CDynamicModelProvider::computeVisibility(const IFrustum *pFrustum, CRenderableVisibility *pVisibility, CRenderableVisibility *pReference)
+{
+	void **ppData;
+	UINT uCount = m_pOpaqueQuery->execute(pFrustum, &ppData);
+	pVisibility->setRenderList(ppData, uCount);
+
+	uCount = m_pSelfillumQuery->execute(pFrustum, &ppData);
+	pVisibility->setSelfillumList(ppData, uCount);
+
+	uCount = m_pTransparentQuery->execute(pFrustum, &ppData);
+	pVisibility->setTransparentList(ppData, uCount);
+
+	{
+		//pVisibility->resetItemTransparentDynamic();
+		pVisibility->setItemTransparentCountDynamic(uCount);
+		auto &rl = pVisibility->getTransparentList();
+		CDynamicModel *pMdl;
+		for(UINT i = 0, l = rl.size(); i < l; ++i)
 		{
-			if(pItem->isEmissive)
+			pMdl = rl[i];
+
+			float3 vDelta = pMdl->getPosition();
+			
+			IXMaterial *pMaterial = pMdl->getTransparentMaterial(0);
+			UINT uPSPcount = pMdl->getPSPcount(0);
+			auto *pItem = pVisibility->getItemTransparentDynamic(i);
+			pItem->pReferenceMdl = pMdl;
+			pItem->uLod = 0;
+			pItem->pMaterial = pMaterial;
+			if(uPSPcount)
 			{
-				m_apModels[i]->render(pItem->uLod, pItem->isTransparent, pItem->isEmissive);
+				pItem->hasPSP = true;
+				pItem->psp = pMdl->getPSP(0, 0);
 			}
+			else
+			{
+				pItem->hasPSP = false;
+			}
+			mem_release(pMaterial);
 		}
 	}
-	m_pMaterialSystem->bindVS(NULL);
-}
 
-void CDynamicModelProvider::computeVisibility(const IFrustum *pFrustum, CRenderableVisibility *pVisibility, CRenderableVisibility *pReference)
-{
-	pVisibility->setItemCountDynamic(m_apModels.size());
-	pVisibility->resetItemTransparentDynamic();
+#if 0
+	return;
 #if 1
 	CVisUpdate cycle(pFrustum, pVisibility, pReference, m_apModels);
 	ID id = m_pCore->forLoop(0, m_apModels.size(), &cycle, 500);
@@ -415,6 +615,7 @@ void CDynamicModelProvider::computeVisibility(const IFrustum *pFrustum, CRendera
 		}
 	}
 #endif
+#endif
 }
 
 void CDynamicModelProvider::getLevelSize(const XEventLevelSize *pData)
@@ -451,7 +652,7 @@ void CDynamicModelProvider::update()
 	time_point tStart = std::chrono::high_resolution_clock::now();
 
 	CDynamicModelShared *pShared;
-	while(m_queueGPUinitShared.tryPop(pShared))
+	while(m_queueGPUinitShared.pop(&pShared))
 	{
 		pShared->initGPUresources();
 		mem_release(pShared);
@@ -463,7 +664,7 @@ void CDynamicModelProvider::update()
 	}
 
 	CDynamicModel *pModel;
-	while(m_queueGPUinitModel.tryPop(pModel))
+	while(m_queueGPUinitModel.pop(&pModel))
 	{
 		pModel->initGPUresources();
 		mem_release(pModel);
@@ -475,6 +676,15 @@ void CDynamicModelProvider::update()
 	}
 }
 
+void CDynamicModelProvider::sync()
+{
+	CDynamicModel *pMdl;
+	while(m_qModelDelete.pop(&pMdl))
+	{
+		mem_delete(pMdl);
+	}
+}
+
 void CDynamicModelProvider::scheduleSharedGPUinit(CDynamicModelShared *pShared)
 {
 	pShared->AddRef();
@@ -530,7 +740,7 @@ void CDynamicModelProvider::renderTransparentObject(CRenderableVisibility *pVisi
 {
 	m_pMaterialSystem->bindVS(m_pVertexShaderHandler);
 	CRenderableVisibility::TransparentModel *pMdl = pVisibility->getItemTransparentDynamic(uIndex);
-	pMdl->pReferenceMdl->render(pMdl->uLod, true);
+	pMdl->pReferenceMdl->render(pMdl->uLod, MF_TRANSPARENT);
 }
 
 void CDynamicModelProvider::notifyModelChanged(CDynamicModel *pModel, XEventModelChanged::TYPE type)
@@ -540,3 +750,27 @@ void CDynamicModelProvider::notifyModelChanged(CDynamicModel *pModel, XEventMode
 	ev.pModel = pModel;
 	m_pModelChangedEventChannel->broadcastEvent(&ev);
 }
+
+IXSceneObjectType* CDynamicModelProvider::getSceneObjectType()
+{
+	return(m_pObjectType);
+}
+
+IXSceneFeature* CDynamicModelProvider::getFeature(XMODEL_FEATURE bmFeature)
+{
+	switch(bmFeature)
+	{
+	case MF_OPAQUE:
+		return(m_pFeatureOpaque);
+	case MF_TRANSPARENT:
+		return(m_pFeatureTransparent);
+	case MF_SELFILLUM:
+		return(m_pFeatureSelfillum);
+	}
+	return(NULL);
+}
+
+void CDynamicModelProvider::enqueueModelDelete(CDynamicModel* pModel)
+{
+	m_qModelDelete.push(pModel);
+}
diff --git a/source/anim/DynamicModelProvider.h b/source/anim/DynamicModelProvider.h
index 17de89e91d0197979667d6c29e891da573095485..6bf8785515d376ce9cff467fa5d77d6dff4ca31d 100644
--- a/source/anim/DynamicModelProvider.h
+++ b/source/anim/DynamicModelProvider.h
@@ -9,6 +9,8 @@
 #include "RenderableVisibility.h"
 #include <common/ConcurrentQueue.h>
 #include <xcommon/XEvents.h>
+#include <xcommon/IXScene.h>
+#include <common/queue.h>
 
 class CDynamicModelProvider;
 class CMaterialChangedEventListener: public IEventListener<XEventMaterialChanged>
@@ -33,6 +35,7 @@ public:
 
 	void onSharedModelReady(CDynamicModelShared *pShared);
 	void onSharedModelRelease(CDynamicModelShared *pShared);
+	void onSharedModelFeaturesChanged(CDynamicModelShared *pShared);
 	void onModelRelease(CDynamicModel *pModel);
 	IXMaterialSystem* getMaterialSystem();
 	IGXDevice* getDevice();
@@ -44,9 +47,12 @@ public:
 	void renderEmissive(CRenderableVisibility *pVisibility);
 	void computeVisibility(const IFrustum *pFrustum, CRenderableVisibility *pVisibility, CRenderableVisibility *pReference=NULL);
 
+	void render(Array<CDynamicModel*> &aRenderList, XMODEL_FEATURE bmWhat);
+
 	void getLevelSize(const XEventLevelSize *pData);
 
 	void update();
+	void sync();
 
 	void scheduleSharedGPUinit(CDynamicModelShared *pShared);
 	void scheduleModelGPUinit(CDynamicModel *pModel);
@@ -57,7 +63,13 @@ public:
 
 	void notifyModelChanged(CDynamicModel *pModel, XEventModelChanged::TYPE type);
 
-	void bindVertexFormat();
+	void bindVertexFormat(bool forInstancing = false);
+
+	IXSceneObjectType* getSceneObjectType();
+
+	IXSceneFeature* getFeature(XMODEL_FEATURE bmFeature);
+
+	void enqueueModelDelete(CDynamicModel* pModel);
 protected:
 	void onMaterialEmissivityChanged(const IXMaterial *pMaterial);
 	void onMaterialTransparencyChanged(const IXMaterial *pMaterial);
@@ -75,11 +87,23 @@ protected:
 	IXCore *m_pCore;
 	IGXDevice *m_pRenderContext = NULL;
 
-	CConcurrentQueue<CDynamicModelShared*> m_queueGPUinitShared;
-	CConcurrentQueue<CDynamicModel*> m_queueGPUinitModel;
+	Queue<CDynamicModelShared*> m_queueGPUinitShared;
+	Queue<CDynamicModel*> m_queueGPUinitModel;
 
 	IXMaterialSystem *m_pMaterialSystem = NULL;
 	XVertexShaderHandler *m_pVertexShaderHandler = NULL;
+	XVertexShaderHandler *m_pVertexShaderInstancedHandler = NULL;
+
+	IXScene *m_pScene = NULL;
+	IXSceneObjectType *m_pObjectType = NULL;
+	IXSceneFeature *m_pFeatureOpaque = NULL;
+	IXSceneFeature *m_pFeatureTransparent = NULL;
+	IXSceneFeature *m_pFeatureSelfillum = NULL;
+	IXSceneQuery *m_pOpaqueQuery = NULL;
+	IXSceneQuery *m_pTransparentQuery = NULL;
+	IXSceneQuery *m_pSelfillumQuery = NULL;
+
+	Queue<CDynamicModel*> m_qModelDelete;
 };
 
 #endif
diff --git a/source/anim/DynamicModelShared.cpp b/source/anim/DynamicModelShared.cpp
index 6f7c8d94d3e2e0a4162845a59a3555879d621fbd..992435fe9c53ff30097fc4850ccc9570a8064b1b 100644
--- a/source/anim/DynamicModelShared.cpp
+++ b/source/anim/DynamicModelShared.cpp
@@ -23,8 +23,7 @@ CDynamicModelShared::~CDynamicModelShared()
 	mem_delete_a(m_ppMaterialsBlob);
 	m_pppMaterials = NULL;
 
-	mem_delete_a(m_isTransparent);
-	mem_delete_a(m_isEmissive);
+	mem_delete_a(m_bmFeatures);
 
 	if(m_pDevice)
 	{
@@ -51,6 +50,8 @@ CDynamicModelShared::~CDynamicModelShared()
 		mem_delete_a(m_puTempTotalIndices);
 		mem_delete_a(m_puTempTotalVertices);
 	}
+
+	mem_release(m_pInstanceBuffer);
 }
 void CDynamicModelShared::AddRef()
 {
@@ -58,8 +59,7 @@ void CDynamicModelShared::AddRef()
 }
 void CDynamicModelShared::Release()
 {
-	--m_uRefCount;
-	if(!m_uRefCount)
+	if(!--m_uRefCount)
 	{
 		delete this;
 	}
@@ -76,8 +76,7 @@ bool CDynamicModelShared::init(IXResourceModelStatic *pResource)
 		m_uMaterialCount = pResource->getMaterialCount();
 		m_uSkinCount = pResource->getSkinCount();
 
-		m_isTransparent = new bool[m_uSkinCount];
-		m_isEmissive = new bool[m_uSkinCount];
+		m_bmFeatures = new XMODEL_FEATURE[m_uSkinCount];
 
 		m_ppMaterialsBlob = new void*[m_uMaterialCount * m_uSkinCount + m_uSkinCount];
 		m_pppMaterials = (IXMaterial***)m_ppMaterialsBlob;
@@ -86,8 +85,7 @@ bool CDynamicModelShared::init(IXResourceModelStatic *pResource)
 		for(UINT i = 0; i < m_uSkinCount; ++i)
 		{
 			m_pppMaterials[i] = (IXMaterial**)(m_ppMaterialsBlob + m_uSkinCount + m_uMaterialCount * i);
-			bool isTransparent = false;
-			bool isEmissive = false;
+			XMODEL_FEATURE bmFeatures = MF_NONE;
 
 			for(UINT j = 0; j < m_uMaterialCount; ++j)
 			{
@@ -110,17 +108,20 @@ bool CDynamicModelShared::init(IXResourceModelStatic *pResource)
 				{
 					if(m_pppMaterials[i][j]->isTransparent())
 					{
-						isTransparent = true;
+						bmFeatures |= MF_TRANSPARENT;
+					}
+					else
+					{
+						bmFeatures |= MF_OPAQUE;
 					}
 					if(m_pppMaterials[i][j]->isEmissive())
 					{
-						isEmissive = true;
+						bmFeatures |= MF_SELFILLUM;
 					}
 				}
 			}
-
-			m_isTransparent[i] = isTransparent;
-			m_isEmissive[i] = isEmissive;
+			
+			m_bmFeatures[i] = bmFeatures;
 		}
 	}
 
@@ -207,6 +208,11 @@ bool CDynamicModelShared::init(IXResourceModelStatic *pResource)
 
 					mem_delete_a(pIndices);
 					mem_delete_a(pVertices);
+
+					if(!m_pInstanceBuffer)
+					{
+						m_pInstanceBuffer = m_pDevice->createConstantBuffer(sizeof(m_instanceData), true);
+					}
 				}
 				else
 				{
@@ -247,6 +253,7 @@ void CDynamicModelShared::onMaterialTransparencyChanged(const IXMaterial *pMater
 	for(UINT i = 0; i < m_uSkinCount; ++i)
 	{
 		bool isTransparent = false;
+		bool isOpaque = false;
 
 		for(UINT j = 0; j < m_uMaterialCount; ++j)
 		{
@@ -256,6 +263,10 @@ void CDynamicModelShared::onMaterialTransparencyChanged(const IXMaterial *pMater
 				{
 					isTransparent = true;
 				}
+				else
+				{
+					isOpaque = true;
+				}
 
 				if(m_pppMaterials[i][j] == pMaterial)
 				{
@@ -264,17 +275,37 @@ void CDynamicModelShared::onMaterialTransparencyChanged(const IXMaterial *pMater
 			}
 		}
 
-		m_isTransparent[i] = isTransparent;
+		if(isTransparent)
+		{
+			m_bmFeatures[i] |= MF_TRANSPARENT;
+		}
+		else
+		{
+			m_bmFeatures[i] &= ~MF_TRANSPARENT;
+		}
+
+		if(isOpaque)
+		{
+			m_bmFeatures[i] |= MF_OPAQUE;
+		}
+		else
+		{
+			m_bmFeatures[i] &= ~MF_OPAQUE;
+		}
 	}
 
 	if(isChanged)
 	{
 		buildPSPs();
+
+		m_pProvider->onSharedModelFeaturesChanged(this);
 	}
 }
 
 void CDynamicModelShared::onMaterialEmissivityChanged(const IXMaterial *pMaterial)
 {
+	bool isChanged = false;
+
 	for(UINT i = 0; i < m_uSkinCount; ++i)
 	{
 		bool isEmissive = false;
@@ -285,9 +316,26 @@ void CDynamicModelShared::onMaterialEmissivityChanged(const IXMaterial *pMateria
 			{
 				isEmissive = true;
 			}
+
+			if(m_pppMaterials[i][j] == pMaterial)
+			{
+				isChanged = true;
+			}
+		}
+
+		if(isEmissive)
+		{
+			m_bmFeatures[i] |= MF_SELFILLUM;
+		}
+		else
+		{
+			m_bmFeatures[i] &= ~MF_SELFILLUM;
 		}
+	}
 
-		m_isEmissive[i] = isEmissive;
+	if(isChanged)
+	{
+		m_pProvider->onSharedModelFeaturesChanged(this);
 	}
 }
 
@@ -313,6 +361,8 @@ void CDynamicModelShared::initGPUresources()
 	mem_delete_a(m_ppTempVertices);
 	mem_delete_a(m_puTempTotalIndices);
 	mem_delete_a(m_puTempTotalVertices);
+
+	m_pInstanceBuffer = m_pDevice->createConstantBuffer(sizeof(m_instanceData), true);
 }
 
 IXResourceModelStatic *CDynamicModelShared::getResource()
@@ -349,28 +399,73 @@ SMAABB CDynamicModelShared::getLocalBound() const
 	return(SMAABB(m_vLocalMin, m_vLocalMax));
 }
 
-void CDynamicModelShared::render(UINT uSkin, UINT uLod, const float4_t &vColor, bool isTransparent, bool isEmissiveOnly)
+void CDynamicModelShared::beginInstancing(UINT uSkin, UINT uLod, XMODEL_FEATURE bmWhat)
 {
-	if(!m_pDevice)
+	assert(!m_isInstancingEnabled);
+	m_isInstancingEnabled = true;
+	m_iInstanceCount = 0;
+	m_uInstancingSkin = uSkin;
+	m_uInstancingLod = uLod;
+	m_bmInstancingFeatures = bmWhat;
+}
+void CDynamicModelShared::endInstancing()
+{
+	assert(m_isInstancingEnabled);
+
+	if(m_iInstanceCount && m_pInstanceBuffer)
 	{
-		return;
+		m_pInstanceBuffer->update(m_instanceData, sizeof(m_instanceData[0]) * m_iInstanceCount);
+
+		m_pProvider->bindVertexFormat(true);
+		render(m_uInstancingSkin, m_uInstancingLod, float4(), m_bmInstancingFeatures);
 	}
 
-	if(uSkin >= m_uSkinCount)
+	m_isInstancingEnabled = false;
+}
+
+void CDynamicModelShared::renderInstanced(const float3 &vPos, const SMQuaternion &qRot, float fScale, const float4_t &vColor)
+{
+	assert(m_isInstancingEnabled);
+
+	m_instanceData[m_iInstanceCount].vPosScale = float4(vPos, fScale);
+	m_instanceData[m_iInstanceCount].qRot = qRot;
+	
+	if(++m_iInstanceCount == MAX_INSTANCES)
 	{
-		uSkin = 0;
+		endInstancing();
+		beginInstancing(m_uInstancingSkin, m_uInstancingLod, m_bmInstancingFeatures);
 	}
+}
+
+bool CDynamicModelShared::isInstancing()
+{
+	return(m_isInstancingEnabled);
+}
+
+void CDynamicModelShared::render(UINT uSkin, UINT uLod, const float4_t &vColor, XMODEL_FEATURE bmFeatures)
+{
+
+	//m_instanceData[MAX_INSTANCES];
+	//IGXConstantBuffer *m_pInstanceBuffer = NULL;
+	//int m_iInstanceCount = 0;
+	//m_isInstancingEnabled
+
 
-	if(isTransparent && !hasTransparentSubsets(uSkin, uLod))
+	if(!m_pDevice)
 	{
 		return;
 	}
 
-	if(isEmissiveOnly && !hasEmissiveSubsets(uSkin, uLod))
+	if(uSkin >= m_uSkinCount)
 	{
-		return;
+		uSkin = 0;
 	}
 
+	if((getFeatures(uSkin, uLod) & bmFeatures) == 0)
+	{
+		return;
+	}
+	
 	if(uLod >= m_aLods.size())
 	{
 		return;
@@ -383,6 +478,11 @@ void CDynamicModelShared::render(UINT uSkin, UINT uLod, const float4_t &vColor,
 
 	IGXContext *pCtx = m_pDevice->getThreadContext();
 
+	if(m_isInstancingEnabled)
+	{
+		pCtx->setVSConstant(m_pInstanceBuffer, 1 /* SCR_OBJECT */);
+	}
+
 	pCtx->setIndexBuffer(m_ppIndexBuffer[uLod]);
 	pCtx->setRenderBuffer(m_ppRenderBuffer[uLod]);
 
@@ -402,11 +502,15 @@ void CDynamicModelShared::render(UINT uSkin, UINT uLod, const float4_t &vColor,
 	for(UINT i = 0; i < m_uMaterialCount; ++i)
 	{
 		pSubset = &m_aLods[uLod][i];
-		
-		if(pSubset->uIndexCount != 0 && m_pppMaterials[uSkin][i] 
-			&& (m_pppMaterials[uSkin][i]->isTransparent() == isTransparent)
-			&& (!isEmissiveOnly || m_pppMaterials[uSkin][i]->isEmissive())
+		auto *pMat = m_pppMaterials[uSkin][i];
+		if(pSubset->uIndexCount != 0 
+			&& pMat
+			&& (
+				((bmFeatures & MF_OPAQUE) && !pMat->isTransparent())
+				|| ((bmFeatures & MF_TRANSPARENT) && pMat->isTransparent())
+				|| ((bmFeatures & MF_SELFILLUM) && pMat->isEmissive())
 			)
+		)
 		{
 			m_pMaterialSystem->bindMaterial(m_pppMaterials[uSkin][i]);
 
@@ -420,33 +524,28 @@ void CDynamicModelShared::render(UINT uSkin, UINT uLod, const float4_t &vColor,
 				break;
 			}
 
-			pCtx->drawIndexed(pSubset->uVertexCount, uPrimCount, pSubset->uStartIndex, pSubset->uStartVertex);
+			if(m_isInstancingEnabled)
+			{
+				pCtx->drawIndexedInstanced(m_iInstanceCount, pSubset->uVertexCount, uPrimCount, pSubset->uStartIndex, pSubset->uStartVertex);
+			}
+			else
+			{
+				pCtx->drawIndexed(pSubset->uVertexCount, uPrimCount, pSubset->uStartIndex, pSubset->uStartVertex);
+			}
 		}
 	}
 }
 
-bool CDynamicModelShared::hasTransparentSubsets(UINT uSkin, UINT uLod)
-{
-	assert(uSkin < m_uSkinCount);
-	if(uSkin >= m_uSkinCount)
-	{
-		return(false);
-	}
-	//! @todo add uLod support
-
-	return(m_isTransparent[uSkin]);
-}
-
-bool CDynamicModelShared::hasEmissiveSubsets(UINT uSkin, UINT uLod)
+XMODEL_FEATURE CDynamicModelShared::getFeatures(UINT uSkin, UINT uLod)
 {
 	assert(uSkin < m_uSkinCount);
 	if(uSkin >= m_uSkinCount)
 	{
-		return(false);
+		return(MF_NONE);
 	}
 	//! @todo add uLod support
 
-	return(m_isEmissive[uSkin]);
+	return(m_bmFeatures[uSkin]);
 }
 
 IXMaterial* CDynamicModelShared::getTransparentMaterial(UINT uSkin, UINT uLod)
diff --git a/source/anim/DynamicModelShared.h b/source/anim/DynamicModelShared.h
index 831a4001bd9e43a903ceefac0ea6dd7e205f6bef..3f551d5218c6c2e576dc4504f25e75d2bcec11e9 100644
--- a/source/anim/DynamicModelShared.h
+++ b/source/anim/DynamicModelShared.h
@@ -8,6 +8,8 @@
 class CDynamicModelProvider;
 class IXMaterialSystem;
 
+#define MAX_INSTANCES 32
+
 class CDynamicModelShared
 {
 public:
@@ -27,13 +29,13 @@ public:
 	float3 getLocalBoundMax() const;
 	SMAABB getLocalBound() const;
 
-	void render(UINT uSkin, UINT uLod, const float4_t &vColor, bool isTransparent, bool isEmissiveOnly = false);
+	void render(UINT uSkin, UINT uLod, const float4_t &vColor, XMODEL_FEATURE bmFeatures);
+	void renderInstanced(const float3 &vPos, const SMQuaternion &qRot, float fScale, const float4_t &vColor);
 
 	void initGPUresources();
 	bool isReady() const;
 
-	bool hasTransparentSubsets(UINT uSkin, UINT uLod);
-	bool hasEmissiveSubsets(UINT uSkin, UINT uLod);
+	XMODEL_FEATURE getFeatures(UINT uSkin, UINT uLod);
 	IXMaterial* getTransparentMaterial(UINT uSkin, UINT uLod);
 
 	void onMaterialTransparencyChanged(const IXMaterial *pMaterial);
@@ -43,10 +45,15 @@ public:
 
 	const Array<float4_t>& getPSPs(UINT uSkin, UINT uLod) const;
 
+	void beginInstancing(UINT uSkin, UINT uLod, XMODEL_FEATURE bmWhat);
+	void endInstancing();
+	bool isInstancing();
+
 protected:
 	void buildPSPs();
 
 protected:
+	//! todo: make it atomic
 	UINT m_uRefCount = 1;
 	IXResourceModelStatic *m_pResource = NULL;
 
@@ -62,12 +69,23 @@ protected:
 	IGXDevice *m_pDevice;
 	IXMaterialSystem *m_pMaterialSystem;
 
+	struct
+	{
+		float4 vPosScale;
+		SMQuaternion qRot;
+	} m_instanceData[MAX_INSTANCES];
+	IGXConstantBuffer *m_pInstanceBuffer = NULL;
+	int m_iInstanceCount = 0;
+	bool m_isInstancingEnabled = false;
+	UINT m_uInstancingSkin = 0;
+	UINT m_uInstancingLod = 0;
+	XMODEL_FEATURE m_bmInstancingFeatures = MF_NONE;
+
 	void **m_ppMaterialsBlob = NULL;
 	IXMaterial ***m_pppMaterials = NULL;
 	UINT m_uMaterialCount = 0;
 	UINT m_uSkinCount = 0;
-	bool *m_isTransparent = NULL; //!< По количеству скинов, истина если есть прозрачные материалы в любом сабсете
-	bool *m_isEmissive = NULL; //!< По количеству скинов, истина если есть светящиеся материалы в любом сабсете
+	XMODEL_FEATURE *m_bmFeatures = NULL; //!< По количеству скинов
 
 	XPT_TOPOLOGY m_topology = XPT_TRIANGLELIST;
 
diff --git a/source/anim/RenderableVisibility.cpp b/source/anim/RenderableVisibility.cpp
index 2d8182f91efd30d75a6045912c15b4762a07bcac..5510983955151ef1a56ebb88edf2e75e290b711c 100644
--- a/source/anim/RenderableVisibility.cpp
+++ b/source/anim/RenderableVisibility.cpp
@@ -38,42 +38,67 @@ void CRenderableVisibility::updateForFrustum(const IFrustum *pFrustum, const IXR
 	m_pProviderDynamic->computeVisibility(pFrustum, this, pRef);
 }
 
-void CRenderableVisibility::append(const IXRenderableVisibility *pOther_)
+static void SortRenderList(Array<CDynamicModel*> &aList)
 {
-	assert(((IXRenderableVisibility*)pOther_)->getPluginId() == getPluginId());
-	const CRenderableVisibility *pOther = (const CRenderableVisibility*)pOther_;
+	aList.quickSort([](CDynamicModel *pA, CDynamicModel *pB){
+		if(pA->getShared() == pB->getShared())
+		{
+			return(pA->getSkin() < pB->getSkin());
+		}
+		return(pA->getShared() < pB->getShared());
+	});
+}
 
-	item_s *pItem;
-	const item_s *pOtherItem;
-	for(UINT i = 0, l = pOther->m_aItems.size(); i < l; ++i)
+static void MergeArrays(Array<CDynamicModel*> &aTo, const Array<CDynamicModel*> &aFrom)
+{
+	if(!aFrom.size())
 	{
-		pOtherItem = &pOther->m_aItems[i];
-		if(pOtherItem->isVisible)
+		return;
+	}
+	if(!aTo.size())
+	{
+		aTo = aFrom;
+		return;
+	}
+
+	aTo.append(aFrom);
+	aTo.quickSort();
+
+	for(UINT i = 0, l = aTo.size(); i < l - 1; ++i)
+	{
+		if(aTo[i] == aTo[i + 1])
 		{
-			pItem = &m_aItems[i];
-			if(pItem->isVisible)
+			if(i + 1 == l - 1)
 			{
-				if(pOtherItem->uLod < pItem->uLod)
-				{
-					pItem->uLod = pOtherItem->uLod;
-				}
+				aTo.erase(l - 1); --l;
 			}
 			else
 			{
-				pItem->isTransparent = pOtherItem->isTransparent;
-				pItem->isEmissive = pOtherItem->isEmissive;
-				pItem->isVisible = true;
-				pItem->uLod = pOtherItem->uLod;
+				if(aTo[l - 2] == aTo[l - 1])
+				{
+					aTo.erase(l - 1); --l;
+				}
+				aTo[i + 1] = aTo[l - 1];
 			}
+
+			++i;
 		}
 	}
+}
+
+void CRenderableVisibility::append(const IXRenderableVisibility *pOther_)
+{
+	assert(((IXRenderableVisibility*)pOther_)->getPluginId() == getPluginId());
+	const CRenderableVisibility *pOther = (const CRenderableVisibility*)pOther_;
 
-	for(UINT i = 0, l = pOther->m_aItemsDynamic.size(); i < l; ++i)
+	item_s *pItem;
+	const item_s *pOtherItem;
+	for(UINT i = 0, l = pOther->m_aItems.size(); i < l; ++i)
 	{
-		pOtherItem = &pOther->m_aItemsDynamic[i];
+		pOtherItem = &pOther->m_aItems[i];
 		if(pOtherItem->isVisible)
 		{
-			pItem = &m_aItemsDynamic[i];
+			pItem = &m_aItems[i];
 			if(pItem->isVisible)
 			{
 				if(pOtherItem->uLod < pItem->uLod)
@@ -84,11 +109,18 @@ void CRenderableVisibility::append(const IXRenderableVisibility *pOther_)
 			else
 			{
 				pItem->isTransparent = pOtherItem->isTransparent;
+				pItem->isEmissive = pOtherItem->isEmissive;
 				pItem->isVisible = true;
 				pItem->uLod = pOtherItem->uLod;
 			}
 		}
 	}
+	
+	MergeArrays(m_aRenderList, pOther->m_aRenderList);
+	SortRenderList(m_aRenderList);
+	MergeArrays(m_aTransparentList, pOther->m_aTransparentList);
+	MergeArrays(m_aSelfillumList, pOther->m_aSelfillumList);
+	SortRenderList(m_aSelfillumList);
 
 	//! @todo implement for transparency too!
 }
@@ -110,16 +142,6 @@ void CRenderableVisibility::substract(const IXRenderableVisibility *pOther_)
 		}
 	}
 
-	for(UINT i = 0, l = pOther->m_aItemsDynamic.size(); i < l; ++i)
-	{
-		pOtherItem = &pOther->m_aItemsDynamic[i];
-		pItem = &m_aItemsDynamic[i];
-		if(pItem->isVisible && pOtherItem->isVisible)
-		{
-			pItem->isVisible = false;
-		}
-	}
-
 	//! @todo implement for transparency too!
 }
 
@@ -140,25 +162,16 @@ CRenderableVisibility::item_s* CRenderableVisibility::getItem(UINT uIndex)
 	return(&m_aItems[uIndex]);
 }
 
-void CRenderableVisibility::setItemCountDynamic(UINT uCount)
+void CRenderableVisibility::setItemTransparentCountDynamic(UINT uCount)
 {
-	if(m_aItemsDynamic.GetAllocSize() > uCount * 2)
+	if(uCount > 0 && m_aItemsDynamicTransparent.GetAllocSize() > uCount * 2)
 	{
-		m_aItemsDynamic.resize(uCount);
+		m_aItemsDynamicTransparent.resize(uCount);
 	}
 	else
 	{
-		m_aItemsDynamic.resizeFast(uCount);
-	}
-}
-
-CRenderableVisibility::item_s* CRenderableVisibility::getItemDynamic(UINT uIndex, bool forceCreate)
-{
-	if(forceCreate || uIndex < m_aItemsDynamic.size())
-	{
-		return(&m_aItemsDynamic[uIndex]);
+		m_aItemsDynamicTransparent.resizeFast(uCount);
 	}
-	return(NULL);
 }
 
 CRenderableVisibility::TransparentModel* CRenderableVisibility::getItemTransparentDynamic(UINT uIndex)
@@ -180,3 +193,47 @@ void CRenderableVisibility::addItemTransparentDynamic(const TransparentModel &md
 {
 	m_aItemsDynamicTransparent.push_back(mdl);
 }
+
+static void SetRenderList(Array<CDynamicModel*> &aList, void **ppData, UINT uCount, bool useSort=true)
+{
+	aList.resizeFast(uCount);
+	if(uCount)
+	{
+		memcpy(&aList[0], ppData, sizeof(void*) * uCount);
+	}
+
+	if(useSort)
+	{
+		SortRenderList(aList);
+	}
+}
+
+void CRenderableVisibility::setRenderList(void **ppData, UINT uCount)
+{
+	SetRenderList(m_aRenderList, ppData, uCount);
+}
+
+void CRenderableVisibility::setTransparentList(void **ppData, UINT uCount)
+{
+	SetRenderList(m_aTransparentList, ppData, uCount, false);
+}
+
+void CRenderableVisibility::setSelfillumList(void **ppData, UINT uCount)
+{
+	SetRenderList(m_aSelfillumList, ppData, uCount);
+}
+
+Array<CDynamicModel*>& CRenderableVisibility::getRenderList()
+{
+	return(m_aRenderList);
+}
+
+Array<CDynamicModel*>& CRenderableVisibility::getSelfillumList()
+{
+	return(m_aSelfillumList);
+}
+
+Array<CDynamicModel*>& CRenderableVisibility::getTransparentList()
+{
+	return(m_aTransparentList);
+}
diff --git a/source/anim/RenderableVisibility.h b/source/anim/RenderableVisibility.h
index 2bd641be7dd8b7c24d74a6433968d483199ec644..061432ac78264c755465481a5bf65e9acc9e86d0 100644
--- a/source/anim/RenderableVisibility.h
+++ b/source/anim/RenderableVisibility.h
@@ -6,7 +6,7 @@
 class CAnimatedModelProvider;
 class CDynamicModelProvider;
 class CDynamicModel;
-class CRenderableVisibility: public IXUnknownImplementation<IXRenderableVisibility>
+class CRenderableVisibility final: public IXUnknownImplementation<IXRenderableVisibility>
 {
 public:
 	CRenderableVisibility(ID idPlugin, CAnimatedModelProvider *m_pProviderAnimated, CDynamicModelProvider *m_pProviderDynamic);
@@ -43,21 +43,31 @@ public:
 	void setItemCount(UINT uCount);
 	item_s* getItem(UINT uIndex);
 
-	void setItemCountDynamic(UINT uCount);
-	item_s* getItemDynamic(UINT uIndex, bool forceCreate=true);
-
+	void setItemTransparentCountDynamic(UINT uCount);
 	void resetItemTransparentDynamic();
 	void addItemTransparentDynamic(const TransparentModel &mdl);
 	UINT getItemTransparentDynamicCount();
 	TransparentModel* getItemTransparentDynamic(UINT uIndex);
+
+	void setRenderList(void **ppData, UINT uCount);
+	void setTransparentList(void **ppData, UINT uCount);
+	void setSelfillumList(void **ppData, UINT uCount);
+
+	Array<CDynamicModel*>& getRenderList();
+	Array<CDynamicModel*>& getTransparentList();
+	Array<CDynamicModel*>& getSelfillumList();
+
 protected:
 	ID m_idPlugin;
 	CAnimatedModelProvider* m_pProviderAnimated;
 	CDynamicModelProvider* m_pProviderDynamic;
 
 	Array<item_s> m_aItems;
-	Array<item_s> m_aItemsDynamic;
 	Array<TransparentModel> m_aItemsDynamicTransparent;
+
+	Array<CDynamicModel*> m_aRenderList;
+	Array<CDynamicModel*> m_aTransparentList;
+	Array<CDynamicModel*> m_aSelfillumList;
 };
 
 #endif
diff --git a/source/anim/Updatable.cpp b/source/anim/Updatable.cpp
index 3e01ae1a1ae03607a1788a43b99c84b1b070a81a..d04c08cd13b194c8bac23e7493ff8084328d2471 100644
--- a/source/anim/Updatable.cpp
+++ b/source/anim/Updatable.cpp
@@ -29,4 +29,5 @@ ID CUpdatable::run(float fDelta)
 void CUpdatable::sync()
 {
 	m_pAnimatedModelProvider->sync();
+	m_pDynamicModelProvider->sync();
 }
diff --git a/source/anim/plugin_main.cpp b/source/anim/plugin_main.cpp
index 42d1c01df4c278989e0eaf333761278612d84e73..1766ff3eeeb315a215d49312a2f0d6e08f8ad211 100644
--- a/source/anim/plugin_main.cpp
+++ b/source/anim/plugin_main.cpp
@@ -5,7 +5,7 @@
 #include "AnimatedModelProvider.h"
 #include "DynamicModelProvider.h"
 
-class CLevelSizeEventListener: public IEventListener<XEventLevelSize>
+class CLevelSizeEventListener final: public IEventListener<XEventLevelSize>
 {
 public:
 	CLevelSizeEventListener(CAnimatedModelProvider *pAnimatedModelProvider, CDynamicModelProvider *pDynamicModelProvider):
@@ -27,11 +27,10 @@ protected:
 class CDSEPlugin: public IXUnknownImplementation<IXPlugin>
 {
 public:
-	void XMETHODCALLTYPE startup(IXCore *pCore) override
+	void init()
 	{
-		m_pCore = pCore;
-		m_pAnimatedModelProvider = new CAnimatedModelProvider(pCore);
-		m_pDynamicModelProvider = new CDynamicModelProvider(pCore);
+		m_pAnimatedModelProvider = new CAnimatedModelProvider(m_pCore);
+		m_pDynamicModelProvider = new CDynamicModelProvider(m_pCore);
 		m_pRenderable = new CRenderable(getID(), m_pAnimatedModelProvider, m_pDynamicModelProvider);
 		m_pUpdatable = new CUpdatable(m_pAnimatedModelProvider, m_pDynamicModelProvider);
 		m_pLevelSizeEventListener = new CLevelSizeEventListener(m_pAnimatedModelProvider, m_pDynamicModelProvider);
@@ -39,9 +38,17 @@ public:
 		m_pCore->getEventChannel<XEventLevelSize>(EVENT_LEVEL_GET_SIZE_GUID)->addListener(m_pLevelSizeEventListener);
 	}
 
+	void XMETHODCALLTYPE startup(IXCore *pCore) override
+	{
+		m_pCore = pCore;
+	}
+
 	void XMETHODCALLTYPE shutdown() override
 	{
-		m_pCore->getEventChannel<XEventLevelSize>(EVENT_LEVEL_GET_SIZE_GUID)->removeListener(m_pLevelSizeEventListener);
+		if(m_pLevelSizeEventListener)
+		{
+			m_pCore->getEventChannel<XEventLevelSize>(EVENT_LEVEL_GET_SIZE_GUID)->removeListener(m_pLevelSizeEventListener);
+		}
 		mem_delete(m_pLevelSizeEventListener);
 		mem_delete(m_pRenderable);
 		mem_delete(m_pUpdatable);
@@ -53,7 +60,7 @@ public:
 	{
 		return(4);
 	}
-	const XGUID * XMETHODCALLTYPE getInterfaceGUID(UINT id) override
+	const XGUID* XMETHODCALLTYPE getInterfaceGUID(UINT id) override
 	{
 		static XGUID s_guid;
 		switch(id)
@@ -75,22 +82,42 @@ public:
 		}
 		return(&s_guid);
 	}
-	IXUnknown * XMETHODCALLTYPE getInterface(const XGUID &guid) override
+	IXUnknown* XMETHODCALLTYPE getInterface(const XGUID &guid) override
 	{
 		if(guid == IXRENDERABLE_GUID)
 		{
+			if(!m_pRenderable)
+			{
+				init();
+			}
+			m_pRenderable->AddRef();
 			return(m_pRenderable);
 		}
 		if(guid == IXUPDATABLE_GUID)
 		{
+			if(!m_pUpdatable)
+			{
+				init();
+			}
+			m_pUpdatable->AddRef();
 			return(m_pUpdatable);
 		}
 		if(guid == IXANIMATEDMODELPROVIDER_GUID)
 		{
+			if(!m_pAnimatedModelProvider)
+			{
+				init();
+			}
+			m_pAnimatedModelProvider->AddRef();
 			return(m_pAnimatedModelProvider);
 		}
 		if(guid == IXDYNAMICMODELPROVIDER_GUID)
 		{
+			if(!m_pDynamicModelProvider)
+			{
+				init();
+			}
+			m_pDynamicModelProvider->AddRef();
 			return(m_pDynamicModelProvider);
 		}
 		return(NULL);
diff --git a/source/common b/source/common
index 0dc80604688a34568513f10d19139692174fc0c9..c928d1428f024e5f5fcd2f93fc18e637ca303d2e 160000
--- a/source/common
+++ b/source/common
@@ -1 +1 @@
-Subproject commit 0dc80604688a34568513f10d19139692174fc0c9
+Subproject commit c928d1428f024e5f5fcd2f93fc18e637ca303d2e
diff --git a/source/core/Config.cpp b/source/core/Config.cpp
index bea39b2daef8385c37599a92307c24af0243aac7..711eea85fc6ad7064a7f1ff22236f12ddc0cc118 100644
--- a/source/core/Config.cpp
+++ b/source/core/Config.cpp
@@ -65,10 +65,10 @@ int CConfig::parse(const char* file)
 	clear();
 	BaseFile = CConfigString(file);
 	FILE * fp = fopen(file, "rb");
-		if(!fp)
-		{
-			return -1;
-		}
+	if(!fp)
+	{
+		return -1;
+	}
 
 	bool bComment = false;
 	bool bPreprocessor = false;
@@ -142,14 +142,14 @@ int CConfig::parse(const char* file)
 					s4 = s4.substr(1, s4.length() - 2);
 				}
 				//printf("%s = %s\n", s1.c_str(), s4.c_str());
-				
+
 				if(!m_mSections.KeyExists(s2))
 				{
 					m_mSections[s2].parent = s3;
 					m_mSections[s2].native = true;
 					if(m_mSections.KeyExists(s3))
 					{
-						for(AssotiativeArray<CConfigString, CValue>::Iterator i = m_mSections[s3].mValues.begin(); i; i++)
+						for(AssotiativeArray<CConfigString, CValue>::Iterator i = m_mSections[s3].mValues.begin(); i; ++i)
 						{
 							m_mSections[s2].mValues[i.first].val = i.second->val;
 						}
@@ -170,7 +170,7 @@ int CConfig::parse(const char* file)
 				int s4len = s4.length();
 				for(int i = 0; i < s4len; ++i)
 				{
-					if (!isspace((unsigned char)s4[i]))
+					if(!isspace((unsigned char)s4[i]))
 					{
 						pos = i;
 					}
@@ -184,7 +184,7 @@ int CConfig::parse(const char* file)
 					m_mSections[s2].native = true;
 					if(m_mSections.KeyExists(s3))
 					{
-						for(AssotiativeArray<CConfigString, CValue>::Iterator i = m_mSections[s3].mValues.begin(); i; i++)
+						for(AssotiativeArray<CConfigString, CValue>::Iterator i = m_mSections[s3].mValues.begin(); i; ++i)
 						{
 							m_mSections[s2].mValues[i.first].val = i.second->val;
 						}
@@ -329,12 +329,12 @@ int CConfig::parse(const char* file)
 
 void CConfig::modify(AssotiativeArray<CConfigString, CSection> & sections, AssotiativeArray<CConfigString, CValue> & values, CConfigString IncName)
 {
-	for(AssotiativeArray<CConfigString, CValue>::Iterator i = m_mFinalValues.begin(); i; i++)
+	for(AssotiativeArray<CConfigString, CValue>::Iterator i = m_mFinalValues.begin(); i; ++i)
 	{
 		values[i.first].val = i.second->val;
 	}
 
-	for (AssotiativeArray<CConfigString, CSection>::Iterator i = m_mSections.begin(); i; i++)
+	for(AssotiativeArray<CConfigString, CSection>::Iterator i = m_mSections.begin(); i; ++i)
 	{
 		if(!sections.KeyExists(i.first))
 		{
@@ -342,7 +342,7 @@ void CConfig::modify(AssotiativeArray<CConfigString, CSection> & sections, Assot
 			sections[i.first].native = false;
 			sections[i.first].Include = IncName;
 		}
-		for(AssotiativeArray<CConfigString, CValue>::Iterator j = m_mSections[i.first].mValues.begin(); j; j++)
+		for(AssotiativeArray<CConfigString, CValue>::Iterator j = m_mSections[i.first].mValues.begin(); j; ++j)
 		{
 			sections[i.first].mValues[j.first].val = j.second->val;
 		}
@@ -362,12 +362,12 @@ int CConfig::parseInclude(CConfigString & file, const CConfigString & dir)
 		inc.pParser->open((dir + file).c_str());
 
 		inc.name = baseName(dir + file);
-/*		if(inc.name == "gl.ltx")
+		/*		if(inc.name == "gl.ltx")
 		{
-			_asm
-			{
-				int 3;
-			};
+		_asm
+		{
+		int 3;
+		};
 		}*/
 		inc.pParser->modify(m_mSections, m_mFinalValues, dir + file);
 		m_vIncludes.push_back(inc);
@@ -390,14 +390,14 @@ const char* CConfig::getKey(const char * section, const char * key)
 {
 	CConfigString keys(key);
 	CConfigString sections(section);
-		if(m_mSections.KeyExists(sections))
+	if(m_mSections.KeyExists(sections))
+	{
+		if(m_mSections[sections].mValues.KeyExists(keys))
 		{
-				if(m_mSections[sections].mValues.KeyExists(keys))
-				{
-					return (m_mSections[sections].mValues[keys].val).c_str();
-				}
-			//return 0;
+			return (m_mSections[sections].mValues[keys].val).c_str();
 		}
+		//return 0;
+	}
 	return 0;
 }
 
@@ -405,18 +405,18 @@ const char* CConfig::getKeyName(const char* section, int key)
 {
 	//CConfigString keys(key);
 	CConfigString sections(section);
-	if (m_mSections.KeyExists(sections))
+	if(m_mSections.KeyExists(sections))
 	{
-		if ((int)m_mSections[sections].mValues.Size() > key)
+		if((int)m_mSections[sections].mValues.Size() > key)
 		{
 			int countiter = 0;
 			AssotiativeArray<CConfigString, CConfig::CValue>::Iterator iter = m_mSections[sections].mValues.begin();
-			for (int i = 0; i < key && iter; i++)
+			for(int i = 0; i < key && iter; ++i)
 			{
-				iter++;
-				countiter++;
+				++iter;
+				++countiter;
 			}
-			if (countiter == key)
+			if(countiter == key)
 				return iter.first->c_str();
 		}
 		return 0;
@@ -430,13 +430,13 @@ const char* CConfig::getSectionName(int num)
 	{
 		int countiter = 0;
 		AssotiativeArray<CConfigString, CConfig::CSection>::Iterator iter = m_mSections.begin();
-		for (int i = 0; i < num && iter; i++)
+		for(int i = 0; i < num && iter; ++i)
 		{
-			iter++;
-			countiter++;
+			++iter;
+			++countiter;
 		}
-			
-		if (countiter == num)
+
+		if(countiter == num)
 			return iter.first->c_str();
 	}
 	return(NULL);
@@ -485,7 +485,7 @@ int CConfig::save()
 		printf(COLOR_GRAY "====== " COLOR_CYAN "CConfig::save() " COLOR_GRAY "======" COLOR_RESET "\n");
 	}
 	int terror = 0;
-	for(AssotiativeArray<CConfigString, CSection>::Iterator i = m_mSections.begin(); i; i++)
+	for(AssotiativeArray<CConfigString, CSection>::Iterator i = m_mSections.begin(); i; ++i)
 	{
 		if(*s_pbDebug)
 		{
@@ -497,7 +497,7 @@ int CConfig::save()
 			{
 				printf(COLOR_YELLOW " modified" COLOR_RESET "\n");
 			}
-			for(AssotiativeArray<CConfigString, CValue>::Iterator j = i.second->mValues.begin(); j; j++)
+			for(AssotiativeArray<CConfigString, CValue>::Iterator j = i.second->mValues.begin(); j; ++j)
 			{
 				if(*s_pbDebug)
 				{
@@ -751,11 +751,11 @@ int CConfig::writeFile(const CConfigString & name, CConfigString section, CConfi
 int CConfig::getSectionCount()
 {
 	int c = m_mSections.Size();
-		int size = m_vIncludes.size();
-		for(int i = 0; i < size; ++i)
-		{
-			//m_vIncludes
-			c += m_vIncludes[i].pParser->getSectionCount();
+	int size = m_vIncludes.size();
+	for(int i = 0; i < size; ++i)
+	{
+		//m_vIncludes
+		c += m_vIncludes[i].pParser->getSectionCount();
 	}
 	return(c);
 }
@@ -763,10 +763,10 @@ int CConfig::getSectionCount()
 int CConfig::getKeyCount()
 {
 	int c = m_mFinalValues.Size();
-		int size = m_vIncludes.size();
-		for(int i = 0; i < size; ++i)
-		{
-			c += m_vIncludes[i].pParser->getKeyCount();
+	int size = m_vIncludes.size();
+	for(int i = 0; i < size; ++i)
+	{
+		c += m_vIncludes[i].pParser->getKeyCount();
 	}
 	return(c);
 }
@@ -775,27 +775,27 @@ int CConfig::getKeyCount(const char* section)
 {
 	CConfigString sections(section);
 
-	if (m_mSections.KeyExists(sections))
+	if(m_mSections.KeyExists(sections))
 	{
-			return(m_mSections[sections].mValues.Size());
-		}
-				return -1;
-		}
+		return(m_mSections[sections].mValues.Size());
+	}
+	return -1;
+}
 
 bool CConfig::sectionExists(const char * section)
 {
 	CConfigString sections(section);
 	if(m_mSections.KeyExists(sections))
-			return(true);
+		return(true);
 	return(false);
-//	return(m_mSections.count(section) != 0 && m_mSections[section].native);
+	//	return(m_mSections.count(section) != 0 && m_mSections[section].native);
 }
 
 bool CConfig::keyExists(const char * section, const char * key)
 {
 	CConfigString sections(section);
 	if(m_mSections.KeyExists(sections))
-			return(m_mSections[sections].mValues.KeyExists(key));
+		return(m_mSections[sections].mValues.KeyExists(key));
 	return(false);
 }
 
@@ -808,7 +808,7 @@ void CConfig::Release()
 void CConfig::clear()
 {
 	int size = m_vIncludes.size();
-	for (int i = 0; i < size; ++i)
+	for(int i = 0; i < size; ++i)
 	{
 		mem_release(m_vIncludes[i].pParser);
 		mem_delete(m_vIncludes[i].pParser);
diff --git a/source/core/Console.cpp b/source/core/Console.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f7f2691d04052d8129df575fb9e0297282b27d28
--- /dev/null
+++ b/source/core/Console.cpp
@@ -0,0 +1,126 @@
+#include "Console.h"
+#include "concmd.h"
+
+#include <common/ConcurrentQueue.h>
+
+void XMETHODCALLTYPE CConsole::registerCommand(const char *szName, XCONCMD cmd, const char *szDesc)
+{
+	Core_0RegisterConcmd(szName, cmd, szDesc);
+}
+void XMETHODCALLTYPE CConsole::registerCommand(const char *szName, XCONCMDARG cmd, const char *szDesc)
+{
+	Core_0RegisterConcmdArg(szName, cmd, szDesc);
+}
+void XMETHODCALLTYPE CConsole::registerCommand(const char *szName, IXConsoleCommand *pCommand, const char *szDesc)
+{
+	Core_0RegisterConcmdClsArg(szName, pCommand, (SXCONCMDCLSARG)&IXConsoleCommand::execute, szDesc);
+}
+
+void XMETHODCALLTYPE CConsole::removeCommand(const char *szName)
+{
+	//! @todo implement me!
+}
+
+void XMETHODCALLTYPE CConsole::registerCVar(const char *szName, const char *szValue, const char *szDesc, int flags)
+{
+	Core_0RegisterCVarString(szName, szValue, szDesc, flags);
+}
+void XMETHODCALLTYPE CConsole::registerCVar(const char *szName, int iValue, const char *szDesc, int flags)
+{
+	Core_0RegisterCVarInt(szName, iValue, szDesc, flags);
+}
+void XMETHODCALLTYPE CConsole::registerCVar(const char *szName, float fValue, const char *szDesc, int flags)
+{
+	Core_0RegisterCVarFloat(szName, fValue, szDesc, flags);
+}
+void XMETHODCALLTYPE CConsole::registerCVar(const char *szName, bool bValue, const char *szDesc, int flags)
+{
+	Core_0RegisterCVarBool(szName, bValue, szDesc, flags);
+}
+
+//! @FIXME Remove that!
+extern std::mutex g_conUpdMtx;
+extern CConcurrentQueue<char*> g_vCommandBuffer;
+void XMETHODCALLTYPE CConsole::execCommand(const char *szCommand)
+{
+	execCommand2("%s", szCommand);
+}
+void CConsole::execCommand2(const char *szFormat, ...)
+{
+	va_list va;
+	va_start(va, szFormat);
+	size_t len = _vscprintf(szFormat, va) + 1;
+	char * buf, *cbuf = NULL;
+	if(len < 4096)
+	{
+		buf = (char*)alloca(len * sizeof(char));
+	}
+	else
+	{
+		cbuf = buf = new char[len];
+	}
+	vsprintf(buf, szFormat, va);
+	va_end(va);
+
+	//g_vCommandBuffer
+
+	char * nl;
+	do
+	{
+		nl = strstr(buf, "\n");
+		if(nl)
+		{
+			*nl = 0;
+			++nl;
+
+			while(isspace(*buf))
+			{
+				++buf;
+			}
+
+			if(!(*buf == '/' && *(buf + 1) == '/') && (len = strlen(buf)))
+			{
+				char * str = new char[len + 1];
+				memcpy(str, buf, len + 1);
+				g_conUpdMtx.lock();
+				g_vCommandBuffer.push(str);
+				g_conUpdMtx.unlock();
+			}
+			buf = nl;
+		}
+	}
+	while(nl);
+
+	while(isspace(*buf))
+	{
+		++buf;
+	}
+
+	if(!(*buf == '/' && *(buf + 1) == '/') && (len = strlen(buf)))
+	{
+		char * str = new char[len + 1];
+		memcpy(str, buf, len + 1);
+		g_conUpdMtx.lock();
+		g_vCommandBuffer.push(str);
+		g_conUpdMtx.unlock();
+	}
+
+	mem_delete_a(cbuf);
+}
+
+const char** XMETHODCALLTYPE CConsole::getPCVarString(const char *szName)
+{
+	return(GET_PCVAR_STRING(szName));
+}
+const int* XMETHODCALLTYPE CConsole::getPCVarInt(const char *szName)
+{
+	return(GET_PCVAR_INT(szName));
+}
+const float* XMETHODCALLTYPE CConsole::getPCVarFloat(const char *szName)
+{
+	return(GET_PCVAR_FLOAT(szName));
+}
+const bool* XMETHODCALLTYPE CConsole::getPCVarBool(const char *szName)
+{
+	return(GET_PCVAR_BOOL(szName));
+}
diff --git a/source/core/Console.h b/source/core/Console.h
new file mode 100644
index 0000000000000000000000000000000000000000..9063ab247ad356c937c0f69e869a54325b033742
--- /dev/null
+++ b/source/core/Console.h
@@ -0,0 +1,28 @@
+#ifndef __CONSOLE_H
+#define __CONSOLE_H
+
+#include <xcommon/IXConsole.h>
+
+class CConsole final: public IXUnknownImplementation<IXConsole>
+{
+public:
+	void XMETHODCALLTYPE registerCommand(const char *szName, XCONCMD cmd, const char *szDesc) override;
+	void XMETHODCALLTYPE registerCommand(const char *szName, XCONCMDARG cmd, const char *szDesc) override;
+	void XMETHODCALLTYPE registerCommand(const char *szName, IXConsoleCommand *pCommand, const char *szDesc) override;
+	void XMETHODCALLTYPE removeCommand(const char *szName) override;
+
+	void XMETHODCALLTYPE registerCVar(const char *szName, const char *szValue, const char *szDesc, int flags = 0) override;
+	void XMETHODCALLTYPE registerCVar(const char *szName, int iValue, const char *szDesc, int flags = 0) override;
+	void XMETHODCALLTYPE registerCVar(const char *szName, float fValue, const char *szDesc, int flags = 0) override;
+	void XMETHODCALLTYPE registerCVar(const char *szName, bool bValue, const char *szDesc, int flags = 0) override;
+
+	void XMETHODCALLTYPE execCommand(const char *szCommand) override;
+	void execCommand2(const char *szFormat, ...) override;
+
+	const char** XMETHODCALLTYPE getPCVarString(const char *szName) override;
+	const int* XMETHODCALLTYPE getPCVarInt(const char *szName) override;
+	const float* XMETHODCALLTYPE getPCVarFloat(const char *szName) override;
+	const bool* XMETHODCALLTYPE getPCVarBool(const char *szName) override;
+};
+
+#endif
diff --git a/source/core/Core.cpp b/source/core/Core.cpp
index 7bb07f2b19513d1f6a3a4f7b5371bdaedf13c095..d2d56b5b0916d4d288298145329f9866dc1dbd7f 100644
--- a/source/core/Core.cpp
+++ b/source/core/Core.cpp
@@ -31,6 +31,8 @@ CCore::CCore(const char *szName)
 	ConsoleRegisterCmds();
 	CvarInitSystem(this);
 
+	m_pConsole = new CConsole();
+
 	Core_0RegisterCVarBool("g_time_run", true, "Запущено ли игровое время?", FCVAR_NOTIFY_OLD);
 	Core_0RegisterCVarFloat("g_time_speed", 1.f, "Скорость/соотношение течения игрового времени", FCVAR_NOTIFY_OLD);
 	Core_0RegisterCVarBool("dbg_config_save", false, "Отладочный вывод процесса сохранения конфига");
@@ -185,11 +187,13 @@ CCore::~CCore()
 	mem_delete(m_pResourceManager);
 	mem_delete(m_pFileSystem);
 	mem_delete(m_pPluginManager);
-	for(AssotiativeArray<XGUID, IBaseEventChannel*>::Iterator i = m_mEventChannels.begin(); i; i++)
+	for(AssotiativeArray<XGUID, IBaseEventChannel*>::Iterator i = m_mEventChannels.begin(); i; ++i)
 	{
 		mem_delete(*i.second);
 	}
 
+	mem_delete(m_pConsole);
+
 	ConsoleDisconnect();
 }
 
@@ -349,76 +353,6 @@ UINT_PTR XMETHODCALLTYPE CCore::getCrtOutputHandler()
 	return(Core_ConsoleGetOutHandler());
 }
 
-//! @FIXME Remove that!
-extern std::mutex g_conUpdMtx;
-extern CConcurrentQueue<char*> g_vCommandBuffer;
-void XMETHODCALLTYPE CCore::execCmd(const char *szCommand)
-{
-	execCmd2("%s", szCommand);
-}
-void CCore::execCmd2(const char * szFormat, ...)
-{
-	va_list va;
-	va_start(va, szFormat);
-	size_t len = _vscprintf(szFormat, va) + 1;
-	char * buf, *cbuf = NULL;
-	if(len < 4096)
-	{
-		buf = (char*)alloca(len * sizeof(char));
-	}
-	else
-	{
-		cbuf = buf = new char[len];
-	}
-	vsprintf(buf, szFormat, va);
-	va_end(va);
-
-	//g_vCommandBuffer
-
-	char * nl;
-	do
-	{
-		nl = strstr(buf, "\n");
-		if(nl)
-		{
-			*nl = 0;
-			++nl;
-
-			while(isspace(*buf))
-			{
-				++buf;
-			}
-
-			if(!(*buf == '/' && *(buf + 1) == '/') && (len = strlen(buf)))
-			{
-				char * str = new char[len + 1];
-				memcpy(str, buf, len + 1);
-				g_conUpdMtx.lock();
-				g_vCommandBuffer.push(str);
-				g_conUpdMtx.unlock();
-			}
-			buf = nl;
-		}
-	}
-	while(nl);
-
-	while(isspace(*buf))
-	{
-		++buf;
-	}
-
-	if(!(*buf == '/' && *(buf + 1) == '/') && (len = strlen(buf)))
-	{
-		char * str = new char[len + 1];
-		memcpy(str, buf, len + 1);
-		g_conUpdMtx.lock();
-		g_vCommandBuffer.push(str);
-		g_conUpdMtx.unlock();
-	}
-
-	mem_delete_a(cbuf);
-}
-
 ID XMETHODCALLTYPE CCore::getThreadId()
 {
 	return(Core_MGetThreadID());
@@ -439,23 +373,6 @@ IXBuffer* XMETHODCALLTYPE CCore::newBuffer()
 	return(new CBuffer());
 }
 
-const char** XMETHODCALLTYPE CCore::getPCVarString(const char *szName)
-{
-	return(GET_PCVAR_STRING(szName));
-}
-const int* XMETHODCALLTYPE CCore::getPCVarInt(const char *szName)
-{
-	return(GET_PCVAR_INT(szName));
-}
-const float* XMETHODCALLTYPE CCore::getPCVarFloat(const char *szName)
-{
-	return(GET_PCVAR_FLOAT(szName));
-}
-const bool* XMETHODCALLTYPE CCore::getPCVarBool(const char *szName)
-{
-	return(GET_PCVAR_BOOL(szName));
-}
-
 ID XMETHODCALLTYPE CCore::forLoop(int iStart, int iEnd, const IParallelForBody *pBody, int iMaxChunkSize)
 {
 	return(g_pTaskManager->forLoop(iStart, iEnd, pBody, iMaxChunkSize));
@@ -465,6 +382,11 @@ void XMETHODCALLTYPE CCore::waitForLoop(ID id)
 	g_pTaskManager->waitFor(id);
 }
 
+IXConsole* XMETHODCALLTYPE CCore::getConsole()
+{
+	return(m_pConsole);
+}
+
 //##########################################################################
 
 EXTERN_C SXCORE_API IXCore* XCoreInit(const char *szName)
diff --git a/source/core/Core.h b/source/core/Core.h
index e31786f4ac8de4f0ec181bb1ad1ef24044f22711..aea449140636ed5cd1c8f097f5c6de6a60cde8f4 100644
--- a/source/core/Core.h
+++ b/source/core/Core.h
@@ -7,13 +7,14 @@
 #include "FileSystem.h"
 #include <xcommon/IXUpdatable.h>
 #include "ResourceManager.h"
+#include "Console.h"
 
 class CModelProvider;
 class CPerfMon;
 class CTimeManager;
 class CTaskManager;
 
-class CCore: public IXUnknownImplementation<IXCore>
+class CCore final: public IXUnknownImplementation<IXCore>
 {
 public:
 	CCore(const char *szName);
@@ -37,8 +38,9 @@ public:
 
 	UINT_PTR XMETHODCALLTYPE getCrtOutputHandler() override; 
 
-	void XMETHODCALLTYPE execCmd(const char *szCommand) override;
-	void execCmd2(const char *szFormat, ...) override;
+	IXConsole* XMETHODCALLTYPE getConsole() override;
+	//void XMETHODCALLTYPE execCmd(const char *szCommand) override;
+	//void execCmd2(const char *szFormat, ...) override;
 
 	ID XMETHODCALLTYPE getThreadId() override; 
 	bool XMETHODCALLTYPE isOnMainThread() override;
@@ -46,10 +48,10 @@ public:
 	IXConfig* XMETHODCALLTYPE newConfig() override;
 	IXBuffer* XMETHODCALLTYPE newBuffer() override;
 
-	const char** XMETHODCALLTYPE getPCVarString(const char *szName) override;
-	const int* XMETHODCALLTYPE getPCVarInt(const char *szName) override;
-	const float* XMETHODCALLTYPE getPCVarFloat(const char *szName) override;
-	const bool* XMETHODCALLTYPE getPCVarBool(const char *szName) override;
+	//const char** XMETHODCALLTYPE getPCVarString(const char *szName) override;
+	//const int* XMETHODCALLTYPE getPCVarInt(const char *szName) override;
+	//const float* XMETHODCALLTYPE getPCVarFloat(const char *szName) override;
+	//const bool* XMETHODCALLTYPE getPCVarBool(const char *szName) override;
 
 	ID XMETHODCALLTYPE forLoop(int iStart, int iEnd, const IParallelForBody *pBody, int iMaxChunkSize = 0) override;
 	void XMETHODCALLTYPE waitForLoop(ID id) override;
@@ -78,6 +80,8 @@ protected:
 	CTimeManager *m_pTimers = NULL;
 	CTaskManager *m_pTaskManager = NULL;
 
+	CConsole *m_pConsole = NULL;
+
 	std::chrono::high_resolution_clock::time_point m_tLastUpdateTime;
 };
 
diff --git a/source/core/TaskManager.cpp b/source/core/TaskManager.cpp
index 0ecc383bcf207ccd21625a8c258c9417cf12c4e6..f91def794ad7c5416c3fd4b613a6237ffa0d022f 100644
--- a/source/core/TaskManager.cpp
+++ b/source/core/TaskManager.cpp
@@ -78,9 +78,7 @@ public:
 
 //##########################################################################
 
-CTaskManager::CTaskManager(unsigned int numThreads):
-m_isSingleThreaded(false),
-m_isRunning(false)
+CTaskManager::CTaskManager(unsigned int numThreads)
 {
 	m_iNumThreads = numThreads;
 	if(!numThreads)
diff --git a/source/core/TaskManager.h b/source/core/TaskManager.h
index 4a2b986a93ef0715dd8cc9e494e366e81db89095..1d96eb8638dd763376a6ad85fbde5f04ff02d94f 100644
--- a/source/core/TaskManager.h
+++ b/source/core/TaskManager.h
@@ -71,7 +71,7 @@ private:
 	std::thread* m_pIOThread;
 	unsigned int m_iNumThreads;
 
-	bool m_isRunning;
+	volatile bool m_isRunning = false;
 
 	TaskList m_TaskList[2]; //!< В главном потоке (синхронно)
 	TaskList m_BackgroundTasks; //!< Фоновые задачи
@@ -95,7 +95,7 @@ private:
 	Condition m_ConditionFor;
 	int m_iNumTasksToWaitFor;
 
-	bool m_isSingleThreaded;
+	bool m_isSingleThreaded = false;
 
 	Array<int> m_aiNumWaitFor;
 };
diff --git a/source/core/concmd.cpp b/source/core/concmd.cpp
index e3b6c653ddaa059874ea0d0c8f99ace61b58cd00..32fa000c11be9b6c83f7eba341e8c48a05584ccb 100644
--- a/source/core/concmd.cpp
+++ b/source/core/concmd.cpp
@@ -49,7 +49,7 @@ char g_szClientPort[8];
 bool CommandConnect();
 void CommandDisconnect();
 
-SX_LIB_API void Core_0RegisterConcmd(char * name, SXCONCMD cmd, const char * desc)
+SX_LIB_API XDEPRECATED void Core_0RegisterConcmd(const char * name, SXCONCMD cmd, const char * desc)
 {
 	ConCmd c;
 	c.type = CMD_DEFAULT;
@@ -63,7 +63,7 @@ SX_LIB_API void Core_0RegisterConcmd(char * name, SXCONCMD cmd, const char * des
 	}
 	g_mCmds[name] = c;
 }
-SX_LIB_API void Core_0RegisterConcmdArg(char * name, SXCONCMDARG cmd, const char * desc)
+SX_LIB_API XDEPRECATED void Core_0RegisterConcmdArg(const char * name, SXCONCMDARG cmd, const char * desc)
 {
 	ConCmd c;
 	c.type = CMD_ARG;
@@ -77,7 +77,7 @@ SX_LIB_API void Core_0RegisterConcmdArg(char * name, SXCONCMDARG cmd, const char
 	}
 	g_mCmds[name] = c;
 }
-SX_LIB_API void Core_0RegisterConcmdCls(char * name, void * pObject, const SXCONCMDCLS &cmd, const char * desc)
+SX_LIB_API XDEPRECATED void Core_0RegisterConcmdCls(const char * name, void * pObject, const SXCONCMDCLS &cmd, const char * desc)
 {
 	ConCmd c;
 	c.type = CMD_CLS;
@@ -91,7 +91,7 @@ SX_LIB_API void Core_0RegisterConcmdCls(char * name, void * pObject, const SXCON
 	}
 	g_mCmds[name] = c;
 }
-SX_LIB_API void Core_0RegisterConcmdClsArg(char * name, void * pObject, const SXCONCMDCLSARG &cmd, const char * desc)
+SX_LIB_API XDEPRECATED void Core_0RegisterConcmdClsArg(const char * name, void * pObject, const SXCONCMDCLSARG &cmd, const char * desc)
 {
 	ConCmd c;
 	c.type = CMD_CLS_ARG;
@@ -280,7 +280,7 @@ SX_LIB_API void Core_0ConsoleUpdate()
 
 	//execute command buffer
 }
-SX_LIB_API void Core_0ConsoleExecCmd(const char * format, ...)
+SX_LIB_API XDEPRECATED void Core_0ConsoleExecCmd(const char * format, ...)
 {
 	va_list va;
 	va_start(va, format);
@@ -588,7 +588,7 @@ void DumpCCommands()
 {
 	int iLenName = 0;
 
-	for(AssotiativeArray<String, ConCmd>::Iterator i = g_mCmds.begin(); i; i++)
+	for(AssotiativeArray<String, ConCmd>::Iterator i = g_mCmds.begin(); i; ++i)
 	{
 		ConCmd * pCmd = i.second;
 		int len = (int)i.first->length();
@@ -601,7 +601,7 @@ void DumpCCommands()
 	sprintf(szRow, COLOR_LGREEN " %%%ds " COLOR_GRAY "| " COLOR_GREEN " %%s" COLOR_RESET "\n", iLenName);
 	printf(szRow, "Name", "Description");
 	char tmpName[64];
-	for(AssotiativeArray<String, ConCmd>::Iterator i = g_mCmds.begin(); i; i++)
+	for(AssotiativeArray<String, ConCmd>::Iterator i = g_mCmds.begin(); i; ++i)
 	{
 		ConCmd * pCmd = i.second;
 		sprintf(tmpName, "%s", i.first->c_str());
diff --git a/source/core/cvars.cpp b/source/core/cvars.cpp
index 01035cb5cf97a4986c1d92eeaba203edca038d39..ad3ddd95203bfc67ae06958ae12989f0f1d78147 100644
--- a/source/core/cvars.cpp
+++ b/source/core/cvars.cpp
@@ -11,7 +11,7 @@ AssotiativeArray<String, CVar> g_mCVars;
 AssotiativeArray<String, CVarPtr> g_mCVarPtrs;
 IEventChannel<XEventCvarChanged> *g_pNotifyEventChannel = NULL;
 
-SX_LIB_API void Core_0RegisterCVarString(const char * name, const char * value, const char * desc, int flags)
+SX_LIB_API XDEPRECATED void Core_0RegisterCVarString(const char * name, const char * value, const char * desc, int flags)
 {
 	if(g_mCVars.KeyExists(name))
 	{
@@ -40,7 +40,7 @@ SX_LIB_API void Core_0RegisterCVarString(const char * name, const char * value,
 	g_mCVars[name] = cv;
 }
 
-SX_LIB_API void Core_0RegisterCVarInt(const char * name, int value, const char * desc, int flags)
+SX_LIB_API XDEPRECATED void Core_0RegisterCVarInt(const char * name, int value, const char * desc, int flags)
 {
 	if(g_mCVars.KeyExists(name))
 	{
@@ -66,7 +66,7 @@ SX_LIB_API void Core_0RegisterCVarInt(const char * name, int value, const char *
 	g_mCVars[name] = cv;
 }
 
-SX_LIB_API void Core_0RegisterCVarFloat(const char * name, float value, const char * desc, int flags)
+SX_LIB_API XDEPRECATED void Core_0RegisterCVarFloat(const char * name, float value, const char * desc, int flags)
 {
 	if(g_mCVars.KeyExists(name))
 	{
@@ -91,7 +91,7 @@ SX_LIB_API void Core_0RegisterCVarFloat(const char * name, float value, const ch
 	g_mCVars[name] = cv;
 }
 
-SX_LIB_API void Core_0RegisterCVarBool(const char * name, bool value, const char * desc, int flags)
+SX_LIB_API XDEPRECATED void Core_0RegisterCVarBool(const char * name, bool value, const char * desc, int flags)
 {
 	if(g_mCVars.KeyExists(name))
 	{
@@ -117,7 +117,7 @@ SX_LIB_API void Core_0RegisterCVarBool(const char * name, bool value, const char
 	g_mCVars[name] = cv;
 }
 
-SX_LIB_API void Core_0RegisterCVarPointer(const char * name, UINT_PTR value)
+SX_LIB_API XDEPRECATED void Core_0RegisterCVarPointer(const char * name, UINT_PTR value)
 {
 	if(g_mCVarPtrs.KeyExists(name))
 	{
@@ -134,7 +134,7 @@ SX_LIB_API void Core_0RegisterCVarPointer(const char * name, UINT_PTR value)
 }
 
 
-SX_LIB_API const char ** Core_0GetPCVarString(const char * name)
+SX_LIB_API XDEPRECATED const char ** Core_0GetPCVarString(const char * name)
 {
 	const AssotiativeArray<String, CVar>::Node * pNode;
 	if(g_mCVars.KeyExists(name, &pNode) && pNode->Val->type == CVAR_STRING)
@@ -144,7 +144,7 @@ SX_LIB_API const char ** Core_0GetPCVarString(const char * name)
 	return(NULL);
 }
 
-SX_LIB_API const int * Core_0GetPCVarInt(const char * name)
+SX_LIB_API XDEPRECATED const int * Core_0GetPCVarInt(const char * name)
 {
 	const AssotiativeArray<String, CVar>::Node * pNode;
 	if(g_mCVars.KeyExists(name, &pNode) && pNode->Val->type == CVAR_INT)
@@ -154,7 +154,7 @@ SX_LIB_API const int * Core_0GetPCVarInt(const char * name)
 	return(NULL);
 }
 
-SX_LIB_API const float * Core_0GetPCVarFloat(const char * name)
+SX_LIB_API XDEPRECATED const float * Core_0GetPCVarFloat(const char * name)
 {
 	const AssotiativeArray<String, CVar>::Node * pNode;
 	if(g_mCVars.KeyExists(name, &pNode) && pNode->Val->type == CVAR_FLOAT)
@@ -164,7 +164,7 @@ SX_LIB_API const float * Core_0GetPCVarFloat(const char * name)
 	return(NULL);
 }
 
-SX_LIB_API const bool * Core_0GetPCVarBool(const char * name)
+SX_LIB_API XDEPRECATED const bool * Core_0GetPCVarBool(const char * name)
 {
 	const AssotiativeArray<String, CVar>::Node * pNode;
 	if(g_mCVars.KeyExists(name, &pNode) && pNode->Val->type == CVAR_BOOL)
@@ -174,7 +174,7 @@ SX_LIB_API const bool * Core_0GetPCVarBool(const char * name)
 	return(NULL);
 }
 
-SX_LIB_API UINT_PTR * Core_0GetPCVarPointer(const char * name)
+SX_LIB_API XDEPRECATED UINT_PTR * Core_0GetPCVarPointer(const char * name)
 {
 	const AssotiativeArray<String, CVarPtr>::Node * pNode;
 	if(g_mCVarPtrs.KeyExists(name, &pNode))
@@ -185,7 +185,7 @@ SX_LIB_API UINT_PTR * Core_0GetPCVarPointer(const char * name)
 }
 
 
-SX_LIB_API void Core_0SetCVarString(const char * name, const char * value)
+SX_LIB_API XDEPRECATED void Core_0SetCVarString(const char * name, const char * value)
 {
 	const AssotiativeArray<String, CVar>::Node * pNode;
 	if(g_mCVars.KeyExists(name, &pNode) && pNode->Val->type == CVAR_STRING && !(pNode->Val->flags & FCVAR_READONLY))
@@ -213,7 +213,7 @@ SX_LIB_API void Core_0SetCVarString(const char * name, const char * value)
 	}
 }
 
-SX_LIB_API void Core_0SetCVarInt(const char * name, int value)
+SX_LIB_API XDEPRECATED void Core_0SetCVarInt(const char * name, int value)
 {
 	const AssotiativeArray<String, CVar>::Node * pNode;
 	if(g_mCVars.KeyExists(name, &pNode) && pNode->Val->type == CVAR_INT && !(pNode->Val->flags & FCVAR_READONLY))
@@ -235,7 +235,7 @@ SX_LIB_API void Core_0SetCVarInt(const char * name, int value)
 	}
 }
 
-SX_LIB_API void Core_0SetCVarFloat(const char * name, float value)
+SX_LIB_API XDEPRECATED void Core_0SetCVarFloat(const char * name, float value)
 {
 	const AssotiativeArray<String, CVar>::Node * pNode;
 	if(g_mCVars.KeyExists(name, &pNode) && pNode->Val->type == CVAR_FLOAT && !(pNode->Val->flags & FCVAR_READONLY))
@@ -257,7 +257,7 @@ SX_LIB_API void Core_0SetCVarFloat(const char * name, float value)
 	}
 }
 
-SX_LIB_API void Core_0SetCVarBool(const char * name, bool value)
+SX_LIB_API XDEPRECATED void Core_0SetCVarBool(const char * name, bool value)
 {
 	const AssotiativeArray<String, CVar>::Node * pNode;
 	if(g_mCVars.KeyExists(name, &pNode) && pNode->Val->type == CVAR_BOOL && !(pNode->Val->flags & FCVAR_READONLY))
@@ -279,7 +279,7 @@ SX_LIB_API void Core_0SetCVarBool(const char * name, bool value)
 	}
 }
 
-SX_LIB_API void Core_0GetCVarAsString(const char * name, char * szOut, int iMaxLength)
+SX_LIB_API XDEPRECATED void Core_0GetCVarAsString(const char * name, char * szOut, int iMaxLength)
 {
 	const AssotiativeArray<String, CVar>::Node * pNode;
 	if(!g_mCVars.KeyExists(name, &pNode))
@@ -403,7 +403,7 @@ void DumpCVars()
 		iLenType = 6,
 		iLenVal = 10;
 
-	for(AssotiativeArray<String, CVar>::Iterator i = g_mCVars.begin(); i; i++)
+	for(AssotiativeArray<String, CVar>::Iterator i = g_mCVars.begin(); i; ++i)
 	{
 		CVar * pCvar = i.second;
 		int len = (int)i.first->length();
@@ -425,7 +425,7 @@ void DumpCVars()
 	printf(szRow, "Name", "Type", "Value", "Description");
 	char tmp[64];
 	char tmpName[64];
-	for(AssotiativeArray<String, CVar>::Iterator i = g_mCVars.begin(); i; i++)
+	for(AssotiativeArray<String, CVar>::Iterator i = g_mCVars.begin(); i; ++i)
 	{
 		CVar * pCvar = i.second;
 		sprintf(tmpName, "%s", i.first->c_str());
diff --git a/source/core/sxcore.cpp b/source/core/sxcore.cpp
index 8ada0b56f3f03b4bee932b78510413da59ae0431..c96b609835a814e7ed09f664d3d24b16f12202ef 100644
--- a/source/core/sxcore.cpp
+++ b/source/core/sxcore.cpp
@@ -219,19 +219,19 @@ void Core_AGetName(char* name)
 }
 #endif
 
-SX_LIB_API IXCore *Core_GetIXCore()
+SX_LIB_API XDEPRECATED IXCore *Core_GetIXCore()
 {
 	return(g_pCore);
 }
 
 //##########################################################################
 
-IFile* Core_CrFile()
+SX_LIB_API XDEPRECATED IFile* Core_CrFile()
 {
 	return new CFile();
 }
 
-IFile* Core_OpFile(const char *szPath, int iType)
+SX_LIB_API XDEPRECATED IFile* Core_OpFile(const char *szPath, int iType)
 {
 	CFile* pFile = new CFile();
 	pFile->open(szPath, iType);
@@ -299,12 +299,12 @@ SX_LIB_API const char* Core_ResPathGetFullPathByRelIndex2(int iRegisterPath, con
 //##########################################################################
 
 
-ISXConfig*  Core_CrConfig()
+SX_LIB_API XDEPRECATED ISXConfig* Core_CrConfig()
 {
 	return new CConfig();
 }
 
-ISXConfig*  Core_OpConfig(const char* path)
+SX_LIB_API XDEPRECATED ISXConfig* Core_OpConfig(const char* path)
 {
 	CConfig* pConfig = new CConfig();
 	pConfig->open(path);
diff --git a/source/core/sxcore.h b/source/core/sxcore.h
index a3ab189b04d232834de49a9f2e1f5408adc6d787..b9e0255e08660f926891c803b59d9d08b9ab1ac8 100644
--- a/source/core/sxcore.h
+++ b/source/core/sxcore.h
@@ -94,7 +94,7 @@ SX_LIB_API void Core_0ExecCommandLine();
 //! Выполняет консольные команды из командной строки
 SX_LIB_API const char *Core_0GetCommandLineArg(const char *szArg, const char *szDefault = NULL);
 
-SX_LIB_API IXCore *Core_GetIXCore();
+SX_LIB_API XDEPRECATED IXCore *Core_GetIXCore();
 
 
 //! установка своего обработчика вывода отладочной информации
@@ -397,10 +397,10 @@ SX_LIB_API int64_t Core_TimeTotalMcsGetU(ID id);
 !@{*/
 
 //! создать экземпляр класса IFile
-SX_LIB_API IFile* Core_CrFile(); 
+SX_LIB_API XDEPRECATED IFile* Core_CrFile();
 
 //! открыть файл
-SX_LIB_API IFile* Core_OpFile(const char* szPath, int iType); 
+SX_LIB_API XDEPRECATED IFile* Core_OpFile(const char* szPath, int iType);
 
 //!@}
 
@@ -442,10 +442,10 @@ struct ISXConfig : public IBaseObject
 @{*/
 
 //! создать файл экземпляр класса ISXLConfig
-SX_LIB_API ISXConfig* Core_CrConfig(); 
+SX_LIB_API XDEPRECATED ISXConfig* Core_CrConfig();
 
 //! открыть файл конфигов
-SX_LIB_API ISXConfig* Core_OpConfig(const char* path);
+SX_LIB_API XDEPRECATED ISXConfig* Core_OpConfig(const char* path);
 
 //!@}
 
@@ -461,13 +461,13 @@ class ConCmdStub{}; /*!< Класс-заглушка, для определен
 typedef void(ConCmdStub::* SXCONCMDCLS)(); /*!< Тип метода для регистрации команды-члена класса без аргументов */
 typedef void(ConCmdStub::* SXCONCMDCLSARG)(int argc, const char ** argv); /*!< Тип метода для регистрации команды-члена класса с аргументами */
 
-SX_LIB_API void Core_0RegisterConcmd(char * name, SXCONCMD cmd, const char * desc = NULL); //!< Регистрация консольной функции без аргументов
-SX_LIB_API void Core_0RegisterConcmdArg(char * name, SXCONCMDARG cmd, const char * desc = NULL); //!< Регистрация консольной функции с аргументами
-SX_LIB_API void Core_0RegisterConcmdCls(char * name, void * pObject, const SXCONCMDCLS &cmd, const char * desc = NULL); //!< Регистрация консольной функции-члена класса без аргументов
-SX_LIB_API void Core_0RegisterConcmdClsArg(char * name, void * pObject, const SXCONCMDCLSARG &cmd, const char * desc = NULL); //!< Регистрация консольной функции-члена класса с аргументами
+SX_LIB_API XDEPRECATED void Core_0RegisterConcmd(const char * name, SXCONCMD cmd, const char * desc = NULL); //!< Регистрация консольной функции без аргументов
+SX_LIB_API XDEPRECATED void Core_0RegisterConcmdArg(const char * name, SXCONCMDARG cmd, const char * desc = NULL); //!< Регистрация консольной функции с аргументами
+SX_LIB_API XDEPRECATED void Core_0RegisterConcmdCls(const char * name, void * pObject, const SXCONCMDCLS &cmd, const char * desc = NULL); //!< Регистрация консольной функции-члена класса без аргументов
+SX_LIB_API XDEPRECATED void Core_0RegisterConcmdClsArg(const char * name, void * pObject, const SXCONCMDCLSARG &cmd, const char * desc = NULL); //!< Регистрация консольной функции-члена класса с аргументами
 
 SX_LIB_API void Core_0ConsoleUpdate(); //!< Обновление консоли, выполнение буфера команд
-SX_LIB_API void Core_0ConsoleExecCmd(const char * format, ...); //!< Добавление команды на исполнение в буфер команд
+SX_LIB_API XDEPRECATED void Core_0ConsoleExecCmd(const char * format, ...); //!< Добавление команды на исполнение в буфер команд
 
 SX_LIB_API UINT_PTR Core_ConsoleGetOutHandler();
 
@@ -540,17 +540,17 @@ __inline void Core_SetOutPtr()
 !@{*/
 
 //! Флаги кваров
-enum CVAR_FLAG
+/*enum CVAR_FLAG
 {
 	FCVAR_NONE       = 0x00, //!< нет
 	FCVAR_CHEAT      = 0x01, //!< Изменение этой переменной с дефолтного значения разрешено только в режиме разработки
 	FCVAR_READONLY   = 0x02,  //!< Только для чтения
 	FCVAR_NOTIFY_OLD = 0x04, //!< Оповещать об изменениях
 	FCVAR_NOTIFY     = 0x08  //!< Оповещать об изменениях
-};
+};*/
 
 //! Регистрирует строковую переменную
-SX_LIB_API void Core_0RegisterCVarString(
+SX_LIB_API XDEPRECATED void Core_0RegisterCVarString(
 	const char * name, //!< Имя квара
 	const char * value,  //!< значение по умолчанию
 	const char * desc=NULL, //!< краткое описание
@@ -558,7 +558,7 @@ SX_LIB_API void Core_0RegisterCVarString(
 	);
 
 //! Регистрирует целую переменную
-SX_LIB_API void Core_0RegisterCVarInt(
+SX_LIB_API XDEPRECATED void Core_0RegisterCVarInt(
 	const char * name, //!< Имя квара
 	int value,  //!< значение по умолчанию
 	const char * desc = NULL, //!< краткое описание
@@ -566,7 +566,7 @@ SX_LIB_API void Core_0RegisterCVarInt(
 	);
 
 //! Регистрирует дробную переменную
-SX_LIB_API void Core_0RegisterCVarFloat(
+SX_LIB_API XDEPRECATED void Core_0RegisterCVarFloat(
 	const char * name, //!< Имя квара
 	float value,  //!< значение по умолчанию
 	const char * desc = NULL, //!< краткое описание
@@ -574,7 +574,7 @@ SX_LIB_API void Core_0RegisterCVarFloat(
 	);
 
 //! Регистрирует логическую переменную
-SX_LIB_API void Core_0RegisterCVarBool(
+SX_LIB_API XDEPRECATED void Core_0RegisterCVarBool(
 	const char * name, //!< Имя квара
 	bool value,  //!< значение по умолчанию
 	const char * desc = NULL, //!< краткое описание
@@ -582,45 +582,45 @@ SX_LIB_API void Core_0RegisterCVarBool(
 	);
 
 //! Регистрирует указатель
-SX_LIB_API void Core_0RegisterCVarPointer(
+SX_LIB_API XDEPRECATED void Core_0RegisterCVarPointer(
 	const char * name, //!< Имя квара
 	UINT_PTR value  //!< значение по умолчанию
 	);
 
 //! Получает указатель на значение строкового квара. При отсутствии квара запрошенного типа возвращает NULL
-SX_LIB_API const char ** Core_0GetPCVarString(const char * name);
+SX_LIB_API XDEPRECATED const char ** Core_0GetPCVarString(const char * name);
 #define GET_PCVAR_STRING(k) Core_0GetPCVarString(k)
 
 //! Получает указатель на значение целочисленного квара. При отсутствии квара запрошенного типа возвращает NULL
-SX_LIB_API const int * Core_0GetPCVarInt(const char * name);
+SX_LIB_API XDEPRECATED const int * Core_0GetPCVarInt(const char * name);
 #define GET_PCVAR_INT(k) Core_0GetPCVarInt(k)
 
 //! Получает указатель на значение дробного квара. При отсутствии квара запрошенного типа возвращает NULL
-SX_LIB_API const float * Core_0GetPCVarFloat(const char * name);
+SX_LIB_API XDEPRECATED const float * Core_0GetPCVarFloat(const char * name);
 #define GET_PCVAR_FLOAT(k) Core_0GetPCVarFloat(k)
 
 //! Получает указатель на значение логического квара. При отсутствии квара запрошенного типа возвращает NULL
-SX_LIB_API const bool * Core_0GetPCVarBool(const char * name);
+SX_LIB_API XDEPRECATED const bool * Core_0GetPCVarBool(const char * name);
 #define GET_PCVAR_BOOL(k) Core_0GetPCVarBool(k)
 
 //! Получает указатель по имени. При отсутствии квара запрошенного типа возвращает NULL
-SX_LIB_API UINT_PTR * Core_0GetPCVarPointer(const char * name);
+SX_LIB_API XDEPRECATED UINT_PTR * Core_0GetPCVarPointer(const char * name);
 #define GET_PCVAR_POINTER(k) Core_0GetPCVarPointer(k)
 
 //! Устанавливает новое значение квара. Должен существовать
-SX_LIB_API void Core_0SetCVarString(const char * name, const char * value);
+SX_LIB_API XDEPRECATED void Core_0SetCVarString(const char * name, const char * value);
 
 //! Устанавливает новое значение квара. Должен существовать
-SX_LIB_API void Core_0SetCVarInt(const char * name, int value);
+SX_LIB_API XDEPRECATED void Core_0SetCVarInt(const char * name, int value);
 
 //! Устанавливает новое значение квара. Должен существовать
-SX_LIB_API void Core_0SetCVarFloat(const char * name, float value);
+SX_LIB_API XDEPRECATED void Core_0SetCVarFloat(const char * name, float value);
 
 //! Устанавливает новое значение квара. Должен существовать
-SX_LIB_API void Core_0SetCVarBool(const char * name, bool value);
+SX_LIB_API XDEPRECATED void Core_0SetCVarBool(const char * name, bool value);
 
 //! Получает значение квара в виде строки
-SX_LIB_API void Core_0GetCVarAsString(const char * name, char * szOut, int iMaxLength);
+SX_LIB_API XDEPRECATED void Core_0GetCVarAsString(const char * name, char * szOut, int iMaxLength);
 
 
 //!@}
diff --git a/source/decals/DecalManager.cpp b/source/decals/DecalManager.cpp
index 4b8a623cd36a0f70683db5b9bb88c660c926c540..bb5c31fffe42105bc5804e0217b4fd7c3d472c3a 100644
--- a/source/decals/DecalManager.cpp
+++ b/source/decals/DecalManager.cpp
@@ -203,7 +203,7 @@ void DecalManager::removeDecal(UINT iDecal)
 
 void DecalManager::clear()
 {
-	for(AssotiativeArray<ID, Array<_DecalMatItem>>::Iterator i = m_MaterialSort.begin(); i; i++)
+	for(AssotiativeArray<ID, Array<_DecalMatItem>>::Iterator i = m_MaterialSort.begin(); i; ++i)
 	{
 		for(int j = 0, lj = i.second->size(); j < lj; ++j)
 		{
@@ -678,7 +678,7 @@ void DecalManager::updateBuffer()
 
 	_DecalTexRange rng;
 	m_iRngs.clear();
-	for(AssotiativeArray<ID, Array<_DecalMatItem>>::Iterator i = m_MaterialSort.begin(); i; i++)
+	for(AssotiativeArray<ID, Array<_DecalMatItem>>::Iterator i = m_MaterialSort.begin(); i; ++i)
 	{
 		for(int j = 0, lj = i.second->size(); j < lj; ++j)
 		{
@@ -708,7 +708,7 @@ void DecalManager::updateBuffer()
 	DecalVertex * pData = (DecalVertex*)alloca(sizeof(DecalVertex) * iVC);
 	
 	iVC = 0;
-	for(AssotiativeArray<ID, Array<_DecalMatItem>>::Iterator i = m_MaterialSort.begin(); i; i++)
+	for(AssotiativeArray<ID, Array<_DecalMatItem>>::Iterator i = m_MaterialSort.begin(); i; ++i)
 	{
 		for(UINT j = 0, l = i.second->size(); j < l; ++j)
 		{
diff --git a/source/game/BaseAnimating.cpp b/source/game/BaseAnimating.cpp
index 7545f94df3c1d54980ad5147c8cc1221cf386ec5..9a84b4e1cad04671a8f978df49c50481cf880adc 100644
--- a/source/game/BaseAnimating.cpp
+++ b/source/game/BaseAnimating.cpp
@@ -206,7 +206,7 @@ void CBaseAnimating::onSync()
 	//	m_pRigidBody->getWorldTransform().setOrigin(F3_BTVEC(getPos()));
 	//	m_pRigidBody->getWorldTransform().setRotation(Q4_BTQUAT(getOrient()));
 	}
-	if(m_pModel)
+	if(m_pModel && m_pModel->isEnabled())
 	{
 		m_pModel->setPosition(getPos());
 		m_pModel->setOrientation(getOrient());
@@ -280,9 +280,9 @@ void CBaseAnimating::initPhysics()
 				btVector3 *pData;
 				int iVertexCount;
 				SPhysics_BuildHull(&tmpShape, &pData, &iVertexCount);
-				for(int i = 0; i < iVertexCount; ++i)
+				for(int j = 0; j < iVertexCount; ++j)
 				{
-					pData[i] *= m_fBaseScale;
+					pData[j] *= m_fBaseScale;
 				}
 				pLocalShape = new btConvexHullShape((float*)pData, iVertexCount, sizeof(btVector3));
 				SPhysics_ReleaseHull(pData, iVertexCount);
@@ -661,7 +661,6 @@ void CBaseAnimating::renderEditor(bool is3D)
 {
 	if(m_pModel)
 	{
-		m_pModel->render(0, false);
-		m_pModel->render(0, true);
+		m_pModel->render(0, MF_OPAQUE | MF_TRANSPARENT);
 	}
 }
diff --git a/source/game/BaseEntity.cpp b/source/game/BaseEntity.cpp
index db0533f24033d7ff51816df6f6deee7554ced9db..4744b82420453c091cd3a85b6a47665f7c255dde 100644
--- a/source/game/BaseEntity.cpp
+++ b/source/game/BaseEntity.cpp
@@ -353,13 +353,19 @@ bool CBaseEntity::setKV(const char * name, const char * value)
 			}
 			else
 			{
+				// check type of pEnt
+				if(field->pfnCheckType && !field->pfnCheckType(pEnt))
+				{
+					LibReport(REPORT_MSG_LEVEL_ERROR, "Unable to set entity field '%s' to entity '%s'. Invalid class. Ent: %s", name, value, m_szName);
+					return(false);
+				}
 				if(field->fnSet.e)
 				{
 					(this->*(field->fnSet.e))(pEnt);
 				}
 				else
 				{
-					this->*((CBaseEntity * ThisClass::*)field->pField) = pEnt;
+					this->*((CBaseEntity* ThisClass::*)field->pField) = pEnt;
 				}
 			}
 			return(true);
diff --git a/source/game/BaseWeapon.cpp b/source/game/BaseWeapon.cpp
index de8689d167bf5b16fdb5ab0c4208f995110ddb19..2af4c1c190cf5fb85db618c176d8521d74fe702e 100644
--- a/source/game/BaseWeapon.cpp
+++ b/source/game/BaseWeapon.cpp
@@ -37,7 +37,7 @@ BEGIN_PROPTABLE(CBaseWeapon)
 	//! Патронов в отсечке
 	DEFINE_FIELD_INT(m_iCutoffSize, PDFF_NOEDIT | PDFF_NOEXPORT, "cutoff_size", "", EDITOR_NONE)
 	//! Текущий режим стрельбы
-	DEFINE_FIELD_INT(m_fireMode, PDFF_NOEDIT, "fire_mode", "", EDITOR_NONE)
+	DEFINE_FIELD_ENUM(FIRE_MODE, m_fireMode, PDFF_NOEDIT, "fire_mode", "", EDITOR_NONE)
 
 	//! Звук извлечения
 	DEFINE_FIELD_STRING(m_szSndDraw, PDFF_NOEDIT | PDFF_NOEXPORT, "snd_draw", "", EDITOR_NONE)
@@ -86,7 +86,7 @@ BEGIN_PROPTABLE(CBaseWeapon)
 	DEFINE_FIELD_FLOAT(m_fAimingRange, PDFF_NOEDIT | PDFF_NOEXPORT, "aiming_range", "", EDITOR_NONE)
 
 	//! тип нарезки ствола: 0 - гладкоствольное; -1 - левая; 1 - правая
-	DEFINE_FIELD_INT(m_rifleType, PDFF_NOEDIT | PDFF_NOEXPORT, "rifle_type", "", EDITOR_NONE)
+	DEFINE_FIELD_ENUM(RIFLE_TYPE, m_rifleType, PDFF_NOEDIT | PDFF_NOEXPORT, "rifle_type", "", EDITOR_NONE)
 	//! шаг нарезки ствола (мм)
 	DEFINE_FIELD_FLOAT(m_fRifleStep, PDFF_NOEDIT | PDFF_NOEXPORT, "rifle_step", "", EDITOR_NONE)
 END_PROPTABLE()
diff --git a/source/game/EntityFactory.cpp b/source/game/EntityFactory.cpp
index d894c15e730a5a63f59fc815394adb7c4f3286f3..ac4de7c989f397e60b0ec0e9e99d763874c4daa9 100644
--- a/source/game/EntityFactory.cpp
+++ b/source/game/EntityFactory.cpp
@@ -128,7 +128,7 @@ void CEntityFactoryMap::getListing(const char ** pszOut, int size)
 	{
 		size = m_iShowInListCount;
 	}
-	for(AssotiativeArray<AAString, IEntityFactory*>::Iterator i = m_mFactories.begin(); i && j < size; i++)
+	for(AssotiativeArray<AAString, IEntityFactory*>::Iterator i = m_mFactories.begin(); i && j < size; ++i)
 	{
 		if(!(*i.second)->isEditorHidden())
 		{
@@ -146,3 +146,26 @@ const char *CEntityFactoryMap::getClassNamePtr(const char *szClassName)
 	}
 	return(NULL);
 }
+
+bool CEntityFactoryMap::IsEntityOfClass(CBaseEntity *pEnt, const char *szClassName)
+{
+	IEntityFactory * pFactory = GetInstance()->getFactory(szClassName);
+	if(pFactory)
+	{
+		proptable_t *pPropTable0 = pFactory->getPropTable();
+
+		proptable_t *pPropTable1 = pEnt->getPropTable();
+
+		do
+		{
+			if(pPropTable0 == pPropTable1)
+			{
+				return(true);
+			}
+
+			pPropTable1 = pPropTable1->pBaseProptable;
+		}
+		while(pPropTable1);
+	}
+	return(false);
+}
diff --git a/source/game/EntityFactory.h b/source/game/EntityFactory.h
index 4929c38ec42f3e7ff1a51effe98e2e8d164ebb6a..3aa8d7ef9128fb79c6c7a4b0adee045b14e9547d 100644
--- a/source/game/EntityFactory.h
+++ b/source/game/EntityFactory.h
@@ -51,6 +51,31 @@ public:
 	void getListing(const char **pszOut, int size);
 
 	const char* getClassNamePtr(const char *szClassName);
+
+	static bool IsEntityOfClass(CBaseEntity *pEnt, const char *szClassName);
+
+	template<typename T>
+	static bool IsEntityOfClass(CBaseEntity *pEnt)
+	{
+		static_assert(std::is_base_of<CBaseEntity, T>::value, "T must be subclass of CBaseEntity");
+
+		proptable_t *pPropTable0 = T::SGetPropTable();
+
+		proptable_t *pPropTable1 = pEnt->getPropTable();
+
+		do
+		{
+			if(pPropTable0 == pPropTable1)
+			{
+				return(true);
+			}
+
+			pPropTable1 = pPropTable1->pBaseProptable;
+		}
+		while(pPropTable1);
+
+		return(false);
+	}
 private:
 	IEntityFactory* getFactory(const char *szName);
 	AssotiativeArray<AAString, IEntityFactory*> m_mFactories;
diff --git a/source/game/EntityManager.cpp b/source/game/EntityManager.cpp
index bfd3dcf407aafc33e8b054ed0a01e3ff19437f69..656172eead6c27bb4b2951b73aac2bef40057747 100644
--- a/source/game/EntityManager.cpp
+++ b/source/game/EntityManager.cpp
@@ -686,7 +686,7 @@ void CEntityManager::loadDynClasses()
 		}
 		if((baseDefs = CEntityFactoryMap::GetInstance()->getDefaults(baseClass)))
 		{
-			for(EntDefaultsMap::Iterator i = baseDefs->begin(); i; i++)
+			for(EntDefaultsMap::Iterator i = baseDefs->begin(); i; ++i)
 			{
 				defs[0][*i.first] = *i.second;
 			}
diff --git a/source/game/FuncTrain.cpp b/source/game/FuncTrain.cpp
index e0ab174f165b30d75940dcbc4f85635ed7c83ca6..531f72f549ceaa01a31032897e1206ffd9a88022 100644
--- a/source/game/FuncTrain.cpp
+++ b/source/game/FuncTrain.cpp
@@ -16,7 +16,7 @@ BEGIN_PROPTABLE(CFuncTrain)
 	DEFINE_FIELD_FLOAT(m_fSpeed, 0, "speed", "Move speed", EDITOR_TEXTFIELD)
 
 	//! path_corner, с которого начнется движение
-	DEFINE_FIELD_ENTITY(m_pStartStop, 0, "start", "Start point", EDITOR_TEXTFIELD)
+	DEFINE_FIELD_ENTITY2(CPathCorner, m_pStartStop, 0, "start", "Start point", EDITOR_TEXTFIELD)
 END_PROPTABLE()
 
 REGISTER_ENTITY(CFuncTrain, func_train);
diff --git a/source/game/GameData.cpp b/source/game/GameData.cpp
index 8fc53d94b61887df04800338d759fc36166e3fde..f311d9e6ca6622a0f09ba897b1c91654bafab347 100644
--- a/source/game/GameData.cpp
+++ b/source/game/GameData.cpp
@@ -27,6 +27,7 @@ See the license in LICENSE
 
 #include <xcommon/XEvents.h>
 #include <xcommon/IAsyncTaskRunner.h>
+#include <xcommon/IXScene.h>
 
 #include "Editable.h"
 
@@ -612,6 +613,12 @@ GameData::GameData(HWND hWnd, bool isGame):
 	Core_0RegisterConcmd("spawn", ccmd_spawn);
 	Core_0RegisterConcmd("observe", ccmd_observe);
 
+	Core_0RegisterConcmd("bvh_height", [](){
+		IXScene *pScene = (IXScene*)Core_GetIXCore()->getPluginManager()->getInterface(IXSCENE_GUID);
+		UINT uDepth = pScene->getTreeHeight();
+		printf("BVH tree height: %u\n", uDepth);
+	});
+
 	//Core_0RegisterCVarFloat("r_default_fov", 45.0f, "Default FOV value");
 	Core_0RegisterCVarBool("cl_mode_editor", false, "Editor control mode");
 	Core_0RegisterCVarBool("cl_grab_cursor", false, "Grab cursor on move");
diff --git a/source/game/PathCorner.cpp b/source/game/PathCorner.cpp
index db8418c0bfafb5762241780bd5a9b0499fd1c400..726020c1186313f88e42fcf97af542d692896a70 100644
--- a/source/game/PathCorner.cpp
+++ b/source/game/PathCorner.cpp
@@ -21,7 +21,7 @@ BEGIN_PROPTABLE(CPathCorner)
 	DEFINE_FIELD_FLOAT(m_fNewSpeed, 0, "speed", "New speed", EDITOR_TEXTFIELD)
 
 	//! Следующая точка пути
-	DEFINE_FIELD_ENTITYFN(m_pNextStop, 0, "next", "Next stop", setNextPoint, EDITOR_TEXTFIELD)
+	DEFINE_FIELD_ENTITY2FN(CPathCorner, m_pNextStop, 0, "next", "Next stop", setNextPoint, EDITOR_TEXTFIELD)
 
 END_PROPTABLE()
 
@@ -46,9 +46,9 @@ CPathCorner::~CPathCorner()
 	}
 }
 
-void CPathCorner::setNextPoint(CBaseEntity *pEnt)
+void CPathCorner::setNextPoint(CPathCorner *pEnt)
 {
-	CPathCorner *pCur = (CPathCorner*)pEnt;
+	CPathCorner *pCur = pEnt;
 	while(pCur && pCur != this)
 	{
 		pCur = pCur->m_pNextStop;
diff --git a/source/game/PathCorner.h b/source/game/PathCorner.h
index 3e3dd661f07627c541455db014272ff5a21a5ef8..6336ef2dad3ff81a54c49d196367b0d2583f97b8 100644
--- a/source/game/PathCorner.h
+++ b/source/game/PathCorner.h
@@ -50,7 +50,7 @@ public:
 
 	void setPos(const float3 & pos);
 
-	void setNextPoint(CBaseEntity *pEnt);
+	void setNextPoint(CPathCorner *pEnt);
 
 protected:
 	//! Пересчитывает путь
diff --git a/source/game/proptable.h b/source/game/proptable.h
index d5a09df641874ff6d9d39ef596b8149b7aac9e1f..4f18c2f55b9fac0c9d8bc3c3e7646d0bcc4acb01 100644
--- a/source/game/proptable.h
+++ b/source/game/proptable.h
@@ -127,6 +127,12 @@ union PFNFIELDSET
 		e(arg)
 	{
 	}
+	template<typename T>
+	PFNFIELDSET(void(CBaseEntity::*arg)(T*)):
+		e((PFNFIELDSETE)arg)
+	{
+		static_assert(std::is_base_of<CBaseEntity, T>::value, "T must be subclass of CBaseEntity");
+	}
 	PFNFIELDSETV3 v3;
 	PFNFIELDSETV4 v4;
 	PFNFIELDSETF f;
@@ -168,7 +174,8 @@ struct inputdata_t
 	float4_t v4Parameter;
 };
 
-typedef void(CBaseEntity::*input_func)(inputdata_t * pInputData);
+typedef void(CBaseEntity::*input_func)(inputdata_t *pInputData);
+typedef bool(*PFNCHECKENTTYPE)(CBaseEntity*);
 
 struct propdata_t
 {
@@ -180,7 +187,7 @@ struct propdata_t
 		szEdName(NULL),
 		editor({})
 	{}
-	propdata_t(fieldtype f, PDF_TYPE t, int fl, const char *key, const char *edname, prop_editor_t ed):
+	propdata_t(fieldtype f, PDF_TYPE t, int fl, const char *key, const char *edname, PFNCHECKENTTYPE pfnCheckType, prop_editor_t ed):
 		pField(f),
 		type(t),
 		flags(fl),
@@ -188,7 +195,7 @@ struct propdata_t
 		szEdName(edname),
 		editor(ed)
 	{}
-	propdata_t(fieldtype f, PDF_TYPE t, int fl, const char *key, const char *edname, PFNFIELDSET _fnSet, prop_editor_t ed):
+	propdata_t(fieldtype f, PDF_TYPE t, int fl, const char *key, const char *edname, PFNFIELDSET _fnSet, PFNCHECKENTTYPE pfnCheckType, prop_editor_t ed):
 		pField(f),
 		type(t),
 		flags(fl),
@@ -216,6 +223,26 @@ struct propdata_t
 	const char * szEdName;
 	prop_editor_t editor;
 	PFNFIELDSET fnSet;
+
+	PFNCHECKENTTYPE pfnCheckType = NULL;
+
+	template<typename T>
+	static fieldtype ToFieldType(T arg)
+	{
+		return((fieldtype)arg);
+	}
+
+	template<typename T>
+	static input_func ToInputFunc(T arg)
+	{
+		return((input_func)arg);
+	}
+
+	template<typename T, typename Targ>
+	static PFNFIELDSET ToPFNFieldSet(void(T::*arg)(Targ))
+	{
+		return((void(CBaseEntity::*)(Targ))arg);
+	}
 };
 
 
@@ -371,32 +398,36 @@ const char * GetEmptyString();
 #define EDITOR_YESNO EDITOR_COMBOBOX COMBO_OPTION("Yes", "1") COMBO_OPTION("No", "0") EDITOR_COMBO_END()
 #define EDITOR_MODEL EDITOR_FILEFIELD FILE_OPTION("Select model", "dse") EDITOR_FILE_END()
 
-#define DEFINE_FIELD_STRING(field, flags, keyname, edname, editor)  , {(fieldtype)&DataClass::field, PDF_STRING,  flags, keyname, edname, editor
-#define DEFINE_FIELD_VECTOR(field, flags, keyname, edname, editor)  , {(fieldtype)&DataClass::field, PDF_VECTOR,  flags, keyname, edname, editor
-#define DEFINE_FIELD_VECTOR4(field, flags, keyname, edname, editor) , {(fieldtype)&DataClass::field, PDF_VECTOR4, flags, keyname, edname, editor
-#define DEFINE_FIELD_ANGLES(field, flags, keyname, edname, editor)  , {(fieldtype)&DataClass::field, PDF_ANGLES,  flags, keyname, edname, editor
-#define DEFINE_FIELD_INT(field, flags, keyname, edname, editor)     , {(fieldtype)&DataClass::field, PDF_INT,     flags, keyname, edname, editor
-#define DEFINE_FIELD_FLOAT(field, flags, keyname, edname, editor)   , {(fieldtype)&DataClass::field, PDF_FLOAT,   flags, keyname, edname, editor
-#define DEFINE_FIELD_BOOL(field, flags, keyname, edname, editor)    , {(fieldtype)&DataClass::field, PDF_BOOL,    flags, keyname, edname, editor
-#define DEFINE_FIELD_ENTITY(field, flags, keyname, edname, editor)  , {(fieldtype)&DataClass::field, PDF_ENTITY,  flags, keyname, edname, editor
-#define DEFINE_FIELD_PARENT(field, flags, keyname, edname, editor)  , {(fieldtype)&DataClass::field, PDF_PARENT,  flags, keyname, edname, editor
-#define DEFINE_FIELD_FLAGS(field, flags, keyname, edname, editor)   , {(fieldtype)&DataClass::field, PDF_FLAGS,   flags, keyname, edname, editor
-
-#define DEFINE_FIELD_STRINGFN(field, flags, keyname, edname, fn, editor)  , {(fieldtype)&DataClass::field, PDF_STRING,  flags, keyname, edname, (void(CBaseEntity::*)(const char*))&ThisClass::fn, editor
-#define DEFINE_FIELD_VECTORFN(field, flags, keyname, edname, fn, editor)  , {(fieldtype)&DataClass::field, PDF_VECTOR,  flags, keyname, edname, (void(CBaseEntity::*)(const float3&))&ThisClass::fn, editor
-#define DEFINE_FIELD_VECTOR4FN(field, flags, keyname, edname, fn, editor) , {(fieldtype)&DataClass::field, PDF_VECTOR4, flags, keyname, edname, (void(CBaseEntity::*)(const float4&))&ThisClass::fn, editor
-#define DEFINE_FIELD_ANGLESFN(field, flags, keyname, edname, fn, editor)  , {(fieldtype)&DataClass::field, PDF_ANGLES,  flags, keyname, edname, (void(CBaseEntity::*)(const SMQuaternion&))&ThisClass::fn, editor
-#define DEFINE_FIELD_INTFN(field, flags, keyname, edname, fn, editor)     , {(fieldtype)&DataClass::field, PDF_INT,     flags, keyname, edname, (void(CBaseEntity::*)(int))&ThisClass::fn, editor
-#define DEFINE_FIELD_FLOATFN(field, flags, keyname, edname, fn, editor)   , {(fieldtype)&DataClass::field, PDF_FLOAT,   flags, keyname, edname, (void(CBaseEntity::*)(float))&ThisClass::fn, editor
-#define DEFINE_FIELD_BOOLFN(field, flags, keyname, edname, fn, editor)    , {(fieldtype)&DataClass::field, PDF_BOOL,    flags, keyname, edname, (void(CBaseEntity::*)(bool))&ThisClass::fn, editor
-#define DEFINE_FIELD_ENTITYFN(field, flags, keyname, edname, fn, editor)  , {(fieldtype)&DataClass::field, PDF_ENTITY,  flags, keyname, edname, (void(CBaseEntity::*)(CBaseEntity*))&ThisClass::fn, editor
+#define DEFINE_FIELD_STRING(field, flags, keyname, edname, editor)              , {propdata_t::ToFieldType<const char* DataClass::*>(&DataClass::field),  PDF_STRING,  flags, keyname, edname, NULL,                                     editor
+#define DEFINE_FIELD_VECTOR(field, flags, keyname, edname, editor)              , {propdata_t::ToFieldType<float3_t DataClass::*>(&DataClass::field),     PDF_VECTOR,  flags, keyname, edname, NULL,                                     editor
+#define DEFINE_FIELD_VECTOR4(field, flags, keyname, edname, editor)             , {propdata_t::ToFieldType<float4_t DataClass::*>(&DataClass::field),     PDF_VECTOR4, flags, keyname, edname, NULL,                                     editor
+#define DEFINE_FIELD_ANGLES(field, flags, keyname, edname, editor)              , {propdata_t::ToFieldType<SMQuaternion DataClass::*>(&DataClass::field), PDF_ANGLES,  flags, keyname, edname, NULL,                                     editor
+#define DEFINE_FIELD_INT(field, flags, keyname, edname, editor)                 , {propdata_t::ToFieldType<int DataClass::*>(&DataClass::field),          PDF_INT,     flags, keyname, edname, NULL,                                     editor
+#define DEFINE_FIELD_ENUM(type, field, flags, keyname, edname, editor)          , {propdata_t::ToFieldType<type DataClass::*>(&DataClass::field),         PDF_INT,     flags, keyname, edname, NULL,                                     editor
+#define DEFINE_FIELD_FLOAT(field, flags, keyname, edname, editor)               , {propdata_t::ToFieldType<float DataClass::*>(&DataClass::field),        PDF_FLOAT,   flags, keyname, edname, NULL,                                     editor
+#define DEFINE_FIELD_BOOL(field, flags, keyname, edname, editor)                , {propdata_t::ToFieldType<bool DataClass::*>(&DataClass::field),         PDF_BOOL,    flags, keyname, edname, NULL,                                     editor
+#define DEFINE_FIELD_ENTITY(field, flags, keyname, edname, editor)              , {propdata_t::ToFieldType<CBaseEntity* DataClass::*>(&DataClass::field), PDF_ENTITY,  flags, keyname, edname, NULL,                                     editor
+#define DEFINE_FIELD_ENTITY2(type, field, flags, keyname, edname, editor)       , {propdata_t::ToFieldType<type* DataClass::*>(&DataClass::field),        PDF_ENTITY,  flags, keyname, edname, CEntityFactoryMap::IsEntityOfClass<type>, editor
+#define DEFINE_FIELD_PARENT(field, flags, keyname, edname, editor)              , {propdata_t::ToFieldType<CBaseEntity* DataClass::*>(&DataClass::field), PDF_PARENT,  flags, keyname, edname, NULL,                                     editor
+#define DEFINE_FIELD_FLAGS(field, flags, keyname, edname, editor)               , {propdata_t::ToFieldType<UINT DataClass::*>(&DataClass::field),         PDF_FLAGS,   flags, keyname, edname, NULL,                                     editor
+
+#define DEFINE_FIELD_STRINGFN(field, flags, keyname, edname, fn, editor)        , {propdata_t::ToFieldType<const char* DataClass::*>(&DataClass::field),  PDF_STRING,  flags, keyname, edname, propdata_t::ToPFNFieldSet<DataClass, const char*>(&DataClass::fn),         NULL,                                     editor
+#define DEFINE_FIELD_VECTORFN(field, flags, keyname, edname, fn, editor)        , {propdata_t::ToFieldType<float3_t DataClass::*>(&DataClass::field),     PDF_VECTOR,  flags, keyname, edname, propdata_t::ToPFNFieldSet<DataClass, const float3&>(&DataClass::fn),       NULL,                                     editor
+#define DEFINE_FIELD_VECTOR4FN(field, flags, keyname, edname, fn, editor)       , {propdata_t::ToFieldType<float4_t DataClass::*>(&DataClass::field),     PDF_VECTOR4, flags, keyname, edname, propdata_t::ToPFNFieldSet<DataClass, const float4&>(&DataClass::fn),       NULL,                                     editor
+#define DEFINE_FIELD_ANGLESFN(field, flags, keyname, edname, fn, editor)        , {propdata_t::ToFieldType<SMQuaternion DataClass::*>(&DataClass::field), PDF_ANGLES,  flags, keyname, edname, propdata_t::ToPFNFieldSet<DataClass, const SMQuaternion&>(&DataClass::fn), NULL,                                     editor
+#define DEFINE_FIELD_INTFN(field, flags, keyname, edname, fn, editor)           , {propdata_t::ToFieldType<int DataClass::*>(&DataClass::field),          PDF_INT,     flags, keyname, edname, propdata_t::ToPFNFieldSet<DataClass, int>(&DataClass::fn),                 NULL,                                     editor
+#define DEFINE_FIELD_ENUMFN(type, field, flags, keyname, edname, fn, editor)    , {propdata_t::ToFieldType<type DataClass::*>(&DataClass::field),         PDF_INT,     flags, keyname, edname, propdata_t::ToPFNFieldSet<DataClass, int>(&DataClass::fn),                 NULL,                                     editor
+#define DEFINE_FIELD_FLOATFN(field, flags, keyname, edname, fn, editor)         , {propdata_t::ToFieldType<float DataClass::*>(&DataClass::field),        PDF_FLOAT,   flags, keyname, edname, propdata_t::ToPFNFieldSet<DataClass, float>(&DataClass::fn),               NULL,                                     editor
+#define DEFINE_FIELD_BOOLFN(field, flags, keyname, edname, fn, editor)          , {propdata_t::ToFieldType<bool DataClass::*>(&DataClass::field),         PDF_BOOL,    flags, keyname, edname, propdata_t::ToPFNFieldSet<DataClass, bool>(&DataClass::fn),                NULL,                                     editor
+#define DEFINE_FIELD_ENTITYFN(field, flags, keyname, edname, fn, editor)        , {propdata_t::ToFieldType<CBaseEntity* DataClass::*>(&DataClass::field), PDF_ENTITY,  flags, keyname, edname, propdata_t::ToPFNFieldSet<DataClass, CBaseEntity*>(&DataClass::fn),        NULL,                                     editor
+#define DEFINE_FIELD_ENTITY2FN(type, field, flags, keyname, edname, fn, editor) , {propdata_t::ToFieldType<type* DataClass::*>(&DataClass::field),        PDF_ENTITY,  flags, keyname, edname, propdata_t::ToPFNFieldSet<DataClass, type*>(&DataClass::fn),               CEntityFactoryMap::IsEntityOfClass<type>, editor
 //#define DEFINE_FIELD_PARENTFN(field, flags, keyname, edname, fn, editor) , {(fieldtype)&DataClass::field, PDF_PARENT, flags, keyname, edname, fn, editor
 //#define DEFINE_FIELD_FLAGSFN(field, flags, keyname, edname, fn, editor)  , {(fieldtype)&DataClass::field, PDF_FLAGS,  flags, keyname, edname, fn, editor
 
-#define DEFINE_INPUT(method, keyname, edname, argtype) , {(input_func)&DataClass::method, argtype, PDFF_NOEDIT | PDFF_INPUT, keyname, edname, EDITOR_NONE
-#define DEFINE_OUTPUT(field, keyname, edname) , {(fieldtype)&DataClass::field, PDF_OUTPUT, PDFF_NOEDIT | PDFF_OUTPUT, keyname, edname, EDITOR_NONE
-#define DEFINE_MESSAGE(method, keyname, edname, argtype) , {(input_func)&DataClass::method, argtype, PDFF_NOEDIT | PDFF_MESSAGE, keyname, edname, EDITOR_NONE
+#define DEFINE_INPUT(method, keyname, edname, argtype)   , {propdata_t::ToInputFunc<void(DataClass::*)(inputdata_t*)>(&DataClass::method), argtype, PDFF_NOEDIT | PDFF_INPUT, keyname, edname, EDITOR_NONE
+#define DEFINE_OUTPUT(field, keyname, edname)            , {propdata_t::ToFieldType<output_t DataClass::*>(&DataClass::field), PDF_OUTPUT, PDFF_NOEDIT | PDFF_OUTPUT, keyname, edname, NULL, EDITOR_NONE
+#define DEFINE_MESSAGE(method, keyname, edname, argtype) , {propdata_t::ToInputFunc<void(DataClass::*)(inputdata_t*)>(&DataClass::method), argtype, PDFF_NOEDIT | PDFF_MESSAGE, keyname, edname, EDITOR_NONE
 
-#define DEFINE_FLAG(value, edname) , {(fieldtype)NULL, PDF_FLAG, value, NULL, edname, {PDE_FLAGS, NULL}}
+#define DEFINE_FLAG(value, edname)                       , {(fieldtype)NULL, PDF_FLAG, value, NULL, edname, NULL, EDITOR_FLAGS
 
 #endif
diff --git a/source/gcore/ShaderPreprocessor.cpp b/source/gcore/ShaderPreprocessor.cpp
index 61863f17688e7d8c3d0033d0531c5f973fcae2b7..262667af99cc37fbdc673dd1dbc16d04d0fd954c 100644
--- a/source/gcore/ShaderPreprocessor.cpp
+++ b/source/gcore/ShaderPreprocessor.cpp
@@ -201,7 +201,7 @@ void CShaderPreprocessor::addIncPath(const String &s)
 
 void CShaderPreprocessor::undefTemp()
 {
-	for(AssotiativeArray<String, _define>::Iterator i = m_mDefs.begin(); i; i++)
+	for(AssotiativeArray<String, _define>::Iterator i = m_mDefs.begin(); i; ++i)
 	{
 		if(i.second->isTemp)
 		{
@@ -1519,7 +1519,7 @@ String CShaderPreprocessor::stringify(const String &expr)
 
 void CShaderPreprocessor::reset()
 {
-	for(AssotiativeArray<String, _define>::Iterator i = m_mDefs.begin(); i; i++)
+	for(AssotiativeArray<String, _define>::Iterator i = m_mDefs.begin(); i; ++i)
 	{
 		i.second->isUndef = true;
 	}
@@ -1540,7 +1540,7 @@ void CShaderPreprocessor::clearIncludeCache()
 UINT CShaderPreprocessor::getIncludesCount()
 {
 	UINT u = 0;
-	for(AssotiativeArray<String, _include>::Iterator i = m_mIncludes.begin(); i; i++)
+	for(AssotiativeArray<String, _include>::Iterator i = m_mIncludes.begin(); i; ++i)
 	{
 		if(i.second->isUsed)
 		{
@@ -1552,7 +1552,7 @@ UINT CShaderPreprocessor::getIncludesCount()
 void CShaderPreprocessor::getIncludes(const char **pszIncludes)
 {
 	UINT u = 0;
-	for(AssotiativeArray<String, _include>::Iterator i = m_mIncludes.begin(); i; i++)
+	for(AssotiativeArray<String, _include>::Iterator i = m_mIncludes.begin(); i; ++i)
 	{
 		if(i.second->isUsed)
 		{
diff --git a/source/gcore/camera.cpp b/source/gcore/camera.cpp
index 41942cd6457054f475491180577ea923e108fe58..1e672aff24c9c76acddee5de9eca6f8294ddd865 100644
--- a/source/gcore/camera.cpp
+++ b/source/gcore/camera.cpp
@@ -11,9 +11,9 @@ CFrustum::CFrustum()
 	memset(m_isPointValid, 0, sizeof(m_isPointValid));
 }
 
-void CFrustum::update(const float4x4* view, const float4x4* proj)
+void CFrustum::update(const float4x4 &mView, const float4x4 &mProj)
 {
-	float4x4 matComb = SMMatrixMultiply(*view, *proj);
+	float4x4 matComb = mView *mProj;
 
 	// right (-x)
 	m_aFrustumPlanes[0].m_vNormal.x = matComb._14 - matComb._11;
@@ -74,11 +74,11 @@ void CFrustum::update(const SMPLANE *pPlanes, bool isNormalized)
 	memset(m_isPointValid, 0, sizeof(m_isPointValid));
 }
 
-bool CFrustum::pointInFrustum(const float3 *point) const
+bool CFrustum::pointInFrustum(const float3 &vPoint) const
 {
 	for(int i = 0; i < 6; ++i)
 	{
-		float tmp = SMVector3Dot(m_aFrustumPlanes[i].m_vNormal, *point) + m_aFrustumPlanes[i].m_fDistance;
+		float tmp = SMVector3Dot(m_aFrustumPlanes[i].m_vNormal, vPoint) + m_aFrustumPlanes[i].m_fDistance;
 		if(int(tmp * 1000.0f) <= 0)
 		{
 			return false;
@@ -87,22 +87,22 @@ bool CFrustum::pointInFrustum(const float3 *point) const
 	return true;
 }
 
-bool CFrustum::polyInFrustum(const float3* p1, const float3* p2, const float3* p3) const
+bool CFrustum::polyInFrustum(const float3 &p1, const float3 &p2, const float3 &p3) const
 {
 	/*if(pointInFrustum(p1) || pointInFrustum(p2) || pointInFrustum(p3))
 	return true;*/
 
 	for(int i = 0; i < 6; i++)
 	{
-		if(int((SMVector3Dot(m_aFrustumPlanes[i].m_vNormal, *p1) + m_aFrustumPlanes[i].m_fDistance) * 1000.f) > 0) continue;
-		if(int((SMVector3Dot(m_aFrustumPlanes[i].m_vNormal, *p2) + m_aFrustumPlanes[i].m_fDistance) * 1000.f) > 0) continue;
-		if(int((SMVector3Dot(m_aFrustumPlanes[i].m_vNormal, *p3) + m_aFrustumPlanes[i].m_fDistance) * 1000.f) > 0) continue;
+		if(int((SMVector3Dot(m_aFrustumPlanes[i].m_vNormal, p1) + m_aFrustumPlanes[i].m_fDistance) * 1000.f) > 0) continue;
+		if(int((SMVector3Dot(m_aFrustumPlanes[i].m_vNormal, p2) + m_aFrustumPlanes[i].m_fDistance) * 1000.f) > 0) continue;
+		if(int((SMVector3Dot(m_aFrustumPlanes[i].m_vNormal, p3) + m_aFrustumPlanes[i].m_fDistance) * 1000.f) > 0) continue;
 		return false;
 	}
 	return true;
 }
 
-bool CFrustum::polyInFrustumAbs(const float3* p1, const float3* p2, const float3* p3) const
+bool CFrustum::polyInFrustumAbs(const float3 &p1, const float3 &p2, const float3 &p3) const
 {
 	if(pointInFrustum(p1) && pointInFrustum(p2) && pointInFrustum(p3))
 		return true;
@@ -110,50 +110,170 @@ bool CFrustum::polyInFrustumAbs(const float3* p1, const float3* p2, const float3
 	return false;
 }
 
-bool CFrustum::sphereInFrustum(const float3 *point, float radius) const
+bool CFrustum::sphereInFrustum(const float3 &vPoint, float radius) const
 {
 	for(int i = 0; i<6; ++i)
 	{
-		if(SMVector3Dot(m_aFrustumPlanes[i].m_vNormal, *point) + m_aFrustumPlanes[i].m_fDistance <= -radius)
+		if(SMVector3Dot(m_aFrustumPlanes[i].m_vNormal, vPoint) + m_aFrustumPlanes[i].m_fDistance <= -radius)
 			return false;
 	}
 	return true;
 }
 
-bool CFrustum::sphereInFrustumAbs(const float3 *point, float radius) const
+bool CFrustum::sphereInFrustumAbs(const float3 &vPoint, float radius) const
 {
 	for(int i = 0; i<6; i++)
 	{
-		if(SMVector3Dot(m_aFrustumPlanes[i].m_vNormal, *point) + m_aFrustumPlanes[i].m_fDistance > -radius)
+		if(SMVector3Dot(m_aFrustumPlanes[i].m_vNormal, vPoint) + m_aFrustumPlanes[i].m_fDistance > -radius)
 			return false;
 	}
 	return true;
 }
 
-bool CFrustum::boxInFrustum(const SMAABB &aabb) const
+bool CFrustum::boxInFrustum(const SMAABB &aabb, bool *pIsStrict) const
 {
-	return(boxInFrustum(&aabb.vMin, &aabb.vMax));
+	return(boxInFrustum(aabb.vMin, aabb.vMax, pIsStrict));
 }
-
-bool CFrustum::boxInFrustum(const float3 *min, const float3 *max) const
+#if 0
+static bool BoxInFrustum1(const CFrustumPlane *m_aFrustumPlanes, const float3 &vMin, const float3 &vMax)
 {
 	for(register int p = 0; p < 6; ++p)
 	{
 		auto &plane = m_aFrustumPlanes[p];
-		if(SMVector3Dot(plane.m_vNormal, *min) + plane.m_fDistance > 0) continue;
-		if(SMVector3Dot(plane.m_vNormal, float3(min->x, min->y, max->z)) + plane.m_fDistance > 0) continue;
-		if(SMVector3Dot(plane.m_vNormal, float3(min->x, max->y, min->z)) + plane.m_fDistance > 0) continue;
-		if(SMVector3Dot(plane.m_vNormal, float3(min->x, max->y, max->z)) + plane.m_fDistance > 0) continue;
-
-		if(SMVector3Dot(plane.m_vNormal, float3(max->x, min->y, min->z)) + plane.m_fDistance > 0) continue;
-		if(SMVector3Dot(plane.m_vNormal, float3(max->x, min->y, max->z)) + plane.m_fDistance > 0) continue;
-		if(SMVector3Dot(plane.m_vNormal, float3(max->x, max->y, min->z)) + plane.m_fDistance > 0) continue;
-		if(SMVector3Dot(plane.m_vNormal, *max) + plane.m_fDistance > 0) continue;
+		if(SMVector3Dot(plane.m_vNormal, vMin) + plane.m_fDistance > 0) continue;
+		if(SMVector3Dot(plane.m_vNormal, float3(vMin.x, vMin.y, vMax.z)) + plane.m_fDistance > 0) continue;
+		if(SMVector3Dot(plane.m_vNormal, float3(vMin.x, vMax.y, vMin.z)) + plane.m_fDistance > 0) continue;
+		if(SMVector3Dot(plane.m_vNormal, float3(vMin.x, vMax.y, vMax.z)) + plane.m_fDistance > 0) continue;
+
+		if(SMVector3Dot(plane.m_vNormal, float3(vMax.x, vMin.y, vMin.z)) + plane.m_fDistance > 0) continue;
+		if(SMVector3Dot(plane.m_vNormal, float3(vMax.x, vMin.y, vMax.z)) + plane.m_fDistance > 0) continue;
+		if(SMVector3Dot(plane.m_vNormal, float3(vMax.x, vMax.y, vMin.z)) + plane.m_fDistance > 0) continue;
+		if(SMVector3Dot(plane.m_vNormal, vMax) + plane.m_fDistance > 0) continue;
+		return(false);
+	}
+	return(true);
+}
+static bool BoxInFrustum2(const CFrustumPlane *m_aFrustumPlanes, const float3 &vMin, const float3 &vMax)
+{
+	bool inside = true;
+
+
+	for(int p = 0; p < 6; ++p)
+	{
+		auto &plane = m_aFrustumPlanes[p];
+		//находим ближайшую к плоскости вершину
+		//проверяем, если она находится за плоскостью, то объект вне врустума
+		float d = SMVector3Sum(SMVectorMax(vMin * plane.m_vNormal, vMax * plane.m_vNormal)) + plane.m_fDistance;
+
+		float d2 = max(vMin.x * plane.m_vNormal.x, vMax.x * plane.m_vNormal.x)
+			+ max(vMin.y * plane.m_vNormal.y, vMax.y * plane.m_vNormal.y)
+			+ max(vMin.z * plane.m_vNormal.z, vMax.z * plane.m_vNormal.z)
+			+ plane.m_fDistance;
+		inside &= d > 0;
+
+		/*if(d < 0.0f)
+		{
+		return(false);
+		}*/
+	}
+	return inside;
+}
+
+
+bool CFrustum::boxInFrustum(const float3 &vMin, const float3 &vMax, bool isStrict) const
+{
+	if(isStrict)
+	{
+		return(false);
+	}
+
+	bool b0 = BoxInFrustum1(m_aFrustumPlanes, vMin, vMax);
+	//bool b1 = BoxInFrustum2(m_aFrustumPlanes, vMin, vMax);
+
+	//assert(b0 == b1);
+
+	//return(b0);
+
+	/*for(register int p = 0; p < 6; ++p)
+	{
+		auto &plane = m_aFrustumPlanes[p];
+		if(SMVector3Dot(plane.m_vNormal, vMin) + plane.m_fDistance > 0) continue;
+		if(SMVector3Dot(plane.m_vNormal, float3(vMin.x, vMin.y, vMax.z)) + plane.m_fDistance > 0) continue;
+		if(SMVector3Dot(plane.m_vNormal, float3(vMin.x, vMax.y, vMin.z)) + plane.m_fDistance > 0) continue;
+		if(SMVector3Dot(plane.m_vNormal, float3(vMin.x, vMax.y, vMax.z)) + plane.m_fDistance > 0) continue;
+
+		if(SMVector3Dot(plane.m_vNormal, float3(vMax.x, vMin.y, vMin.z)) + plane.m_fDistance > 0) continue;
+		if(SMVector3Dot(plane.m_vNormal, float3(vMax.x, vMin.y, vMax.z)) + plane.m_fDistance > 0) continue;
+		if(SMVector3Dot(plane.m_vNormal, float3(vMax.x, vMax.y, vMin.z)) + plane.m_fDistance > 0) continue;
+		if(SMVector3Dot(plane.m_vNormal, vMax) + plane.m_fDistance > 0) continue;
 		return(false);
 	}
+	return(true);*/
+	auto fn = isStrict ? SMVectorMin : SMVectorMax;
+
+	//bool inside = true;
+
+
+	for(int p = 0; p < 6; ++p)
+	{
+		auto &plane = m_aFrustumPlanes[p];
+		//находим ближайшую к плоскости вершину
+		//проверяем, если она находится за плоскостью, то объект вне врустума
+		float d = SMVector3Sum(fn(vMin * plane.m_vNormal, vMax * plane.m_vNormal)) + plane.m_fDistance;
+
+		float d2 = max(vMin.x * plane.m_vNormal.x, vMax.x * plane.m_vNormal.x)
+			+ max(vMin.y * plane.m_vNormal.y, vMax.y * plane.m_vNormal.y)
+			+ max(vMin.z * plane.m_vNormal.z, vMax.z * plane.m_vNormal.z)
+			+ plane.m_fDistance;
+		//inside &= d > 0;
+
+		if(d <= 0.0f)
+		{
+			assert(!b0);
+			return(false);
+		}
+	}
+	//assert(inside == b0);
+	//return inside;
 
+	assert(b0);
 	return(true);
 }
+#endif
+bool CFrustum::boxInFrustum(const float3 &vMin, const float3 &vMax, bool *pIsStrict) const
+{
+	bool isVisible = true;
+	bool isVisibleStrict = true;
+
+	for(int p = 0; p < 6; ++p)
+	{
+		auto &plane = m_aFrustumPlanes[p];
+		//находим ближайшую к плоскости вершину
+		//проверяем, если она находится за плоскостью, то объект вне врустума
+		float3 vTmpMin = vMin * plane.m_vNormal;
+		float3 vTmpMax = vMax * plane.m_vNormal;
+		float d = SMVector3Sum(SMVectorMax(vTmpMin, vTmpMax)) + plane.m_fDistance;
+
+		isVisible &= d > 0.0f;
+
+		if(isVisible && pIsStrict)
+		{
+			d = SMVector3Sum(SMVectorMin(vTmpMin, vTmpMax)) + plane.m_fDistance;
+			isVisibleStrict &= d > 0.0f;
+		}
+	}
+
+	//bool b0 = BoxInFrustum1(m_aFrustumPlanes, vMin, vMax);
+
+	//assert(isVisible == b0);
+
+	if(pIsStrict)
+	{
+		*pIsStrict = isVisible && isVisibleStrict;
+	}
+
+	return(isVisible);
+}
 
 bool CFrustum::frustumInFrustum(const IFrustum *pOther) const
 {
@@ -413,7 +533,7 @@ float CCamera::getFOV() const
 
 void CCamera::updateFrustum(const float4x4 *pProjection)
 {
-	m_oFrustum.update(&m_mView, pProjection);
+	m_oFrustum.update(m_mView, *pProjection);
 }
 
 const IFrustum* CCamera::getFrustum()
diff --git a/source/gcore/camera.h b/source/gcore/camera.h
index 50f1905f42a4e880758b46938bbc515755014036..11d234ca449a676c374bba37d2d5448e1c155b04 100644
--- a/source/gcore/camera.h
+++ b/source/gcore/camera.h
@@ -17,18 +17,18 @@ public:
 
 	CFrustum();
 
-	void update(const float4x4 *pView, const float4x4 *pProj) override;
+	void update(const float4x4 &mView, const float4x4 &mProj) override;
 	void update(const SMPLANE *pPlanes, bool isNormalized = false) override;
 
-	bool pointInFrustum(const float3 *pPoint) const override;
-	bool polyInFrustum(const float3 *pPount1, const float3 *pPount2, const float3 *pPount3) const override;
-	bool polyInFrustumAbs(const float3 *pPount1, const float3 *pPount2, const float3 *pPount3) const override;
+	bool pointInFrustum(const float3 &vPoint) const override;
+	bool polyInFrustum(const float3 &vPount1, const float3 &vPount2, const float3 &vPount3) const override;
+	bool polyInFrustumAbs(const float3 &vPount1, const float3 &vPount2, const float3 &vPount3) const override;
 
-	bool sphereInFrustum(const float3 *pPount, float fRadius) const override;
+	bool sphereInFrustum(const float3 &vPount, float fRadius) const override;
 
-	bool sphereInFrustumAbs(const float3 *pPount, float fRadius) const override;
-	bool boxInFrustum(const float3 *pMin, const float3 *pMax) const override;
-	bool boxInFrustum(const SMAABB &aabb) const override;
+	bool sphereInFrustumAbs(const float3 &vPount, float fRadius) const override;
+	bool boxInFrustum(const float3 &vMin, const float3 &vMax, bool *pIsStrict = NULL) const override;
+	bool boxInFrustum(const SMAABB &aabb, bool *pIsStrict = NULL) const override;
 
 	bool frustumInFrustum(const IFrustum *pOther) const override;
 
diff --git a/source/gui/DOM.cpp b/source/gui/DOM.cpp
index b9b9e4c6785767da28988cdb828288fc9c523380..df3411f6c730b828e157eeb66fc65680f30d6a8a 100644
--- a/source/gui/DOM.cpp
+++ b/source/gui/DOM.cpp
@@ -80,7 +80,7 @@ namespace gui
 				wprintf(L"  ");
 			}
 			wprintf(L"<%s", CDOMnode::getNodeNameById(m_iNodeId).c_str());
-			for(AssotiativeArray<StringW, StringW>::Iterator i = m_mAttributes.begin(); i; i++)
+			for(AssotiativeArray<StringW, StringW>::Iterator i = m_mAttributes.begin(); i; ++i)
 			{
 				wprintf(L" %s=\"%s\"", i.first->c_str(), i.second->c_str());
 			}
diff --git a/source/gui/ICSS.cpp b/source/gui/ICSS.cpp
index d2a6be6822bc1aec220ef574c038a38b53a15bea..2bff4f3ff3097efcc72ebb7f6085b5f1d9aa3b99 100644
--- a/source/gui/ICSS.cpp
+++ b/source/gui/ICSS.cpp
@@ -300,7 +300,7 @@ namespace gui
 #ifdef _DEBUG
 		void ICSS::debugDumpStyles()
 		{
-			for(AssotiativeArray<StringW, ICSSstyleSet>::Iterator i = m_styleSets.begin(); i; i++)
+			for(AssotiativeArray<StringW, ICSSstyleSet>::Iterator i = m_styleSets.begin(); i; ++i)
 			{
 				wprintf(L"########################################\n");
 				wprintf(L"%s\n", i.first->c_str());
@@ -4072,7 +4072,7 @@ namespace gui
 
 		/*void ICSS::ReloadAllStyles()
 		{
-			for(AssotiativeArray<StringW, ICSSstyleSet>::Iterator i = m_styleSets.begin(); i; i++)
+			for(AssotiativeArray<StringW, ICSSstyleSet>::Iterator i = m_styleSets.begin(); i; ++i)
 			{
 				ICSSstyleSet css(this);
 				css.LoadFromString(*i.first);
diff --git a/source/gui/IEvent.h b/source/gui/IEvent.h
index 12df53be63c4736901cdf8c28329533ac4767042..917c8788386ededfdc1faf286cadf46aac633055 100644
--- a/source/gui/IEvent.h
+++ b/source/gui/IEvent.h
@@ -56,6 +56,8 @@ namespace gui
 		int clientY = 0;
 		int offsetX = 0;
 		int offsetY = 0;
+
+		void *pCallbackData = NULL;
 	};
 };
 
diff --git a/source/gui/IHTMLparser.cpp b/source/gui/IHTMLparser.cpp
index e958e1885d110ba8c859dbe14a9b38f76b5a69a7..4ca54eaf9441f48f33a3a2ca0cc179334d81d0a9 100644
--- a/source/gui/IHTMLparser.cpp
+++ b/source/gui/IHTMLparser.cpp
@@ -471,7 +471,7 @@ namespace gui
 													}
 												}
 											}
-											for(AssotiativeArray<StringW, StringW>::Iterator j = attrs.begin(); j; j++)
+											for(AssotiativeArray<StringW, StringW>::Iterator j = attrs.begin(); j; ++j)
 											{
 												pCur->setAttribute(j.first, j.second);
 											}
diff --git a/source/gui/SXGUI.cpp b/source/gui/SXGUI.cpp
index 8686748e61f78a9e25d4fb00bd43ed27d8b6c312..a30120cf908058a74acfa266d7a5ecf78ac15197 100644
--- a/source/gui/SXGUI.cpp
+++ b/source/gui/SXGUI.cpp
@@ -609,20 +609,20 @@ namespace gui
 		return(NULL);
 	}
 
-	void CDesktopStack::registerCallback(const char *cbName, GUI_CALLBACK cb)
+	void CDesktopStack::registerCallback(const char *cbName, GUI_CALLBACK cb, void *pUserData)
 	{
 		if(cb)
 		{
 			//mCallbacks[StringW(String(cbName))].push_back(cb);
-			m_mCallbacks[StringW(String(cbName))] = cb;
+			m_mCallbacks[StringW(String(cbName))] = {cb, pUserData};
 		}
 	}
 
-	void CDesktopStack::registerCallbackDefault(GUI_CALLBACK_WC cb, BOOL greedy)
+	void CDesktopStack::registerCallbackDefault(GUI_CALLBACK_WC cb, BOOL greedy, void *pUserData)
 	{
 		if(cb)
 		{
-			(greedy ? m_mCallbacksDefaults : m_mCallbacksDefaultsWC).push_back(cb);
+			(greedy ? m_mCallbacksDefaults : m_mCallbacksDefaultsWC).push_back({cb, pUserData});
 		}
 	}
 
@@ -671,21 +671,40 @@ namespace gui
 		return(m_pDevice);
 	}
 
-	GUI_CALLBACK CDesktopStack::getCallbackByName(const char *_cbName)
+	GUI_CALLBACK CDesktopStack::getCallbackByName(const char *cbName)
 	{
-		StringW cbName = String(_cbName);
-		if(m_mCallbacks.KeyExists(cbName))
+		SimpleCallback *pCB = getFullCallbackByName(cbName);
+		if(pCB)
 		{
-			return(m_mCallbacks[cbName]);
+			return(pCB->fn);
 		}
 		return(NULL);
 	}
 
 	GUI_CALLBACK CDesktopStack::getCallbackByName(const StringW &cbName)
+	{
+		SimpleCallback *pCB = getFullCallbackByName(cbName);
+		if(pCB)
+		{
+			return(pCB->fn);
+		}
+		return(NULL);
+	}
+
+	CDesktopStack::SimpleCallback* CDesktopStack::getFullCallbackByName(const char *_cbName)
+	{
+		StringW cbName = String(_cbName);
+		if(m_mCallbacks.KeyExists(cbName))
+		{
+			return(&m_mCallbacks[cbName]);
+		}
+		return(NULL);
+	}
+	CDesktopStack::SimpleCallback* CDesktopStack::getFullCallbackByName(const StringW &cbName)
 	{
 		if(m_mCallbacks.KeyExists(cbName))
 		{
-			return(m_mCallbacks[cbName]);
+			return(&m_mCallbacks[cbName]);
 		}
 		return(NULL);
 	}
@@ -705,7 +724,7 @@ namespace gui
 		{
 			return;
 		}
-		for(AssotiativeArray<StringW, IDesktop*>::Iterator i = m_mDesktops.begin(); i; i++)
+		for(AssotiativeArray<StringW, IDesktop*>::Iterator i = m_mDesktops.begin(); i; ++i)
 		{
 			if(dp == *(i.second))
 			{
@@ -804,21 +823,28 @@ namespace gui
 
 	void CDesktopStack::execCallback(const StringW &cmd, IEvent *ev)
 	{
-		GUI_CALLBACK cb = getCallbackByName(cmd);
+		DefaultCallback *pDefCB;
+		SimpleCallback *cb = getFullCallbackByName(cmd);
 		if(cb)
 		{
-			cb(ev);
+			ev->pCallbackData = cb->pUserData;
+			cb->fn(ev);
 		}
 		else
 		{
+			
 			for(UINT i = 0, l = m_mCallbacksDefaults.size(); i < l; ++i)
 			{
-				m_mCallbacksDefaults[i](cmd.c_str(), ev);
+				pDefCB = &m_mCallbacksDefaults[i];
+				ev->pCallbackData = pDefCB->pUserData;
+				pDefCB->fn(cmd.c_str(), ev);
 			}
 		}
 		for(UINT i = 0, l = m_mCallbacksDefaultsWC.size(); i < l; ++i)
 		{
-			m_mCallbacksDefaultsWC[i](cmd.c_str(), ev);
+			pDefCB = &m_mCallbacksDefaultsWC[i];
+			ev->pCallbackData = pDefCB->pUserData;
+			pDefCB->fn(cmd.c_str(), ev);
 		}
 	}
 
diff --git a/source/gui/SXGUI.h b/source/gui/SXGUI.h
index a870f6a2022b7f01768d69980bd523d4afb66eff..a316d3e9c4a99502a552ecb77365aa119288fae5 100644
--- a/source/gui/SXGUI.h
+++ b/source/gui/SXGUI.h
@@ -49,9 +49,9 @@ namespace gui
 		IDesktop* getDesktopA(const char *name) override;
 		IDesktop* getDesktopW(const WCHAR *name) override;
 
-		void registerCallback(const char *cbName, GUI_CALLBACK cb) override;
+		void registerCallback(const char *cbName, GUI_CALLBACK cb, void *pUserData = NULL) override;
 
-		void registerCallbackDefault(GUI_CALLBACK_WC cb, BOOL greedy = FALSE) override;
+		void registerCallbackDefault(GUI_CALLBACK_WC cb, BOOL greedy = FALSE, void *pUserData = NULL) override;
 
 		GUI_CALLBACK getCallbackByName(const char *cbName) override;
 		GUI_CALLBACK getCallbackByName(const StringW &cbName) override;
@@ -143,9 +143,21 @@ namespace gui
 		bool m_bShowCursor = true;
 		AssotiativeArray<StringW, IDesktop*> m_mDesktops;
 
-		AssotiativeArray<StringW, /* Array< */GUI_CALLBACK/* > */> m_mCallbacks;
-		Array<GUI_CALLBACK_WC> m_mCallbacksDefaults;
-		Array<GUI_CALLBACK_WC> m_mCallbacksDefaultsWC;
+		struct SimpleCallback
+		{
+			GUI_CALLBACK fn;
+			void *pUserData;
+		};
+
+		struct DefaultCallback
+		{
+			GUI_CALLBACK_WC fn;
+			void *pUserData;
+		};
+
+		AssotiativeArray<StringW, /* Array< */SimpleCallback/* > */> m_mCallbacks;
+		Array<DefaultCallback> m_mCallbacksDefaults;
+		Array<DefaultCallback> m_mCallbacksDefaultsWC;
 		
 		IGXVertexBuffer *m_pQuadVerticesXYZ;
 		IGXVertexBuffer *m_pQuadVerticesXYZTex16;
@@ -154,6 +166,11 @@ namespace gui
 		IGXRenderBuffer *m_pQuadRenderXYZTex16;
 
 		IGXConstantBuffer *m_pVSTransformConstant = NULL;
+
+
+
+		SimpleCallback* getFullCallbackByName(const char *cbName);
+		SimpleCallback* getFullCallbackByName(const StringW &cbName);
 	};
 
 	class CGUI: public IXUnknownImplementation<IGUI>
diff --git a/source/gui/Texture.cpp b/source/gui/Texture.cpp
index 9be1d734ce23ae997fb6da28f3a4e06a6df39e7a..120e59fe9e5ad562367473c6a04879df12ecbd2c 100644
--- a/source/gui/Texture.cpp
+++ b/source/gui/Texture.cpp
@@ -58,7 +58,7 @@ namespace gui
 
 	void CTextureManager::release()
 	{
-		for(AssotiativeArray<StringW, CTexture*>::Iterator i = m_mTextures.begin(); i != m_mTextures.end(); i++)
+		for(AssotiativeArray<StringW, CTexture*>::Iterator i = m_mTextures.begin(); i; ++i)
 		{
 			(*i.second)->release();
 			mem_delete(*i.second);
diff --git a/source/gui/guimain.h b/source/gui/guimain.h
index 133979f18ac01835245292199e87a1a9e6c7a2fe..71c483f10013ea2839ff203035e48594e4be3f9d 100644
--- a/source/gui/guimain.h
+++ b/source/gui/guimain.h
@@ -65,10 +65,10 @@ namespace gui
 		virtual IDesktop* getDesktopW(const WCHAR *name) = 0;
 
 		//Регистрация обработчика
-		virtual void registerCallback(const char *cbName, GUI_CALLBACK cb) = 0;
+		virtual void registerCallback(const char *cbName, GUI_CALLBACK cb, void *pUserData = NULL) = 0;
 
 		//Регистрация обработчика по умолчанию
-		virtual void registerCallbackDefault(GUI_CALLBACK_WC cb, BOOL greedy = FALSE) = 0;
+		virtual void registerCallbackDefault(GUI_CALLBACK_WC cb, BOOL greedy = FALSE, void *pUserData = NULL) = 0;
 
 		virtual GUI_CALLBACK getCallbackByName(const char *cbName) = 0;
 		virtual GUI_CALLBACK getCallbackByName(const StringW &cbName) = 0;
diff --git a/source/light/light.cpp b/source/light/light.cpp
index 77ad6c2bfb2d3da850a6a252fe6d4feac77c17df..e85a7715a234d5f0ffd32b650fa4571e647e1e6b 100644
--- a/source/light/light.cpp
+++ b/source/light/light.cpp
@@ -188,7 +188,7 @@ void CXLight::updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, cons
 {
 	updateFrustum();
 
-	if(useLPV && m_pFrustum->boxInFrustum(&vLPVmin, &vLPVmax))
+	if(useLPV && m_pFrustum->boxInFrustum(vLPVmin, vLPVmax))
 	{
 		m_renderType |= LRT_LPV;
 	}
@@ -254,7 +254,7 @@ void CXLightPoint::updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin,
 	m_renderType = LRT_NONE;
 	
 	float3 vOrigin = getPosition();
-	if(pMainCamera->getFrustum()->sphereInFrustum(&vOrigin, getMaxDistance()))
+	if(pMainCamera->getFrustum()->sphereInFrustum(vOrigin, getMaxDistance()))
 	{
 		m_renderType |= LRT_LIGHT;
 	}
@@ -475,11 +475,11 @@ void CXLightSun::updateFrustum()
 
 			if(i == 0)
 			{
-				m_pFrustum->update(&split.mView, &split.mProj);
+				m_pFrustum->update(split.mView, split.mProj);
 			}
 			else
 			{
-				m_pPSSMFrustum[i - 1]->update(&split.mView, &split.mProj);
+				m_pPSSMFrustum[i - 1]->update(split.mView, split.mProj);
 			}
 		}
 	}
@@ -525,7 +525,7 @@ void CXLightSun::updateFrustum()
 		m_mReflectiveProj = SMMatrixOrthographicLH(fGridRadius * 2.0f, fGridRadius * 2.0f, PSSM_LIGHT_NEAR, fMaxDistance);
 		m_mReflectiveView = SMMatrixLookToLH(vGridCenter - vLightDir * (fMaxDistance /*- fRadius * 2*/ * 0.5f), vLightDir, vUpDir);
 
-		m_pReflectiveFrustum->update(&m_mReflectiveView, &m_mReflectiveProj);
+		m_pReflectiveFrustum->update(m_mReflectiveView, m_mReflectiveProj);
 	}
 }
 
@@ -642,7 +642,7 @@ void CXLightSpot::updateFrustum()
 	SMMATRIX mView = SMMatrixLookAtLH(vPos, vPos + vDir, vUp);
 	SMMATRIX mProj = SMMatrixPerspectiveFovLH(getOuterAngle(), 1.0f, 0.025f, getMaxDistance());
 
-	m_pFrustum->update(&mView, &mProj);
+	m_pFrustum->update(mView, mProj);
 }
 
 void CXLightSpot::updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax, bool useLPV)
diff --git a/source/mtrl/MaterialSystem.cpp b/source/mtrl/MaterialSystem.cpp
index 8962829c673645f8a0a67f11411b9fad95ab216f..bb331b79367497dc02197dbca3805c9e70dc32c5 100644
--- a/source/mtrl/MaterialSystem.cpp
+++ b/source/mtrl/MaterialSystem.cpp
@@ -141,7 +141,7 @@ bool CMaterialSystem::loadMaterial(const char *szName, CMaterial *pMaterial)
 
 void XMETHODCALLTYPE CMaterialSystem::reloadAll()
 {
-	for(AssotiativeArray<String, CMaterial*>::Iterator i = m_mapMaterials.begin(); i; i++)
+	for(AssotiativeArray<String, CMaterial*>::Iterator i = m_mapMaterials.begin(); i; ++i)
 	{
 		if(*(i.second))
 		{
@@ -1076,7 +1076,7 @@ static void ParseTexturesConstants(Array<CMaterialSystem::MaterialProperty> &aPr
 
 void CMaterialSystem::updateReferences()
 {
-	for(AssotiativeArray<String, VertexFormatData>::Iterator i = m_mVertexFormats.begin(); i; i++)
+	for(AssotiativeArray<String, VertexFormatData>::Iterator i = m_mVertexFormats.begin(); i; ++i)
 	{
 		VertexFormatData *pFormat = i.second;
 		for(UINT j = 0, jl = m_aGeometryShaders.size(); j < jl; ++j)
@@ -1144,7 +1144,7 @@ void CMaterialSystem::updateReferences()
 		}
 	}
 
-	for(AssotiativeArray<String, MaterialShader>::Iterator i = m_mMaterialShaders.begin(); i; i++)
+	for(AssotiativeArray<String, MaterialShader>::Iterator i = m_mMaterialShaders.begin(); i; ++i)
 	{
 		MaterialShader *pShader = i.second;
 		
@@ -1431,7 +1431,7 @@ void CMaterialSystem::updateReferences()
 #if 1
 	if(m_isMateriallesRenderPassDirty)
 	{
-		for(AssotiativeArray<String, RenderPass>::Iterator i = m_mRenderPasses.begin(); i; i++)
+		for(AssotiativeArray<String, RenderPass>::Iterator i = m_mRenderPasses.begin(); i; ++i)
 		{
 			RenderPass *pRP = i.second;
 			if(!pRP->bSkipMaterialShader)
@@ -1444,7 +1444,7 @@ void CMaterialSystem::updateReferences()
 
 			UINT uOldSize = aVariantDefines.size();
 
-			for(AssotiativeArray<String, VertexFormatData>::Iterator i = m_mVertexFormats.begin(); i; i++)
+			for(AssotiativeArray<String, VertexFormatData>::Iterator i = m_mVertexFormats.begin(); i; ++i)
 			{
 				VertexFormatData *pFormat = i.second;
 				pRP->aPassFormats[pFormat->uID].aPassVariants.clearFast();
@@ -1489,7 +1489,7 @@ void CMaterialSystem::updateReferences()
 
 void CMaterialSystem::cleanData() 
 {
-	for(AssotiativeArray<String, VertexFormatData>::Iterator i = m_mVertexFormats.begin(); i; i++)
+	for(AssotiativeArray<String, VertexFormatData>::Iterator i = m_mVertexFormats.begin(); i; ++i)
 	{
 		for(UINT j = 0, jl = i.second->aDecl.size(); j < jl; ++j)
 		{
@@ -1535,7 +1535,7 @@ void CMaterialSystem::cleanData()
 		m_poolGSdata.Delete(m_aGeometryShaders[i]);
 	}
 
-	for(AssotiativeArray<String, RenderPass>::Iterator i = m_mRenderPasses.begin(); i; i++)
+	for(AssotiativeArray<String, RenderPass>::Iterator i = m_mRenderPasses.begin(); i; ++i)
 	{
 		RenderPass *pPass = i.second;
 		free((void*)pPass->szName);
@@ -1576,7 +1576,7 @@ void CMaterialSystem::cleanData()
 		}
 	}
 
-	for(AssotiativeArray<String, MaterialShader>::Iterator i = m_mMaterialShaders.begin(); i; i++)
+	for(AssotiativeArray<String, MaterialShader>::Iterator i = m_mMaterialShaders.begin(); i; ++i)
 	{
 		MaterialShader *pShader = i.second;
 		free((void*)pShader->szName);
@@ -1686,7 +1686,7 @@ void CMaterialSystem::onGlobalFlagChanged(CGlobalFlag *pFlag)
 	Array<XMaterialShaderHandler*> aShaders;
 	const char *szFlag = pFlag->getName();
 
-	for(AssotiativeArray<String, MaterialShader>::Iterator i = m_mMaterialShaders.begin(); i; i++)
+	for(AssotiativeArray<String, MaterialShader>::Iterator i = m_mMaterialShaders.begin(); i; ++i)
 	{
 		bool isFound = false;
 		for(UINT j = 0, jl = i.second->aProperties.size(); j < jl; ++j)
@@ -1722,7 +1722,7 @@ void CMaterialSystem::onGlobalFlagChanged(CGlobalFlag *pFlag)
 
 	if(aShaders.size())
 	{
-		for(AssotiativeArray<String, CMaterial*>::Iterator i = m_mapMaterials.begin(); i; i)
+		for(AssotiativeArray<String, CMaterial*>::Iterator i = m_mapMaterials.begin(); i; ++i)
 		{
 			if(aShaders.indexOf((*i.second)->getShaderHandler()) >= 0)
 			{
@@ -2222,7 +2222,7 @@ CMaterial::~CMaterial()
 		mem_delete_a(m_aPassCache[i].pConstantsBlob);
 	}
 
-	for(AssotiativeArray<AAString, MaterialTexture>::Iterator i = m_mapTextures.begin(); i; i++)
+	for(AssotiativeArray<AAString, MaterialTexture>::Iterator i = m_mapTextures.begin(); i; ++i)
 	{
 		mem_release(i.second->pTexture);
 	}
@@ -2643,7 +2643,7 @@ void CMaterial::preparePass(UINT uPass)
 
 void CMaterial::clearStaticFlags()
 {
-	for(AssotiativeArray<AAString, CMaterialFlag>::Iterator i = m_mapFlags.begin(); i; i++)
+	for(AssotiativeArray<AAString, CMaterialFlag>::Iterator i = m_mapFlags.begin(); i; ++i)
 	{
 		i.second->clearStatic();
 	}
diff --git a/source/render/OcclusionCuller.cpp b/source/render/OcclusionCuller.cpp
index 83d9bee17cdada9f46bf6b9c23c975ba57fa3bc5..e4123b229061a3684c639edf38a71ed0b386a803 100644
--- a/source/render/OcclusionCuller.cpp
+++ b/source/render/OcclusionCuller.cpp
@@ -81,7 +81,7 @@ bool XMETHODCALLTYPE COcclusionCuller::isSphereVisible(const float3 &vOrigin, fl
 	return(true);
 }
 
-bool XMETHODCALLTYPE COcclusionCuller::isAABBvisible(const float3 &vMin, const float3 &vMax) const
+bool XMETHODCALLTYPE COcclusionCuller::isAABBvisible(const SMAABB &aabb) const
 {
 	return(true);
 }
diff --git a/source/render/OcclusionCuller.h b/source/render/OcclusionCuller.h
index 327e147fa61abf98b5cb7629b5db426442a783f2..c521bb09888ad0efff3bf7bf9e29d7393708ebf0 100644
--- a/source/render/OcclusionCuller.h
+++ b/source/render/OcclusionCuller.h
@@ -7,7 +7,7 @@
 #define OCCLUSION_BUFFER_WIDTH 16
 #define OCCLUSION_BUFFER_HEIGHT 16
 
-class COcclusionCuller: public IXUnknownImplementation<IXOcclusionCuller>
+class COcclusionCuller final: public IXUnknownImplementation<IXOcclusionCuller>
 {
 public:
 	COcclusionCuller();
@@ -21,7 +21,7 @@ public:
 
 	bool XMETHODCALLTYPE isSphereVisible(const float3 &vOrigin, float fRadius) const override;
 
-	bool XMETHODCALLTYPE isAABBvisible(const float3 &vMin, const float3 &vMax) const override;
+	bool XMETHODCALLTYPE isAABBvisible(const SMAABB &aabb) const override;
 
 protected:
 	ICamera *m_pCamera = NULL;
diff --git a/source/render/RenderPipeline.cpp b/source/render/RenderPipeline.cpp
index e6b8369b6ffc4c939af7cd2360087cf0fcc357c3..16f67b33b154e589aa280d35c5e7ddafea0d8d79 100644
--- a/source/render/RenderPipeline.cpp
+++ b/source/render/RenderPipeline.cpp
@@ -1,4 +1,4 @@
-#include "RenderPipeline.h"
+#include "RenderPipeline.h"
 
 #include <core/sxcore.h>
 
diff --git a/source/render/Scene.cpp b/source/render/Scene.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..808f88d675ffea7ba771bb74fd108f70106aa7ac
--- /dev/null
+++ b/source/render/Scene.cpp
@@ -0,0 +1,1184 @@
+#include "Scene.h"
+#include <xcommon/resource/IXModel.h>
+#include <xcommon/resource/IXResourceManager.h>
+#include <xcommon/resource/IXModelProvider.h>
+#include <xcommon/IPluginManager.h>
+#include <xcommon/render/IXOcclusionCuller.h>
+#include <queue>
+
+CSceneObject::CSceneObject(CScene *pScene, const SMAABB &aabb, void *pUserData, NodeType bmType, NodeFeature bmFeatures):
+	m_pScene(pScene),
+	m_aabb(aabb),
+	m_pUserData(pUserData),
+	m_bmFeatures(bmFeatures),
+	m_bmType(bmType)
+{
+}
+CSceneObject::~CSceneObject()
+{
+	m_pScene->removeObject(this);
+}
+
+void XMETHODCALLTYPE CSceneObject::update(const SMAABB &aabb)
+{
+	m_pScene->enqueueObjectUpdate(this, aabb);
+}
+
+void CSceneObject::setNode(CSceneNode *pNode)
+{
+	m_pNode = pNode;
+}
+CSceneNode* CSceneObject::getNode()
+{
+	return(m_pNode);
+}
+
+const SMAABB& CSceneObject::getAABB() const
+{
+	return(m_aabb);
+}
+
+void* CSceneObject::getUserData() const
+{
+	return(m_pUserData);
+}
+
+void XMETHODCALLTYPE CSceneObject::FinalRelease()
+{
+	m_pScene->enqueueObjectDelete(this);
+}
+
+void CSceneObject::updateFeatures(NodeFeature bmFeatures)
+{
+	m_pScene->enqueueObjectUpdateFeatures(this, bmFeatures);
+}
+
+void XMETHODCALLTYPE CSceneObject::setFeature(IXSceneFeature *pFeat, bool isSet)
+{
+	CSceneFeature *pFeature = (CSceneFeature*)pFeat;
+	NodeFeature bmFeatures = m_bmFeatures;
+
+	if(isSet)
+	{
+		bmFeatures |= pFeature->getFeature();
+	}
+	else
+	{
+		bmFeatures &= ~pFeature->getFeature();
+	}
+
+	updateFeatures(bmFeatures);
+}
+
+void XMETHODCALLTYPE CSceneObject::setFeatures(IXSceneFeature **ppFeatures)
+{
+	/*CSceneFeature *pFeature = (CSceneFeature*)pFeat;*/
+	NodeFeature bmFeatures = 0;
+
+	while(*ppFeatures)
+	{
+		bmFeatures |= ((CSceneFeature*)(*ppFeatures))->getFeature();
+		++ppFeatures;
+	}
+
+	updateFeatures(bmFeatures);
+}
+
+//##########################################################################
+
+CSceneQuery::CSceneQuery(CScene *pScene, CSceneObjectType *pObjectType):
+	m_pScene(pScene),
+	m_bmType(pObjectType->getType())
+{
+}
+CSceneQuery::~CSceneQuery()
+{
+}
+
+UINT XMETHODCALLTYPE CSceneQuery::execute(const IFrustum *pFrustum, void ***pppObjects, IXOcclusionCuller *pOcclusionCuller)
+{
+	m_aQueryResponse.clearFast();
+
+	queryObjectsInternal(m_pScene->m_pRootNode, pFrustum);
+
+	if(m_aQueryResponse.size())
+	{
+		*pppObjects = &m_aQueryResponse[0];
+	}
+	else
+	{
+		*pppObjects = NULL;
+	}
+	return(m_aQueryResponse.size());
+}
+
+void CSceneQuery::queryObjectsInternal(CSceneNode *pNode, const IFrustum *pFrustum, bool isFullyVisible, IXOcclusionCuller *pOcclusionCuller)
+{
+	if(!pNode)
+	{
+		return;
+	}
+
+	if((pNode->getTypes() & m_bmType) == 0 || !testFeatures(pNode->getFeatures(), false))
+	{
+		return;
+	}
+
+	/*if(!isFullyVisible)
+	{
+		if(!pFrustum->boxInFrustum(pNode->getAABB()))
+		{
+			return;
+		}
+
+		isFullyVisible = pFrustum->boxInFrustum(pNode->getAABB(), true);
+	}*/
+	if(!isFullyVisible && (!pFrustum->boxInFrustum(pNode->getAABB(), &isFullyVisible) || !(!pOcclusionCuller || pOcclusionCuller->isAABBvisible(pNode->getAABB()))))
+	{
+		return;
+	}
+
+	for(UINT i = 0; i < BVH_CHILD_COUNT; ++i)
+	{
+		queryObjectsInternal(pNode->getChild(i, false), pFrustum, isFullyVisible);
+	}
+
+	auto &aObjects = pNode->getObjects();
+	for(UINT i = 0, l = aObjects.size(); i < l; ++i)
+	{
+		auto &pObj = aObjects[i];
+
+		if(!(pObj->getType() == m_bmType && testFeatures(pObj->getFeatures())))
+		{
+			continue;
+		}
+
+		if(pObj->getType() == m_bmType && testFeatures(pObj->getFeatures()) && (isFullyVisible || (pFrustum->boxInFrustum(pObj->getAABB()) && (!pOcclusionCuller || pOcclusionCuller->isAABBvisible(pNode->getAABB())))))
+		{
+			m_aQueryResponse.push_back(pObj->getUserData());
+		}
+	}
+}
+
+void XMETHODCALLTYPE CSceneQuery::setOP(XSCENE_QUERY_OP op)
+{
+	m_op = op;
+}
+
+void XMETHODCALLTYPE CSceneQuery::setFeature(IXSceneFeature *pFeat, XSCENE_QUERY_FEATURE mode)
+{
+	CSceneFeature *pFeature = (CSceneFeature*)pFeat;
+
+	NodeFeature bmFeature = pFeature->getFeature();
+
+	switch(mode)
+	{
+	case SQF_SET:
+		m_bmSet |= bmFeature;
+		m_bmUnset &= ~bmFeature;
+		break;
+	case SQF_UNSET:
+		m_bmSet &= ~bmFeature;
+		m_bmUnset |= bmFeature;
+		break;
+	case SQF_ANY:
+		m_bmSet &= ~bmFeature;
+		m_bmUnset &= ~bmFeature;
+		break;
+	}
+}
+
+bool CSceneQuery::testFeatures(NodeFeature bmFeatures, bool isStrict)
+{
+	bool isPassed = m_bmSet == 0;
+	if(!isPassed)
+	{
+		NodeFeature bmSet = m_bmSet & bmFeatures;
+		switch(m_op)
+		{
+		case SQO_AND:
+			isPassed = bmSet == m_bmSet;
+			break;
+		case SQO_OR:
+			isPassed = bmSet != 0;
+			break;
+		}
+	}
+
+	if((isPassed && m_op == SQO_AND || m_op == SQO_OR) && isStrict && m_bmUnset)
+	{
+		NodeFeature bmUnset = m_bmUnset & ~bmFeatures;
+		switch(m_op)
+		{
+		case SQO_AND:
+			isPassed = bmUnset == m_bmUnset;
+			break;
+		case SQO_OR:
+			isPassed = bmUnset != 0;
+			break;
+		}
+	}
+
+	return(isPassed);
+}
+
+//##########################################################################
+
+CSceneObjectType::CSceneObjectType(CScene *pScene, UINT uId):
+	m_pScene(pScene),
+	m_bmType(1 << uId)
+{
+}
+CSceneObjectType::~CSceneObjectType()
+{
+}
+
+IXSceneObject* XMETHODCALLTYPE CSceneObjectType::newObject(const SMAABB &aabb, void *pUserData, IXSceneFeature **ppFeatures)
+{
+	return(m_pScene->newObject(aabb, pUserData, m_bmType, ppFeatures));
+}
+IXSceneQuery* XMETHODCALLTYPE CSceneObjectType::newQuery()
+{
+	return(new CSceneQuery(m_pScene, this));
+}
+
+//##########################################################################
+
+int s_splitval = 42;
+
+CSceneNode::CSceneNode(CScene *pScene, CSceneNode *pParent):
+	m_pScene(pScene),
+	m_pParent(pParent)
+{
+	for(UINT i = 0; i < BVH_CHILD_COUNT; ++i)
+	{
+		m_pChildren[i] = NULL;
+	}
+}
+CSceneNode::~CSceneNode()
+{
+	for(UINT i = 0; i < BVH_CHILD_COUNT; ++i)
+	{
+		m_pScene->deleteNode(m_pChildren[i]);
+		m_pChildren[i] = NULL;
+	}
+
+	for(UINT i = 0, l = m_aObjects.size(); i < l; ++i)
+	{
+		m_aObjects[i]->setNode(NULL);
+	}
+}
+
+void CSceneNode::addObject(CSceneObject *pObject, bool force)
+{
+	assert(force || !pObject->getNode());
+
+	CSceneNode *pNode = findNode(pObject->getAABB());
+	assert(pNode);
+
+	pNode->insertObject(pObject);
+	SMAABB aabb = pObject->getAABB();
+	pNode->growExtents(aabb);
+
+	pNode->updateFeatures();
+}
+void CSceneNode::removeObject(CSceneObject *pObject)
+{
+	assert(pObject->getNode() == this);
+
+	removeObject(pObject, pObject->getAABB());
+}
+
+bool CSceneNode::removeObject(CSceneObject *pObject, const SMAABB &aabbOld)
+{
+	shrinkExtents(aabbOld);
+	int idx = m_aObjects.indexOf(pObject);
+	assert(idx >= 0);
+	if(idx < 0)
+	{
+		// cerr << "Warning: octree " << this
+		// 	<< " tried to remove node "
+		// 	<< n << " which it doesn’t contain" << endl;
+		if(m_pParent)
+		{
+			// cerr << "Parent says it should be in "
+			// 	<< m_parent->FindNode(n) << endl;
+			// cerr << "Trying parent" << endl;
+			return(m_pParent->removeObject(pObject, pObject->getAABB()));
+		}
+		return(false);
+	}
+	m_aObjects.erase(idx);
+
+	updateFeatures();
+
+	// THIS MUST COME LAST
+	testSuicide();
+	return(true);
+}
+
+void CSceneNode::updateFeatures()
+{
+	CSceneNode *pNode = this;
+	NodeFeature bmNewFeatures;
+	NodeType bmNewTypes;
+
+	while(pNode)
+	{
+		bmNewFeatures = 0;
+		bmNewTypes = 0;
+
+		for(UINT i = 0; i < BVH_CHILD_COUNT; ++i)
+		{
+			if(pNode->m_pChildren[i])
+			{
+				bmNewFeatures |= pNode->m_pChildren[i]->m_bmFeatures;
+				bmNewTypes |= pNode->m_pChildren[i]->m_bmTypes;
+			}
+		}
+
+		for(UINT i = 0, l = pNode->m_aObjects.size(); i < l; ++i)
+		{
+			bmNewFeatures |= pNode->m_aObjects[i]->getFeatures();
+			bmNewTypes |= pNode->m_aObjects[i]->getType();
+		}
+
+		if(pNode->m_bmFeatures == bmNewFeatures && pNode->m_bmTypes == bmNewTypes)
+		{
+			break;
+		}
+		
+		pNode->m_bmFeatures = bmNewFeatures;
+		pNode->m_bmTypes = bmNewTypes;
+
+		pNode = pNode->m_pParent;
+	}
+}
+
+bool CSceneNode::updateObject(CSceneObject *pObject, const SMAABB &aabbOld)
+{
+	SMAABB aabb = pObject->getAABB();
+	CSceneNode *pNode = this;
+	
+	while(pNode->m_pParent && !SMIsAABBInsideAABB(aabb, pNode->getAABB(false)))
+	{
+		pNode = pNode->m_pParent;
+	}
+
+	// Find its final reinsertion point
+	pNode = pNode->findNode(pObject->getAABB());
+	if(pNode != this)
+	{
+		// Moved to a new node
+		pNode->addObject(pObject, true);
+		if(!removeObject(pObject, aabbOld))
+		{
+			return(false);
+		}
+	}
+	else
+	{
+		// Just update the extents of this node
+		growExtents(aabb);
+		shrinkExtents(aabbOld);
+	}
+	return(true);
+}
+
+const SMAABB& CSceneNode::getAABB(bool shouldSplit)
+{
+	if(shouldSplit)
+	{
+		if(!m_isExtentsCorrect)
+		{
+			ScopedSpinLock lock(m_lock);
+
+			if(!m_isExtentsCorrect)
+			{
+				updateExtents();
+			}
+		}
+
+		if(!m_isSplit && m_aObjects.size() >= (UINT)s_splitval)
+		{
+			ScopedSpinLock lock(m_lock);
+
+			if(!m_isSplit && m_aObjects.size() >= (UINT)s_splitval)
+			{
+				doSplit();
+			}
+		}
+	}
+	return(m_aabb);
+}
+
+CSceneNode* CSceneNode::getChild(int idx, bool shouldCreate)
+{
+	assert(idx < BVH_CHILD_COUNT);
+
+	if(idx < 0)
+	{
+		return(this);
+	}
+
+	if(!m_pChildren[idx] && shouldCreate)
+	{
+		m_pChildren[idx] = m_pScene->newNode(this);
+	}
+
+	return(m_pChildren[idx]);
+}
+
+CSceneNode* CSceneNode::findNode(const SMAABB &aabb)
+{
+	CSceneNode *pNode = this;
+	int c = selectChild(aabb);
+	while(c >= 0)
+	{
+		pNode = pNode->getChild(c);
+		c = pNode->selectChild(aabb);
+	}
+	return(pNode);
+}
+
+int CSceneNode::selectChild(const SMAABB &aabb)
+{
+	if(!m_isSplit)
+	{
+		return(-1);
+	}
+
+	int c = 0;
+
+	// If this object’s bounding volume is at least 1/8
+	// the last known tree bounding volume, just keep it
+	// here
+	SMAABB aabbIntersection;
+	if(SMAABBIntersectAABB(aabb, m_aabb, &aabbIntersection)
+		&& (SMAABBVolume(aabbIntersection) > SMAABBVolume(m_aabb) * 0.125f))
+	{
+		return(-1);
+	}
+
+	if(aabb.vMax.x < m_vSplit.x)
+	{
+		c += 1;
+	}
+	else if(aabb.vMin.x > m_vSplit.x)
+	{
+		c += 2;
+	}
+
+	if(aabb.vMax.y < m_vSplit.y)
+	{
+		c += 3;
+	}
+	else if(aabb.vMin.y > m_vSplit.y)
+	{
+		c += 6;
+	}
+
+	if(aabb.vMax.z < m_vSplit.z)
+	{
+		c += 9;
+	}
+	else if(aabb.vMin.z > m_vSplit.z)
+	{
+		c += 18;
+	}
+
+	assert(c < BVH_CHILD_COUNT);
+	//{
+	//	cerr << this << "s == " << m_issplit
+	//		<< ":" << m_split.x << ","
+	//		<< m_split.y << ","
+	//		<< m_split.z
+	//		<< " c == " << c
+	//		<< " sz == " << m_children.size() << "/"
+	//		<< TREETYPE << endl;
+	//	abort();
+	//}
+	return(c);
+}
+
+void CSceneNode::insertObject(CSceneObject *pObject)
+{
+	m_aObjects.push_back(pObject);
+	pObject->setNode(this);
+}
+
+void CSceneNode::growExtents(const SMAABB &aabb)
+{
+	CSceneNode *pNode = this;
+	while(pNode)
+	{
+		if(pNode->m_isExtentsCorrect)
+		{
+			m_aabb = SMAABBConvex(m_aabb, aabb);
+		}
+		pNode = pNode->m_pParent;
+	}
+}
+
+void CSceneNode::shrinkExtents(const SMAABB &aabb)
+{
+	CSceneNode *pNode = this;
+	while(pNode)
+	{
+		if(
+			aabb.vMin.x <= pNode->m_aabb.vMin.x
+		 || aabb.vMin.y <= pNode->m_aabb.vMin.y
+		 || aabb.vMin.z <= pNode->m_aabb.vMin.z
+
+		 || aabb.vMax.x >= pNode->m_aabb.vMax.x
+		 || aabb.vMax.y >= pNode->m_aabb.vMax.y
+		 || aabb.vMax.z >= pNode->m_aabb.vMax.z
+		)
+		{
+			pNode->m_isExtentsCorrect = false;
+		}
+
+		pNode = pNode->m_pParent;
+	}
+}
+
+void CSceneNode::testSuicide()
+{
+	if(m_pParent)
+	{
+		bool killme = !m_aObjects.size();
+
+		for(UINT i = 0; killme && i < BVH_CHILD_COUNT; ++i)
+		{
+			killme = !m_pChildren[i];
+		}
+
+		if(killme)
+		{
+			m_pParent->removeChild(this);
+		}
+	}
+}
+
+void CSceneNode::removeChild(CSceneNode *pNode)
+{
+	bool shouldUnsplit = true;
+
+	for(UINT i = 0; i < BVH_CHILD_COUNT; ++i)
+	{
+		CSceneNode *pTmp = m_pChildren[i];
+
+		if(m_pChildren[i] == pNode)
+		{
+			m_pScene->deleteNode(m_pChildren[i]);
+			m_pChildren[i] = NULL;
+		}
+		else if(m_pChildren[i])
+		{
+			shouldUnsplit = false;
+		}
+	}
+	if(shouldUnsplit)
+	{
+		unsplit();
+	}
+
+	testSuicide();
+}
+
+void CSceneNode::unsplit()
+{
+	m_isSplit = false;
+	for(UINT i = 0; i < BVH_CHILD_COUNT; ++i)
+	{
+		assert(!m_pChildren[i]);
+	}
+}
+
+void CSceneNode::updateExtents()
+{
+	bool set = false;
+
+	float3 vNewSplit;
+	int ct = 0;
+
+	for(UINT i = 0, l = m_aObjects.size(); i < l; ++i)
+	{
+		const SMAABB &aabb = m_aObjects[i]->getAABB();
+
+		if (!set)
+		{
+			m_aabb = aabb;
+			set = true;
+		}
+		else
+		{
+			m_aabb = SMAABBConvex(m_aabb, aabb);
+		}
+		vNewSplit += (aabb.vMin + aabb.vMax) * 0.5f;
+		ct++;
+	}
+
+	for(UINT i = 0; i < BVH_CHILD_COUNT; ++i)
+	{
+		if (m_pChildren[i])
+		{
+			const SMAABB &aabb = m_pChildren[i]->getAABB(false);
+
+			if(!set)
+			{
+				m_aabb = aabb;
+				set = true;
+			}
+			else
+			{
+				m_aabb = SMAABBConvex(m_aabb, aabb);
+			}
+
+			vNewSplit += m_pChildren[i]->m_vSplit * (float)s_splitval;
+			ct += s_splitval;
+		}
+	}
+
+	m_vSplit = vNewSplit / (float)ct;
+
+	m_isExtentsCorrect = true;
+}
+
+void CSceneNode::doSplit()
+{
+	assert(!m_isSplit);
+	assert(m_aObjects.size());
+
+	// Find the split point
+	m_vSplit = float3();
+	float ttl = 0.0f;
+
+	for(UINT i = 0, l = m_aObjects.size(); i < l; ++i)
+	// for (Nodes::iterator iter = m_nodes.begin(); iter != m_nodes.end(); ++iter)
+	{
+		m_vSplit += m_aObjects[i]->getAABB().vMax;
+		ttl += 1.0f;
+	}
+
+	m_vSplit /= ttl;
+	m_isSplit = true;
+
+	bool safe = false, first = true;
+	int lc = 0;
+	//Array<CSceneObject*> here;
+	int iHere = 0;
+	for(UINT i = 0, l = m_aObjects.size(); i < l; ++i)
+	// for (Nodes::iterator iter = m_nodes.begin(); iter != m_nodes.end(); ++iter)
+	{
+		int c = selectChild(m_aObjects[i]->getAABB());
+		// Make sure at least one object doesn’t go into the
+		// same child as everyone else
+		if(!safe)
+		{
+			if(c < 0)
+			{
+				safe = true;
+			}
+			else
+			{
+				if(first)
+				{
+					lc = c;
+					first = false;
+				}
+				else
+				{
+					safe = (c != lc);
+				}
+			}
+		}
+		if(c < 0)
+		{
+			m_aObjects[iHere++] = m_aObjects[i];
+			//here.push_back(m_aObjects[i]);
+		}
+		else
+		{
+			getChild(c)->insertObject(m_aObjects[i]);
+		}
+	}
+	m_aObjects.resizeFast(iHere);
+	//m_aObjects.swap(here);
+
+
+	if(!safe)
+	{
+		// Oops, all objects went into the same child node!
+		// Take them back.
+		CSceneNode *cn = getChild(lc);
+
+		
+		for(int i = cn->m_aObjects.size(); i >= 0; --i)
+		{
+			CSceneObject *n = cn->m_aObjects[i];
+			cn->removeObject(n);
+			insertObject(n);
+		}
+
+		// The last removal marked this node as unsplit, so
+		// we’ll just have to do this all over again next time
+		// this AABB is queried, unless we simply keep this
+		// node split
+		m_isSplit = true;
+	}
+	for(UINT i = 0; i < BVH_CHILD_COUNT; ++i)
+	// for (Children::iterator iter = m_children.begin(); iter != m_children.end(); ++iter)
+	{
+		if(m_pChildren[i])
+		{
+			m_pChildren[i]->updateExtents();
+			m_pChildren[i]->updateFeatures();
+
+			if(m_pChildren[i]->m_bmTypes == 0)
+			{
+				int a = 0;
+			}
+		}
+	}
+
+	if(!safe)
+	{
+		updateFeatures();
+	}
+}
+
+Array<CSceneObject*>& CSceneNode::getObjects()
+{
+	return(m_aObjects);
+}
+
+//##########################################################################
+
+class CDevBVHrenderInc final: public IXConsoleCommand
+{
+public:
+	CDevBVHrenderInc(IXCore *pCore):
+		m_pCore(pCore)
+	{
+	}
+	void XMETHODCALLTYPE execute(int argc, const char **argv) override
+	{
+		auto pConsole = m_pCore->getConsole();
+		static const int *dev_bvh_render_inc = pConsole->getPCVarInt("dev_bvh_render_level");
+		pConsole->execCommand2("dev_bvh_render_level %d", *dev_bvh_render_inc + 1);
+	}
+
+private:
+	IXCore *m_pCore;
+};
+
+class CDevBVHrenderDec final: public IXConsoleCommand
+{
+public:
+	CDevBVHrenderDec(IXCore *pCore):
+		m_pCore(pCore)
+	{
+	}
+	void XMETHODCALLTYPE execute(int argc, const char **argv) override
+	{
+		auto pConsole = m_pCore->getConsole();
+		static const int *dev_bvh_render_inc = pConsole->getPCVarInt("dev_bvh_render_level");
+		if(*dev_bvh_render_inc >= 0)
+		{
+			pConsole->execCommand2("dev_bvh_render_level %d", *dev_bvh_render_inc - 1);
+		}
+	}
+
+private:
+	IXCore *m_pCore;
+};
+
+class CCvarListener final: public IEventListener<XEventCvarChanged>
+{
+public:
+	CCvarListener(IXCore *pCore, CScene *pScene):
+		m_pCore(pCore),
+		m_pScene(pScene)
+	{
+	}
+	void onEvent(const XEventCvarChanged *pData) override
+	{
+		static const int *dev_bvh_render_inc = m_pCore->getConsole()->getPCVarInt("dev_bvh_render_level");
+		
+		if(pData->pCvar == dev_bvh_render_inc)
+		{
+			m_pScene->drawLevel(*dev_bvh_render_inc);
+		}
+	}
+
+private:
+	IXCore *m_pCore;
+	CScene *m_pScene;
+};
+
+//##########################################################################
+
+CScene::CScene(IXCore *pCore):
+	m_pCore(pCore)
+{
+	m_pDevBVHrenderInc = new CDevBVHrenderInc(pCore);
+	m_pDevBVHrenderDec = new CDevBVHrenderDec(pCore);
+	m_pCvarListener = new CCvarListener(pCore, this);
+
+	auto pConsole = m_pCore->getConsole();
+
+	pConsole->registerCommand("dev_bvh_render_inc", m_pDevBVHrenderInc, "Увеличивает квар dev_bvh_render_level на 1");
+	pConsole->registerCommand("dev_bvh_render_dec", m_pDevBVHrenderDec, "Уменьшает квар dev_bvh_render_level на 1");
+	pConsole->registerCVar("dev_bvh_render_level", -1, "", FCVAR_NOTIFY);
+
+	m_pCore->getEventChannel<XEventCvarChanged>(EVENT_CVAR_CHANGED_GUID)->addListener(m_pCvarListener);
+
+	m_pRootNode = newNode(NULL);
+}
+CScene::~CScene()
+{
+	deleteNode(m_pRootNode);
+
+	auto pConsole = m_pCore->getConsole();
+	pConsole->removeCommand("dev_bvh_render_inc");
+	pConsole->removeCommand("dev_bvh_render_dec");
+
+	m_pCore->getEventChannel<XEventCvarChanged>(EVENT_CVAR_CHANGED_GUID)->removeListener(m_pCvarListener);
+	mem_delete(m_pCvarListener);
+	mem_delete(m_pDevBVHrenderInc);
+	mem_delete(m_pDevBVHrenderDec);
+}
+
+IXSceneObjectType* XMETHODCALLTYPE CScene::registerObjectType(const char *szName)
+{
+	assert(strlen(szName) < AAS_MAXLEN);
+
+	CSceneObjectType *pOut;
+
+	const AssotiativeArray<AAString, CSceneObjectType*>::Node *pNode;
+	if(m_mapTypes.KeyExists(szName, &pNode))
+	{
+		pOut = *pNode->Val;
+	}
+	else
+	{
+		if(m_mapFeatures.Size() >= sizeof(NodeType) * 8)
+		{
+			LibReport(REPORT_MSG_LEVEL_ERROR, "Scene object type limit exceeded");
+			return(NULL);
+		}
+
+		pOut = new CSceneObjectType(this, m_mapTypes.Size());
+
+		AAString sKey;
+		sKey.setName(szName);
+		m_mapTypes[sKey] = pOut;
+	}
+
+	pOut->AddRef();
+	return(pOut);
+}
+IXSceneObjectType* XMETHODCALLTYPE CScene::getObjectType(const char *szName)
+{
+	assert(strlen(szName) < AAS_MAXLEN);
+
+	const AssotiativeArray<AAString, CSceneObjectType*>::Node *pNode;
+	if(m_mapTypes.KeyExists(szName, &pNode))
+	{
+		CSceneObjectType *pOut = *pNode->Val;
+		pOut->AddRef();
+		return(pOut);
+	}
+
+	return(NULL);
+}
+
+IXSceneFeature* XMETHODCALLTYPE CScene::registerObjectFeature(const char *szName)
+{
+	assert(strlen(szName) < AAS_MAXLEN);
+
+	CSceneFeature *pOut;
+
+	const AssotiativeArray<AAString, CSceneFeature*>::Node *pNode;
+	if(m_mapFeatures.KeyExists(szName, &pNode))
+	{
+		pOut = *pNode->Val;
+	}
+	else
+	{
+		if(m_mapFeatures.Size() >= sizeof(NodeFeature) * 8)
+		{
+			LibReport(REPORT_MSG_LEVEL_ERROR, "Scene object feature limit exceeded");
+			return(NULL);
+		}
+		pOut = new CSceneFeature(m_mapFeatures.Size());
+
+		AAString sKey;
+		sKey.setName(szName);
+		m_mapFeatures[sKey] = pOut;
+	}
+
+	//pOut->AddRef();
+	return(pOut);
+}
+IXSceneFeature* XMETHODCALLTYPE CScene::getObjectFeature(const char *szName)
+{
+	assert(strlen(szName) < AAS_MAXLEN);
+
+	const AssotiativeArray<AAString, CSceneFeature*>::Node *pNode;
+	if(m_mapFeatures.KeyExists(szName, &pNode))
+	{
+		CSceneFeature *pOut = *pNode->Val;
+		//pOut->AddRef();
+		return(pOut);
+	}
+
+	return(NULL);
+}
+
+IXSceneObject* CScene::newObject(const SMAABB &aabb, void *pUserData, NodeType bmType, IXSceneFeature **ppFeatures)
+{
+	NodeFeature bmFeatures = 0;
+	while(ppFeatures && *ppFeatures)
+	{
+		bmFeatures |= ((CSceneFeature*)(*ppFeatures))->getFeature();
+		++ppFeatures;
+	}
+
+	CSceneObject *pObject;
+	{
+		ScopedSpinLock lock(m_lockPoolObjects);
+		pObject = m_poolObjects.Alloc(this, aabb, pUserData, bmType, 0);
+	}
+	m_qUpdate.emplace({SMAABB(), pObject, 0, UpdateItem::ADD});
+	return(pObject);
+}
+
+void CScene::enqueueObjectUpdate(CSceneObject* pObject, const SMAABB &aabb)
+{
+	m_qUpdate.emplace({aabb, pObject, 0, UpdateItem::UPDATE});
+}
+void CScene::enqueueObjectUpdateFeatures(CSceneObject* pObject, NodeFeature bmFeatures)
+{
+	m_qUpdate.emplace({SMAABB(), pObject, bmFeatures, UpdateItem::UPDATE_FEATURES});
+}
+void CScene::enqueueObjectDelete(CSceneObject* pObject)
+{
+	m_qUpdate.emplace({SMAABB(), pObject, 0, UpdateItem::REMOVE});
+}
+
+static UINT GetMaxDepth(CSceneNode *pNode)
+{
+	if(!pNode)
+	{
+		return(0);
+	}
+
+	UINT uCur = 0;
+	for(UINT i = 0; i < BVH_CHILD_COUNT; ++i)
+	{
+		UINT uTmp = GetMaxDepth(pNode->getChild(i, false));
+		if(uCur < uTmp)
+		{
+			uCur = uTmp;
+		}
+	}
+
+	return(1 + uCur);
+}
+
+UINT XMETHODCALLTYPE CScene::getTreeHeight()
+{
+	if(!m_pRootNode)
+	{
+		return(0);
+	}
+	return(GetMaxDepth(m_pRootNode));
+	/*
+	Create a queue.
+	Push root into the queue.
+	height = 0
+	Loop
+		nodeCount = size of queue
+        
+			// If the number of nodes at this level is 0, return height
+		if nodeCount is 0
+			return Height;
+		else
+			increase Height
+
+			// Remove nodes of this level and add nodes of 
+			// next level
+		while (nodeCount > 0)
+			pop node from front
+			push its children to queue
+			decrease nodeCount
+		   // At this point, queue has nodes of next level
+	*/
+
+	std::queue<CSceneNode*> q;
+	q.push(m_pRootNode);
+	UINT uHeight = 0;
+	while(true)
+	{
+		UINT uNodeCount = (UINT)q.size();
+
+		if(!uNodeCount)
+		{
+			return(uHeight);
+		}
+		
+		++uHeight;
+		while(uNodeCount)
+		{
+			CSceneNode* pNode = q.front();
+			q.pop();
+			if(pNode->getChild(true))
+			{
+				q.push(pNode->getChild(true));
+			}
+			if(pNode->getChild(false))
+			{
+				q.push(pNode->getChild(false));
+			}
+			--uNodeCount;
+		}
+	}
+
+	//return(GetMaxDepth(m_pRootNode));
+}
+
+void CScene::addObject(CSceneObject *pObject)
+{
+	m_pRootNode->addObject(pObject);
+}
+void CScene::removeObject(CSceneObject *pObject)
+{
+	CSceneNode *pNode = pObject->getNode();
+	pNode->removeObject(pObject);
+}
+
+void CScene::drawLevel(int iLvl)
+{
+	for(UINT i = 0, l = m_aModels.size(); i < l; ++i)
+	{
+		mem_release(m_aModels[i]);
+	}
+	m_aModels.clearFast();
+
+	if(iLvl >= 0)
+	{
+		drawLevelInternal(m_pRootNode, iLvl);
+	}
+}
+
+void CScene::drawLevelInternal(CSceneNode *pNode, int iLvl, int iCurLvl)
+{
+	if(!pNode)
+	{
+		return;
+	}
+
+	if(iLvl == iCurLvl)
+	{
+		IXResourceManager *pRM = m_pCore->getResourceManager();
+		IXResourceModelStatic *pResouce = pRM->newResourceModelStatic();
+
+		pResouce->setMaterialCount(1, 1);
+		pResouce->setMaterial(0, 0, "dev_trigger");
+
+		UINT uVtxCount = 8;
+		UINT uIdxCount = 36;
+		pResouce->addLod(1, &uVtxCount, &uIdxCount);
+		XResourceModelStaticSubset *pSubset = pResouce->getSubset(0, 0);
+
+		SMAABB aabb = pNode->getAABB();
+
+		UINT pIndices[] = {
+			0, 3, 1, 0, 2, 3,
+			0, 4, 6, 0, 6, 2,
+			1, 7, 5, 1, 3, 7, 
+			4, 5, 7, 4, 7, 6, 
+			2, 6, 7, 2, 7, 3, 
+			0, 1, 4, 4, 1, 5
+		};
+		memcpy(pSubset->pIndices, pIndices, sizeof(pIndices));
+		XResourceModelStaticVertex pVertices[] = {
+			{float3_t(aabb.vMin.x, aabb.vMin.y, aabb.vMin.z)},
+			{float3_t(aabb.vMin.x, aabb.vMin.y, aabb.vMax.z)},
+			{float3_t(aabb.vMin.x, aabb.vMax.y, aabb.vMin.z)},
+			{float3_t(aabb.vMin.x, aabb.vMax.y, aabb.vMax.z)},
+			{float3_t(aabb.vMax.x, aabb.vMin.y, aabb.vMin.z)},
+			{float3_t(aabb.vMax.x, aabb.vMin.y, aabb.vMax.z)},
+			{float3_t(aabb.vMax.x, aabb.vMax.y, aabb.vMin.z)},
+			{float3_t(aabb.vMax.x, aabb.vMax.y, aabb.vMax.z)}
+		};
+
+		memcpy(pSubset->pVertices, pVertices, sizeof(pVertices));
+
+		IXModelProvider *pProvider = (IXModelProvider*)m_pCore->getPluginManager()->getInterface(IXMODELPROVIDER_GUID);
+		IXDynamicModel *pModel;
+		if(pProvider->createDynamicModel(pResouce, &pModel))
+		{
+			m_aModels.push_back(pModel);
+			//pModel->setPosition((aabb.vMax + aabb.vMin) * 0.5f);
+		}
+		mem_release(pResouce);
+	}
+	else
+	{
+		for(UINT i = 0; i < BVH_CHILD_COUNT; ++i)
+		{
+			drawLevelInternal(pNode->getChild(i, false), iLvl, iCurLvl + 1);
+		}
+	}
+}
+
+CSceneNode* CScene::newNode(CSceneNode *pParent)
+{
+	return(m_poolNodes.Alloc(this, pParent));
+}
+void CScene::deleteNode(CSceneNode *pNode)
+{
+	if(pNode)
+	{
+		m_poolNodes.Delete(pNode);
+	}
+}
+
+void CScene::sync()
+{
+	UpdateItem item;
+	SMAABB aabbOld;
+	while(m_qUpdate.pop(&item))
+	{
+		switch(item.action)
+		{
+		case UpdateItem::ADD:
+			addObject(item.pObject);
+			break;
+
+		case UpdateItem::REMOVE:
+			m_poolObjects.Delete(item.pObject);
+			break;
+
+		case UpdateItem::UPDATE:
+			aabbOld = item.pObject->m_aabb;
+			item.pObject->m_aabb = item.aabbNew;
+			if(item.pObject->m_pNode)
+			{
+				item.pObject->m_pNode->updateObject(item.pObject, aabbOld);
+			}
+			break;
+
+		case UpdateItem::UPDATE_FEATURES:
+			item.pObject->m_bmFeatures = item.bmFeatures;
+			if(item.pObject->m_pNode)
+			{
+				item.pObject->m_pNode->updateFeatures();
+			}
+			break;
+		}
+	}
+}
diff --git a/source/render/Scene.h b/source/render/Scene.h
new file mode 100644
index 0000000000000000000000000000000000000000..323a6c2f7ea05bc26fb5e26303688e4baecfbdff
--- /dev/null
+++ b/source/render/Scene.h
@@ -0,0 +1,276 @@
+#ifndef __SCENE_H
+#define __SCENE_H
+
+#include <xcommon/IXScene.h>
+#include <xcommon/IXCore.h>
+#include <common/aastring.h>
+#include <common/queue.h>
+
+#define BVH_CHILD_COUNT 27
+
+typedef UINT NodeFeature;
+typedef UINT NodeType;
+
+//##########################################################################
+
+class CScene;
+class CSceneNode;
+class CSceneObjectType;
+class CSceneObject final: public IXUnknownImplementation<IXSceneObject>
+{
+	friend class CScene;
+public:
+
+	CSceneObject(CScene *pScene, const SMAABB &aabb, void *pUserData, NodeType bmType, NodeFeature bmFeatures);
+	~CSceneObject();
+	
+	void XMETHODCALLTYPE update(const SMAABB &aabb) override;
+	void updateFeatures(NodeFeature bmFeatures);
+
+	void XMETHODCALLTYPE setFeature(IXSceneFeature *pFeat, bool isSet) override;
+	void XMETHODCALLTYPE setFeatures(IXSceneFeature **ppFeatures) override;
+
+	void setNode(CSceneNode *pNode);
+	CSceneNode* getNode();
+
+	const SMAABB& getAABB() const;
+
+	void* getUserData() const;
+
+	NodeFeature getFeatures() const
+	{
+		return(m_bmFeatures);
+	}
+	NodeType getType() const
+	{
+		return(m_bmType);
+	}
+
+private:
+	SMAABB m_aabb;
+
+	CScene *m_pScene;
+	CSceneNode *m_pNode = NULL;
+
+	NodeFeature m_bmFeatures;
+	NodeType m_bmType;
+
+	void *m_pUserData;
+
+	void XMETHODCALLTYPE FinalRelease() override;
+};
+
+//##########################################################################
+
+class CSceneFeature final: public IXUnknownImplementation<IXSceneFeature>
+{
+public:
+	CSceneFeature(UINT uId):
+		m_bmFeature(1 << uId)
+	{
+	}
+
+	NodeFeature getFeature()
+	{
+		return(m_bmFeature);
+	}
+
+private:
+	NodeFeature m_bmFeature;
+};
+
+//##########################################################################
+
+class CSceneQuery final: public IXUnknownImplementation<IXSceneQuery>
+{
+public:
+	CSceneQuery(CScene *pScene, CSceneObjectType *pObjectType);
+	~CSceneQuery();
+
+	UINT XMETHODCALLTYPE execute(const IFrustum *pFrustum, void ***pppObjects, IXOcclusionCuller *pOcclusionCuller = NULL) override;
+
+	void XMETHODCALLTYPE setOP(XSCENE_QUERY_OP op) override;
+
+	void XMETHODCALLTYPE setFeature(IXSceneFeature *pFeat, XSCENE_QUERY_FEATURE mode) override;
+
+private:
+	CScene *m_pScene;
+	NodeType m_bmType;
+	Array<void*> m_aQueryResponse;
+
+	XSCENE_QUERY_OP m_op = SQO_AND;
+	NodeFeature m_bmSet = 0;
+	NodeFeature m_bmUnset = 0;
+
+	void queryObjectsInternal(CSceneNode *pNode, const IFrustum *pFrustum, bool isFullyVisible = false, IXOcclusionCuller *pOcclusionCuller = NULL);
+	bool testFeatures(NodeFeature bmFeatures, bool isStrict = true);
+};
+
+//##########################################################################
+
+class CSceneObjectType final: public IXUnknownImplementation<IXSceneObjectType>
+{
+public:
+	CSceneObjectType(CScene *pScene, UINT uId);
+	~CSceneObjectType();
+
+	IXSceneObject* XMETHODCALLTYPE newObject(const SMAABB &aabb, void *pUserData, IXSceneFeature **ppFeatures = NULL) override;
+	IXSceneQuery* XMETHODCALLTYPE newQuery() override;
+
+	NodeType getType()
+	{
+		return(m_bmType);
+	}
+
+private:
+	CScene *m_pScene;
+
+	NodeType m_bmType;
+};
+
+//##########################################################################
+
+class CSceneNode final
+{
+	friend class CScene;
+public:
+
+	CSceneNode(CScene *pScene, CSceneNode *pParent = NULL);
+	~CSceneNode();
+
+	SX_ALIGNED_OP_MEM2();
+
+	void addObject(CSceneObject *pObject, bool force = false);
+	void removeObject(CSceneObject *pObject);
+
+	bool updateObject(CSceneObject *pObject, const SMAABB &aabbOld);
+
+	const SMAABB& getAABB(bool doSplit = true);
+
+	CSceneNode* getChild(int idx, bool shouldCreate = true);
+	
+	Array<CSceneObject*>& getObjects();
+
+	NodeFeature getFeatures() const
+	{
+		return(m_bmFeatures);
+	}
+	NodeType getTypes() const
+	{
+		return(m_bmTypes);
+	}
+
+protected:
+	int selectChild(const SMAABB &aabb);
+	CSceneNode* findNode(const SMAABB &aabb);
+	void insertObject(CSceneObject *pObject);
+	bool removeObject(CSceneObject *pObject, const SMAABB &aabbOld);
+
+	void growExtents(const SMAABB &aabb);
+	void shrinkExtents(const SMAABB &aabb);
+	void updateExtents();
+
+	void testSuicide();
+	void unsplit();
+	void doSplit();
+
+	void updateFeatures();
+
+	void removeChild(CSceneNode *pNode);
+
+	CSceneNode *m_pParent = NULL;
+	CSceneNode *m_pChildren[BVH_CHILD_COUNT];
+
+	CScene *m_pScene;
+
+	SMAABB m_aabb;
+
+	Array<CSceneObject*> m_aObjects;
+
+	volatile bool m_isSplit = false;
+	float3 m_vSplit;
+
+	volatile bool m_isExtentsCorrect = false;
+
+	SpinLock m_lock;
+
+	NodeFeature m_bmFeatures = 0;
+	NodeType m_bmTypes = 0;
+};
+
+//##########################################################################
+
+class CDevBVHrenderInc;
+class CDevBVHrenderDec;
+class CCvarListener;
+class CScene final: public IXUnknownImplementation<IXScene>
+{
+	friend class CSceneObject;
+	friend class CSceneQuery;
+public:
+	CScene(IXCore *pCore);
+	~CScene();
+
+	IXSceneObjectType* XMETHODCALLTYPE registerObjectType(const char *szName) override;
+	IXSceneObjectType* XMETHODCALLTYPE getObjectType(const char *szName) override;
+
+	IXSceneFeature* XMETHODCALLTYPE registerObjectFeature(const char *szName) override;
+	IXSceneFeature* XMETHODCALLTYPE getObjectFeature(const char *szName) override;
+
+	IXSceneObject* newObject(const SMAABB &aabb, void *pUserData, NodeType bmType, IXSceneFeature **ppFeatures);
+
+	UINT XMETHODCALLTYPE getTreeHeight() override;
+
+	void drawLevel(int iLvl);
+
+	CSceneNode* newNode(CSceneNode *pParent);
+	void deleteNode(CSceneNode *pNode);
+
+	void enqueueObjectUpdate(CSceneObject* pObject, const SMAABB &aabb);
+	void enqueueObjectUpdateFeatures(CSceneObject* pObject, NodeFeature bmFeatures);
+	void enqueueObjectDelete(CSceneObject* pObject);
+
+	void sync();
+	
+protected:
+	void addObject(CSceneObject *pObject);
+	void removeObject(CSceneObject *pObject);
+
+	IXCore *m_pCore;
+private:
+	CSceneNode *m_pRootNode = NULL;
+
+	MemAlloc<CSceneNode> m_poolNodes;
+	MemAlloc<CSceneObject> m_poolObjects;
+	SpinLock m_lockPoolObjects;
+
+	struct UpdateItem
+	{
+		SMAABB aabbNew;
+		CSceneObject *pObject;
+		NodeFeature bmFeatures;
+		enum
+		{
+			UPDATE,
+			UPDATE_FEATURES,
+			REMOVE,
+			ADD
+		} action;
+	};
+
+	Queue<UpdateItem> m_qUpdate;
+
+	CDevBVHrenderInc *m_pDevBVHrenderInc = NULL;
+	CDevBVHrenderDec *m_pDevBVHrenderDec = NULL;
+
+	CCvarListener *m_pCvarListener = NULL;
+
+	Array<IXModel*> m_aModels;
+
+	AssotiativeArray<AAString, CSceneObjectType*> m_mapTypes;
+	AssotiativeArray<AAString, CSceneFeature*> m_mapFeatures;
+
+	void drawLevelInternal(CSceneNode *pNode, int iLvl, int iCurLvl = 0);
+};
+
+#endif
diff --git a/source/render/Updatable.cpp b/source/render/Updatable.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..16bf9d7e45be9583ec4a12bb682f94f87db6daf1
--- /dev/null
+++ b/source/render/Updatable.cpp
@@ -0,0 +1,27 @@
+#include "Updatable.h"
+
+CUpdatable::CUpdatable(CScene *pScene):
+	m_pScene(pScene)
+{
+
+}
+
+UINT CUpdatable::startup()
+{
+	return(30);
+}
+
+void CUpdatable::shutdown()
+{
+}
+
+
+ID CUpdatable::run(float fDelta)
+{
+	return(-1);
+}
+
+void CUpdatable::sync()
+{
+	m_pScene->sync();
+}
diff --git a/source/render/Updatable.h b/source/render/Updatable.h
new file mode 100644
index 0000000000000000000000000000000000000000..84cc2433a0044773ec732dfe0bcc3885a0f43fae
--- /dev/null
+++ b/source/render/Updatable.h
@@ -0,0 +1,22 @@
+#ifndef __UPDATABLE_H
+#define __UPDATABLE_H
+
+#include <xcommon/IXUpdatable.h>
+#include "Scene.h"
+
+class CUpdatable: public IXUnknownImplementation<IXUpdatable>
+{
+public:
+	CUpdatable(CScene *pScene);
+
+	UINT startup() override;
+	void shutdown() override;
+
+	ID run(float fDelta) override;
+	void sync() override;
+
+protected:
+	CScene *m_pScene;
+};
+
+#endif
diff --git a/source/render/sxrender.cpp b/source/render/sxrender.cpp
index bb92f5d5a42cc3ff2c8e67710d06aee45d87578c..70c767bb6c2d9731c3900139bc3de3b83ba560ea 100644
--- a/source/render/sxrender.cpp
+++ b/source/render/sxrender.cpp
@@ -9,6 +9,8 @@ See the license in LICENSE
 #include <render/render_func.h>
 
 #include "RenderPipeline.h"
+#include "Scene.h"
+#include "Updatable.h"
 
 #define SXRENDER_VERSION 1
 
@@ -71,9 +73,14 @@ SX_LIB_API void SRender_0Create(const char *szName, HWND hWnd3D, HWND hWndParent
 
 		//***********************
 
+		CScene *pScene = new CScene(Core_GetIXCore());
+		Core_GetIXCore()->getPluginManager()->registerInterface(IXSCENE_GUID, pScene);
+
+		CUpdatable *pUpdatable = new CUpdatable(pScene);
+		Core_GetIXCore()->getPluginManager()->registerInterface(IXUPDATABLE_GUID, pUpdatable);
+
 		g_pPipeline = new CRenderPipeline(SGCore_GetDXDevice());
 		Core_GetIXCore()->setRenderPipeline(g_pPipeline);
-
 	}
 	else
 		LibReport(REPORT_MSG_LEVEL_ERROR, "%s - not init argument [name]", GEN_MSG_LOCATION);
diff --git a/source/terrax/mainWindow.cpp b/source/terrax/mainWindow.cpp
index 4dc66368ec69ec709f44b0a0e04c6cb4565781da..a7f9dfa4e2c0148f318443efc5db893acc56e728 100644
--- a/source/terrax/mainWindow.cpp
+++ b/source/terrax/mainWindow.cpp
@@ -636,7 +636,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 		{
 			RECT rcTopLeft;
 			GetClientRect(g_hTopLeftWnd, &rcTopLeft);
-			g_pEngine->getCore()->execCmd2("r_win_width %d\nr_win_height %d", rcTopLeft.right - rcTopLeft.left, rcTopLeft.bottom - rcTopLeft.top);
+			g_pEngine->getCore()->getConsole()->execCommand2("r_win_width %d\nr_win_height %d", rcTopLeft.right - rcTopLeft.left, rcTopLeft.bottom - rcTopLeft.top);
 		}
 
 		SendMessage(g_hStatusWnd, WM_SIZE, wParam, lParam);
@@ -2580,7 +2580,7 @@ void XUpdatePropWindow()
 		g_pPropWindow->setClassName("");
 	}
 
-	for(AssotiativeArray<AAString, prop_item_s>::Iterator i = mProps.begin(); i; i++)
+	for(AssotiativeArray<AAString, prop_item_s>::Iterator i = mProps.begin(); i; ++i)
 	{
 		g_pPropWindow->addPropField(&i.second->xPropField, i.second->szValue);
 	}
diff --git a/source/terrax/terrax.cpp b/source/terrax/terrax.cpp
index 8e7cd852bc99ff8e10879fabe3510159c5453a7d..c7eb03005ef91db34250c27d11a511b87f480879 100644
--- a/source/terrax/terrax.cpp
+++ b/source/terrax/terrax.cpp
@@ -457,15 +457,15 @@ int main(int argc, char **argv)
 	g_pEngine = pEngine;
 	CEngineCallback engineCb;
 	pEngine->initGraphics((XWINDOW_OS_HANDLE)g_hTopLeftWnd, &engineCb);
-	pEngine->getCore()->execCmd("gmode editor");
-	pEngine->getCore()->execCmd("exec ../config_editor.cfg");
+	pEngine->getCore()->getConsole()->execCommand("gmode editor");
+	pEngine->getCore()->getConsole()->execCommand("exec ../config_editor.cfg");
 	CRenderPipeline *pPipeline = new CRenderPipeline(Core_GetIXCore());
 	XInitGuiWindow(false);
 
 	RECT rcTopLeft;
 	GetClientRect(g_hTopLeftWnd, &rcTopLeft);
 
-	g_pEngine->getCore()->execCmd2("r_win_width %d\nr_win_height %d", rcTopLeft.right - rcTopLeft.left, rcTopLeft.bottom - rcTopLeft.top);
+	g_pEngine->getCore()->getConsole()->execCommand2("r_win_width %d\nr_win_height %d", rcTopLeft.right - rcTopLeft.left, rcTopLeft.bottom - rcTopLeft.top);
 
 
 	IPluginManager *pPluginManager = Core_GetIXCore()->getPluginManager();
diff --git a/source/xEngine/Engine.cpp b/source/xEngine/Engine.cpp
index 110bcef5a2e0cb0d2ab9fb875165efc294477dfe..a562c741f329795b40ab4be4aad3cf14c7f70071 100644
--- a/source/xEngine/Engine.cpp
+++ b/source/xEngine/Engine.cpp
@@ -233,7 +233,7 @@ bool XMETHODCALLTYPE CEngine::initGraphics(XWINDOW_OS_HANDLE hWindow, IXEngineCa
 	// init updatable
 	m_pCore->initUpdatable();
 
-	getCore()->execCmd("exec ../config_sys.cfg");
+	getCore()->getConsole()->execCommand("exec ../config_sys.cfg");
 
 	return(true);
 }
@@ -256,7 +256,7 @@ bool XMETHODCALLTYPE CEngine::initServer()
 	// init updatable
 	Core_GetIXCore()->initUpdatable();
 
-	getCore()->execCmd("exec ../config_sys.cfg");
+	getCore()->getConsole()->execCommand("exec ../config_sys.cfg");
 
 	return(true);
 }
diff --git a/source/xcommon/IXConsole.h b/source/xcommon/IXConsole.h
new file mode 100644
index 0000000000000000000000000000000000000000..f75c3072a70f3d0b498ba8ddc2d8cfa4ff92fa9e
--- /dev/null
+++ b/source/xcommon/IXConsole.h
@@ -0,0 +1,59 @@
+#ifndef __IXCONSOLE_H
+#define __IXCONSOLE_H
+
+#include <gdefines.h>
+
+typedef void(*XCONCMD)(); /*!< Тип функции для регистрации команды без аргументов */
+typedef void(*XCONCMDARG)(int argc, const char **argv); /*!< Тип функции для регистрации команды с аргументами */
+
+//! Флаги кваров
+enum CVAR_FLAG
+{
+	FCVAR_NONE = 0x00, //!< нет
+	FCVAR_CHEAT = 0x01, //!< Изменение этой переменной с дефолтного значения разрешено только в режиме разработки
+	FCVAR_READONLY = 0x02,  //!< Только для чтения
+	FCVAR_NOTIFY_OLD = 0x04, //!< Оповещать об изменениях
+	FCVAR_NOTIFY = 0x08  //!< Оповещать об изменениях
+};
+
+//##########################################################################
+
+class IXConsoleCommand
+{
+public:
+	virtual void XMETHODCALLTYPE execute(int argc, const char **argv) = 0;
+};
+
+//##########################################################################
+
+class IXConsole: public IXUnknown
+{
+public:
+	//! Регистрация консольной функции без аргументов
+	virtual void XMETHODCALLTYPE registerCommand(const char *szName, XCONCMD cmd, const char *szDesc) = 0;
+	//! Регистрация консольной функции с аргументами
+	virtual void XMETHODCALLTYPE registerCommand(const char *szName, XCONCMDARG cmd, const char *szDesc) = 0;
+	//! Регистрация консольной функции с аргументами
+	virtual void XMETHODCALLTYPE registerCommand(const char *szName, IXConsoleCommand *pCommand, const char *szDesc) = 0;
+
+	virtual void XMETHODCALLTYPE removeCommand(const char *szName) = 0;
+
+	//! Регистрирует строковую переменную
+	virtual void XMETHODCALLTYPE registerCVar(const char *szName, const char *szValue, const char *szDesc, int flags = 0) = 0;
+	//! Регистрирует целочисленную переменную
+	virtual void XMETHODCALLTYPE registerCVar(const char *szName, int iValue, const char *szDesc, int flags = 0) = 0;
+	//! Регистрирует вещественную переменную
+	virtual void XMETHODCALLTYPE registerCVar(const char *szName, float fValue, const char *szDesc, int flags = 0) = 0;
+	//! Регистрирует логическую переменную
+	virtual void XMETHODCALLTYPE registerCVar(const char *szName, bool bValue, const char *szDesc, int flags = 0) = 0;
+
+	virtual void XMETHODCALLTYPE execCommand(const char *szCommand) = 0;
+	virtual void execCommand2(const char *szFormat, ...) = 0;
+
+	virtual const char** XMETHODCALLTYPE getPCVarString(const char *szName) = 0;
+	virtual const int* XMETHODCALLTYPE getPCVarInt(const char *szName) = 0;
+	virtual const float* XMETHODCALLTYPE getPCVarFloat(const char *szName) = 0;
+	virtual const bool* XMETHODCALLTYPE getPCVarBool(const char *szName) = 0;
+};
+
+#endif
diff --git a/source/xcommon/IXCore.h b/source/xcommon/IXCore.h
index dcd4d4930b86f2239cb73f6987393a65eb13878d..1f541ed8257e958c2d3e67e72e88918dc4d945f4 100644
--- a/source/xcommon/IXCore.h
+++ b/source/xcommon/IXCore.h
@@ -9,6 +9,7 @@
 #include "IXRenderPipeline.h"
 #include "IXConfig.h"
 #include "IXBuffer.h"
+#include "IXConsole.h"
 
 #include <fcntl.h>
 #include <io.h>
@@ -31,8 +32,7 @@ public:
 
 	virtual UINT_PTR XMETHODCALLTYPE getCrtOutputHandler() = 0;
 
-	virtual void XMETHODCALLTYPE execCmd(const char *szCommand) = 0;
-	virtual void execCmd2(const char *szFormat, ...) = 0;
+	virtual IXConsole* XMETHODCALLTYPE getConsole() = 0;
 
 	//@FIXME: Remove that!
 	virtual void initUpdatable() = 0;
@@ -44,11 +44,6 @@ public:
 	virtual IXConfig* XMETHODCALLTYPE newConfig() = 0;
 	virtual IXBuffer* XMETHODCALLTYPE newBuffer() = 0;
 
-	virtual const char** XMETHODCALLTYPE getPCVarString(const char *szName) = 0;
-	virtual const int* XMETHODCALLTYPE getPCVarInt(const char *szName) = 0;
-	virtual const float* XMETHODCALLTYPE getPCVarFloat(const char *szName) = 0;
-	virtual const bool* XMETHODCALLTYPE getPCVarBool(const char *szName) = 0;
-
 	virtual ID XMETHODCALLTYPE forLoop(int iStart, int iEnd, const IParallelForBody *pBody, int iMaxChunkSize = 0) = 0;
 	virtual void XMETHODCALLTYPE waitForLoop(ID id) = 0;
 
diff --git a/source/xcommon/IXPlugin.h b/source/xcommon/IXPlugin.h
index 84dc897a0da1ccdbd8dd98ccbd57dbae4893a190..6fcbfce3e0c1be8153e76166ab49e63bc35bc1f9 100644
--- a/source/xcommon/IXPlugin.h
+++ b/source/xcommon/IXPlugin.h
@@ -6,7 +6,7 @@
 #define X_PLUGIN_ENTRYPOINT XPluginMain
 #define X_PLUGIN_API extern "C" __declspec(dllexport)
 #define IXPLUGIN_VERSION 1
-#define DECLARE_XPLUGIN(cls) class cls##Imp:public cls{                                                   \
+#define DECLARE_XPLUGIN(cls) class cls##Imp final: public cls{                                            \
 public:                                                                                                   \
 	cls##Imp(ID id):m_id(id){}                                                                            \
 	ID XMETHODCALLTYPE getID()override{return(m_id);}                                                     \
diff --git a/source/xcommon/IXScene.h b/source/xcommon/IXScene.h
new file mode 100644
index 0000000000000000000000000000000000000000..7cd789aa696ece207269a768cb19ed28f6deb3f3
--- /dev/null
+++ b/source/xcommon/IXScene.h
@@ -0,0 +1,140 @@
+#ifndef __IXSCENE_H
+#define __IXSCENE_H
+
+#include <gdefines.h>
+#include "render/IFrustum.h"
+
+class IXOcclusionCuller;
+
+//! Особенность объекта
+class IXSceneFeature: public IXUnknown
+{
+};
+
+//##########################################################################
+
+//! Объект сцены
+class IXSceneObject: public IXUnknown
+{
+public:
+	/*!
+		Обновляет AABB объекта
+		@threadsafe full
+	*/
+	virtual void XMETHODCALLTYPE update(const SMAABB &aabb) = 0;
+
+	/*!
+		Устанавливает особенность объекта
+		@threadsafe full
+	*/
+	virtual void XMETHODCALLTYPE setFeature(IXSceneFeature *pFeat, bool isSet) = 0;
+
+	/*!
+		Устанавливает массив особенностей объекта. Последний элемент должен быть NULL
+		@threadsafe full
+	*/
+	virtual void XMETHODCALLTYPE setFeatures(IXSceneFeature **ppFeatures) = 0;
+};
+
+//##########################################################################
+
+//! Операции запроса
+enum XSCENE_QUERY_OP
+{
+	SQO_AND, //<! Должны выполниться все условия
+	SQO_OR   //<! Должно выполниться любое из условий
+};
+
+//! Требования к особенностям
+enum XSCENE_QUERY_FEATURE
+{
+	SQF_ANY,  //!< Наличие/отсутствие особенности не важно
+	SQF_SET,  //!< Особенность должна присутствовать
+	SQF_UNSET //!< Особенность должна отсутствовать
+};
+
+//##########################################################################
+
+//! Объект запроса к сцене
+class IXSceneQuery: public IXUnknown
+{
+public:
+	/*!
+		Выполняет запрос, возвращает количество найденных объектов, 
+		в pppObjects записывается указатель на массив пользовательских указателей найденных объектов
+		@threadsafe sync
+	*/
+	virtual UINT XMETHODCALLTYPE execute(const IFrustum *pFrustum, void ***pppObjects, IXOcclusionCuller *pOcclusionCuller = NULL) = 0;
+
+	/*!
+		Устанавливает операцию выборки
+		@threadsafe none
+	*/
+	virtual void XMETHODCALLTYPE setOP(XSCENE_QUERY_OP op) = 0;
+
+	/*!
+		Устанавливает требование к особенности
+		@threadsafe none
+	*/
+	virtual void XMETHODCALLTYPE setFeature(IXSceneFeature *pFeat, XSCENE_QUERY_FEATURE mode) = 0;
+};
+
+//##########################################################################
+
+//! Тип объекта сцены
+class IXSceneObjectType: public IXUnknown
+{
+public:
+	/*!
+		Добавляет новый объект в систему
+		@threadsafe full
+	*/
+	virtual IXSceneObject* XMETHODCALLTYPE newObject(const SMAABB &aabb, void *pUserData, IXSceneFeature **ppFeatures = NULL) = 0;
+	
+	/*!
+		Создает новый запрос
+		@threadsafe full
+	*/
+	virtual IXSceneQuery* XMETHODCALLTYPE newQuery() = 0;
+};
+
+//##########################################################################
+
+// {FF050B41-5158-4380-990B-7E61E9F72B15}
+#define IXSCENE_GUID DEFINE_XGUID(0xff050b41, 0x5158, 0x4380, 0x99, 0xb, 0x7e, 0x61, 0xe9, 0xf7, 0x2b, 0x15)
+
+class IXScene: public IXUnknown
+{
+public:
+	/*!
+		Регистрирует новый тип объекта, либо возвращает существующий
+		@threadsafe none
+	*/
+	virtual IXSceneObjectType* XMETHODCALLTYPE registerObjectType(const char *szName) = 0;
+
+	/*!
+		Возвращает тип объекта по имени
+		@threadsafe none
+	*/
+	virtual IXSceneObjectType* XMETHODCALLTYPE getObjectType(const char *szName) = 0;
+	
+	/*!
+		Регистрирует новую особенность объекта, либо возвращает существующую
+		@threadsafe none
+	*/
+	virtual IXSceneFeature* XMETHODCALLTYPE registerObjectFeature(const char *szName) = 0;
+
+	/*!
+		Возвращает особенность объекта по имени
+		@threadsafe none
+	*/
+	virtual IXSceneFeature* XMETHODCALLTYPE getObjectFeature(const char *szName) = 0;
+
+	/*!
+		Возвращает высоту дерева
+		@threadsafe none
+	*/
+	virtual UINT XMETHODCALLTYPE getTreeHeight() = 0;
+};
+
+#endif
diff --git a/source/xcommon/render/IFrustum.h b/source/xcommon/render/IFrustum.h
index e7c3c401b29a74b99c011bbcd395b6769a652e3a..8d665e18173fa7cef77ef880b30be41b7bcb2bb8 100644
--- a/source/xcommon/render/IFrustum.h
+++ b/source/xcommon/render/IFrustum.h
@@ -10,31 +10,31 @@ class IFrustum: public IXUnknown
 public:
 	//! обновление фрустума, на вход матрицы по которым необходимо построить фрустум
 	virtual void update(
-		const float4x4 *pView,	//<! видовая матрица
-		const float4x4 *pProj	//<! проекционная матрица
+		const float4x4 &mView,	//<! видовая матрица
+		const float4x4 &mProj	//<! проекционная матрица
 	) = 0;
 
 	virtual void update(const SMPLANE *pPlanes, bool isNormalized = false) = 0;
 
 	//! находится ли точка во фрустуме
-	virtual bool pointInFrustum(const float3 *pPoint) const = 0;
+	virtual bool pointInFrustum(const float3 &vPoint) const = 0;
 
 	//! находится ли треугольник во фрутстуме
-	virtual bool polyInFrustum(const float3 *pPoint1, const float3 *pPoint2, const float3 *pPoint3) const = 0;
+	virtual bool polyInFrustum(const float3 &vPoint1, const float3 &vPoint2, const float3 &vPoint3) const = 0;
 
 	//! находится ли полигон во фрустуме полностью
-	virtual bool polyInFrustumAbs(const float3 *pPoint1, const float3 *pPoint2, const float3 *pPoint3) const = 0;
+	virtual bool polyInFrustumAbs(const float3 &vPoint1, const float3 &vPoint2, const float3 &vPoint3) const = 0;
 
 
 	//! находится ли полигон во фрустуме
-	virtual bool sphereInFrustum(const float3 *pPoint, float fRadius) const = 0;
+	virtual bool sphereInFrustum(const float3 &vPoint, float fRadius) const = 0;
 
 	//! находится ли сфера во фрустуме полностью
-	virtual bool sphereInFrustumAbs(const float3 *pPoint, float fRadius) const = 0;
+	virtual bool sphereInFrustumAbs(const float3 &vPoint, float fRadius) const = 0;
 
-	//! находится ли параллелепипед (описанный точками экстремума) во фрустуме
-	virtual bool boxInFrustum(const float3 *pMin, const float3 *pMax) const = 0;
-	virtual bool boxInFrustum(const SMAABB &aabb) const = 0;
+	//! находится ли параллелепипед (описанный точками экстремума) во фрустуме. isStrict задает строгий режим проверки (AABB должен полностью входить в фрустум) иначе отслеживается частичное пересечение
+	virtual bool boxInFrustum(const float3 &vMin, const float3 &vMax, bool *pIsStrict = NULL) const = 0;
+	virtual bool boxInFrustum(const SMAABB &aabb, bool *pIsStrict = NULL) const = 0;
 
 	//! находится ли параллелепипед (описанный точками экстремума) во фрустуме
 	virtual bool frustumInFrustum(const IFrustum *pOther) const = 0;
diff --git a/source/xcommon/render/IXOcclusionCuller.h b/source/xcommon/render/IXOcclusionCuller.h
index 6063004d99cb0ef3ce53217df5417458476e9018..b21d9cdbb22e1daf88757872de60d1e4ad349e9c 100644
--- a/source/xcommon/render/IXOcclusionCuller.h
+++ b/source/xcommon/render/IXOcclusionCuller.h
@@ -20,8 +20,8 @@ public:
 	//! видна ли сфера
 	virtual bool XMETHODCALLTYPE isSphereVisible(const float3 &vOrigin, float fRadius) const = 0;
 
-	//! находится ли параллелепипед (описанный точками экстремума) во фрустуме
-	virtual bool XMETHODCALLTYPE isAABBvisible(const float3 &vMin, const float3 &vMax) const = 0;
+	//! находится ли параллелепипед во фрустуме
+	virtual bool XMETHODCALLTYPE isAABBvisible(const SMAABB &aabb) const = 0;
 };
 
 #endif
diff --git a/source/xcommon/resource/IXModel.h b/source/xcommon/resource/IXModel.h
index ec11f87e0978862a0227f09b56661ada084a9430..0c084c68da6e45dd1d9f6726c81749db80cd7abb 100644
--- a/source/xcommon/resource/IXModel.h
+++ b/source/xcommon/resource/IXModel.h
@@ -4,6 +4,15 @@
 #include <gdefines.h>
 #include "IXResourceModel.h"
 
+enum XMODEL_FEATURE
+{
+	MF_NONE = 0x0000,
+	MF_OPAQUE = 0x0001,
+	MF_TRANSPARENT = 0x0002,
+	MF_SELFILLUM = 0x0004
+};
+DEFINE_ENUM_FLAG_OPERATORS(XMODEL_FEATURE);
+
 class IXStaticModel;
 class IXDynamicModel;
 class IXAnimatedModel;
@@ -11,9 +20,9 @@ class IXAnimatedModel;
 class IXModel: public IXUnknown
 {
 public:
-	virtual IXAnimatedModel * XMETHODCALLTYPE asAnimatedModel() = 0;
-	virtual IXDynamicModel * XMETHODCALLTYPE asDynamicModel() = 0;
-	virtual IXStaticModel * XMETHODCALLTYPE asStaticModel() = 0;
+	virtual IXAnimatedModel* XMETHODCALLTYPE asAnimatedModel() = 0;
+	virtual IXDynamicModel* XMETHODCALLTYPE asDynamicModel() = 0;
+	virtual IXStaticModel* XMETHODCALLTYPE asStaticModel() = 0;
 
 
 	virtual float3 XMETHODCALLTYPE getPosition() const = 0;
@@ -36,13 +45,13 @@ public:
 	virtual void XMETHODCALLTYPE setColor(const float4 &vColor) = 0;
 
 	virtual UINT XMETHODCALLTYPE getPhysboxCount(UINT uPartIndex = 0) const = 0;
-	virtual const IModelPhysbox * XMETHODCALLTYPE getPhysBox(UINT id, UINT uPartIndex = 0) const = 0;
-	virtual const IXResourceModel * XMETHODCALLTYPE getResource(UINT uIndex = 0) = 0;
+	virtual const IModelPhysbox* XMETHODCALLTYPE getPhysBox(UINT id, UINT uPartIndex = 0) const = 0;
+	virtual const IXResourceModel* XMETHODCALLTYPE getResource(UINT uIndex = 0) = 0;
 
 	virtual bool XMETHODCALLTYPE isEnabled() const = 0;
 	virtual void XMETHODCALLTYPE enable(bool yesNo) = 0;
 
-	virtual void XMETHODCALLTYPE render(UINT uLod, bool isTransparent) = 0;
+	virtual void XMETHODCALLTYPE render(UINT uLod, XMODEL_FEATURE bmFeatures) = 0;
 };
 
 // Implemented in geom plugin
@@ -72,14 +81,14 @@ class IXAnimatedModel: public IXDynamicModel
 {
 public:
 	virtual UINT XMETHODCALLTYPE getPartsCount() const = 0;
-	virtual const char * XMETHODCALLTYPE getPartName(UINT uIndex) const = 0;
+	virtual const char* XMETHODCALLTYPE getPartName(UINT uIndex) const = 0;
 	virtual UINT XMETHODCALLTYPE getPartIndex(const char *szName) = 0;
 	virtual XMODEL_PART_FLAGS XMETHODCALLTYPE getPartFlags(UINT uIndex) const = 0;
 	virtual bool XMETHODCALLTYPE isPartEnabled(UINT uIndex) const = 0;
 	virtual void XMETHODCALLTYPE enablePart(UINT uIndex, bool yesNo) = 0;
 
 	virtual UINT XMETHODCALLTYPE getHitboxCount(UINT uPartIndex = 0) const = 0;
-	virtual const XResourceModelHitbox * XMETHODCALLTYPE getHitbox(UINT id, UINT uPartIndex = 0) const = 0;
+	virtual const XResourceModelHitbox* XMETHODCALLTYPE getHitbox(UINT id, UINT uPartIndex = 0) const = 0;
 
 	/*! Запускает воспроизведения анимации
 		@param[in] szName Имя анимации
@@ -145,7 +154,7 @@ public:
 	/*! Возвращает имя указанной кости
 		@param[in] id Номер кости
 	*/
-	virtual const char * XMETHODCALLTYPE getBoneName(UINT id) const = 0;
+	virtual const char* XMETHODCALLTYPE getBoneName(UINT id) const = 0;
 
 	/*! Проверяет, воспроизводится ли анимация
 	*/
@@ -167,7 +176,7 @@ public:
 	virtual void XMETHODCALLTYPE setController(UINT id, float fValue) = 0;
 
 	virtual UINT XMETHODCALLTYPE getControllersCount() const = 0;
-	virtual const char * XMETHODCALLTYPE getControllerName(UINT id) = 0;
+	virtual const char* XMETHODCALLTYPE getControllerName(UINT id) = 0;
 	virtual UINT XMETHODCALLTYPE getControllerId(const char *szName) = 0;
 
 	// Коллбек на изменение состояния анимации!