Ogre-Next Minimap and FogOfWar Question

Discussion area about developing with Ogre-Next (2.1, 2.2 and beyond)


Lax
Gnoll
Posts: 645
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 59

Ogre-Next Minimap and FogOfWar Question

Post by Lax »

Hi,

I'm trying to create a minimap with fog of war.
The minimap rendering is working, but if I also use fog of war texture. I get no error, but also I see nothing.

The theoretical approach is as follows:

  • Create minimapTexture
  • Create fogOfWarTexture
    -> Render scene to minimapTexture
    -> Use QUAD_PASS and material to blend the fogOfWarTexture over the miniMapTexture
    -> UpdateFogOfWarTexture, to change the pixels, where the target SceneNode is located to discover the place.

This is my code:

Code: Select all

Ogre::String minimapTextureName = "MinimapRT";
this->minimapTexture = this->createMinimapTexture(minimapTextureName, 256, 256);
Ogre::String fogOfWarTextureName = "FogOfWarTexture";
this->fogOfWarTexture = this->createFogOfWarTexture(fogOfWarTextureName, 256, 256);

this->fogOfWarStagingTexture = this->textureManager->getStagingTexture(this->fogOfWarTexture->getWidth(), this->fogOfWarTexture->getHeight(), 1u, 1u, this->fogOfWarTexture->getPixelFormat());

Ogre::CompositorManager2* compositorManager = WorkspaceModule::getInstance()->getCompositorManager();

// Create minimap node
Ogre::CompositorNodeDef* nodeDef = compositorManager->addNodeDefinition("MinimapNode");
#if 1
Ogre::TextureDefinitionBase::TextureDefinition* texDef = nodeDef->addTextureDefinition("MinimapRT");
texDef->format = Ogre::PFG_RGBA8_UNORM_SRGB;

RenderTargetViewDef* rtv = nodeDef->addRenderTextureView("MinimapRT");
RenderTargetViewEntry attachment;
attachment.textureName = "MinimapRT";
rtv->colourAttachments.push_back(attachment);
#else
nodeDef->addTextureSourceName("MinimapRT", 0, Ogre::TextureDefinitionBase::TEXTURE_INPUT);
#endif

nodeDef->setNumTargetPass(2);
{
	Ogre::CompositorTargetDef* targetDef = nodeDef->addTargetPass("MinimapRT");
	targetDef->setNumPasses(1);
	{
		// Clear Pass
		{
			Ogre::CompositorPassSceneDef* passClear;
			passClear = static_cast<Ogre::CompositorPassSceneDef*>(targetDef->addPass(Ogre::PASS_CLEAR));

		passClear->mClearColour[0] = Ogre::ColourValue(0.2f, 0.4f, 0.6f);
		// passClear->setAllStoreActions(Ogre::StoreAction::Store);
		passClear->mStoreActionColour[0] = Ogre::StoreAction::Store;
		passClear->mStoreActionDepth = Ogre::StoreAction::Store;
		passClear->mStoreActionStencil = Ogre::StoreAction::Store;
	}

	{
		// Scene pass for minimap
		Ogre::CompositorPassSceneDef* passScene = static_cast<Ogre::CompositorPassSceneDef*>(
			targetDef->addPass(Ogre::PASS_SCENE));
		passScene->mCameraName = cameraCompPtr->getCamera()->getName();

		passScene->setAllLoadActions(Ogre::LoadAction::Clear);
		passScene->mClearColour[0] = Ogre::ColourValue::Black;

		passScene->mStoreActionColour[0] = Ogre::StoreAction::StoreOrResolve;
		passScene->mStoreActionDepth = Ogre::StoreAction::DontCare;
		passScene->mStoreActionStencil = Ogre::StoreAction::DontCare;

		passScene->mVisibilityMask = 0x00000001; // Custom mask for minimap objects

		passScene->mIncludeOverlays = false;

	}
}
}
nodeDef->mapOutputChannel(0, "MinimapRT");

// Create blending node
Ogre::CompositorNodeDef* nodeDef = compositorManager->addNodeDefinition("BlendingNode");
nodeDef->addTextureSourceName("MinimapRT", 0, Ogre::TextureDefinitionBase::TEXTURE_INPUT);
nodeDef->addTextureSourceName("FogOfWarTexture", 1, Ogre::TextureDefinitionBase::TEXTURE_INPUT);

nodeDef->setNumTargetPass(1);
{
	Ogre::CompositorTargetDef* targetDef = nodeDef->addTargetPass("FogOfWarTexture");
	targetDef->setNumPasses(1);
	{
		Ogre::CompositorPassQuadDef* quadPass = static_cast<Ogre::CompositorPassQuadDef*>(targetDef->addPass(Ogre::PASS_QUAD));
		quadPass->mMaterialName = "Minimap/FogOfWar";
		quadPass->addQuadTextureSource(0, "MinimapRT");
		quadPass->addQuadTextureSource(1, "FogOfWarTexture");
	}
}
nodeDef->mapOutputChannel(0, "FogOfWarTexture");

// Create workspace
this->minimapWorkspaceName = "MinimapWorkspaceWithFoW";
Ogre::CompositorWorkspaceDef* workspaceDef = compositorManager->addWorkspaceDefinition(this->minimapWorkspaceName);
workspaceDef->clearAllInterNodeConnections();

this->externalChannels.resize(1);
this->externalChannels[0] = this->minimapTexture;

this->workspace = compositorManager->addWorkspace(this->gameObjectPtr->getSceneManager(), this->externalChannels, cameraCompPtr->getCamera(), this->minimapWorkspaceName, true);

// Create MyGUI widget for the minimap
this->minimapWidget = MyGUI::Gui::getInstancePtr()->createWidgetReal<MyGUI::ImageBox>("ImageBox", MyGUI::FloatCoord(0.0f, 0.0, 0.2f, 0.2f), MyGUI::Align::HCenter, "Overlapped");
minimapWidget->setImageTexture(this->minimapTexture->getNameStr());

// Update fog of war:
	void MinimapComponent::updateFogOfWarTexture(const Ogre::Vector2& position, Ogre::Real radius)
	{
		this->fogOfWarStagingTexture->startMapRegion();
		TextureBox texBox = this->fogOfWarStagingTexture->mapRegion(this->fogOfWarTexture->getWidth(), this->fogOfWarTexture->getHeight(), 1u, 1u, this->fogOfWarTexture->getPixelFormat());
		// Assuming a 2D texture, the z-coordinate will be 0
		size_t z = 0;

	const size_t bytesPerPixel = texBox.bytesPerPixel;

	for (uint32 y = 0; y < this->fogOfWarTexture->getHeight(); y++)
	{
		uint8* RESTRICT_ALIAS pixBoxData = reinterpret_cast<uint8 * RESTRICT_ALIAS>(texBox.at(0, y, 0));
		for (uint32 x = 0; x < this->fogOfWarTexture->getWidth(); x++)
		{
			float dx = x - position.x;
			float dy = y - position.y;
			float distance = sqrt(dx * dx + dy * dy);

			if (distance < radius)
			{
				const size_t dstIdx = x * bytesPerPixel;
				float rgba[4];
				// Create blank texture without details
				rgba[0] = 1.0f;
				rgba[1] = 1.0f;
				rgba[2] = 1.0f;
				rgba[3] = 1.0f;
				PixelFormatGpuUtils::packColour(rgba, this->fogOfWarTexture->getPixelFormat(), &pixBoxData[dstIdx]);
			}
			else
			{
				const size_t dstIdx = x * bytesPerPixel;
				float rgba[4];
				// Create blank texture without details
				rgba[0] = 1.0f;
				rgba[1] = 0.0f;
				rgba[2] = 0.0f;
				rgba[3] = 0.0f;
				PixelFormatGpuUtils::packColour(rgba, this->fogOfWarTexture->getPixelFormat(), &pixBoxData[dstIdx]);
			}
		}
	}

	this->fogOfWarStagingTexture->stopMapRegion();
	this->fogOfWarStagingTexture->upload(texBox, this->fogOfWarTexture, 0, 0, 0);

	this->fogOfWarTexture->notifyDataIsReady();

	this->workspace->_validateFinalTarget();
	this->workspace->_beginUpdate(true);
	this->workspace->_update();
	this->workspace->_endUpdate(true);
}


// My Material Minimap/FogOfWar:
// GLSL shaders
fragment_program FogOfWarPostprocess_fp_GLSL glsl
{
	source FogOfWarBlend_fp.glsl
	default_params
	{
		param_named backgroundMap int 0
	}
}

// HLSL shaders
fragment_program FogOfWarPostprocess_fp_HLSL hlsl
{
	source FogOfWar_fp.hlsl
	entry_point main
	target ps_5_0 ps_4_0 ps_4_0_level_9_1 ps_4_0_level_9_3
}

fragment_program FogOfWar_fp unified
{
	delegate FogOfWarPostprocess_fp_HLSL
	delegate FogOfWarPostprocess_fp_GLSL
}

material Minimap/FogOfWar
{
    technique
    {
        pass
        {
			vertex_program_ref Ogre/Compositor/Quad_vs
			{
			}
		
        // Use a custom shader for the fog of war blending
        fragment_program_ref FogOfWar_fp
        {
            param_named MinimapRT int 0
			param_named FogOfWarTexture int 1
        }

        // Set the minimap texture
        texture_unit 
        {
            texture MinimapRT
        }
		
		// Set the fog of war texture
        texture_unit
        {
            texture FogOfWarTexture
        }
    }
}
}

// My Shader: FogOfWar_fp.hlsl
Texture2D MinimapRT : register(t0);
Texture2D FogOfWarTexture : register(t1);
SamplerState samLinear : register(s0);

struct PS_INPUT
{
    float4 Position : SV_POSITION;
    float2 UV : TEXCOORD0;
};

float4 main(PS_INPUT input) : SV_TARGET
{
    float4 minimapColor = MinimapRT.Sample(samLinear, input.UV);
    float4 fogColor = FogOfWarTexture.Sample(samLinear, input.UV);
    return minimapColor * fogColor;
}

Mayby somebody sees what is going wrong here. I'm not sure how those textures can be handled in Ogre correctly.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
Crystal Hammer
Gnome
Posts: 391
Joined: Sat Jun 23, 2007 5:16 pm
x 99

Re: Ogre-Next Minimap and FogOfWar Question

Post by Crystal Hammer »

IDK I haven't created this from C++, but you have
targetDef->setNumPasses(1);
while you add 2 passes:

Code: Select all

// Clear Pass
		passClear = static_cast<Ogre::CompositorPassSceneDef*>(targetDef->addPass(Ogre::PASS_CLEAR));

and

Code: Select all

		// Scene pass for minimap
			targetDef->addPass(Ogre::PASS_SCENE));

So shouldn't it be 2 here?
targetDef->setNumPasses(2);

Lax
Gnoll
Posts: 645
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 59

Re: Ogre-Next Minimap and FogOfWar Question

Post by Lax »

Hi,

yes you are right. I experimented a lot. But this does not solve the issue.

I will restructure the code here for better readability.

Once again:

The theoretical approach is as follows:

Create minimapTexture
Create fogOfWarTexture

  • Render scene to minimapTexture
  • Use QUAD_PASS and material to blend the fogOfWarTexture over the miniMapTexture
  • UpdateFogOfWarTexture, to change the pixels, where the target SceneNode is located to discover the place.

Creating the textures:

Code: Select all

Ogre::String minimapTextureName = "MinimapRT";
this->minimapTexture = this->createMinimapTexture(minimapTextureName, 256, 256);
Ogre::String fogOfWarTextureName = "FogOfWarTexture";
this->fogOfWarTexture = this->createFogOfWarTexture(fogOfWarTextureName, 256, 256);

this->fogOfWarStagingTexture = this->textureManager->getStagingTexture(this->fogOfWarTexture->getWidth(), this->fogOfWarTexture->getHeight(), 1u, 1u, this->fogOfWarTexture->getPixelFormat());

Creating the minimap node:

Code: Select all

Ogre::CompositorManager2* compositorManager = WorkspaceModule::getInstance()->getCompositorManager();

Ogre::CompositorNodeDef* nodeDef = compositorManager->addNodeDefinition("MinimapNode");

Ogre::TextureDefinitionBase::TextureDefinition* texDef = nodeDef->addTextureDefinition("MinimapRT");
texDef->format = Ogre::PFG_RGBA8_UNORM_SRGB;

RenderTargetViewDef* rtv = nodeDef->addRenderTextureView("MinimapRT");
RenderTargetViewEntry attachment;
attachment.textureName = "MinimapRT";
rtv->colourAttachments.push_back(attachment);

nodeDef->setNumTargetPass(1);
{
	Ogre::CompositorTargetDef* targetDef = nodeDef->addTargetPass("MinimapRT");
	targetDef->setNumPasses(2);
	{
		// Clear Pass
		{
			Ogre::CompositorPassSceneDef* passClear;
			passClear = static_cast<Ogre::CompositorPassSceneDef*>(targetDef->addPass(Ogre::PASS_CLEAR));

	passClear->mClearColour[0] = Ogre::ColourValue(0.2f, 0.4f, 0.6f);
	// passClear->setAllStoreActions(Ogre::StoreAction::Store);
	passClear->mStoreActionColour[0] = Ogre::StoreAction::Store;
	passClear->mStoreActionDepth = Ogre::StoreAction::Store;
	passClear->mStoreActionStencil = Ogre::StoreAction::Store;
}

{
	// Scene pass for minimap
	Ogre::CompositorPassSceneDef* passScene = static_cast<Ogre::CompositorPassSceneDef*>(
		targetDef->addPass(Ogre::PASS_SCENE));
	passScene->mCameraName = cameraCompPtr->getCamera()->getName();

	passScene->setAllLoadActions(Ogre::LoadAction::Clear);
	passScene->mClearColour[0] = Ogre::ColourValue::Black;

	passScene->mStoreActionColour[0] = Ogre::StoreAction::StoreOrResolve;
	passScene->mStoreActionDepth = Ogre::StoreAction::DontCare;
	passScene->mStoreActionStencil = Ogre::StoreAction::DontCare;

	passScene->mVisibilityMask = 0x00000001; // Custom mask for minimap objects

	passScene->mIncludeOverlays = false;

}
}
}
nodeDef->mapOutputChannel(0, "MinimapRT");

Creating the blending node:

Code: Select all

Ogre::CompositorNodeDef* nodeDef = compositorManager->addNodeDefinition("BlendingNode");
nodeDef->addTextureSourceName("MinimapRT", 0, Ogre::TextureDefinitionBase::TEXTURE_INPUT);
nodeDef->addTextureSourceName("FogOfWarTexture", 1, Ogre::TextureDefinitionBase::TEXTURE_INPUT);

nodeDef->setNumTargetPass(1);
{
	Ogre::CompositorTargetDef* targetDef = nodeDef->addTargetPass("FogOfWarTexture");
	targetDef->setNumPasses(1);
	{
		Ogre::CompositorPassQuadDef* quadPass = static_cast<Ogre::CompositorPassQuadDef*>(targetDef->addPass(Ogre::PASS_QUAD));
		quadPass->mMaterialName = "Minimap/FogOfWar";
		quadPass->addQuadTextureSource(0, "MinimapRT");
		quadPass->addQuadTextureSource(1, "FogOfWarTexture");
	}
}
nodeDef->mapOutputChannel(0, "FogOfWarTexture");

Creating the workspace:

Code: Select all

this->minimapWorkspaceName = "MinimapWorkspaceWithFoW";
Ogre::CompositorWorkspaceDef* workspaceDef = compositorManager->addWorkspaceDefinition(this->minimapWorkspaceName);
workspaceDef->clearAllInterNodeConnections();

this->externalChannels.resize(1);
this->externalChannels[0] = this->minimapTexture;

this->workspace = compositorManager->addWorkspace(this->gameObjectPtr->getSceneManager(), this->externalChannels, cameraCompPtr->getCamera(), this->minimapWorkspaceName, true);

// Create MyGUI widget for the minimap
this->minimapWidget = MyGUI::Gui::getInstancePtr()->createWidgetReal<MyGUI::ImageBox>("ImageBox", MyGUI::FloatCoord(0.0f, 0.0, 0.2f, 0.2f), MyGUI::Align::HCenter, "Overlapped");
minimapWidget->setImageTexture(this->minimapTexture->getNameStr());

Updating the fog of war texture:

Code: Select all

	void MinimapComponent::updateFogOfWarTexture(const Ogre::Vector2& position, Ogre::Real radius)
	{
		this->fogOfWarStagingTexture->startMapRegion();
		TextureBox texBox = this->fogOfWarStagingTexture->mapRegion(this->fogOfWarTexture->getWidth(), this->fogOfWarTexture->getHeight(), 1u, 1u, this->fogOfWarTexture->getPixelFormat());
		// Assuming a 2D texture, the z-coordinate will be 0
		size_t z = 0;

const size_t bytesPerPixel = texBox.bytesPerPixel;

for (uint32 y = 0; y < this->fogOfWarTexture->getHeight(); y++)
{
	uint8* RESTRICT_ALIAS pixBoxData = reinterpret_cast<uint8 * RESTRICT_ALIAS>(texBox.at(0, y, 0));
	for (uint32 x = 0; x < this->fogOfWarTexture->getWidth(); x++)
	{
		float dx = x - position.x;
		float dy = y - position.y;
		float distance = sqrt(dx * dx + dy * dy);

		if (distance < radius)
		{
			const size_t dstIdx = x * bytesPerPixel;
			float rgba[4];
			// Create blank texture without details
			rgba[0] = 1.0f;
			rgba[1] = 1.0f;
			rgba[2] = 1.0f;
			rgba[3] = 1.0f;
			PixelFormatGpuUtils::packColour(rgba, this->fogOfWarTexture->getPixelFormat(), &pixBoxData[dstIdx]);
		}
		else
		{
			const size_t dstIdx = x * bytesPerPixel;
			float rgba[4];
			// Create blank texture without details
			rgba[0] = 1.0f;
			rgba[1] = 0.0f;
			rgba[2] = 0.0f;
			rgba[3] = 0.0f;
			PixelFormatGpuUtils::packColour(rgba, this->fogOfWarTexture->getPixelFormat(), &pixBoxData[dstIdx]);
		}
	}
}

this->fogOfWarStagingTexture->stopMapRegion();
this->fogOfWarStagingTexture->upload(texBox, this->fogOfWarTexture, 0, 0, 0);

this->fogOfWarTexture->notifyDataIsReady();

this->workspace->_validateFinalTarget();
this->workspace->_beginUpdate(true);
this->workspace->_update();
this->workspace->_endUpdate(true);
}

My Material Minimap/FogOfWar:

Code: Select all

// GLSL shaders
fragment_program FogOfWarPostprocess_fp_GLSL glsl
{
	source FogOfWarBlend_fp.glsl
	default_params
	{
		param_named backgroundMap int 0
	}
}

// HLSL shaders
fragment_program FogOfWarPostprocess_fp_HLSL hlsl
{
	source FogOfWar_fp.hlsl
	entry_point main
	target ps_5_0 ps_4_0 ps_4_0_level_9_1 ps_4_0_level_9_3
}

fragment_program FogOfWar_fp unified
{
	delegate FogOfWarPostprocess_fp_HLSL
	delegate FogOfWarPostprocess_fp_GLSL
}

material Minimap/FogOfWar
{
    technique
    {
        pass
        {
			vertex_program_ref Ogre/Compositor/Quad_vs
			{
			}
		
    // Use a custom shader for the fog of war blending
    fragment_program_ref FogOfWar_fp
    {
        param_named MinimapRT int 0
		param_named FogOfWarTexture int 1
    }

    // Set the minimap texture
    texture_unit 
    {
        texture MinimapRT
    }
	
	// Set the fog of war texture
    texture_unit
    {
        texture FogOfWarTexture
    }
}
}
}

My Shader: FogOfWar_fp.hlsl

Code: Select all

Texture2D MinimapRT : register(t0);
Texture2D FogOfWarTexture : register(t1);
SamplerState samLinear : register(s0);

struct PS_INPUT
{
    float4 Position : SV_POSITION;
    float2 UV : TEXCOORD0;
};

float4 main(PS_INPUT input) : SV_TARGET
{
    float4 minimapColor = MinimapRT.Sample(samLinear, input.UV);
    float4 fogColor = FogOfWarTexture.Sample(samLinear, input.UV);
    return minimapColor * fogColor;
}

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

Lax
Gnoll
Posts: 645
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 59

Re: Ogre-Next Minimap and FogOfWar Question

Post by Lax »

I got minimap with fog of war working. I have 2 cameras:
1) For the scene
2) Another placed in the middle of the scene looking to the ground (Minimap Camera).
I'm rendering the scene to a minimap texture and placing the minimap texture to MyGUI::ImageBox.
I create and draw the fog of war texture and place the texture to anoter MyGUI::ImageBox, which lays over the minimap ImageBox.

This works well, if no Terra is included:

But as soon as I use terra, I see no hills but ugly artifacts. For the scenario, i deactivated the fog of war texture, just minimap texture is rendered. See:

