📄 etsplattingmanager.cpp
字号:
/*
EDITABLE TERRAIN MANAGER for Ogre
Copyright (C) 2007 Holger Frydrych <h.frydrych@gmx.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
As a special exception, you may use this file as part of a free software
library without restriction. Specifically, if other files instantiate
templates or use macros or inline functions from this file, or you compile
this file and link it with other files to produce an executable, this
file does not by itself cause the resulting executable to be covered by
the GNU General Public License. This exception does not however
invalidate any other reasons why the executable file might be covered by
the GNU General Public License.
*/
#include "ETSplattingManager.h"
#include "ETBrush.h"
#include <OgreResource.h>
#include <OgreImage.h>
#include <OgreTextureManager.h>
#include <OgreStringConverter.h>
#include <OgreHardwarePixelBuffer.h>
#include <OgreException.h>
#include <OgreLogManager.h>
#include <sstream>
using namespace Ogre;
using namespace std;
using Ogre::uint;
using Ogre::ushort;
namespace ET
{
namespace Impl
{
/** Handles a single alpha coverage map texture */
class CoverageMap : public ManualResourceLoader
{
public:
CoverageMap(const String& name, const String& group, uint width, uint height, int channels, bool black = true);
~CoverageMap();
/** Retrieve coverage map from image. */
void loadFromImage(const Image& image);
/** Save coverage map to image. */
void saveToImage(Image& image);
/** Get the map's value for texture index c at position (x, y) */
uchar getValue(uint x, uint y, uint c) const;
/** Set the map's value for texture index c at position (x, y) */
void setValue(uint x, uint y, uint c, uchar val);
/** Copy the editing buffer into the texture's pixel buffer. */
void updateTexture();
/** Implemented from ManualResourceLoader to support reloading of the coverage map. */
void loadResource(Resource*);
/** Texture resource name for this coverage map. */
const String& getName() const;
private:
PixelFormat getFormat(int channels);
int getChannels(PixelFormat format);
uint mWidth, mHeight, mSize;
int mChannels;
uchar* mData;
TexturePtr mTexture;
};
CoverageMap::CoverageMap(const String& name, const String& group, uint width, uint height, int channels, bool black)
: mWidth(width), mHeight(height), mChannels(channels), mSize(width*height*channels)
{
mData = new uchar[mSize];
memset(mData, 0, mSize);
// the first channel of the first coverage map is set to full value so that
// terrain initially is rendered with the first splatting texture
if (!black)
for (uint i = 0; i < mSize; i += mChannels)
mData[i] = 255;
// create a manually managed texture resource
mTexture = TextureManager::getSingleton().createManual(name, group, TEX_TYPE_2D,
width, height, 1, 0, getFormat(mChannels), TU_DEFAULT, this);
}
CoverageMap::~CoverageMap()
{
delete[] mData;
TextureManager::getSingleton().remove(mTexture->getName());
}
void CoverageMap::loadResource(Resource*)
{
// the texture has requested to (re)load, we just copy our edit buffer
// into the texture
mTexture->createInternalResources();
updateTexture();
}
void CoverageMap::updateTexture()
{
// write the edit buffer into the texture's pixel buffer
HardwarePixelBufferSharedPtr buffer = mTexture->getBuffer();
PixelBox pixelBox (mWidth, mHeight, 1, getFormat(mChannels), mData);
Image::Box imageBox (0, 0, mWidth, mHeight);
buffer->blitFromMemory(pixelBox, imageBox);
}
const String& CoverageMap::getName() const
{
return mTexture->getName();
}
uchar CoverageMap::getValue(uint x, uint y, uint c) const
{
return mData[(y*mWidth + x)*mChannels + c];
}
void CoverageMap::setValue(uint x, uint y, uint c, uchar val)
{
mData[(y*mWidth + x)*mChannels + c] = val;
}
PixelFormat CoverageMap::getFormat(int channels)
{
switch (channels)
{
case 1: return PF_BYTE_A;
case 2: return PF_BYTE_LA;
case 3: return PF_BYTE_RGB;
case 4: return PF_BYTE_RGBA;
case -1: return PF_BYTE_A;
case -2: return PF_BYTE_LA;
case -3: return PF_BYTE_BGR;
case -4: return PF_BYTE_BGRA;
default: return PF_UNKNOWN;
}
}
int CoverageMap::getChannels(PixelFormat format)
{
switch (format)
{
case PF_BYTE_A: return 1;
case PF_BYTE_LA: return 2;
case PF_BYTE_RGB: return 3;
case PF_BYTE_BGR: return -3;
case PF_BYTE_RGBA: return 4;
case PF_BYTE_BGRA: return -4;
default: return 0;
}
}
void CoverageMap::loadFromImage(const Image& image)
{
if (image.getWidth() != mWidth || image.getHeight() != mHeight)
OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Given image doesn't conform to the used width and height.", "CoverageMap::loadFromImage");
if (image.getFormat() != getFormat(mChannels) && image.getFormat() != getFormat(-mChannels))
OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Given image is of invalid pixel format.", "CoverageMap::loadFromImage");
memcpy(mData, image.getData(), image.getSize());
if (getChannels(image.getFormat()) <= -3)
{
// need RGB(A), but given BGR(A) so invert
for (uint i = 0; i < mSize; i += mChannels)
swap(mData[i], mData[i+2]);
}
updateTexture();
}
void CoverageMap::saveToImage(Image& image)
{
image.loadDynamicImage(mData, mWidth, mHeight, 1, getFormat(mChannels));
}
struct SplattingImpl
{
String baseName;
String group;
uint width, height;
uint channels;
uint numTextures;
uint numMaps;
typedef vector<Impl::CoverageMap*> MapList;
MapList maps;
void paint(uint texture, uint x, uint y, float edit)
{
uint map = texture / channels;
uint channel = texture % channels;
int val = maps[map]->getValue(x, y, channel) + (int) (255.0 * edit);
if (val > 255)
val = 255;
if (val < 0)
val = 0;
maps[map]->setValue(x, y, channel, val);
balance(x, y, texture, val);
}
void balance(uint x, uint y, uint texture, int val)
{
// this method ensures that the values at (x, y) of all channels in all maps sum up to 255,
// otherwise the terrain will get over- or underlighted at that position
int sum = 0;
for (uint i = 0; i < numMaps; ++i)
{
for (uint j = 0; j < channels; ++j)
{
// skip the texture we painted with, otherwise we'd be
// undoing the change
if (i * channels + j == texture)
continue;
sum += maps[i]->getValue(x, y, j);
}
}
if (sum == 0)
{
// all other textures are 0, so set selected texture to full value
maps[texture/channels]->setValue(x, y, texture%channels, 255);
return;
}
// reduce/add all other channels as necessary
int diff = sum - (255 - val);
for (uint i = 0; i < numMaps; ++i)
{
for (uint j = 0; j < channels; ++j)
{
// skip the texture we painted with, otherwise we'd be
// undoing the change
if (i * channels + j == texture)
continue;
uchar v = maps[i]->getValue(x, y, j);
v -= (uchar) floor(0.5 + diff * (float(v) / sum));
maps[i]->setValue(x, y, j, v);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -