[2.2] normal pbs reflection / help needed / gltf loader

Discussion area about developing with Ogre2 branches (2.1, 2.2 and beyond)
Post Reply
screwt
Kobold
Posts: 29
Joined: Wed Feb 28, 2007 9:18 am
x 2

[2.2] normal pbs reflection / help needed / gltf loader

Post by screwt »

I there, i'm tring to port the Ybalrid gltf loader to 2.2.

https://github.com/Ybalrid/Ogre_glTF

You can see the progress here:

https://github.com/screwt/Ogre_glTF/tree/ogre_v2.2.4

But i'm stuck with the normal mapping.
I think i'm missing something with the scene, lghts or renderSystem init, thus reflection is messed up:

Image

On the above capture, i removed all textures but the normal.

The scene is initialized here: https://github.com/screwt/Ogre_glTF/blo ... h/main.cpp
The model is there: https://github.com/screwt/Ogre_glTF/tre ... agedHelmet

Could you give me some advice?

-- EDIT --

Here is what happen with a sphere (debugPack.zip) and the "floor_bump.PNG" from Ogre Samples.

Image

Loading code:

Code: Select all


	camera->setNearClipDistance(0.001f);
	camera->setFarClipDistance(100);
	camera->setPosition(2.5f, 0.6f, 2.5f);
	camera->lookAt({ 0, -0.1f, 0 });
	camera->setAutoAspectRatio(true);

	auto light = smgr->createLight();
	smgr->getRootSceneNode()->createChildSceneNode()->attachObject(light);
	light->setType(Ogre::Light::LT_DIRECTIONAL);
	light->setDirection(Ogre::Vector3 { -1, -1, -0.5f });
	light->setPowerScale(5);
	
	light = smgr->createLight();
	smgr->getRootSceneNode()->createChildSceneNode()->attachObject(light);
	light->setDiffuseColour(0.8f, 0.4f, 0.2f); //Warm
	light->setSpecularColour(0.8f, 0.4f, 0.2f);
	light->setPowerScale(20);
	light->setType(Ogre::Light::LT_POINT);
	light->getParentNode()->setPosition(0, -5, 0);

	Ogre::Item* item = smgr->createItem(
		"Sphere1000.mesh", Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, Ogre::SCENE_DYNAMIC);

	auto hlmsPbs = static_cast<Ogre::HlmsPbs*>(Ogre::Root::getSingleton().getHlmsManager()->getHlms(Ogre::HlmsTypes::HLMS_PBS));
	Ogre::String datablockName		  = "RefractiveWall";
	Ogre::HlmsPbsDatablock* datablock = static_cast<Ogre::HlmsPbsDatablock*>(
		hlmsPbs->createDatablock(datablockName, datablockName, Ogre::HlmsMacroblock(), Ogre::HlmsBlendblock(), Ogre::HlmsParamVec()));
	// Assign a normal map so the refractions are much more visually pleasing (and obvious)
	datablock->setTexture(Ogre::PBSM_NORMAL, "floor_bump.PNG");

	item->setDatablock(datablock);

	Ogre::SceneNode* sphereNode = smgr->getRootSceneNode(Ogre::SCENE_DYNAMIC)->createChildSceneNode(Ogre::SCENE_DYNAMIC);
	sphereNode->setPosition(1.5, 0, -0.2);
	sphereNode->attachObject(item);


Scene init code:

Code: Select all

        root->getRenderSystem()->setConfigOption("sRGB Gamma Conversion", "Yes");
	Ogre::Window* window = root->initialise(true, "Gltf loader sample");
	
	auto smgr = root->createSceneManager(Ogre::ST_GENERIC, 2);
	smgr->showBoundingBoxes(true);
	smgr->setDisplaySceneNodes(true);
	auto camera = smgr->createCamera("cam");
	//Setup rendering pipeline
	auto compositor			   = root->getCompositorManager2();
	const char workspaceName[] = "workspace0";
	compositor->createBasicWorkspaceDef(workspaceName, Ogre::ColourValue { 0.2f, 0.3f, 0.4f });
	auto workspace = compositor->addWorkspace(smgr, window->getTexture(), camera, workspaceName, true);

	DeclareHlmsLibrary("../Data");
-- EDIT --

Here is what i have with a fresh start from EmptyProject sample:

Image

Also strange isn't it?

How i load the model:

Code: Select all

        mCameraController = new CameraController( mGraphicsSystem, false );

		auto smgr = mGraphicsSystem->getSceneManager();
		auto light = smgr->createLight();
		smgr->getRootSceneNode()->createChildSceneNode()->attachObject(light);
		light->setType(Ogre::Light::LT_DIRECTIONAL);
		light->setDirection(Ogre::Vector3{ -1, -1, -0.5f });
		light->setPowerScale(5);

		light = smgr->createLight();
		smgr->getRootSceneNode()->createChildSceneNode()->attachObject(light);
		light->setDiffuseColour(0.8f, 0.4f, 0.2f); //Warm
		light->setSpecularColour(0.8f, 0.4f, 0.2f);
		light->setPowerScale(20);
		light->setType(Ogre::Light::LT_POINT);
		light->getParentNode()->setPosition(0, -5, 0);

		mSphere = smgr->createItem(
			"Sphere1000.mesh", Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, Ogre::SCENE_DYNAMIC);

		auto hlmsPbs = static_cast<Ogre::HlmsPbs*>(Ogre::Root::getSingleton().getHlmsManager()->getHlms(Ogre::HlmsTypes::HLMS_PBS));
		Ogre::String datablockName = "RefractiveWall";
		Ogre::HlmsPbsDatablock* datablock = static_cast<Ogre::HlmsPbsDatablock*>(
			hlmsPbs->createDatablock(datablockName, datablockName, Ogre::HlmsMacroblock(), Ogre::HlmsBlendblock(), Ogre::HlmsParamVec()));
		// Assign a normal map so the refractions are much more visually pleasing (and obvious)
		datablock->setTexture(Ogre::PBSM_NORMAL, "floor_bump.PNG");

		mSphere->setDatablock(datablock);

		Ogre::SceneNode* sphereNode = smgr->getRootSceneNode(Ogre::SCENE_DYNAMIC)->createChildSceneNode(Ogre::SCENE_DYNAMIC);
		sphereNode->setPosition(1.5, 0, -0.2);
		sphereNode->attachObject(mSphere);


		/*
		light->setType(Ogre::Light::LT_SPOTLIGHT);
		light->setDirection(Ogre::Vector3 { 0, 0, -1 });
		light->setPowerScale(5);
		light->getParentNode()->setPosition(0, 0, 5);
		*/

		light = smgr->createLight();
		smgr->getRootSceneNode()->createChildSceneNode()->attachObject(light);
		light->setType(Ogre::Light::LT_DIRECTIONAL);
		light->setDirection(Ogre::Vector3{ +1, +1, +0.5f });
		light->setPowerScale(5);

