Caelum
InternalUtilities.cpp
1 /*
2 This file is part of Caelum.
3 See http://www.ogre3d.org/wiki/index.php/Caelum
4 
5 Copyright (c) 2006-2007 Caelum team. See Contributors.txt for details.
6 
7 Caelum is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Caelum is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Lesser General Public License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public License
18 along with Caelum. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include "CaelumPrecompiled.h"
22 #include "CaelumExceptions.h"
23 #include "InternalUtilities.h"
24 #include "PrivatePtr.h"
25 
26 namespace Caelum
27 {
29  float fx, float fy, Ogre::Image *img, bool wrapX)
30  {
31  // Don't -> all the time, and avoid unsigned warnings
32  int imgWidth = static_cast<int>(img->getWidth ());
33  int imgHeight = static_cast<int>(img->getHeight ());
34 
35  // Calculate pixel y coord.
36  int py = Ogre::Math::IFloor(Ogre::Math::Abs (fy) * (imgHeight - 1));
37  // Snap to py image bounds.
38  py = std::max(0, std::min(py, imgHeight - 1));
39 
40  // Get the two closest pixels on x.
41  // px1 and px2 are the closest integer pixels to px.
42  float px = fx * (img->getWidth () - 1);
43  int px1, px2;
44  px1 = Ogre::Math::IFloor(px);
45  px2 = Ogre::Math::ICeil(px);
46 
47  if (wrapX) {
48  // Wrap x coords. The funny addition ensures that it does
49  // "the right thing" for negative values.
50  px1 = (px1 % imgWidth + imgWidth) % imgWidth;
51  px2 = (px2 % imgWidth + imgWidth) % imgWidth;
52  } else {
53  px1 = std::max(0, std::min(px1, imgWidth - 1));
54  px2 = std::max(0, std::min(px2, imgWidth - 1));
55  }
56 
57  // Calculate the interpolated pixel
58  Ogre::ColourValue c1, c2, cf;
59  c1 = img->getColourAt (px1, py, 0);
60  c2 = img->getColourAt (px2, py, 0);
61 
62  // Blend the two pixels together.
63  // diff is the weight between pixel 1 and pixel 2.
64  float diff = px - px1;
65  cf = c1 * (1 - diff) + c2 * diff;
66 
67  return cf;
68  }
69 
70  const Ogre::String InternalUtilities::pointerToString (void* pointer)
71  {
72  std::stringstream stream;
73  stream.width(2 * sizeof(void *));
74  stream.fill('0');
75  stream.unsetf(std::ios::dec);
76  stream.setf(std::ios::hex);
77  stream.setf(std::ios::uppercase);
78  stream << reinterpret_cast<ptrdiff_t>(pointer);
79  return stream.str();
80  }
81 
83  const Ogre::String& originalName,
84  const Ogre::String& cloneName)
85  {
86  Ogre::MaterialPtr scriptMaterial = Ogre::MaterialManager::getSingletonPtr()->getByName(originalName);
87  if (scriptMaterial.isNull()) {
88  CAELUM_THROW_UNSUPPORTED_EXCEPTION (
89  "Can't find material \"" + originalName + "\"",
90  "Caelum");
91  }
92 
93  // Create clone
94  Caelum::PrivateMaterialPtr clonedMaterial (scriptMaterial->clone (cloneName));
95 
96  // Test clone loads and there is at least on supported technique
97  clonedMaterial->load ();
98  if (clonedMaterial->getBestTechnique () == 0) {
99  CAELUM_THROW_UNSUPPORTED_EXCEPTION (
100  "Can't load material \"" + originalName + "\": " + clonedMaterial->getUnsupportedTechniquesExplanation(),
101  "Caelum");
102  }
103 
104  return clonedMaterial.release();
105  }
106 
107  Ogre::CompositorPtr InternalUtilities::checkCompositorSupported (const Ogre::String& name)
108  {
109  Ogre::CompositorPtr comp = Ogre::CompositorManager::getSingletonPtr()->getByName(name);
110  if (comp.isNull()) {
111  CAELUM_THROW_UNSUPPORTED_EXCEPTION (
112  "Can't find compositor \"" + name + "\"",
113  "Caelum");
114  }
115 
116  // Check the compositor is supported after loading.
117  comp->load ();
118  if (comp->getNumSupportedTechniques () == 0) {
119  CAELUM_THROW_UNSUPPORTED_EXCEPTION (
120  "Can't load compositor \"" + name + "\"",
121  "Caelum");
122  }
123 
124  return comp;
125  }
126 
127  void InternalUtilities::generateSphericDome (const Ogre::String &name, int segments, DomeType type)
128  {
129  // Return now if already exists
130  if (Ogre::MeshManager::getSingleton ().resourceExists (name)) {
131  return;
132  }
133 
134  Ogre::LogManager::getSingleton ().logMessage (
135  "Caelum: Creating " + name + " sphere mesh resource...");
136 
137  // Use the mesh manager to create the mesh
138  Ogre::MeshPtr msh = Ogre::MeshManager::getSingleton ().createManual (name, RESOURCE_GROUP_NAME);
139  // Create a submesh
140  Ogre::SubMesh *sub = msh->createSubMesh ();
141 
142  // Create the shared vertex data
143  Ogre::VertexData *vertexData = new Ogre::VertexData ();
144  msh->sharedVertexData = vertexData;
145 
146  // Define the vertices' format
147  Ogre::VertexDeclaration *vertexDecl = vertexData->vertexDeclaration;
148  size_t currOffset = 0;
149  // Position
150  vertexDecl->addElement (0, currOffset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
151  currOffset += Ogre::VertexElement::getTypeSize (Ogre::VET_FLOAT3);
152  // Normal
153  vertexDecl->addElement (0, currOffset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
154  currOffset += Ogre::VertexElement::getTypeSize (Ogre::VET_FLOAT3);
155  // Texture coordinates
156  vertexDecl->addElement (0, currOffset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 0);
157  currOffset += Ogre::VertexElement::getTypeSize (Ogre::VET_FLOAT2);
158 
159  // Allocate the vertex buffer
160  switch (type) {
161  case DT_SKY_DOME:
162  vertexData->vertexCount = segments * (segments - 1) + 2;
163  break;
164  case DT_IMAGE_STARFIELD:
165  vertexData->vertexCount = (segments + 1) * (segments + 1);
166  break;
167  };
168  Ogre::HardwareVertexBufferSharedPtr vBuf = Ogre::HardwareBufferManager::getSingleton ().createVertexBuffer (vertexDecl->getVertexSize (0), vertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
169  Ogre::VertexBufferBinding *binding = vertexData->vertexBufferBinding;
170  binding->setBinding (0, vBuf);
171 
172  float *pVertex = static_cast<float *>(vBuf->lock (Ogre::HardwareBuffer::HBL_DISCARD));
173 
174  // Allocate the index buffer
175  switch (type) {
176  case DT_SKY_DOME:
177  sub->indexData->indexCount = 2 * segments * (segments - 1) * 3;
178  break;
179  case DT_IMAGE_STARFIELD:
180  sub->indexData->indexCount = 2 * (segments - 1) * segments * 3;
181  break;
182  };
183  sub->indexData->indexBuffer = Ogre::HardwareBufferManager::getSingleton ().createIndexBuffer (Ogre::HardwareIndexBuffer::IT_16BIT, sub->indexData->indexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
184  Ogre::HardwareIndexBufferSharedPtr iBuf = sub->indexData->indexBuffer;
185  unsigned short *pIndices = static_cast<unsigned short *>(iBuf->lock (Ogre::HardwareBuffer::HBL_DISCARD));
186 
187  // Fill the buffers
188  switch (type) {
189  case DT_SKY_DOME:
190  fillGradientsDomeBuffers (pVertex, pIndices, segments);
191  break;
192  case DT_IMAGE_STARFIELD:
193  fillStarfieldDomeBuffers (pVertex, pIndices, segments);
194  break;
195  };
196 
197  // Close the vertex buffer
198  vBuf->unlock ();
199 
200  // Close the index buffer
201  iBuf->unlock ();
202 
203  // Finishing it...
204  sub->useSharedVertices = true;
205  msh->_setBounds (Ogre::AxisAlignedBox (-1, -1, -1, 1, 1, 1), false);
206  msh->_setBoundingSphereRadius (1);
207  msh->load ();
208 
209  Ogre::LogManager::getSingleton ().logMessage (
210  "Caelum: generateSphericDome DONE");
211  }
212 
213  void InternalUtilities::fillGradientsDomeBuffers (float *pVertex, unsigned short *pIndices, int segments)
214  {
215  const float deltaLatitude = Ogre::Math::PI / (float )segments;
216  const float deltaLongitude = Ogre::Math::PI * 2.0 / (float )segments;
217 
218  // Generate the rings
219  for (int i = 1; i < segments; i++) {
220  float r0 = Ogre::Math::Sin (Ogre::Radian (i * deltaLatitude));
221  float y0 = Ogre::Math::Cos (Ogre::Radian (i * deltaLatitude));
222 
223  for (int j = 0; j < segments; j++) {
224  float x0 = r0 * Ogre::Math::Sin (Ogre::Radian (j * deltaLongitude));
225  float z0 = r0 * Ogre::Math::Cos (Ogre::Radian (j * deltaLongitude));
226 
227  *pVertex++ = x0;
228  *pVertex++ = y0;
229  *pVertex++ = z0;
230 
231  *pVertex++ = -x0;
232  *pVertex++ = -y0;
233  *pVertex++ = -z0;
234 
235  *pVertex++ = 0;
236  *pVertex++ = 1 - y0;
237  }
238  }
239 
240  // Generate the "north pole"
241  *pVertex++ = 0; // Position
242  *pVertex++ = 1;
243  *pVertex++ = 0;
244  *pVertex++ = 0; // Normal
245  *pVertex++ = -1;
246  *pVertex++ = 0;
247  *pVertex++ = 0; // UV
248  *pVertex++ = 0;
249 
250  // Generate the "south pole"
251  *pVertex++ = 0; // Position
252  *pVertex++ = -1;
253  *pVertex++ = 0;
254  *pVertex++ = 0; // Normal
255  *pVertex++ = 1;
256  *pVertex++ = 0;
257  *pVertex++ = 0; // UV
258  *pVertex++ = 2;
259 
260  // Generate the mid segments
261  for (int i = 0; i < segments - 2; i++) {
262  for (int j = 0; j < segments; j++) {
263  *pIndices++ = segments * i + j;
264  *pIndices++ = segments * i + (j + 1) % segments;
265  *pIndices++ = segments * (i + 1) + (j + 1) % segments;
266  *pIndices++ = segments * i + j;
267  *pIndices++ = segments * (i + 1) + (j + 1) % segments;
268  *pIndices++ = segments * (i + 1) + j;
269  }
270  }
271 
272  // Generate the upper cap
273  for (int i = 0; i < segments; i++) {
274  *pIndices++ = segments * (segments - 1);
275  *pIndices++ = (i + 1) % segments;
276  *pIndices++ = i;
277  }
278 
279  // Generate the lower cap
280  for (int i = 0; i < segments; i++) {
281  *pIndices++ = segments * (segments - 1) + 1;
282  *pIndices++ = segments * (segments - 2) + i;
283  *pIndices++ = segments * (segments - 2) + (i + 1) % segments;
284  }
285  }
286 
287  void InternalUtilities::fillStarfieldDomeBuffers (float *pVertex, unsigned short *pIndices, int segments)
288  {
289  const float deltaLatitude = Ogre::Math::PI / (float )segments;
290  const float deltaLongitude = Ogre::Math::PI * 2.0 / (float )segments;
291 
292  // Generate the rings
293  for (int i = 0; i <= segments; i++) {
294  float r0 = Ogre::Math::Sin (Ogre::Radian (i * deltaLatitude));
295  float y0 = Ogre::Math::Cos (Ogre::Radian (i * deltaLatitude));
296 
297  for (int j = 0; j <= segments; j++) {
298  float x0 = r0 * Ogre::Math::Sin (Ogre::Radian (j * deltaLongitude));
299  float z0 = r0 * Ogre::Math::Cos (Ogre::Radian (j * deltaLongitude));
300 
301  *pVertex++ = x0;
302  *pVertex++ = y0;
303  *pVertex++ = z0;
304 
305  *pVertex++ = -x0;
306  *pVertex++ = -y0;
307  *pVertex++ = -z0;
308 
309  *pVertex++ = (float )j / (float )segments;
310  *pVertex++ = 1 - (y0 * 0.5 + 0.5);
311  }
312  }
313 
314  // Generate the mid segments
315  int vRowSize = segments + 1;
316  for (int i = 1; i < segments; i++) {
317  for (int j = 0; j < segments; j++) {
318  int baseIdx = vRowSize * i + j;
319  *pIndices++ = baseIdx;
320  *pIndices++ = baseIdx + 1;
321  *pIndices++ = baseIdx + vRowSize + 1;
322  *pIndices++ = baseIdx + 1;
323  *pIndices++ = baseIdx;
324  *pIndices++ = baseIdx - vRowSize;
325  }
326  }
327  }
328 }
static const Ogre::String pointerToString(void *pointer)
Quickly format a pointer as a string; in hex.
static Ogre::MaterialPtr checkLoadMaterialClone(const Ogre::String &originalName, const Ogre::String &cloneName)
Creates a private clone of a material from a script.
static void generateSphericDome(const Ogre::String &name, int segments, DomeType domeType)
Creates a longitude-latitude sky dome.
DomeType
Enumeration of types of sky domes.
static Ogre::ColourValue getInterpolatedColour(float fx, float fy, Ogre::Image *img, bool wrapX=true)
Gets the interpolated colour between two pixels from an image.
static const String RESOURCE_GROUP_NAME
Resource group name for caelum resources.
static Ogre::CompositorPtr checkCompositorSupported(const Ogre::String &name)
Fetch a compositor by name and check it can be loaded properly.
Caelum namespace.
Definition: Astronomy.cpp:24