Browse Source

Add some missing functionality in deferred decals

Panagiotis Christopoulos Charitos 9 years ago
parent
commit
2e90a8bcf1

+ 16 - 13
README.md

@@ -10,17 +10,16 @@ OpenGL 4.5 and Vulkan 1.0 (Beta).
 License
 =======
 
-AnKi's license is BSD. This practicaly means that you can use the source or
-parts of the source on proprietary and non proprietary products as long as you
-follow the conditions of the license.
+AnKi's license is BSD. This practicaly means that you can use the source or parts of the source on proprietary and non 
+proprietary products as long as you follow the conditions of the license.
 
 See LICENSE file for more info.
 
 Building AnKi
 =============
 
-AnKi's build system is using CMake. A great effort was made to ease the building
-process that's why the number of external dependencies are almost none.
+AnKi's build system is using CMake. A great effort was made to ease the building process that's why the number of 
+external dependencies are almost none.
 
 On Linux
 --------
@@ -40,7 +39,7 @@ To build the release version:
 	$cmake ..
 	$make
 
-To view and configure the build options you can use ccmake tool or similar:
+To view and configure the build options you can use ccmake tool or other similar tool:
 
 	$cd <path_to_anki>/build
 	$ccmake .
@@ -55,9 +54,8 @@ Prerequisites:
 - CMake 2.8 and up
 - Mingw-w64 4.8 and up
 	- Install to a path without spaces
-	- Append the path where mingw's binaries are located (eg
-	  C:/mingw-w64/x86_64-4.9.3-win32-seh-rt_v4-rev1/mingw64/bin) to the PATH
-	  environment variable
+	- Append the path where mingw's binaries are located (eg C:/mingw-w64/x86_64-4.9.3-win32-seh-rt_v4-rev1/mingw64/bin)
+	  to the PATH environment variable
 
 To build the release version:
 
@@ -72,11 +70,16 @@ To build the release version:
 
 > NOTE: If you have a better way to build on Windows please let us know.
 
-> NOTE 2: The Windows build tends to brake often since Windows is not the 
-> primary developement platform. Please report any bugs.
+> NOTE 2: The Windows build tends to brake often since Windows is not the primary developement platform. Please report 
+> any bugs.
 
 Next steps
 ==========
 
-Try to build with samples enabled (see the relevant option in your CMake GUI) 
-and try running the simple_scene executable. More samples will follow.
+Try to build with samples enabled (see the relevant option in your CMake GUI, ANKI_BUILD_SAMPLES) and try running the 
+simple_scene executable. All samples must run from within the samples directory.
+
+	$cd path/to/anki/samples
+	$./path/to/build/samples/simple_scene/simple_scene
+
+More samples will follow.

+ 1 - 1
sandbox/Main.cpp

@@ -10,7 +10,7 @@
 
 using namespace anki;
 
-#define PLAYER 0
+#define PLAYER 1
 #define MOUSE 1
 
 class MyApp : public App

+ 21 - 15
shaders/Is.frag.glsl

@@ -21,6 +21,7 @@ layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_msRt2;
 layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2D u_msDepthRt;
 
 layout(ANKI_TEX_BINDING(1, 0)) uniform sampler2D u_diffDecalTex;
+layout(ANKI_TEX_BINDING(1, 1)) uniform sampler2D u_normalRoughnessDecalTex;
 
 layout(location = 0) in vec2 in_texCoord;
 layout(location = 1) flat in int in_instanceId;
@@ -61,7 +62,7 @@ void debugIncorrectColor(inout vec3 c)
 }
 
 // Compute the colors of a decal.
