1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of OGRE
4 (Object-oriented Graphics Rendering Engine)
5 For the latest info, see http://www.ogre3d.org/
6 
7 Copyright (c) 2000-2013 Torus Knot Software Ltd
8 
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15 
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 -----------------------------------------------------------------------------
27 */
28 #include "OgreTerrainLayerBlendMap.h"
29 #include "OgreHardwarePixelBuffer.h"
30 #include "OgreTerrain.h"
31 #include "OgreImage.h"
32 
33 #if OGRE_COMPILER == OGRE_COMPILER_MSVC
34 // we do lots of conversions here, casting them all is tedious & cluttered, we know what we're doing
35 #   pragma warning (disable : 4244)
36 #endif
37 
38 namespace Ogre
39 {
40 	//---------------------------------------------------------------------
TerrainLayerBlendMap(Terrain * parent,uint8 layerIndex,HardwarePixelBuffer * buf)41 	TerrainLayerBlendMap::TerrainLayerBlendMap(Terrain* parent, uint8 layerIndex,
42 		HardwarePixelBuffer* buf)
43 		: mParent(parent)
44 		, mLayerIdx(layerIndex)
45 		, mChannel((layerIndex-1) % 4)
46 		, mDirty(false)
47 		, mBuffer(buf)
48 		, mData(0)
49 	{
50 		mData = static_cast<float*>(OGRE_MALLOC(mBuffer->getWidth() * mBuffer->getHeight() * sizeof(float), MEMCATEGORY_RESOURCE));
51 
52 		// we know which of RGBA we need to look at, now find it in the format
53 		// because we can't guarantee what precise format the RS gives us
54 		unsigned char rgbaShift[4];
55 		PixelFormat fmt = mBuffer->getFormat();
56 		PixelUtil::getBitShifts(fmt, rgbaShift);
57 		mChannelOffset = rgbaShift[mChannel] / 8; // /8 to convert to bytes
58 #if OGRE_ENDIAN == OGRE_ENDIAN_BIG
59 		// invert (dealing bytewise)
60 		mChannelOffset = PixelUtil::getNumElemBytes(fmt) - mChannelOffset - 1;
61 #endif
62 		download();
63 
64 	}
65 	//---------------------------------------------------------------------
~TerrainLayerBlendMap()66 	TerrainLayerBlendMap::~TerrainLayerBlendMap()
67 	{
68 		OGRE_FREE(mData, MEMCATEGORY_RESOURCE);
69 		mData = 0;
70 	}
71 	//---------------------------------------------------------------------
download()72 	void TerrainLayerBlendMap::download()
73 	{
74 		float* pDst = mData;
75 		// Download data
76 		Image::Box box(0, 0, mBuffer->getWidth(), mBuffer->getHeight());
77 		uint8* pSrc = static_cast<uint8*>(mBuffer->lock(box, HardwareBuffer::HBL_READ_ONLY).data);
78 		pSrc += mChannelOffset;
79 		size_t srcInc = PixelUtil::getNumElemBytes(mBuffer->getFormat());
80 		for (size_t y = box.top; y < box.bottom; ++y)
81 		{
82 			for (size_t x = box.left; x < box.right; ++x)
83 			{
84 				*pDst++ = static_cast<float>(*pSrc) / 255.0f;
85 				pSrc += srcInc;
86 			}
87 		}
88 		mBuffer->unlock();
89 
90 	}
91 	//---------------------------------------------------------------------
convertWorldToUVSpace(const Vector3 & worldPos,Real * outX,Real * outY)92 	void TerrainLayerBlendMap::convertWorldToUVSpace(const Vector3& worldPos, Real *outX, Real* outY)
93 	{
94 		Vector3 terrainSpace;
95 		mParent->getTerrainPosition(worldPos, &terrainSpace);
96 		*outX = terrainSpace.x;
97 		*outY = 1.0f - terrainSpace.y;
98 	}
99 	//---------------------------------------------------------------------
convertUVToWorldSpace(Real x,Real y,Vector3 * outWorldPos)100 	void TerrainLayerBlendMap::convertUVToWorldSpace(Real x, Real y, Vector3* outWorldPos)
101 	{
102 		mParent->getPosition(x, 1.0f - y, 0, outWorldPos);
103 	}
104 	//---------------------------------------------------------------------
convertUVToImageSpace(Real x,Real y,size_t * outX,size_t * outY)105 	void TerrainLayerBlendMap::convertUVToImageSpace(Real x, Real y, size_t* outX, size_t* outY)
106 	{
107 		*outX = (unsigned long)(x * (mBuffer->getWidth() - 1));
108 		*outY = (unsigned long)(y * (mBuffer->getHeight() - 1));
109 	}
110 	//---------------------------------------------------------------------
convertImageToUVSpace(size_t x,size_t y,Real * outX,Real * outY)111 	void TerrainLayerBlendMap::convertImageToUVSpace(size_t x, size_t y, Real* outX, Real* outY)
112 	{
113 		*outX = x / (Real)(mBuffer->getWidth() - 1);
114 		*outY = y / (Real)(mBuffer->getHeight() - 1);
115 	}
116 	//---------------------------------------------------------------------
convertImageToTerrainSpace(size_t x,size_t y,Real * outX,Real * outY)117 	void TerrainLayerBlendMap::convertImageToTerrainSpace(size_t x, size_t y, Real* outX, Real* outY)
118 	{
119 		convertImageToUVSpace(x, y, outX, outY);
120 		*outY = 1.0f - *outY;
121 	}
122 	//---------------------------------------------------------------------
convertTerrainToImageSpace(Real x,Real y,size_t * outX,size_t * outY)123 	void TerrainLayerBlendMap::convertTerrainToImageSpace(Real x, Real y, size_t* outX, size_t* outY)
124 	{
125 		convertUVToImageSpace(x, 1.0f - y, outX, outY);
126 	}
127 	//---------------------------------------------------------------------
getBlendValue(size_t x,size_t y)128 	float TerrainLayerBlendMap::getBlendValue(size_t x, size_t y)
129 	{
130 		return *(mData + y * mBuffer->getWidth() + x);
131 	}
132 	//---------------------------------------------------------------------
setBlendValue(size_t x,size_t y,float val)133 	void TerrainLayerBlendMap::setBlendValue(size_t x, size_t y, float val)
134 	{
135 		*(mData + y * mBuffer->getWidth() + x) = val;
136 		dirtyRect(Rect(x, y, x+1, y+1));
137 
138 	}
139 	//---------------------------------------------------------------------
getBlendPointer()140 	float* TerrainLayerBlendMap::getBlendPointer()
141 	{
142 		return mData;
143 	}
144 	//---------------------------------------------------------------------
dirty()145 	void TerrainLayerBlendMap::dirty()
146 	{
147 		Rect rect;
148 		rect.top = 0; rect.bottom = mBuffer->getHeight();
149 		rect.left = 0; rect.right = mBuffer->getWidth();
150 		dirtyRect(rect);
151 
152 	}
153 	//---------------------------------------------------------------------
dirtyRect(const Rect & rect)154 	void TerrainLayerBlendMap::dirtyRect(const Rect& rect)
155 	{
156 		if (mDirty)
157 		{
158 			mDirtyBox.left = std::min(mDirtyBox.left, static_cast<uint32>(rect.left));
159 			mDirtyBox.top = std::min(mDirtyBox.top, static_cast<uint32>(rect.top));
160 			mDirtyBox.right = std::max(mDirtyBox.right, static_cast<uint32>(rect.right));
161 			mDirtyBox.bottom = std::max(mDirtyBox.bottom, static_cast<uint32>(rect.bottom));
162 		}
163 		else
164 		{
165 			mDirtyBox.left = static_cast<uint32>(rect.left);
166 			mDirtyBox.right = static_cast<uint32>(rect.right);
167 			mDirtyBox.top = static_cast<uint32>(rect.top);
168 			mDirtyBox.bottom = static_cast<uint32>(rect.bottom);
169 			mDirty = true;
170 		}
171 	}
172 	//---------------------------------------------------------------------
update()173 	void TerrainLayerBlendMap::update()
174 	{
175 		if (mData && mDirty)
176 		{
177 			// Upload data
178 			float* pSrcBase = mData + mDirtyBox.top * mBuffer->getWidth() + mDirtyBox.left;
179 			uint8* pDstBase = static_cast<uint8*>(mBuffer->lock(mDirtyBox, HardwarePixelBuffer::HBL_NORMAL).data);
180 			pDstBase += mChannelOffset;
181 			size_t dstInc = PixelUtil::getNumElemBytes(mBuffer->getFormat());
182 			for (size_t y = 0; y < mDirtyBox.getHeight(); ++y)
183 			{
184 				float* pSrc = pSrcBase + y * mBuffer->getWidth();
185 				uint8* pDst = pDstBase + y * mBuffer->getWidth() * dstInc;
186 				for (size_t x = 0; x < mDirtyBox.getWidth(); ++x)
187 				{
188 					*pDst = static_cast<uint8>(*pSrc++ * 255);
189 					pDst += dstInc;
190 				}
191 			}
192 			mBuffer->unlock();
193 
194 			mDirty = false;
195 
196 			// make sure composite map is updated
197 			// mDirtyBox is in image space, convert to terrain units
198 			Rect compositeMapRect;
199 			float blendToTerrain = (float)mParent->getSize() / (float)mBuffer->getWidth();
200 			compositeMapRect.left = (long)(mDirtyBox.left * blendToTerrain);
201 			compositeMapRect.right = (long)(mDirtyBox.right * blendToTerrain + 1);
202 			compositeMapRect.top = (long)((mBuffer->getHeight() - mDirtyBox.bottom) * blendToTerrain);
203 			compositeMapRect.bottom = (long)((mBuffer->getHeight() - mDirtyBox.top) * blendToTerrain + 1);
204 			mParent->_dirtyCompositeMapRect(compositeMapRect);
205 			mParent->updateCompositeMapWithDelay();
206 
207 		}
208 	}
209 	//---------------------------------------------------------------------
blit(const PixelBox & src,const Box & dstBox)210 	void TerrainLayerBlendMap::blit(const PixelBox &src, const Box &dstBox)
211 	{
212 		const PixelBox* srcBox = &src;
213 
214 		if (srcBox->getWidth() != dstBox.getWidth() || srcBox->getHeight() != dstBox.getHeight())
215 		{
216 			// we need to rescale src to dst size first (also confvert format)
217 			void* tmpData = OGRE_MALLOC(dstBox.getWidth() * dstBox.getHeight(), MEMCATEGORY_GENERAL);
218 			srcBox = OGRE_NEW PixelBox(dstBox.getWidth(), dstBox.getHeight(), 1, PF_L8, tmpData);
219 
220 			Image::scale(src, *srcBox);
221 		}
222 
223 		// pixel conversion
224 		PixelBox dstMemBox(dstBox, PF_L8, mData);
225 		PixelUtil::bulkPixelConversion(*srcBox, dstMemBox);
226 
227 		if (srcBox != &src)
228 		{
229 			// free temp
230 			OGRE_FREE(srcBox->data, MEMCATEGORY_GENERAL);
231 			OGRE_DELETE srcBox;
232 			srcBox = 0;
233 		}
234 
235 		Rect dRect(dstBox.left, dstBox.top, dstBox.right, dstBox.bottom);
236 		dirtyRect(dRect);
237 
238 	}
239 	//---------------------------------------------------------------------
blit(const PixelBox & src)240 	void TerrainLayerBlendMap::blit(const PixelBox &src)
241 	{
242 		blit(src, Box(0,0,0,mBuffer->getWidth(),mBuffer->getHeight(),1));
243 	}
244 	//---------------------------------------------------------------------
loadImage(const Image & img)245 	void TerrainLayerBlendMap::loadImage(const Image& img)
246 	{
247 		blit(img.getPixelBox());
248 	}
249 	//---------------------------------------------------------------------
loadImage(DataStreamPtr & stream,const String & ext)250 	void TerrainLayerBlendMap::loadImage(DataStreamPtr& stream, const String& ext)
251 	{
252 		Image img;
253 		img.load(stream, ext);
254 		loadImage(img);
255 	}
256 	//---------------------------------------------------------------------
loadImage(const String & filename,const String & groupName)257 	void TerrainLayerBlendMap::loadImage(const String& filename, const String& groupName)
258 	{
259 		Image img;
260 		img.load(filename, groupName);
261 		loadImage(img);
262 	}
263 
264 
265 }
266 
267