Support single image splitting for cube maps. Closes #445.
This commit is contained in:
Родитель
5a79a7c552
Коммит
f1e25381a3
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
Загрузка…
Ссылка в новой задаче