-void appendDecalColors(in Decal decal, in vec3 fragPos, inout vec3 diffuseColor)
+void appendDecalColors(in Decal decal, in vec3 fragPos, inout vec3 diffuseColor, inout float roughness)
 {
 	vec4 texCoords4 = decal.texProjectionMat * vec4(fragPos, 1.0);
 	vec2 texCoords2 = texCoords4.xy / texCoords4.w;
@@ -69,10 +70,14 @@ void appendDecalColors(in Decal decal, in vec3 fragPos, inout vec3 diffuseColor)
 	// Clamp the tex coords. Expect a border in the texture atlas
 	texCoords2 = clamp(texCoords2, 0.0, 1.0);
 
-	texCoords2 = texCoords2 * decal.uv.zw + decal.uv.xy;
-	vec4 dcol = texture(u_diffDecalTex, texCoords2);
+	vec2 diffUv = texCoords2 * decal.diffUv.zw + decal.diffUv.xy;
+	vec4 dcol = texture(u_diffDecalTex, diffUv);
+	diffuseColor = mix(diffuseColor, dcol.rgb, dcol.a * decal.blendFactors[0]);
 
-	diffuseColor = mix(diffuseColor, dcol.rgb, dcol.a);
+	// Roughness
+	vec2 roughnessUv = texCoords2 * decal.normRoughnessUv.zw + decal.normRoughnessUv.xy;
+	float r = texture(u_normalRoughnessDecalTex, roughnessUv).w;
+	roughness = mix(roughness, r, dcol.a * decal.blendFactors[1]);
 }
 
 void readIndirect(in uint idxOffset,
@@ -144,11 +149,6 @@ void main()
 	subsurface = gbuffer.subsurface;
 	emission = gbuffer.emission;
 
-	float a2 = pow(roughness, 2.0);
-
-	// Ambient and emissive color
-	out_color = diffCol * emission;
-
 	// Get counts and offsets
 	uint clusterIdx = computeClusterIndexUsingTileIdx(u_lightingUniforms.nearFarClustererMagicPad1.x,
 		u_lightingUniforms.nearFarClustererMagicPad1.z,
@@ -169,9 +169,14 @@ void main()
 	{
 		Decal decal = u_decals[u_lightIndices[idxOffset++]];
 
-		appendDecalColors(decal, fragPos, diffCol);
+		appendDecalColors(decal, fragPos, diffCol, roughness);
 	}
 
+	float a2 = pow(roughness, 2.0);
+
+	// Ambient and emissive color
+	out_color = diffCol * emission;
+
 	// Point lights
 	count = u_lightIndices[idxOffset++];
 	while(count-- != 0)
@@ -219,22 +224,23 @@ void main()
 #if INDIRECT_ENABLED
 	vec3 eye = -viewDir;
 	vec3 r = reflect(eye, normal);
-	float reflLod = float(IR_MIPMAP_COUNT) * gbuffer.roughness;
+	float reflLod = float(IR_MIPMAP_COUNT) * roughness;
 
 	vec3 specIndirect, diffIndirect;
 	readIndirect(idxOffset, fragPos, r, normal, reflLod, specIndirect, diffIndirect);
 
-	diffIndirect *= gbuffer.diffuse;
+	diffIndirect *= diffCol;
 
 	// Finalize the indirect specular
-	float ndotv = dot(gbuffer.normal, viewDir);
-	vec2 envBRDF = texture(u_integrationLut, vec2(gbuffer.roughness, ndotv)).xy;
-	specIndirect = specIndirect * (gbuffer.specular * envBRDF.x + envBRDF.y);
+	float ndotv = dot(normal, viewDir);
+	vec2 envBRDF = texture(u_integrationLut, vec2(roughness, ndotv)).xy;
+	specIndirect = specIndirect * (specCol * envBRDF.x + envBRDF.y);
 
 	out_color += specIndirect + diffIndirect;
 #endif
 
 #if 0
+	out_color = diffCol;
 	uint count = scount;
 	if(count == 0)
 	{

+ 3 - 1
shaders/IsFsCommon.glsl

@@ -51,8 +51,10 @@ struct ReflectionProbe
 // Decal
 struct Decal
 {
-	vec4 uv;
+	vec4 diffUv;
+	vec4 normRoughnessUv;
 	mat4 texProjectionMat;
+	vec4 blendFactors;
 };
 
 layout(ANKI_UBO_BINDING(LIGHT_SET, LIGHT_UBO_BINDING), std140, row_major) uniform u0_

+ 6 - 6
shaders/Pps.frag.glsl

@@ -139,15 +139,15 @@ void main()
 	out_color += bloom;
 #endif
 
-	out_color = colorGrading(out_color);
-
-#if DBG_ENABLED
-	out_color += textureLod(u_dbgRt, uv, 0.0).rgb;
-#endif
+// out_color = colorGrading(out_color);
 
 #if 0
 	{
-		out_color = textureLod(u_smaaBlendTex, uv, 0.0).rgb;
+		out_color = textureLod(u_isRt, uv, 0.0).rgb;
 	}
 #endif
+
+#if DBG_ENABLED
+	out_color += textureLod(u_dbgRt, uv, 0.0).rgb;
+#endif
 }

+ 12 - 2
src/anki/renderer/Is.cpp

@@ -185,7 +185,7 @@ Error Is::binLights(RenderingContext& ctx)
 {
 	updateCommonBlock(ctx);
 
-	TexturePtr diffDecalTex;
+	TexturePtr diffDecalTex, normRoughnessDecalTex;
 
 	ANKI_CHECK(m_lightBin->bin(*ctx.m_frustumComponent,
 		getFrameAllocator(),
@@ -197,7 +197,8 @@ Error Is::binLights(RenderingContext& ctx)
 		ctx.m_is.m_dynBufferInfo.m_uniformBuffers[DECALS_LOCATION],
 		ctx.m_is.m_dynBufferInfo.m_storageBuffers[CLUSTERS_LOCATION],
 		ctx.m_is.m_dynBufferInfo.m_storageBuffers[LIGHT_IDS_LOCATION],
-		diffDecalTex));
+		diffDecalTex,
+		normRoughnessDecalTex));
 
 	ResourceGroupInitInfo rcinit;
 	if(diffDecalTex)
@@ -210,6 +211,15 @@ Error Is::binLights(RenderingContext& ctx)
 		rcinit.m_textures[0].m_texture = m_dummyTex;
 	}
 