Has anybody an idea, why this is happending?
The strange thing is, I move the main camera and this affects the minimap texture, which cannot be, as those two cameras have nothing todo with each other.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

Lax
Gnoll
Posts: 645
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 59

Re: Ogre-Next Minimap and FogOfWar Question

Post by Lax »

At least I got rid of the ugly artifacts on the minimap with terra.
The solution is strange:

I have to create terra not with the main camera, but the camera for the minimap.

But still I see no hills on the minimap.

I think I will fake that, by painting and adapting the terra heightmap over the minimap :D .

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
Crystal Hammer
Gnome
Posts: 391
Joined: Sat Jun 23, 2007 5:16 pm
x 99

Re: Ogre-Next Minimap and FogOfWar Question

Post by Crystal Hammer »

Are you caling terra->update for each RTT and main view?
Hmm actually IDK if this is needed? I see this code already in
void TerraWorkspaceListener::passPreExecute(
Ah..

Code: Select all

	@brief TerraWorkspaceListener
		This listener allows Terra to cast shadows in Point and Spot shadow maps
		This feature is optional.

		If the listener is not registered, Terra won't cast shadows in point
		and spot lights
	*/

So I guess either register this TerraWorkspaceListener, or call terra->update if you have your own CompositorWorkspaceListener already?
I'm not sure, I don't actually have it, and so I got errors in splitscreen but only for page lods visiblity I think.

Lax
Gnoll
Posts: 645
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 59

Re: Ogre-Next Minimap and FogOfWar Question

Post by Lax »

Yes, I'm calling update, but its only for point and spotlight.

I discarted the idea of putting the heightmap directly over the minimap, as it is to complicated. E.g. what if the player moves and the minimap moves. Then I have to move the heighmap as well etc.

Another price question to anyone:

  • The fog of war texture fill routine is totally slow. On debug mode I get an FPS decrease from 140fps to 20fps.

This is my code:

Code: Select all

this->fogOfWarStagingTexture->startMapRegion();

Ogre::TextureBox texBox = this->fogOfWarStagingTexture->mapRegion(this->fogOfWarTexture->getWidth(), this->fogOfWarTexture->getHeight(), 1u, 1u, this->fogOfWarTexture->getPixelFormat());

for (uint32 y = 0; y < texSize; y++)
{
	uint8* RESTRICT_ALIAS pixBoxData = reinterpret_cast<uint8 * RESTRICT_ALIAS>(texBox.at(0, y, 0));
	for (uint32 x = 0; x < texSize; x++)
	{
	    ...
	    PixelFormatGpuUtils::packColour(rgba, this->fogOfWarTexture->getPixelFormat(), &pixBoxData[dstIdx]);
	}
}
this->fogOfWarStagingTexture->stopMapRegion();
this->fogOfWarStagingTexture->upload(texBox, this->fogOfWarTexture, 0, 0, 0);

Now I tried to use that in a different std::thread with its own running loop.

I works for 2 seconds and after that I get an ugly crash in vaoManager in Ogre.

Is there a way, in which a TextureGpu can be filled in its own thread?

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5436
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1343

Re: Ogre-Next Minimap and FogOfWar Question

Post by dark_sylinc »

Hi!

Yes. You can optimize that in various ways. Obviously the size / resolution is a direct optimization.

But once we go past that:

  1. PixelFormatGpuUtils::packColour is user friendly, but it is not optimized for performance. If you know the exact pixel format, convert it by hand.

  2. You can't put startMapRegion(), stopMapRegion() and upload() in a thread. But you CAN put the loop into its own thread. Something like this (pseudo code):

Code: Select all

if( !thread_handle.done() )
    return; // Still processing a job started in an earlier frame
else if( !thread_handle.empty() )
{
    // Upload the work started an earlier frame.
    this->fogOfWarStagingTexture->stopMapRegion();
    this->fogOfWarStagingTexture->upload(texBox, this->fogOfWarTexture, 0, 0, 0);
}

// Start a new job.
this->fogOfWarStagingTexture->startMapRegion();

Ogre::TextureBox texBox = this->fogOfWarStagingTexture->mapRegion(this->fogOfWarTexture->getWidth(), this->fogOfWarTexture->getHeight(), 1u, 1u, this->fogOfWarTexture->getPixelFormat());

auto thread_handle = thread.spawn( [] {
for (uint32 y = 0; y < texSize; y++)
{
	uint8* RESTRICT_ALIAS pixBoxData = reinterpret_cast<uint8 * RESTRICT_ALIAS>(texBox.at(0, y, 0));
	for (uint32 x = 0; x < texSize; x++)
	{
	    ...
	    PixelFormatGpuUtils::packColour(rgba, this->fogOfWarTexture->getPixelFormat(), &pixBoxData[dstIdx]);
	}
}
}

// Do not block. After N frames when the thread is done, it will be uploaded.

Note that you can put it in multiple threads. For example on a dual core have thread0 render half the rows, thread1 render the other half.
You get diminishing returns for having more threads because you will be limited by bandwidth, not compute. So having like 2-6 threads is likely somewhere near the sweet spot.

Alternative suggestion

I discarted the idea of putting the heightmap directly over the minimap, as it is to complicated. E.g. what if the player moves and the minimap moves. Then I have to move the heighmap as well etc.

  1. Render the whole terrain from above in Ortho projection with high resolution into a RenderTexture. Keep that texture.

    1. If the terrain is too big to the point that LOD becomes a problem, render it in chunks (e.g. move the camera to the first quadrant and render 2048x2048 region, then move to the next quadrant; 4 times until you get the whole 4096x4096)

    2. Alternatively, set m_basePixelDimension to a very high value to have very little LOD.

    3. Optional: Since that texture will be frozen from now on, copy it to a non RenderTexture using TextureGpu::copyTo. This should be slightly more efficient because you're telling the GPU to keep the data in a read-only texture

  2. When making the minimap, compose it by having the texture from previous step as the first layer, and then a layer on top with the fog.

For the m_basePixelDimension case, I used the following code:

Code: Select all

/** Lower values makes LOD very aggressive. Higher values less aggressive.
@param basePixelDimension
	Must be power of 2.
*/
void setBasePixelDimension( uint32 basePixelDimension );

//-----------------------------------------------------------------------------------
void Terra::setBasePixelDimension( uint32 basePixelDimension )
{
	m_basePixelDimension = basePixelDimension;
	if( !m_heightMap.empty() && !m_terrainCells->empty() )
	{
		HlmsDatablock *datablock = m_terrainCells[0].back().getDatablock();

	calculateOptimumSkirtSize();
	createTerrainCells();

	if( datablock )
	{
		for( size_t i = 0u; i < 2u; ++i )
		{
			for( TerrainCell &terrainCell : m_terrainCells[i] )
				terrainCell.setDatablock( datablock );
		}
	}
}
}

Edit: Added setBasePixelDimension to master.

Lax
Gnoll
Posts: 645
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 59

Re: Ogre-Next Minimap and FogOfWar Question

Post by Lax »

Hi,

thanks for your suggestions.

Do I need to call startMapRegion, stopMapRegtion frequently outside the thread?

And what about the upload. Because the upload uses the texbox, which is created locally inside the thread update:

Code: Select all

Ogre::TextureBox texBox = this->fogOfWarStagingTexture->mapRegion(this->fogOfWarTexture->getWidth(), this->fogOfWarTexture->getHeight(), 1u, 1u, this->fogOfWarTexture->getPixelFormat());
...

this->fogOfWarStagingTexture->upload(texBox, this->fogOfWarTexture, 0, 0, 0);

Also as for the heightmap: The issue is different: What I get rendered to texture is just the blendmap. Hence I see if something is painted on terrain, but I see nothing from the heightmap, no hills nothing. That is the issue.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5436
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1343

Re: Ogre-Next Minimap and FogOfWar Question

Post by dark_sylinc »

Lax wrote: Sat Jul 20, 2024 7:43 pm

Do I need to call startMapRegion, stopMapRegtion frequently outside the thread?

Define "frequently"?

You just call startMapRegion() before signaling the thread to start, and then stopMapRegtion() once you get the signal from the thread that it is done.

Main thread should be the one calling startMapRegion / stopMapRegtion.

And what about the upload. Because the upload uses the texbox, which is created locally inside the thread update:

According to my own comments (LOL I had forgotten) StagingTexture::mapRegion can be called from a worker thread (but not from multiple threads at the same time).

The contents of the TextureBox are important, but you can copy them by value once you're done.

e.g.

Code: Select all

Ogre::TextureBox globalBox;

void workerThread()
{
barrier(); // Wait for startMapRegion.
// Worker Thread
Ogre::TextureBox myBox = s->mapRegion( ... );
// <Upload to myBox>
globalBox = myBox; // Copy by value
barrier(); // Tell main thread to go on.
}

void mainThread()
{
s->startMapRegion( ... );
barrier(); // Signal worker thread to wake up.
barrier(); // Wait for worker thread to finish.
s->stopMapRegion();
s->upload( globalBox, ... ); // Upload using the copied-by-value box
}

Of course this example makes it completely synchronous but you get the idea.

Btw if you're willing to waste BW but simplify the code; you could simply make worker thread create an Ogre::Image2 pointer; and when you're done you just call:

Code: Select all

texture->scheduleTransitionTo( Ogre::GpuResidency::Resident, imagePtr, true );

OgreNext will stream it in the background. See Samples/2.0/ApiUsage/UpdatingDecalsAndAreaLightTex/UpdatingDecalsAndAreaLightTexGameState.cpp

AH! If you don't want to waste BW, you can pretend the texture is loaded from file, then call:

Code: Select all

areaTex->scheduleTransitionTo( Ogre::GpuResidency::Resident );

And provide the data via ResourceLoadingListener::grouplessResourceLoading.
ResourceLoadingListener::grouplessResourceLoading will be called from a worker thread by TextureGpuManager to provide the content for the texture.

Since we are pretending the data is loaded from file (even though it's not), your listener will identify the texture by its name.

You can register such listener with ResourceGroupManager::getSingleton().setLoadingListener().

Lax
Gnoll
Posts: 645
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 59

Re: Ogre-Next Minimap and FogOfWar Question

Post by Lax »

Define "frequently"?

You just call startMapRegion() before signaling the thread to start, and then stopMapRegtion() once you get the signal from the thread that it is done.

My updateFogOfWarTexture is called in an update loop, since e.g. if a player is moving, the fog must be updated, hence I would like to start the fog of war painting thread just once and update the texture.

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5436
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1343

Re: Ogre-Next Minimap and FogOfWar Question

Post by dark_sylinc »

Let me put it another way:

If you want to update it every frame, you have to call stopMapRegion and upload every frame. However if the CPU cannot keep up with it, your framerate will be affected. Perhaps it won't be running as low as 20 fps; but it will be impacted.

If you instead let it update at its own pace (i.e. "whenever it's ready") you will be able to keep almost 140 fps (but the minimap updated at a different frequency, e.g. at 30 fps to throw a random number).

There is no technical rule that says you must have it finished before you render every frame.

Lax
Gnoll
Posts: 645
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 59

Re: Ogre-Next Minimap and FogOfWar Question

Post by Lax »

Code: Select all

If you instead let it update at its own pace (i.e. "whenever it's ready") you will be able to keep almost 140 fps (but the minimap updated at a different frequency, e.g. at 30 fps to throw a random number).

Ok, that is what I thought and what I observed. Even running in thread and let wait the main thread, is not a good solution. So I update at 30 FPS and that increases the performance.

Do you have an idea, why terra is not rendered correctly on the minimap (missing hills etc.).
I just create a simple workspace for the minimap without shadows etc.:

Code: Select all

void MinimapComponent::createMinimapNode(void)
{
	Ogre::CompositorManager2* compositorManager = WorkspaceModule::getInstance()->getCompositorManager();

this->minimapNodeName = "MinimapNode";
Ogre::CompositorNodeDef* nodeDef = compositorManager->addNodeDefinition(this->minimapNodeName);

nodeDef->addTextureSourceName("MinimapRT", 0, Ogre::TextureDefinitionBase::TEXTURE_INPUT);

nodeDef->setNumTargetPass(1);
{
	Ogre::CompositorTargetDef* targetDef = nodeDef->addTargetPass("MinimapRT");
	targetDef->setNumPasses(2);
	{
		// Clear Pass
		{
			Ogre::CompositorPassSceneDef* passClear;
			passClear = static_cast<Ogre::CompositorPassSceneDef*>(targetDef->addPass(Ogre::PASS_CLEAR));

			passClear->mClearColour[0] = Ogre::ColourValue(0.2f, 0.4f, 0.6f);
			// passClear->setAllStoreActions(Ogre::StoreAction::Store);
			passClear->mStoreActionColour[0] = Ogre::StoreAction::Store;
			passClear->mStoreActionDepth = Ogre::StoreAction::Store;
			passClear->mStoreActionStencil = Ogre::StoreAction::Store;
		}

		{
			// Scene pass for minimap
			Ogre::CompositorPassSceneDef* passScene = static_cast<Ogre::CompositorPassSceneDef*>(
				targetDef->addPass(Ogre::PASS_SCENE));
			passScene->mCameraName = this->cameraComponent->getCamera()->getName();

			passScene->setAllLoadActions(Ogre::LoadAction::Clear);
			passScene->mClearColour[0] = Ogre::ColourValue::Black;

			passScene->mStoreActionColour[0] = Ogre::StoreAction::StoreOrResolve;
			passScene->mStoreActionDepth = Ogre::StoreAction::DontCare;
			passScene->mStoreActionStencil = Ogre::StoreAction::DontCare;

			// If set, nothing is visible somehow from terra
			// passScene->mVisibilityMask = this->hideId->getULong(); // Custom mask for minimap objects

			passScene->mIncludeOverlays = false;

		}
	}
}
nodeDef->mapOutputChannel(0, "MinimapRT");
}

and then the workspace:

Code: Select all

this->minimapWorkspaceName = "MinimapWorkspaceWithFoW";
Ogre::CompositorWorkspaceDef* workspaceDef = compositorManager->addWorkspaceDefinition(this->minimapWorkspaceName);
workspaceDef->clearAllInterNodeConnections();

workspaceDef->connectExternal(0, this->minimapNodeName, 0);

this->externalChannels.resize(1);
this->externalChannels[0] = this->minimapTexture;

this->workspace = compositorManager->addWorkspace(this->gameObjectPtr->getSceneManager(), this->externalChannels, this->cameraComponent->getCamera(), this->minimapWorkspaceName, true);

and then I put the texture into a MyGUI::ImageBox for rendering:

Code: Select all

// Create MyGUI widget for the minimap
if (nullptr == this->minimapWidget)
{
	Ogre::Vector4 geometry = this->minimapGeometry->getVector4();
	this->minimapWidget = MyGUI::Gui::getInstancePtr()->createWidgetReal<MyGUI::ImageBox>("ImageBox", MyGUI::FloatCoord(geometry.x, geometry.y, geometry.z, geometry.w), MyGUI::Align::HCenter, "Overlapped");
	this->minimapWidget->setImageTexture(this->minimapTexture->getNameStr());
}
else
{
	this->minimapWidget->setVisible(true);
}

I have to create terra with the minimap camera instead of the main scene camera to get rid of the black artifacts.

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
Crystal Hammer
Gnome
Posts: 391
Joined: Sat Jun 23, 2007 5:16 pm
x 99

Re: Ogre-Next Minimap and FogOfWar Question

Post by Crystal Hammer »

Just few ideas, maybe worth checking.
How about HlmsPbsTerraShadows? Did we suspect already if that maybe gets faulty? Maybe those odd shadows come from it?
I'm guessing you already do hlmsPbs->setListener for all Pbs materials to receive terra shadows?

Also do you have any custom worskpace listeners? If so, do you also addListener to your custom minimap workspace?

Do you update minimap worskpace manually? I have still an issue with my minimap, and I have to create its RTTs as auto updated (and waste GPU doing it every frame). Otherwise I get minimap fully shadowed if I create them as manually updated.

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5436
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1343

Re: Ogre-Next Minimap and FogOfWar Question

Post by dark_sylinc »

It may be easier if you're able to repro the problem in the original Terrain sample.

You don't have to use MyGUI, you can simply use v1 Overlays (see ShadowMap sample which creates an UI to show the contents of the shadow maps on screen).

Lax
Gnoll
Posts: 645
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 59

Re: Ogre-Next Minimap and FogOfWar Question

Post by Lax »

Hi,

How about HlmsPbsTerraShadows? Did we suspect already if that maybe gets faulty? Maybe those odd shadows come from it?
I'm guessing you already do hlmsPbs->setListener for all Pbs materials to receive terra shadows?

Yes I do

Also do you have any custom worskpace listeners? If so, do you also addListener to your custom minimap workspace?

Yes I do

Do you update minimap worskpace manually? I have still an issue with my minimap, and I have to create its RTTs as auto updated (and waste GPU doing it every frame). Otherwise I get minimap fully shadowed if I create them as manually updated.

I did for testing:

Code: Select all

this->workspace->_validateFinalTarget();
					this->workspace->_beginUpdate(true);
					this->workspace->_update();
					this->workspace->_endUpdate(true);

But It has no effect.

This is the result:

Image

There should be the hills visible, instead I see in the right down corner a diagonal strange line.

I also tried to look with the minimap camera at 80degree instead of 90degree to the ground, but without success.

It may be easier if you're able to repro the problem in the original Terrain sample.

Seems like an idea. I must find time to do that.

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

Lax
Gnoll
Posts: 645
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 59

Re: Ogre-Next Minimap and FogOfWar Question

Post by Lax »

Hi,

I extended the tutorial_terrain example using the minimap. I also created a git branch, but I'm not able to push anything on the branch due to (no idea).

Hence here are the two files, in which the minimap is rendered:

https://www.lukas-kalinowski.com/Homepa ... ameState.h
https://www.lukas-kalinowski.com/Homepa ... eState.cpp

In the example I see the hills on the minimap :x

So I have no idea what is wrong with my code :(

But terra must be created with the mMinimapCamera. You can switch the #if 0, to set what is happening.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

Lax
Gnoll
Posts: 645
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 59

Re: Ogre-Next Minimap and FogOfWar Question

Post by Lax »

Oh boy, I found the heighmap issue.

In my terra.cpp createNormalTexture function I used besides:

Code: Select all

 m_normalMapTex->scheduleTransitionTo( GpuResidency::Resident );

also:

Code: Select all

m_normalMapTex->notifyDataIsReady();

This caused, that heighmap is not rendered^^
I have no idea why that happend. Maybe @dark_sylinc can bring light into the shadows :)

Now the topic left is: Why there are artifacts, if main camera is used, instead of minimap camera.

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

Lax
Gnoll
Posts: 645
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 59

Re: Ogre-Next Minimap and FogOfWar Question

Post by Lax »

Hi,

just wanted to show the minimap progress. I added a lot of features and It works not that performant, but nice!

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5436
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1343

Re: Ogre-Next Minimap and FogOfWar Question

Post by dark_sylinc »

Ok I finally got some time to see your sample code. I'm sorry for taking this long.

Ahhhh!!! Now I understand! You have 2 cameras!!! I thought you were simply moving the camera around when it was minimap rendering time (rather than using two cameras).

You can swap the Camera in a Listener and update Terra again. Then after you're done you restore the original Terrain state (as long as you don't have shadow nodes and point/spot lights in the minimap pass) with _swapSavedState.

We already show how to do this in the sample.

The original sample uses _swapSavedState for point and spot light shadows:

  1. It's the turn to render from the spot light shadow caster camera perspective.

  2. We call _swapSavedState() to change to the "alternate" state.

  3. We set the new camera and call update() (we also call setCastShadows because we want to cast shadows in point/spot passes but not on directional passes since for directional light we use a custom Compute Shader solution. But that's irrelevant to you).

  4. Rendering happens.

  5. Once the pass is done, you need to call setCamera( nullptr ) followed by _swapSavedState() to restore the original Terra state.

You pretty much need to do the same thing with a Compositor workspace listener when it's time to render the minimap.

What's important is that when you call terra->update(), we perform various calculations to render the terrain optimally from the camera's perspective. Since the minimap is a different camera with entirely settings, you're basically rendering exactly what the main camera sees, which is not what you want.

Cheers

Lax
Gnoll
Posts: 645
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 59

Re: Ogre-Next Minimap and FogOfWar Question

Post by Lax »

Hi,

thanks for your reply. I know you are a busy man.

So you mean, I should adapt the:

Code: Select all

void TerraWorkspaceListener::passPreExecute( CompositorPass *pass )

function?

Do you have some rough example code to point me in the right direction?

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
Crystal Hammer
Gnome
Posts: 391
Joined: Sat Jun 23, 2007 5:16 pm
x 99

Re: Ogre-Next Minimap and FogOfWar Question

Post by Crystal Hammer »

Interesting.
I wonder if this is related to my minimap issue. All my editor RTTs have own cameras. Two of them render terrain.
But I have to create RTT workspaces as auto updated (true). If they are manual (false) I get everything dark, shadowed in them.
It was that issue long ago which has MANUAL_RTT define in SR3 code.