Last edited by screwt on Sat Oct 17, 2020 2:34 pm, edited 1 time in total.

duck2
Gnoblar
Posts: 6
Joined: Wed Jul 31, 2019 11:45 am
x 5

Re: [2.2] normal pbs reflection / help needed

Post by duck2 »

I can't build your project because you are still using OgreTexture.h instead of OgreTextureGpu.h, but i think i know your problem.
Normal mapping requires tangent vectors. None of official gltf samples have tangent vector and Ogre doesn't have tangent generator yet.

You can use this simple library: https://github.com/mlimper/tgen.
Just drop tgen.h and tgen.cpp into your project and call tgen::computeCornerTSpace with vertex positions, texcoords, and triangle indices from gltf.

screwt
Kobold
Posts: 29
Joined: Wed Feb 28, 2007 9:18 am
x 2

Re: [2.2] normal pbs reflection / help needed

Post by screwt »

I can't build your project because you are still using OgreTexture.h instead of OgreTextureGpu.h
Did you checkout the ogre_v2.2.4 branch?

I'll look to the tip you gave me today.

Thanks for your help :)

duck2
Gnoblar
Posts: 6
Joined: Wed Jul 31, 2019 11:45 am
x 5

Re: [2.2] normal pbs reflection / help needed

Post by duck2 »

Did you checkout the ogre_v2.2.4 branch?
ah... silly me :oops:

Some sample doesn't even have normals, so you'll need normal generator too.
I implemented almost every non-extension gltf features except occlusion map (not supported by Ogre) and interpolation animation (too lazy).
Fell free to ask if you have another gltf problem.
Good luck.
Last edited by duck2 on Sat Oct 17, 2020 10:01 am, edited 1 time in total.

screwt
Kobold
Posts: 29
Joined: Wed Feb 28, 2007 9:18 am
x 2

Re: [2.2] normal pbs reflection / help needed

Post by screwt »

Thinking about it the gltf sample i use comes from here:

https://github.com/Ybalrid/Ogre_glTF/tr ... agedHelmet