+	if(normRoughnessDecalTex)
+	{
+		rcinit.m_textures[1].m_texture = normRoughnessDecalTex;
+	}
+	else
+	{
+		rcinit.m_textures[1].m_texture = m_dummyTex;
+	}
+
 	U64 hash = rcinit.computeHash();
 	if(hash != m_rcGroup1Hash)
 	{

+ 30 - 7
src/anki/renderer/LightBin.cpp

@@ -66,8 +66,10 @@ public:
 class ShaderDecal
 {
 public:
-	Vec4 m_uv;
+	Vec4 m_diffUv;
+	Vec4 m_normRoughnessUv;
 	Mat4 m_texProjectionMat;
+	Vec4 m_blendFactors;
 };
 
 static const U MAX_TYPED_LIGHTS_PER_CLUSTER = 16;
@@ -317,6 +319,8 @@ public:
 
 	TexturePtr m_diffDecalTexAtlas;
 	SpinLock m_diffDecalTexAtlasMtx;
+	TexturePtr m_normalRoughnessDecalTexAtlas;
+	SpinLock m_normalRoughnessDecalTexAtlasMtx;
 
 	LightBin* m_bin = nullptr;
 };
@@ -363,7 +367,8 @@ Error LightBin::bin(FrustumComponent& frc,
 	TransientMemoryToken& decalsToken,
 	TransientMemoryToken& clustersToken,
 	TransientMemoryToken& lightIndicesToken,
-	TexturePtr& diffuseDecalTexAtlas)
+	TexturePtr& diffuseDecalTexAtlas,
+	TexturePtr& normalRoughnessDecalTexAtlas)
 {
 	ANKI_TRACE_START_EVENT(RENDERER_LIGHT_BINNING);
 
@@ -487,6 +492,7 @@ Error LightBin::bin(FrustumComponent& frc,
 	ANKI_CHECK(m_threadPool->waitForAllThreadsToFinish());
 
 	diffuseDecalTexAtlas = ctx.m_diffDecalTexAtlas;
+	normalRoughnessDecalTexAtlas = ctx.m_normalRoughnessDecalTexAtlas;
 
 	ANKI_TRACE_STOP_EVENT(RENDERER_LIGHT_BINNING);
 	return ErrorCode::NONE;
@@ -809,19 +815,36 @@ void LightBin::writeAndBinDecal(
 	I idx = ctx.m_decalCount.fetchAdd(1);
 	ShaderDecal& decal = ctx.m_decals[idx];
 
-	TexturePtr diffAtlas;
+	TexturePtr atlas;
 	Vec4 uv;
-	decalc.getDiffuseAtlasInfo(uv, diffAtlas);
-	decal.m_uv = Vec4(uv.x(), uv.y(), uv.z() - uv.x(), uv.w() - uv.y());
+	F32 blendFactor;
+	decalc.getDiffuseAtlasInfo(uv, atlas, blendFactor);
+	decal.m_diffUv = Vec4(uv.x(), uv.y(), uv.z() - uv.x(), uv.w() - uv.y());
+	decal.m_blendFactors[0] = blendFactor;
 
 	{
 		LockGuard<SpinLock> lock(ctx.m_diffDecalTexAtlasMtx);
-		if(ctx.m_diffDecalTexAtlas && ctx.m_diffDecalTexAtlas != diffAtlas)
+		if(ctx.m_diffDecalTexAtlas && ctx.m_diffDecalTexAtlas != atlas)
 		{
 			ANKI_LOGF("All decals should have the same tex atlas");
 		}
 
-		ctx.m_diffDecalTexAtlas = diffAtlas;
+		ctx.m_diffDecalTexAtlas = atlas;
+	}
+
+	decalc.getNormalRoughnessAtlasInfo(uv, atlas, blendFactor);
+	decal.m_normRoughnessUv = Vec4(uv.x(), uv.y(), uv.z() - uv.x(), uv.w() - uv.y());
+	decal.m_blendFactors[1] = blendFactor;
+
+	if(atlas)
+	{
+		LockGuard<SpinLock> lock(ctx.m_normalRoughnessDecalTexAtlasMtx);
+		if(ctx.m_normalRoughnessDecalTexAtlas && ctx.m_normalRoughnessDecalTexAtlas != atlas)
+		{
+			ANKI_LOGF("All decals should have the same tex atlas");
+		}
+
+		ctx.m_normalRoughnessDecalTexAtlas = atlas;
 	}
 
 	// bias * proj_l * view_l * world_c

+ 2 - 1
src/anki/renderer/LightBin.h

@@ -44,7 +44,8 @@ public:
 		TransientMemoryToken& decalsToken,
 		TransientMemoryToken& clustersToken,
 		TransientMemoryToken& lightIndicesToken,
-		TexturePtr& diffuseDecalTexAtlas);
+		TexturePtr& diffuseDecalTexAtlas,
+		TexturePtr& normalRoughnessDecalTexAtlas);
 
 	const Clusterer& getClusterer() const
 	{

+ 22 - 1
src/anki/scene/DecalComponent.h

@@ -33,6 +33,11 @@ public:
 		return setLayer(texAtlasFname, texAtlasSubtexName, blendFactor, LayerType::DIFFUSE);
 	}
 
+	ANKI_USE_RESULT Error setNormalRoughnessDecal(CString texAtlasFname, CString texAtlasSubtexName, F32 blendFactor)
+	{
+		return setLayer(texAtlasFname, texAtlasSubtexName, blendFactor, LayerType::NORMAL_ROUGHNESS);
+	}
+
 	/// Update the internal structures.
 	void updateShape(F32 width, F32 height, F32 depth)
 	{
@@ -81,10 +86,25 @@ public:
 		return m_biasProjViewMat;
 	}
 
-	void getDiffuseAtlasInfo(Vec4& uv, TexturePtr& tex) const
+	void getDiffuseAtlasInfo(Vec4& uv, TexturePtr& tex, F32& blendFactor) const
 	{
 		uv = m_layers[LayerType::DIFFUSE].m_uv;
 		tex = m_layers[LayerType::DIFFUSE].m_atlas->getGrTexture();
+		blendFactor = m_layers[LayerType::DIFFUSE].m_blendFactor;
+	}
+
+	void getNormalRoughnessAtlasInfo(Vec4& uv, TexturePtr& tex, F32& blendFactor) const
+	{
+		uv = m_layers[LayerType::NORMAL_ROUGHNESS].m_uv;
+		if(m_layers[LayerType::NORMAL_ROUGHNESS].m_atlas)
+		{
+			tex = m_layers[LayerType::NORMAL_ROUGHNESS].m_atlas->getGrTexture();
+		}
+		else
+		{
+			tex.reset(nullptr);
+		}
+		blendFactor = m_layers[LayerType::NORMAL_ROUGHNESS].m_blendFactor;
 	}
 
 	const Vec3& getVolumeSize() const
@@ -96,6 +116,7 @@ private:
 	enum class LayerType
 	{
 		DIFFUSE,
+		NORMAL_ROUGHNESS,
 		COUNT
 	};
 

+ 1 - 1
src/anki/script/LuaBinder.cpp

@@ -97,7 +97,7 @@ Error LuaBinder::evalString(const CString& str)
 	int e = luaL_dostring(m_l, &str[0]);
 	if(e)
 	{
-		ANKI_LOGE("%s", lua_tostring(m_l, -1));
+		ANKI_LOGE("%s (line:%d)", lua_tostring(m_l, -1));
 		lua_pop(m_l, 1);
 		err = ErrorCode::USER_DATA;
 	}

+ 59 - 0
src/anki/script/Scene.cpp

@@ -1167,6 +1167,64 @@ static int wrapDecalComponentsetDiffuseDecal(lua_State* l)
 	return 0;
 }
 
