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> </texture>
\endcode \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. 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.
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 \section Materials_Techniques Techniques and passes

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

@ -42,6 +42,20 @@
namespace Urho3D 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) : TextureCube::TextureCube(Context* context) :
Texture(context), Texture(context),
lockedLevel_(-1) lockedLevel_(-1)
@ -96,21 +110,96 @@ bool TextureCube::BeginLoad(Deserializer& source)
loadImages_.Clear(); loadImages_.Clear();
XMLElement textureElem = loadParameters_->GetRoot(); XMLElement textureElem = loadParameters_->GetRoot();
XMLElement faceElem = textureElem.GetChild("face"); XMLElement imageElem = textureElem.GetChild("image");
while (faceElem) // Single image and multiple faces with layout
if (imageElem)
{ {
String name = faceElem.GetAttribute("name"); String name = imageElem.GetAttribute("name");
String faceTexPath, faceTexName, faceTexExt;
SplitPath(name, faceTexPath, faceTexName, faceTexExt);
// If path is empty, add the XML file path // If path is empty, add the XML file path
if (faceTexPath.Empty()) if (GetPath(name).Empty())
name = texPath + name; name = texPath + name;
loadImages_.Push(cache->GetTempResource<Image>(name)); CubeMapLayout layout = (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
cache->StoreResourceDependency(this, name); SharedPtr<Image> image = cache->GetTempResource<Image>(name);
if (!image)
return false;
faceElem = faceElem.GetNext("face"); 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");
// If path is empty, add the XML file path
if (GetPath(name).Empty())
name = texPath + name;
loadImages_.Push(cache->GetTempResource<Image>(name));
cache->StoreResourceDependency(this, name);
faceElem = faceElem.GetNext("face");
}
} }
// Precalculate mip levels if async loading // Precalculate mip levels if async loading

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

@ -186,6 +186,16 @@ enum CubeMapFace
MAX_CUBEMAP_FACES 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. /// Update mode for render surface viewports.
enum RenderSurfaceUpdateMode enum RenderSurfaceUpdateMode
{ {

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

@ -42,6 +42,20 @@
namespace Urho3D 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) : TextureCube::TextureCube(Context* context) :
Texture(context) Texture(context)
{ {
@ -97,23 +111,98 @@ bool TextureCube::BeginLoad(Deserializer& source)
loadImages_.Clear(); loadImages_.Clear();
XMLElement textureElem = loadParameters_->GetRoot(); XMLElement textureElem = loadParameters_->GetRoot();
XMLElement faceElem = textureElem.GetChild("face"); XMLElement imageElem = textureElem.GetChild("image");
while (faceElem) // Single image and multiple faces with layout
if (imageElem)
{ {
String name = faceElem.GetAttribute("name"); String name = imageElem.GetAttribute("name");
String faceTexPath, faceTexName, faceTexExt;
SplitPath(name, faceTexPath, faceTexName, faceTexExt);
// If path is empty, add the XML file path // If path is empty, add the XML file path
if (faceTexPath.Empty()) if (GetPath(name).Empty())
name = texPath + name; name = texPath + name;
loadImages_.Push(cache->GetTempResource<Image>(name)); CubeMapLayout layout = (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
cache->StoreResourceDependency(this, name); SharedPtr<Image> image = cache->GetTempResource<Image>(name);
if (!image)
return false;
faceElem = faceElem.GetNext("face"); 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");
// If path is empty, add the XML file path
if (GetPath(name).Empty())
name = texPath + name;
loadImages_.Push(cache->GetTempResource<Image>(name));
cache->StoreResourceDependency(this, name);
faceElem = faceElem.GetNext("face");
}
}
// Precalculate mip levels if async loading // Precalculate mip levels if async loading
if (GetAsyncLoadState() == ASYNC_LOADING) if (GetAsyncLoadState() == ASYNC_LOADING)
{ {