And it works pretty good using Ogre_glTF (https://github.com/Ybalrid/Ogre_glTF/) with ogre v2.1.

I think the model is ok, it's probably a material probleme.

I'm trying to build simple sample to reproduce the problème easily.
i implemented almost every non-extension gltf features
Have you done a gltf plugin for Ogre? (that loads gltf file and is opensource)


-- EDIT --

I'm doing progress, the normal mapping was bad (i tried to convert the input texture but badly i think)
Now you can see the texture is mapped correctly. Have a look to the pipe on the back of the helmet.

This is what i get applying normal texture only using ogre2.2.
video: https://raw.githubusercontent.com/screw ... er/out.mp4

This is how it looks like using ogre2.1 (normal texture only)
video: https://raw.githubusercontent.com/screw ... r/out2.mp4

Still a problem, i guess it s a material probleme or a scene setting problem...
Seems like a metalic effect is applied on the 2.2 version.

in both case the datablock is created this way:

Code: Select all

	datablock = static_cast<Ogre::HlmsPbsDatablock*>(HlmsPbs->createDatablock(
	     Ogre::IdString(material.name),
	     material.name,
	     Ogre::HlmsMacroblock {},
	     Ogre::HlmsBlendblock {},
	     Ogre::HlmsParamVec {}));
	datablock->setWorkflow(Ogre::HlmsPbsDatablock::Workflows::MetallicWorkflow);
	// ogre v2.1
	datablock->setTexture(Ogre::PbsTextureTypes::PBSM_NORMAL, 0, texture);
	// ogre v2.2
	datablock->setTexture(Ogre::PbsTextureTypes::PBSM_NORMAL, texture);


then it's applied this way

Code: Select all

subItem->setDatablock(datablock);
-- EDIT --

Still looking for the answer...
I had a look to the material hlms scripts, there is many changes from 2.1 to 2.2 i think this explains the differences between my two tests.
BTW i'm a bit lost in all those scripts, how they are loaded wich one in which case...

duck2
Gnoblar
Posts: 6
Joined: Wed Jul 31, 2019 11:45 am
x 5

Re: [2.2] normal pbs reflection / help needed / gltf loader

Post by duck2 »

Have you done a gltf plugin for Ogre? (that loads gltf file and is opensource)
Mine is not opensource and tighly integrated with our custom engine. Sorry.


For normal map you need to use Ogre::TextureFilter::TypePrepareForNormalMapping.

Code: Select all

Ogre::TextureGpu* ogreTexture = textureManager->createTexture(
	name,
	Ogre::GpuPageOutStrategy::Discard,
	Ogre::TextureFlags::ManualTexture | Ogre::TextureFlags::AutomaticBatching, 
	Ogre::TextureTypes::Type2D,
	Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
	Ogre::TextureFilter::TypePrepareForNormalMapping | Ogre::TextureFilter::TypeGenerateDefaultMipmaps);

ogreTexture->setResolution(width, height);
ogreTexture->setPixelFormat(format);
ogreTexture->setNumMipmaps(Ogre::PixelFormatGpuUtils::getMaxMipmapCount(width, height, 1));
ogreTexture->_transitionTo(Ogre::GpuResidency::Resident, nullptr);
ogreTexture->_setNextResidencyStatus(Ogre::GpuResidency::Resident);

auto sizeInBytes = Ogre::PixelFormatGpuUtils::calculateSizeBytes(width, height, 1, 1, format, 1, 4);
auto buffer = reinterpret_cast<std::uint8_t*>(OGRE_MALLOC_SIMD(sizeInBytes, Ogre::MEMCATEGORY_RESOURCE));

Ogre::Image2 ogreImage;
ogreImage.loadDynamicImage(buffer, width, height, 1, Ogre::TextureTypes::Type2D, format, true, 1);
std::memcpy(buffer, dataFromGLTF, sizeInBytes);
ogreImage.generateMipmaps(ogreTexture->prefersLoadingFromFileAsSRGB(), Ogre::Image2::FILTER_GAUSSIAN_HIGH);
ogreImage.uploadTo(ogreTexture, 0, ogreImage.getNumMipmaps() - 1);

I tried to extract some code without my engine parts. It may or may not works.
It would be better if you commit your progress on github, so i can test it in my system.
BTW i'm a bit lost in all those scripts, how they are loaded wich one in which case...
Me too :lol:
I just copy paste every script everytime i need to update Ogre.
Ogre already provides almost all gltf features out of the box if you know where to look, no need to touch HLMS scripts.
IMO the biggest Ogre problem right now is the legacy v1 that makes everything double complicated. I spent 2 weeks to make morph animation works.
Last edited by duck2 on Sat Oct 17, 2020 6:04 pm, edited 2 times in total.

duck2
Gnoblar
Posts: 6
Joined: Wed Jul 31, 2019 11:45 am
x 5

Re: [2.2] normal pbs reflection / help needed / gltf loader

Post by duck2 »

I'll help you make gltf loader by posting code snippets here.
We'll use official gltf 2.0 samples as milestone.

Why not Pull Request? Because the knowledge will be drown in the sea of code and google can't help you.
Things that i will post are actually pretty basic stuff and can be used outside gltf.
The gods and goddess of Ogre are too busy creating advanced stuff, they forget to teach us the peasants how to make simple stuff.

@screwt I'll need your help to stitch together the code until we have a proper gltf loader. You can take all the credits.
This duck2 account is supposed to be a throwaway account, no need to give this account any credits.

screwt
Kobold
Posts: 29
Joined: Wed Feb 28, 2007 9:18 am
x 2

Re: [2.2] normal pbs reflection / help needed / gltf loader

Post by screwt »

Thanks for the snippet.

I tried it:

https://github.com/screwt/Ogre_glTF/blo ... r.cpp#L205

But i have the exact same result.

screwt
Kobold
Posts: 29
Joined: Wed Feb 28, 2007 9:18 am
x 2

Re: [2.2] normal pbs reflection / help needed / gltf loader

Post by screwt »

Ok it works see : https://github.com/screwt/Ogre_glTF/tree/ogre_v2.2.4

I have to setup the roughness & metalness texture now.

duck2
Gnoblar
Posts: 6
Joined: Wed Jul 31, 2019 11:45 am
x 5

Re: [2.2] normal pbs reflection / help needed / gltf loader

Post by duck2 »

As i promised before, i've extracted majority gltf loader code from my engine.

First is the mesh loader.
Like i said before, gltf models may not have tangents so we'll use this library https://github.com/mlimper/tgen.
In tgen.h file, we need to change some code from

Code: Select all

typedef std::size_t VIndexT;
typedef double RealT;
into

Code: Select all

typedef std::uint32_t VIndexT;
typedef float RealT;
Now we are ready.

This is how i create mesh from scratch:

Code: Select all

Ogre::IndexBufferPacked* CreateManualIndexBuffer(Ogre::IndexBufferPacked::IndexType indexType, const void* data, int indexCount)
{
	auto typeSize = indexType == Ogre::IndexBufferPacked::IT_16BIT ? sizeof(std::uint16_t) : sizeof(std::uint32_t);

	auto simdBuffer = OGRE_MALLOC_SIMD(indexCount * typeSize, Ogre::MEMCATEGORY_GEOMETRY);
	std::memcpy(simdBuffer, data, indexCount * typeSize);

	auto indexBuffer = Ogre::Root::getSingleton().getRenderSystem()->getVaoManager()->createIndexBuffer(indexType, indexCount, Ogre::BT_IMMUTABLE, simdBuffer, false);

	OGRE_FREE_SIMD(simdBuffer, Ogre::MEMCATEGORY_GEOMETRY);

	return indexBuffer;
}

Ogre::VertexBufferPacked* CreateManualVertexBuffer(Ogre::VertexElement2Vec vertexElements, const void* data, int vertexCount)
{
	auto vertexSize = Ogre::VaoManager::calculateVertexSize(vertexElements);

	auto simdBuffer = OGRE_MALLOC_SIMD(vertexCount * vertexSize, Ogre::MEMCATEGORY_GEOMETRY);
	std::memcpy(simdBuffer, data, vertexCount * vertexSize);

	auto vertexBuffer = Ogre::Root::getSingleton().getRenderSystem()->getVaoManager()->createVertexBuffer(vertexElements, vertexCount, Ogre::BT_IMMUTABLE, simdBuffer, false);

	OGRE_FREE_SIMD(simdBuffer, Ogre::MEMCATEGORY_GEOMETRY);

	return vertexBuffer;
}

Ogre::SubMesh* CreateManualSubMesh(Ogre::MeshPtr mesh, const Ogre::VertexBufferPackedVec& vertexBufferList, Ogre::IndexBufferPacked* indexBuffer, Ogre::OperationType operationType)
{
	auto subMesh = mesh->createSubMesh();
	auto ogreVAO = Ogre::Root::getSingleton().getRenderSystem()->getVaoManager()->createVertexArrayObject(vertexBufferList, indexBuffer, operationType);

	subMesh->mVao[Ogre::VpNormal].push_back(ogreVAO);
	subMesh->mVao[Ogre::VpShadow].push_back(ogreVAO);

	return subMesh;
}
Helper functions:

Code: Select all

Ogre::VertexElementType GetVertexElementTypeFromGLTF(int componentType, int componentCount)
{
	switch (componentType) {
	case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: {
		switch (componentCount) {
		case 2: return Ogre::VET_USHORT2;
		case 4: return Ogre::VET_USHORT4;
		default:
			OGRE_EXCEPT(Ogre::Exception::ERR_NOT_IMPLEMENTED, "", __FUNCTION__);
			break;
		}
		break;
	}
	case TINYGLTF_COMPONENT_TYPE_FLOAT: {
		switch (componentCount) {
		case 2: return Ogre::VET_FLOAT2;
		case 3: return Ogre::VET_FLOAT3;
		case 4: return Ogre::VET_FLOAT4;
		default:
			OGRE_EXCEPT(Ogre::Exception::ERR_NOT_IMPLEMENTED, "", __FUNCTION__);
			break;
		}
		break;
	}
	default:
		OGRE_EXCEPT(Ogre::Exception::ERR_NOT_IMPLEMENTED, "", __FUNCTION__);
		break;
	}
}

Ogre::VertexElementSemantic GetVertexElementSemanticFromGLTF(std::string gltfSemanticName)
{
	if (gltfSemanticName == "POSITION")
		return Ogre::VES_POSITION;
	else if (gltfSemanticName == "NORMAL")
		return Ogre::VES_NORMAL;
	else if (gltfSemanticName == "TANGENT")
		return Ogre::VES_TANGENT;
	else if (gltfSemanticName == "TEXCOORD_0")
		return Ogre::VES_TEXTURE_COORDINATES;
	else if (gltfSemanticName == "TEXCOORD_1")
		return Ogre::VES_TEXTURE_COORDINATES;
	else if (gltfSemanticName == "COLOR_0")
		return Ogre::VES_DIFFUSE;
	else if (gltfSemanticName == "JOINTS_0")
		return Ogre::VES_BLEND_INDICES;
	else if (gltfSemanticName == "WEIGHTS_0")
		return Ogre::VES_BLEND_WEIGHTS;
	else
		OGRE_EXCEPT(Ogre::Exception::ERR_NOT_IMPLEMENTED, "", __FUNCTION__);
}
The mesh loader function:

Code: Select all

Ogre::MeshPtr CreateMeshFromGLTF(const std::string& meshName, const tinygltf::Model& gltfModel, const tinygltf::Mesh& gltfMesh, bool needVertexNormal)
{
	auto mesh = Ogre::MeshManager::getSingleton().getByName(meshName);
	if (mesh != nullptr)
		return mesh;

	mesh = Ogre::MeshManager::getSingletonPtr()->createManual(meshName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

	Ogre::Aabb boundingBox;
	for (auto primitiveIndex = 0; primitiveIndex < gltfMesh.primitives.size(); ++primitiveIndex) {
		auto& gltfPrimitive = gltfMesh.primitives[primitiveIndex];

		auto vertexCount = 0;
		Ogre::VertexElement2Vec vertexElements;
		for (auto& [gltfSemanticName, accessorIndex] : gltfPrimitive.attributes) {
			auto& gltfAccessor = gltfModel.accessors[accessorIndex];

			auto type = GetVertexElementTypeFromGLTF(gltfAccessor.componentType, tinygltf::GetNumComponentsInType(gltfAccessor.type));
			auto semantic = GetVertexElementSemanticFromGLTF(gltfSemanticName);
			vertexElements.emplace_back(Ogre::VertexElement2(type, semantic));

			vertexCount = gltfAccessor.count;
		}

		bool needGenerateNormals = needVertexNormal;
		bool needGenerateTangents = true;

		std::vector<std::uint32_t> triangleIndices;
		std::vector<std::uint16_t> joints;
		std::vector<float> weights;

		auto HasSemantic = [&](Ogre::VertexElementSemantic semantic) {
			for (auto&& element : vertexElements)
				if (element.mSemantic == semantic)
					return true;
			return false;
		};

		needGenerateNormals &= !HasSemantic(Ogre::VES_NORMAL);
		if (needGenerateNormals)
			vertexElements.emplace_back(Ogre::VertexElement2(Ogre::VET_FLOAT3, Ogre::VES_NORMAL));

		needGenerateTangents &= HasSemantic(Ogre::VES_POSITION);
		needGenerateTangents &= HasSemantic(Ogre::VES_TEXTURE_COORDINATES);
		needGenerateTangents &= !HasSemantic(Ogre::VES_TANGENT);
		if (needGenerateTangents)
			vertexElements.emplace_back(Ogre::VertexElement2(Ogre::VET_FLOAT3, Ogre::VES_TANGENT));

		Ogre::IndexBufferPacked* indexBuffer = nullptr;
		if (gltfPrimitive.indices >= 0) {
			auto& gltfAccessor = gltfModel.accessors[gltfPrimitive.indices];
			auto& gltfBufferView = gltfModel.bufferViews[gltfAccessor.bufferView];
			auto& gltfBuffer = gltfModel.buffers[gltfBufferView.buffer];

			auto byteStride = gltfAccessor.ByteStride(gltfBufferView);
			auto indexCount = gltfAccessor.count;

			auto sourceOffset = gltfBuffer.data.data() + gltfBufferView.byteOffset + gltfAccessor.byteOffset;
			switch (gltfAccessor.componentType) {
			case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE: {
				std::vector<std::uint16_t> buffer(indexCount);
				for (auto i = 0; i < indexCount; ++i)
					buffer[i] = *reinterpret_cast<const std::uint8_t*>(sourceOffset + i * byteStride);
				indexBuffer = CreateManualIndexBuffer(Ogre::IndexBufferPacked::IT_16BIT, buffer.data(), indexCount);

				if (needGenerateNormals || needGenerateTangents) {
					triangleIndices.resize(indexCount);
					std::copy(std::begin(buffer), std::end(buffer), std::begin(triangleIndices));
				}
				break;
			}
			case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: {
				std::vector<std::uint16_t> buffer(indexCount);
				std::memcpy(buffer.data(), sourceOffset, indexCount * byteStride);
				indexBuffer = CreateManualIndexBuffer(Ogre::IndexBufferPacked::IT_16BIT, buffer.data(), indexCount);

				if (needGenerateNormals || needGenerateTangents) {
					triangleIndices.resize(indexCount);
					std::copy(std::begin(buffer), std::end(buffer), std::begin(triangleIndices));
				}
				break;
			}
			case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: {
				std::vector<std::uint32_t> buffer(indexCount);
				std::memcpy(buffer.data(), sourceOffset, indexCount * byteStride);
				indexBuffer = CreateManualIndexBuffer(Ogre::IndexBufferPacked::IT_32BIT, buffer.data(), indexCount);

				if (needGenerateNormals || needGenerateTangents) {
					triangleIndices.resize(indexCount);
					std::copy(std::begin(buffer), std::end(buffer), std::begin(triangleIndices));
				}
				break;
			}
			default:
				OGRE_EXCEPT(Ogre::Exception::ERR_NOT_IMPLEMENTED, "", __FUNCTION__);
				break;
			}
		} else {
			if (vertexCount < std::numeric_limits<std::uint16_t>::max()) {
				std::vector<std::uint16_t> buffer(vertexCount);
				for (auto i = 0; i < vertexCount; ++i)
					buffer[i] = i;
				indexBuffer = CreateManualIndexBuffer(Ogre::IndexBufferPacked::IT_16BIT, buffer.data(), vertexCount);
			} else {
				std::vector<std::uint32_t> buffer(vertexCount);
				for (auto i = 0; i < vertexCount; ++i)
					buffer[i] = i;
				indexBuffer = CreateManualIndexBuffer(Ogre::IndexBufferPacked::IT_32BIT, buffer.data(), vertexCount);
			}

			if (needGenerateNormals || needGenerateTangents) {
				triangleIndices.resize(vertexCount);
				for (auto i = 0; i < vertexCount; ++i)
					triangleIndices[i] = i;
			}
		}

		auto elementStride = 0;
		auto vertexSize = Ogre::VaoManager::calculateVertexSize(vertexElements);

		std::vector<char> buffer(vertexCount* vertexSize);
		std::vector<float> positions(vertexCount * 3);
		std::vector<float> uvs(vertexCount * 2);
		std::vector<float> normals(vertexCount * 3);
		for (auto& [gltfSemanticName, accessorIndex] : gltfPrimitive.attributes) {
			auto& gltfAccessor = gltfModel.accessors[accessorIndex];
			auto& gltfBufferView = gltfModel.bufferViews[gltfAccessor.bufferView];
			auto& gltfBuffer = gltfModel.buffers[gltfBufferView.buffer];

			auto byteStride = gltfAccessor.ByteStride(gltfBufferView);
			auto componentSize = tinygltf::GetComponentSizeInBytes(gltfAccessor.componentType);
			auto componentCount = tinygltf::GetNumComponentsInType(gltfAccessor.type);

			auto attributeSize = componentSize * componentCount;
			auto destinationOffset = buffer.data() + elementStride;
			auto sourceOffset = gltfBuffer.data.data() + gltfBufferView.byteOffset + gltfAccessor.byteOffset;
			for (auto i = 0; i < vertexCount; ++i)
				std::memcpy(destinationOffset + i * vertexSize, sourceOffset + i * byteStride, attributeSize);

			if (gltfAccessor.sparse.isSparse) {
				std::vector<std::uint32_t> sparseIndices;
				{
					auto& gltfIndicesBufferView = gltfModel.bufferViews[gltfAccessor.sparse.indices.bufferView];
					auto& gltfIndicesBuffer = gltfModel.buffers[gltfIndicesBufferView.buffer];

					auto indicesByteStride = tinygltf::GetComponentSizeInBytes(gltfAccessor.sparse.indices.componentType);
					auto indicesSourceOffset = gltfIndicesBuffer.data.data() + gltfIndicesBufferView.byteOffset + gltfAccessor.sparse.indices.byteOffset;

					sparseIndices.resize(gltfAccessor.sparse.count);
					switch (gltfAccessor.sparse.indices.componentType) {
					case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE:
						for (auto i = 0; i < gltfAccessor.sparse.count; ++i)
							sparseIndices[i] = *reinterpret_cast<const std::uint8_t*>(indicesSourceOffset + i * indicesByteStride);
						break;
					case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
						for (auto i = 0; i < gltfAccessor.sparse.count; ++i)
							sparseIndices[i] = *reinterpret_cast<const std::uint16_t*>(indicesSourceOffset + i * indicesByteStride);
						break;
					case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
						std::memcpy(sparseIndices.data(), sourceOffset, gltfAccessor.sparse.count * indicesByteStride);
						break;
					default:
						OGRE_EXCEPT(Ogre::Exception::ERR_NOT_IMPLEMENTED, "", __FUNCTION__);
						break;
					}
				}

				// Replace vertices
				{
					auto& gltfValuesBufferView = gltfModel.bufferViews[gltfAccessor.sparse.values.bufferView];
					auto& gltfValuesBuffer = gltfModel.buffers[gltfValuesBufferView.buffer];

					auto valuesByteStride = gltfAccessor.ByteStride(gltfValuesBufferView);
					auto valuesSourceOffset = gltfValuesBuffer.data.data() + gltfValuesBufferView.byteOffset + gltfAccessor.sparse.values.byteOffset;

					for (auto i = 0; i < gltfAccessor.sparse.count; ++i)
						std::memcpy(destinationOffset + sparseIndices[i] * vertexSize, valuesSourceOffset + i * valuesByteStride, attributeSize);
				}
			}

			if (gltfSemanticName == "POSITION") {
				Ogre::Vector3 minimumPoint(gltfAccessor.minValues[0], gltfAccessor.minValues[1], gltfAccessor.minValues[2]);
				Ogre::Vector3 maximumPoint(gltfAccessor.maxValues[0], gltfAccessor.maxValues[1], gltfAccessor.maxValues[2]);
				if (primitiveIndex == 0)
					boundingBox = Ogre::Aabb::newFromExtents(minimumPoint, maximumPoint);
				else
					boundingBox.merge(Ogre::Aabb::newFromExtents(minimumPoint, maximumPoint));

				if (needGenerateNormals || needGenerateTangents)
					for (auto i = 0; i < vertexCount; ++i)
						std::memcpy(positions.data() + i * 3, destinationOffset + i * vertexSize, sizeof(float) * 3);
			} else if (gltfSemanticName == "NORMAL") {
				if (needGenerateTangents)
					for (auto i = 0; i < vertexCount; ++i)
						std::memcpy(normals.data() + i * 3, destinationOffset + i * vertexSize, sizeof(float) * 3);
			} else if (gltfSemanticName == "TEXCOORD_0") {
				if (needGenerateTangents)
					for (auto i = 0; i < vertexCount; ++i)
						std::memcpy(uvs.data() + i * 2, destinationOffset + i * vertexSize, sizeof(float) * 2);
			} else if (gltfSemanticName == "JOINTS_0") {
				joints.resize(vertexCount* componentCount);
				for (auto i = 0; i < vertexCount; ++i)
					std::memcpy(joints.data() + i * componentCount, destinationOffset + i * vertexSize, attributeSize);
			} else if (gltfSemanticName == "WEIGHTS_0") {
				weights.resize(vertexCount* componentCount);
				for (auto i = 0; i < vertexCount; ++i)
					std::memcpy(weights.data() + i * componentCount, destinationOffset + i * vertexSize, attributeSize);
			}

			elementStride += attributeSize;
		}

		if (needGenerateNormals) {
			for (auto i = 0; i < triangleIndices.size() / 3; ++i) {
				auto i0 = triangleIndices[i * 3 + 0] * 3;
				auto i1 = triangleIndices[i * 3 + 1] * 3;
				auto i2 = triangleIndices[i * 3 + 2] * 3;

				auto p0 = &positions[i0];
				auto p1 = &positions[i1];
				auto p2 = &positions[i2];

				auto v0 = Ogre::Vector3(p0[0], p0[1], p0[2]);
				auto v1 = Ogre::Vector3(p1[0], p1[1], p1[2]);
				auto v2 = Ogre::Vector3(p2[0], p2[1], p2[2]);

				auto n = (v1 - v0).crossProduct(v2 - v0).normalisedCopy();
				std::memcpy(&normals[i0], &n, sizeof(float) * 3);
				std::memcpy(&normals[i1], &n, sizeof(float) * 3);
				std::memcpy(&normals[i2], &n, sizeof(float) * 3);
			}

			auto destinationOffset = buffer.data() + elementStride;
			for (auto i = 0; i < vertexCount; ++i)
				std::memcpy(destinationOffset + i * vertexSize, normals.data() + i * 3, sizeof(float) * 3);

			elementStride += sizeof(float) * 3;
		}

		if (needGenerateTangents) {
			std::vector<float> cornerTangents;
			std::vector<float> cornerBitangents;
			tgen::computeCornerTSpace(triangleIndices, triangleIndices, positions, uvs, cornerTangents, cornerBitangents);

			std::vector<float> vertexTangents;
			std::vector<float> vertexBitangents;
			tgen::computeVertexTSpace(triangleIndices, cornerTangents, cornerBitangents, vertexCount, vertexTangents, vertexBitangents);

			if (HasSemantic(Ogre::VES_NORMAL))
				tgen::orthogonalizeTSpace(normals, vertexTangents, vertexBitangents);

			auto destinationOffset = buffer.data() + elementStride;
			for (auto i = 0; i < vertexCount; ++i)
				std::memcpy(destinationOffset + i * vertexSize, vertexTangents.data() + i * 3, sizeof(float) * 3);
		}

		auto vertexBuffer = CreateManualVertexBuffer(vertexElements, buffer.data(), vertexCount);

		Ogre::VertexBufferPackedVec vertexBufferList;
		vertexBufferList.emplace_back(vertexBuffer);

		auto subMesh = CreateManualSubMesh(mesh, vertexBufferList, indexBuffer, Ogre::OT_TRIANGLE_LIST);
		if (HasSemantic(Ogre::VES_BLEND_INDICES) && HasSemantic(Ogre::VES_BLEND_WEIGHTS)) {
			auto componentCount = joints.size() / vertexCount;
			for (auto i = 0; i < vertexCount; ++i)
				for (auto j = 0; j < componentCount; ++j)
					subMesh->addBoneAssignment(Ogre::VertexBoneAssignment(i, joints[i * componentCount + j], weights[i * componentCount + j]));
			subMesh->_compileBoneAssignments();
		}

		if (gltfPrimitive.targets.size() > 0) {
			std::vector<std::vector<float>> positionsList;
			std::vector<std::vector<float>> normalsList;
			for (auto& gltfDisplacements : gltfPrimitive.targets) {
				auto elementStride = 0;
				for (auto& [gltfSemanticName, accessorIndex] : gltfDisplacements) {
					if (gltfSemanticName == "TANGENT")
						continue;

					auto& gltfAccessor = gltfModel.accessors[accessorIndex];
					auto& gltfBufferView = gltfModel.bufferViews[gltfAccessor.bufferView];
					auto& gltfBuffer = gltfModel.buffers[gltfBufferView.buffer];

					auto byteStride = gltfAccessor.ByteStride(gltfBufferView);
					auto sourceOffset = gltfBuffer.data.data() + gltfBufferView.byteOffset + gltfAccessor.byteOffset;

					std::vector<float> buffer(vertexCount * 3);
					for (auto i = 0; i < vertexCount; ++i)
						std::memcpy(reinterpret_cast<char*>(buffer.data()) + i * sizeof(float) * 3, sourceOffset + i * byteStride, sizeof(float) * 3);

					if (gltfSemanticName == "POSITION")
						positionsList.emplace_back(buffer);
					else if (gltfSemanticName == "NORMAL")
						normalsList.emplace_back(buffer);
					else
						OGRE_EXCEPT(Ogre::Exception::ERR_NOT_IMPLEMENTED, "", __FUNCTION__);
				}
			}

			std::vector<const float*> plist;
			for (auto& positions : positionsList)
				plist.emplace_back(positions.data());

			if (normalsList.size() > 0) {
				std::vector<const float*> nlist;
				for (auto& normals : normalsList)
					nlist.emplace_back(normals.data());
				subMesh->createPoses(plist.data(), nlist.data(), plist.size(), positionsList[0].size());
			} else {
				subMesh->createPoses(plist.data(), nullptr, plist.size(), positionsList[0].size());
			}
		}
	}

	mesh->_setBounds(boundingBox, false);
	mesh->_setBoundingSphereRadius(boundingBox.getRadius());

	return mesh;
}
Last edited by duck2 on Mon Oct 19, 2020 12:23 am, edited 1 time in total.

duck2
Gnoblar
Posts: 6
Joined: Wed Jul 31, 2019 11:45 am
x 5

Re: [2.2] normal pbs reflection / help needed / gltf loader

Post by duck2 »

Next is texture loader.

This is how i create texture from scratch:

Code: Select all

Ogre::TextureGpu* CreateManualTexture(const std::string& textureName, std::uint32_t textureWidth, std::uint32_t textureHeight, Ogre::PixelFormatGpu textureFormat, std::uint32_t textureFlags, std::uint32_t textureFilters)
{
	auto texture = Ogre::Root::getSingleton().getRenderSystem()->getTextureGpuManager()->createTexture(textureName, Ogre::GpuPageOutStrategy::Discard, textureFlags, Ogre::TextureTypes::Type2D, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, textureFilters);

	texture->setResolution(textureWidth, textureHeight);
	texture->setPixelFormat(textureFormat);
	texture->setNumMipmaps(Ogre::PixelFormatGpuUtils::getMaxMipmapCount(textureWidth, textureHeight, 1));
	texture->_transitionTo(Ogre::GpuResidency::Resident, nullptr);
	texture->_setNextResidencyStatus(Ogre::GpuResidency::Resident);

	return texture;
}

void UpdateTexture(Ogre::TextureGpu* texture, const void* data)
{
	auto sizeBytes = Ogre::PixelFormatGpuUtils::calculateSizeBytes(texture->getWidth(), texture->getHeight(), 1, 1, texture->getPixelFormat(), 1, 4);

	auto simdBuffer = reinterpret_cast<std::uint8_t*>(OGRE_MALLOC_SIMD(sizeBytes, Ogre::MEMCATEGORY_RESOURCE));

	Ogre::Image2 image;
	image.loadDynamicImage(simdBuffer, texture->getWidth(), texture->getHeight(), 1, Ogre::TextureTypes::Type2D, texture->getPixelFormat(), true, 1);
	std::memcpy(simdBuffer, data, sizeBytes);

	image.generateMipmaps(texture->prefersLoadingFromFileAsSRGB(), Ogre::Image2::FILTER_GAUSSIAN_HIGH);
	image.uploadTo(texture, 0, image.getNumMipmaps() - 1);
}
gltf texture loader:

Code: Select all

enum class ImageType
{
	BaseColor,
	Normal,
	Metallic,
	Roughness,
	Emissive
};

Ogre::TextureGpu* CreateTextureFromGLTF(const std::string& textureName, const tinygltf::Image& gltfImage, ImageType imageType)
{
	auto texture = Ogre::Root::getSingleton().getRenderSystem()->getTextureGpuManager()->findTextureNoThrow(textureName);
	if (texture != nullptr)
		return texture;

	OGRE_ASSERT_LOW(gltfImage.pixel_type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE);
	OGRE_ASSERT_LOW(gltfImage.component == 4);

	auto flags = 0 | Ogre::TextureFlags::ManualTexture;
	auto filters = 0 | Ogre::TextureFilter::TypeGenerateDefaultMipmaps;

	switch (imageType) {
	case ImageType::BaseColor:
	case ImageType::Emissive:
		flags |= Ogre::TextureFlags::PrefersLoadingFromFileAsSRGB | Ogre::TextureFlags::AutomaticBatching;
		break;
	case ImageType::Normal:
		flags |= Ogre::TextureFlags::AutomaticBatching;
		filters |= Ogre::TextureFilter::TypePrepareForNormalMapping;
		break;
	case ImageType::Metallic:
	case ImageType::Roughness:
		flags |= Ogre::TextureFlags::AutomaticBatching;
		filters |= Ogre::TextureFilter::TypeLeaveChannelR;
		break;
	default:
		OGRE_EXCEPT(Ogre::Exception::ERR_NOT_IMPLEMENTED, "", __FUNCTION__);
		break;
	}

	texture = CreateManualTexture(textureName, gltfImage.width, gltfImage.height, Ogre::PFG_RGBA8_UNORM, flags, filters);

	switch (imageType) {
	case ImageType::Metallic: {
		std::vector<std::uint8_t> buffer(gltfImage.width * gltfImage.height * 4);

		auto sourceOffset = gltfImage.image.data() + 2;
		for (auto y = 0; y < gltfImage.height; ++y)
			for (auto x = 0; x < gltfImage.width; ++x)
				buffer[(x + y * gltfImage.width) * 4] = sourceOffset[(x + y * gltfImage.width) * 4];

		UpdateTexture(texture, buffer.data());
		break;
	}
	case ImageType::Roughness: {
		std::vector<std::uint8_t> buffer(gltfImage.width * gltfImage.height * 4);

		auto sourceOffset = gltfImage.image.data() + 1;
		for (auto y = 0; y < gltfImage.height; ++y)
			for (auto x = 0; x < gltfImage.width; ++x)
				buffer[(x + y * gltfImage.width) * 4] = sourceOffset[(x + y * gltfImage.width) * 4];

		UpdateTexture(texture, buffer.data());
		break;
	}
	default:
		UpdateTexture(texture, gltfImage.image.data());
		break;
	}

	return texture;
}

duck2
Gnoblar
Posts: 6
Joined: Wed Jul 31, 2019 11:45 am
x 5

Re: [2.2] normal pbs reflection / help needed / gltf loader

Post by duck2 »

Next is material loader.

Helper functions:

Code: Select all

Ogre::HlmsSamplerblock GetSamplerblockFromGLTF(const tinygltf::Sampler& gltfSampler)
{
	Ogre::HlmsSamplerblock samplerblock;

	switch (gltfSampler.minFilter) {
	case TINYGLTF_TEXTURE_FILTER_NEAREST:
		samplerblock.mMinFilter = Ogre::FO_POINT;
		samplerblock.mMipFilter = Ogre::FO_NONE;
		break;
	case TINYGLTF_TEXTURE_FILTER_LINEAR:
		samplerblock.mMinFilter = Ogre::FO_LINEAR;
		samplerblock.mMipFilter = Ogre::FO_NONE;
		break;
	case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST:
		samplerblock.mMinFilter = Ogre::FO_POINT;
		samplerblock.mMipFilter = Ogre::FO_POINT;
		break;
	case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST:
		samplerblock.mMinFilter = Ogre::FO_LINEAR;
		samplerblock.mMipFilter = Ogre::FO_POINT;
		break;
	case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR:
		samplerblock.mMinFilter = Ogre::FO_POINT;
		samplerblock.mMipFilter = Ogre::FO_LINEAR;
		break;
	case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR:
		samplerblock.mMinFilter = Ogre::FO_LINEAR;
		samplerblock.mMipFilter = Ogre::FO_LINEAR;
		break;
	default:
		samplerblock.mMinFilter = Ogre::FO_POINT;
		samplerblock.mMipFilter = Ogre::FO_LINEAR;
		break;
	}

	switch (gltfSampler.magFilter) {
	case TINYGLTF_TEXTURE_FILTER_NEAREST:
		samplerblock.mMagFilter = Ogre::FO_POINT;
		break;
	case TINYGLTF_TEXTURE_FILTER_LINEAR:
		samplerblock.mMagFilter = Ogre::FO_LINEAR;
		break;
	default:
		samplerblock.mMagFilter = Ogre::FO_LINEAR;
		break;
	}

	switch (gltfSampler.wrapS) {
	case TINYGLTF_TEXTURE_WRAP_REPEAT:
		samplerblock.mU = Ogre::TAM_WRAP;
		break;
	case TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE:
		samplerblock.mU = Ogre::TAM_CLAMP;
		break;
	case TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT:
		samplerblock.mU = Ogre::TAM_MIRROR;
		break;
	default:
		OGRE_EXCEPT(Ogre::Exception::ERR_NOT_IMPLEMENTED, "", __FUNCTION__);
		break;
	}

	switch (gltfSampler.wrapS) {
	case TINYGLTF_TEXTURE_WRAP_REPEAT:
		samplerblock.mV = Ogre::TAM_WRAP;
		break;
	case TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE:
		samplerblock.mV = Ogre::TAM_CLAMP;
		break;
	case TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT:
		samplerblock.mV = Ogre::TAM_MIRROR;
		break;
	default:
		OGRE_EXCEPT(Ogre::Exception::ERR_NOT_IMPLEMENTED, "", __FUNCTION__);
		break;
	}

	return samplerblock;
}
gltf metallic workflow loader:

Code: Select all

Ogre::HlmsDatablock* CreateDatablockFromGLTF(const std::string& datablockName, const std::string& gltfPath, const tinygltf::Model& gltfModel, const tinygltf::Material& gltfMaterial, Ogre::TextureGpu* environmentMap = nullptr)
{
	auto hlmsPbs = static_cast<Ogre::HlmsPbs*>(Ogre::Root::getSingleton().getHlmsManager()->getHlms(Ogre::HLMS_PBS));
	auto datablock = static_cast<Ogre::HlmsPbsDatablock*>(hlmsPbs->getDatablock(datablockName));
	if (datablock != nullptr)
		return datablock;

	datablock = static_cast<Ogre::HlmsPbsDatablock*>(hlmsPbs->createDatablock(datablockName, datablockName, Ogre::HlmsMacroblock(), Ogre::HlmsBlendblock(), Ogre::HlmsParamVec()));
	datablock->setWorkflow(Ogre::HlmsPbsDatablock::MetallicWorkflow);

	// pbrMetallicRoughness
	{
		// baseColorFactor
		{
			auto r = gltfMaterial.pbrMetallicRoughness.baseColorFactor[0];
			auto g = gltfMaterial.pbrMetallicRoughness.baseColorFactor[1];
			auto b = gltfMaterial.pbrMetallicRoughness.baseColorFactor[2];
			datablock->setDiffuse(Ogre::Vector3(r, g, b));
		}

		// baseColorTexture
		if (gltfMaterial.pbrMetallicRoughness.baseColorTexture.index >= 0) {
			auto& gltfTexture = gltfModel.textures[gltfMaterial.pbrMetallicRoughness.baseColorTexture.index];
			auto& gltfImage = gltfModel.images[gltfTexture.source];

			auto textureName = gltfPath + "#" + std::to_string(gltfTexture.source);
			auto texture = CreateTextureFromGLTF(textureName, gltfImage, ImageType::BaseColor);
			datablock->setTexture(Ogre::PBSM_DIFFUSE, texture);
			datablock->setTextureUvSource(Ogre::PBSM_DIFFUSE, gltfMaterial.pbrMetallicRoughness.baseColorTexture.texCoord);

			if (gltfTexture.sampler >= 0) {
				auto& gltfSampler = gltfModel.samplers[gltfTexture.sampler];

				auto samplerblock = GetSamplerblockFromGLTF(gltfSampler);
				datablock->setSamplerblock(Ogre::PBSM_DIFFUSE, samplerblock);
			}
		}

		// metallicFactor
		{
			datablock->setMetalness(gltfMaterial.pbrMetallicRoughness.metallicFactor);

			if ((environmentMap != nullptr) && (gltfMaterial.pbrMetallicRoughness.metallicFactor > 0.0))
				datablock->setTexture(Ogre::PBSM_REFLECTION, environmentMap);
		}

		// roughnessFactor
		{
			datablock->setRoughness(std::max(0.02, gltfMaterial.pbrMetallicRoughness.roughnessFactor));
		}

		// metallicRoughnessTexture
		if (gltfMaterial.pbrMetallicRoughness.metallicRoughnessTexture.index >= 0) {
			auto& gltfTexture = gltfModel.textures[gltfMaterial.pbrMetallicRoughness.metallicRoughnessTexture.index];
			auto& gltfImage = gltfModel.images[gltfTexture.source];

			auto textureMetallicName = gltfPath + "#Metallic" + std::to_string(gltfTexture.source);
			auto textureMetallic = CreateTextureFromGLTF(textureMetallicName, gltfImage, ImageType::Metallic);
			datablock->setTexture(Ogre::PBSM_METALLIC, textureMetallic);
			datablock->setTextureUvSource(Ogre::PBSM_METALLIC, gltfMaterial.pbrMetallicRoughness.metallicRoughnessTexture.texCoord);

			auto textureRoughnessName = gltfPath + "#Roughness" + std::to_string(gltfTexture.source);
			auto textureRoughness = CreateTextureFromGLTF(textureRoughnessName, gltfImage, ImageType::Roughness);
			datablock->setTexture(Ogre::PBSM_ROUGHNESS, textureRoughness);
			datablock->setTextureUvSource(Ogre::PBSM_ROUGHNESS, gltfMaterial.pbrMetallicRoughness.metallicRoughnessTexture.texCoord);

			if (gltfTexture.sampler >= 0) {
				auto& gltfSampler = gltfModel.samplers[gltfTexture.sampler];

				auto samplerblock = GetSamplerblockFromGLTF(gltfSampler);
				datablock->setSamplerblock(Ogre::PBSM_METALLIC, samplerblock);
				datablock->setSamplerblock(Ogre::PBSM_ROUGHNESS, samplerblock);
			}
		}

		// normalTexture
		if (gltfMaterial.normalTexture.index >= 0) {
			auto& gltfTexture = gltfModel.textures[gltfMaterial.normalTexture.index];
			auto& gltfImage = gltfModel.images[gltfTexture.source];

			auto textureName = gltfPath + "#" + std::to_string(gltfTexture.source);
			auto texture = CreateTextureFromGLTF(textureName, gltfImage, ImageType::Normal);
			datablock->setTexture(Ogre::PBSM_NORMAL, texture);
			datablock->setTextureUvSource(Ogre::PBSM_NORMAL, gltfMaterial.normalTexture.texCoord);

			if (gltfTexture.sampler >= 0) {
				auto& gltfSampler = gltfModel.samplers[gltfTexture.sampler];

				auto samplerblock = GetSamplerblockFromGLTF(gltfSampler);
				datablock->setSamplerblock(Ogre::PBSM_NORMAL, samplerblock);
			}
		}

		// occlusionTexture
		if (gltfMaterial.occlusionTexture.index >= 0) {
		}

		// emissiveTexture
		if (gltfMaterial.emissiveTexture.index >= 0) {
			auto& gltfTexture = gltfModel.textures[gltfMaterial.emissiveTexture.index];
			auto& gltfImage = gltfModel.images[gltfTexture.source];

			auto textureName = gltfPath + "#" + std::to_string(gltfTexture.source);
			auto texture = CreateTextureFromGLTF(textureName, gltfImage, ImageType::Emissive);
			datablock->setTexture(Ogre::PBSM_EMISSIVE, texture);
			datablock->setTextureUvSource(Ogre::PBSM_EMISSIVE, gltfMaterial.emissiveTexture.texCoord);

			if (gltfTexture.sampler >= 0) {
				auto& gltfSampler = gltfModel.samplers[gltfTexture.sampler];

				auto samplerblock = GetSamplerblockFromGLTF(gltfSampler);
				datablock->setSamplerblock(Ogre::PBSM_EMISSIVE, samplerblock);
			}
		}

		// emissiveFactor
		{
			auto r = gltfMaterial.emissiveFactor[0];
			auto g = gltfMaterial.emissiveFactor[1];
			auto b = gltfMaterial.emissiveFactor[2];
			datablock->setEmissive(Ogre::Vector3(r, g, b));
		}

		// alphaMode
		{
			if (gltfMaterial.alphaMode == "OPAQUE") {
				datablock->setAlphaTest(Ogre::CMPF_ALWAYS_PASS);
				datablock->setTransparency(1.0f, Ogre::HlmsPbsDatablock::None);
			} else if (gltfMaterial.alphaMode == "MASK") {
				datablock->setAlphaTest(Ogre::CMPF_GREATER_EQUAL);
				datablock->setTransparency(1.0f, Ogre::HlmsPbsDatablock::None);
			} else if (gltfMaterial.alphaMode == "BLEND") {
				datablock->setAlphaTest(Ogre::CMPF_ALWAYS_PASS);
				datablock->setTransparency(1.0f, Ogre::HlmsPbsDatablock::Transparent);
			} else
				OGRE_EXCEPT(Ogre::Exception::ERR_NOT_IMPLEMENTED, "", __FUNCTION__);
		}

		// alphaCutoff
		datablock->setAlphaTestThreshold(gltfMaterial.alphaCutoff);

		// doubleSided
		datablock->setTwoSidedLighting(gltfMaterial.doubleSided);
	}

	return datablock;
}
You give this function an environment map, you can have shiny helmet 8).

All of these code should be able to cover at least half official gltf samples. We still need node loader and animations.
As you can see, none of these require changing hlms scripts or shaders.

screwt
Kobold
Posts: 29
Joined: Wed Feb 28, 2007 9:18 am
x 2

Re: [2.2] normal pbs reflection / help needed / gltf loader

Post by screwt »

Wow thanks a lot this will help the refactoring i'm doing.


-- EDIT --

Pull Request has been sent to YBalrid : https://github.com/Ybalrid/Ogre_glTF/pull/31

Post Reply