+/// Pre-wrap method DecalComponent::setNormalRoughnessDecal.
+static inline int pwrapDecalComponentsetNormalRoughnessDecal(lua_State* l)
+{
+	UserData* ud;
+	(void)ud;
+	void* voidp;
+	(void)voidp;
+	PtrSize size;
+	(void)size;
+
+	LuaBinder::checkArgsCount(l, 4);
+
+	// Get "this" as "self"
+	if(LuaBinder::checkUserData(l, 1, classnameDecalComponent, -1979693900066114370, ud))
+	{
+		return -1;
+	}
+
+	DecalComponent* self = ud->getData<DecalComponent>();
+
+	// Pop arguments
+	const char* arg0;
+	if(LuaBinder::checkString(l, 2, arg0))
+	{
+		return -1;
+	}
+
+	const char* arg1;
+	if(LuaBinder::checkString(l, 3, arg1))
+	{
+		return -1;
+	}
+
+	F32 arg2;
+	if(LuaBinder::checkNumber(l, 4, arg2))
+	{
+		return -1;
+	}
+
+	// Call the method
+	self->setNormalRoughnessDecal(arg0, arg1, arg2);
+
+	return 0;
+}
+
+/// Wrap method DecalComponent::setNormalRoughnessDecal.
+static int wrapDecalComponentsetNormalRoughnessDecal(lua_State* l)
+{
+	int res = pwrapDecalComponentsetNormalRoughnessDecal(l);
+	if(res >= 0)
+	{
+		return res;
+	}
+
+	lua_error(l);
+	return 0;
+}
+
 /// Pre-wrap method DecalComponent::updateShape.
 static inline int pwrapDecalComponentupdateShape(lua_State* l)
 {
@@ -1230,6 +1288,7 @@ static inline void wrapDecalComponent(lua_State* l)
 {
 	LuaBinder::createClass(l, classnameDecalComponent);
 	LuaBinder::pushLuaCFuncMethod(l, "setDiffuseDecal", wrapDecalComponentsetDiffuseDecal);
+	LuaBinder::pushLuaCFuncMethod(l, "setNormalRoughnessDecal", wrapDecalComponentsetNormalRoughnessDecal);
 	LuaBinder::pushLuaCFuncMethod(l, "updateShape", wrapDecalComponentupdateShape);
 	lua_settop(l, 0);
 }

+ 7 - 0
src/anki/script/Scene.xml

@@ -146,6 +146,13 @@ static SceneGraph* getSceneGraph(lua_State* l)
 						<arg>F32</arg>
 					</args>
 				</method>
+				<method name="setNormalRoughnessDecal">
+					<args>
+						<arg>CString</arg>
+						<arg>CString</arg>
+						<arg>F32</arg>
+					</args>
+				</method>
 				<method name="updateShape">
 					<args>
 						<arg>F32</arg>

+ 1 - 1
thirdparty

@@ -1 +1 @@
-Subproject commit d2210ab120d284b0b3413bb6b395948e3793d5fa
+Subproject commit 39e747eb0817641986d8ebf86288b313a4df0aa1

+ 12 - 6
tools/blender_anki_additions.patch

@@ -1,8 +1,8 @@
 diff --git a/source/blender/collada/EffectExporter.cpp b/source/blender/collada/EffectExporter.cpp
-index 13dc1ed..77d6a6e 100644
+index 76b5114..6b87128 100644
 --- a/source/blender/collada/EffectExporter.cpp
 +++ b/source/blender/collada/EffectExporter.cpp
-@@ -46,7 +46,9 @@ extern "C" {
+@@ -47,7 +47,9 @@ extern "C" {
  	#include "BKE_customdata.h"
  	#include "BKE_mesh.h"
  	#include "BKE_material.h"
@@ -12,7 +12,7 @@ index 13dc1ed..77d6a6e 100644
  
  // OB_MESH is assumed
  static std::string getActiveUVLayerName(Object *ob)
-@@ -169,6 +171,26 @@ void EffectsExporter::writeTextures(COLLADASW::EffectProfile &ep,
+@@ -170,6 +172,26 @@ void EffectsExporter::writeTextures(COLLADASW::EffectProfile &ep,
  		texture.setChildElementName("bump");
  		ep.addExtraTechniqueColorOrTexture(COLLADASW::ColorOrTexture(texture));
  	}
@@ -39,7 +39,7 @@ index 13dc1ed..77d6a6e 100644
  }
  
  void EffectsExporter::operator()(Material *ma, Object *ob)
-@@ -397,6 +419,67 @@ void EffectsExporter::operator()(Material *ma, Object *ob)
+@@ -398,6 +420,67 @@ void EffectsExporter::operator()(Material *ma, Object *ob)
  		}
  	}
  
@@ -108,7 +108,7 @@ index 13dc1ed..77d6a6e 100644
  	ep.addProfileElements();
  	bool twoSided = false;
 diff --git a/source/blender/collada/GeometryExporter.cpp b/source/blender/collada/GeometryExporter.cpp
-index 7c7c57f..00c909d 100644
+index 7c7c57f..b02ba47 100644
 --- a/source/blender/collada/GeometryExporter.cpp
 +++ b/source/blender/collada/GeometryExporter.cpp
 @@ -47,6 +47,7 @@ extern "C" {
@@ -119,7 +119,7 @@ index 7c7c57f..00c909d 100644
  }
  
  #include "collada_internal.h"
-@@ -143,13 +144,127 @@ void GeometryExporter::operator()(Object *ob)
+@@ -143,13 +144,133 @@ void GeometryExporter::operator()(Object *ob)
  			createPolylist(0, has_uvs, has_color, ob, me, geom_id, norind);
  		}
  	}
@@ -138,6 +138,12 @@ index 7c7c57f..00c909d 100644
 +			"reflection_proxy",
 +			"occluder",
 +			"collision_mesh",
++			"decal_diffuse_atlas",
++			"decal_diffuse_sub_texture",
++			"decal_diffuse_factor",
++			"decal_normal_roughness_atlas",
++			"decal_normal_roughness_sub_texture",
++			"decal_normal_roughness_factor",
 +			NULL};
 +
 +		ID *mesh_id = (ID*)ob->data;

+ 27 - 4
tools/scene/Exporter.cpp

@@ -829,7 +829,7 @@ void Exporter::visitNode(const aiNode* ainode)
 				collisionMesh = prop.second;
 				special = false;
 			}
-			else if(prop.first == "decal" && prop.second == "true")
+			else if(prop.first.find("decal_") == 0)
 			{
 				DecalNode decal;
 				for(const auto& pr : m_scene->mMeshes[meshIndex]->mProperties)
@@ -842,6 +842,22 @@ void Exporter::visitNode(const aiNode* ainode)
 					{
 						decal.m_diffuseSubTextureName = pr.second;
 					}
+					else if(pr.first == "decal_diffuse_factor")
+					{
+						decal.m_factors[0] = std::stof(pr.second);
+					}
+					else if(pr.first == "decal_normal_roughness_atlas")
+					{
+						decal.m_normalRoughnessAtlasFilename = pr.second;
+					}
+					else if(pr.first == "decal_normal_roughness_sub_texture")
+					{
+						decal.m_normalRoughnessSubTextureName = pr.second;
+					}
+					else if(pr.first == "decal_normal_roughness_factor")
+					{
+						decal.m_factors[1] = std::stof(pr.second);
+					}
 				}
 
 				if(decal.m_diffuseTextureAtlasFilename.empty() || decal.m_diffuseSubTextureName.empty())
