Support single image splitting for cube maps. Closes #445.

This commit is contained in:
Lasse Öörni 2014-09-13 15:33:55 +03:00
Родитель 5a79a7c552
Коммит f1e25381a3
4 изменённых файлов: 237 добавлений и 23 удалений

Просмотреть файл

@ -950,8 +950,34 @@ Textures can have an accompanying XML file which specifies load-time parameters,
</texture>
\endcode
The sRGB flag controls both whether the texture should be sampled with sRGB to linear conversion, and if used as a rendertarget, pixels should be converted back to sRGB when writing to it.
To control whether the backbuffer should use sRGB conversion on write, call \ref Graphics::SetSRGB "SetSRGB()" on the Graphics subsystem.
The sRGB flag controls both whether the texture should be sampled with sRGB to linear conversion, and if used as a rendertarget, pixels should be converted back to sRGB when writing to it. To control whether the backbuffer should use sRGB conversion on write, call \ref Graphics::SetSRGB "SetSRGB()" on the Graphics subsystem.
\section Materials_CubeMapTextures Cube map textures
Using cube map textures requires an XML file to define the cube map face textures or layout. In this case the XML file *is* the texture resource name in material scripts or in LoadResource() calls.
Individual face textures are defined in the XML like this: (see Bin/Data/Textures/Skybox.xml for an example)
\code
<cubemap>
<face name="PositiveX_TextureName" />
<face name="NegativeX_TextureName" />
<face name="PositiveY_TextureName" />
<face name="NegativeY_TextureName" />
<face name="PositiveZ_TextureName" />
<face name="NegativeZ_TextureName" />
</cubemap>
\endcode
Using a single image texture and a layout is used like this:
\code
<cubemap>
<image name="TextureName" layout="horizontal|horizontalnvidia|horizontalcross|verticalcross|blender" />
</cubemap>
\endcode
For the layout definitions, see http://www.cgtextures.com/content.php?action=tutorial&name=cubemaps and http://en.wikibooks.org/wiki/Blender_3D:_Noob_to_Pro/Build_a_skybox
\section Materials_Techniques Techniques and passes

Просмотреть файл

@ -42,6 +42,20 @@
namespace Urho3D
{
static const char* cubeMapLayoutNames[] = {
"horizontal",
"horizontalnvidia",
"horizontalcross",
"verticalcross",
"blender",
0
};
static SharedPtr<Image> GetTileImage(Image* src, int tileX, int tileY, int tileWidth, int tileHeight)
{
return SharedPtr<Image>(src->GetSubimage(IntRect(tileX * tileWidth, tileY * tileHeight, (tileX + 1) * tileWidth, (tileY + 1) * tileHeight)));
}
TextureCube::TextureCube(Context* context) :
Texture(context),
lockedLevel_(-1)
@ -96,15 +110,89 @@ bool TextureCube::BeginLoad(Deserializer& source)
loadImages_.Clear();
XMLElement textureElem = loadParameters_->GetRoot();
XMLElement imageElem = textureElem.GetChild("image");
// Single image and multiple faces with layout
if (imageElem)
{
String name = imageElem.GetAttribute("name");
// If path is empty, add the XML file path
if (GetPath(name).Empty())
name = texPath + name;
CubeMapLayout layout = (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
SharedPtr<Image> image = cache->GetTempResource<Image>(name);
if (!image)
return false;
int faceWidth, faceHeight;
loadImages_.Resize(MAX_CUBEMAP_FACES);
switch (layout)
{
case CML_HORIZONTAL:
faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
faceHeight = image->GetHeight();
loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 3, 0, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 4, 0, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 5, 0, faceWidth, faceHeight);
break;
case CML_HORIZONTALNVIDIA:
faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
faceHeight = image->GetHeight();
for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
loadImages_[i] = GetTileImage(image, i, 0, faceWidth, faceHeight);
break;
case CML_HORIZONTALCROSS:
faceWidth = image->GetWidth() / 4;
faceHeight = image->GetHeight() / 3;
loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 3, 1, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
break;
case CML_VERTICALCROSS:
faceWidth = image->GetWidth() / 3;
faceHeight = image->GetHeight() / 4;
loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 3, faceWidth, faceHeight);
if (loadImages_[FACE_NEGATIVE_Z])
loadImages_[FACE_NEGATIVE_Z]->FlipVertical();
break;
case CML_BLENDER:
faceWidth = image->GetWidth() / 3;
faceHeight = image->GetHeight() / 2;
loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
break;
}
}
// Face per image
else
{
XMLElement faceElem = textureElem.GetChild("face");
while (faceElem)
{
String name = faceElem.GetAttribute("name");
String faceTexPath, faceTexName, faceTexExt;
SplitPath(name, faceTexPath, faceTexName, faceTexExt);
// If path is empty, add the XML file path
if (faceTexPath.Empty())
if (GetPath(name).Empty())
name = texPath + name;
loadImages_.Push(cache->GetTempResource<Image>(name));
@ -112,6 +200,7 @@ bool TextureCube::BeginLoad(Deserializer& source)
faceElem = faceElem.GetNext("face");
}
}
// Precalculate mip levels if async loading
if (GetAsyncLoadState() == ASYNC_LOADING)

Просмотреть файл

@ -186,6 +186,16 @@ enum CubeMapFace
MAX_CUBEMAP_FACES
};
/// Cubemap single image layout modes.
enum CubeMapLayout
{
CML_HORIZONTAL = 0,
CML_HORIZONTALNVIDIA,
CML_HORIZONTALCROSS,
CML_VERTICALCROSS,
CML_BLENDER
};
/// Update mode for render surface viewports.
enum RenderSurfaceUpdateMode
{

Просмотреть файл

@ -42,6 +42,20 @@
namespace Urho3D
{
static const char* cubeMapLayoutNames[] = {
"horizontal",
"horizontalnvidia",
"horizontalcross",
"verticalcross",
"blender",
0
};
static SharedPtr<Image> GetTileImage(Image* src, int tileX, int tileY, int tileWidth, int tileHeight)
{
return SharedPtr<Image>(src->GetSubimage(IntRect(tileX * tileWidth, tileY * tileHeight, (tileX + 1) * tileWidth, (tileY + 1) * tileHeight)));
}
TextureCube::TextureCube(Context* context) :
Texture(context)
{
@ -97,15 +111,89 @@ bool TextureCube::BeginLoad(Deserializer& source)
loadImages_.Clear();
XMLElement textureElem = loadParameters_->GetRoot();
XMLElement imageElem = textureElem.GetChild("image");
// Single image and multiple faces with layout
if (imageElem)
{
String name = imageElem.GetAttribute("name");
// If path is empty, add the XML file path
if (GetPath(name).Empty())
name = texPath + name;
CubeMapLayout layout = (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
SharedPtr<Image> image = cache->GetTempResource<Image>(name);
if (!image)
return false;
int faceWidth, faceHeight;
loadImages_.Resize(MAX_CUBEMAP_FACES);
switch (layout)
{
case CML_HORIZONTAL:
faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
faceHeight = image->GetHeight();
loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 3, 0, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 4, 0, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 5, 0, faceWidth, faceHeight);
break;
case CML_HORIZONTALNVIDIA:
faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
faceHeight = image->GetHeight();
for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
loadImages_[i] = GetTileImage(image, i, 0, faceWidth, faceHeight);
break;
case CML_HORIZONTALCROSS:
faceWidth = image->GetWidth() / 4;
faceHeight = image->GetHeight() / 3;
loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 3, 1, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
break;
case CML_VERTICALCROSS:
faceWidth = image->GetWidth() / 3;
faceHeight = image->GetHeight() / 4;
loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 3, faceWidth, faceHeight);
if (loadImages_[FACE_NEGATIVE_Z])
loadImages_[FACE_NEGATIVE_Z]->FlipVertical();
break;
case CML_BLENDER:
faceWidth = image->GetWidth() / 3;
faceHeight = image->GetHeight() / 2;
loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
break;
}
}
// Face per image
else
{
XMLElement faceElem = textureElem.GetChild("face");
while (faceElem)
{
String name = faceElem.GetAttribute("name");
String faceTexPath, faceTexName, faceTexExt;
SplitPath(name, faceTexPath, faceTexName, faceTexExt);
// If path is empty, add the XML file path
if (faceTexPath.Empty())
if (GetPath(name).Empty())
name = texPath + name;
loadImages_.Push(cache->GetTempResource<Image>(name));
@ -113,6 +201,7 @@ bool TextureCube::BeginLoad(Deserializer& source)
faceElem = faceElem.GetNext("face");
}
}
// Precalculate mip levels if async loading
if (GetAsyncLoadState() == ASYNC_LOADING)