@@ -849,13 +865,14 @@ void Exporter::visitNode(const aiNode* ainode)
 					ERROR("Missing decal information");
 				}
 
-				aiMatrix4x4 trf = ainode->mTransformation;
+				aiMatrix4x4 trf = toAnkiMatrix(ainode->mTransformation);
 				decal.m_size = getNonUniformScale(trf);
 				removeScale(trf);
 				decal.m_transform = trf;
 
 				m_decals.push_back(decal);
 				special = true;
+				break;
 			}
 		}
 
@@ -1047,11 +1064,17 @@ void Exporter::exportAll()
 
 		writeNodeTransform("node", decal.m_transform);
 
-		file << "decalc = node:getSceneNodeBase():getLightComponent()\n";
+		file << "decalc = node:getSceneNodeBase():getDecalComponent()\n";
 		file << "decalc:setDiffuseDecal(\"" << decal.m_diffuseTextureAtlasFilename << "\", \""
-			 << decal.m_diffuseSubTextureName << "\", 1.0)\n";
+			 << decal.m_diffuseSubTextureName << "\", " << decal.m_factors[0] << ")\n";
 		file << "decalc:updateShape(" << decal.m_size.x << ", " << decal.m_size.y << ", " << decal.m_size.z << ")\n";
 
+		if(!decal.m_normalRoughnessAtlasFilename.empty())
+		{
+			file << "decalc:setNormalRoughnessDecal(\"" << decal.m_normalRoughnessAtlasFilename << "\", \""
+				 << decal.m_normalRoughnessSubTextureName << "\", " << decal.m_factors[1] << ")\n";
+		}
+
 		++i;
 	}
 

+ 3 - 0
tools/scene/Exporter.h

@@ -97,7 +97,10 @@ public:
 	aiMatrix4x4 m_transform;
 	std::string m_diffuseTextureAtlasFilename;
 	std::string m_diffuseSubTextureName;
+	std::string m_normalRoughnessAtlasFilename;
+	std::string m_normalRoughnessSubTextureName;
 	aiVector3D m_size;
+	std::array<float, 2> m_factors = {{1.0, 1.0}};
 };
 
 /// AnKi exporter.

+ 0 - 1
tools/texture/convert_image.py

@@ -285,7 +285,6 @@ def identify_image(in_file):
 	width = 0
 	height = 0
 
-	print(["identify", "-verbose" , in_file])
 	proc = subprocess.Popen(["identify", "-verbose" , in_file], stdout=subprocess.PIPE)
 
 	stdout_str = proc.stdout.read()