1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 #include <Inventor/engines/SoHeightMapToNormalMap.h>
34 
35 #include <boost/scoped_array.hpp>
36 
37 #include <Inventor/SbVec3f.h>
38 #include <Inventor/SbImage.h>
39 #include "engines/SoSubEngineP.h"
40 
41 /*!
42   \class SoHeightMapToNormalMap SoHeightMapToNormalMap.h Inventor/engines/SoHeightMapToNormalMap.h
43   \brief Engine for computing a normal map from a height map.
44 
45   This engine will create a normal map texture from a height map texture.
46   You can use it in an Inventor file like this:
47 
48   \code
49   Texture2 {
50     image = HeightMapToNormalMap {
51       sourceImage = Texture2 { filename "HeightMap.jpg" } . image
52     } . image
53   }
54   \endcode
55 
56   Be aware that the field connections will remain active, so both
57   Texture2 nodes and the HeightMapToNormalMap engine will be kept resident
58   in memory (unless you intervene manually and detach the engine) even
59   though only the "outer" Texture2 node is needed. This can give quite
60   a big memory use overhead.
61 
62   \ingroup engines
63   \COIN_CLASS_EXTENSION
64   \since Coin 3.0
65 */
66 
67 /*!
68   \enum SoHeightMapToNormalMap::NormalMapFormat
69   Enumeration of available normal map formats.
70 */
71 
72 /*!
73   \var SoHeightMapToNormalMap::NormalMapFormat SoHeightMapToNormalMap::INT8
74   Encode the normals as a 3 component byte texture.
75   This is the only option for now, as long as float textures are not conveniently
76   supported in Coin.
77 */
78 
79 /*!
80   \var SoMFEnum SoHeightMapToNormalMap::format
81   This setting decides what kind of normal map is generated.  For now, only the
82   INT8 format is available, and it is the default value.
83 */
84 
85 SO_ENGINE_SOURCE(SoHeightMapToNormalMap);
86 
87 /*!
88   Class initializer.
89 */
90 void
initClass(void)91 SoHeightMapToNormalMap::initClass(void)
92 {
93   SO_ENGINE_INTERNAL_INIT_CLASS(SoHeightMapToNormalMap);
94 }
95 
96 /*!
97   Constructor.
98 */
SoHeightMapToNormalMap(void)99 SoHeightMapToNormalMap::SoHeightMapToNormalMap(void)
100 {
101   SO_ENGINE_INTERNAL_CONSTRUCTOR(SoHeightMapToNormalMap);
102 
103   SO_ENGINE_ADD_INPUT(format, (INT8));
104 
105   SO_ENGINE_DEFINE_ENUM_VALUE(NormalMapFormat, INT8);
106   SO_ENGINE_SET_SF_ENUM_TYPE(format, NormalMapFormat);
107 }
108 
109 /*!
110   Static function for computing a normal map from a height map.
111   This function can be used directly without any engine instantiation.
112 */
113 void
convert(const unsigned char * srcptr,SbVec2s size,int nc,SbImage & dst_out)114 SoHeightMapToNormalMap::convert(const unsigned char * srcptr, SbVec2s size, int nc, SbImage & dst_out)
115 {
116   float dx, dy;
117   int width = size[0];
118   int height = size[1];
119   boost::scoped_array<unsigned char> dstarray(new unsigned char[width*height*3]);
120   unsigned char * dstptr = dstarray.get();
121   unsigned char red;
122   SbVec3f n;
123 
124 #define GET_PIXEL_RED(x_, y_) \
125   srcptr[(y_)*width*nc + (x_)*nc]
126 
127   for (int y = 0; y < height; y++) {
128     for (int x = 0; x < width; x++) {
129       // do Y Sobel filter
130       red = GET_PIXEL_RED((x-1+width) % width, (y+1) % height);
131       dy  = static_cast<float>(red) / 255.0f * -1.0f;
132 
133       red = GET_PIXEL_RED(x % width, (y+1) % height);
134       dy += static_cast<float>(red) / 255.0f * -2.0f;
135 
136       red = GET_PIXEL_RED((x+1) % width, (y+1) % height);
137       dy += static_cast<float>(red) / 255.0f * -1.0f;
138 
139       red = GET_PIXEL_RED((x-1+width) % width, (y-1+height) % height);
140       dy += static_cast<float>(red) / 255.0f *  1.0f;
141 
142       red = GET_PIXEL_RED(x % width, (y-1+height) % height);
143       dy += static_cast<float>(red) / 255.0f *  2.0f;
144 
145       red = GET_PIXEL_RED((x+1) % width, (y-1+height) % height);
146       dy += static_cast<float>(red) / 255.0f *  1.0f;
147 
148       // Do X Sobel filter
149       red = GET_PIXEL_RED((x-1+width) % width, (y-1+height) % height);
150       dx  = static_cast<float>(red) / 255.0f * -1.0f;
151 
152       red = GET_PIXEL_RED((x-1+width) % width, y % height);
153       dx += static_cast<float>(red) / 255.0f * -2.0f;
154 
155       red = GET_PIXEL_RED((x-1+width) % width, (y+1) % height);
156       dx += static_cast<float>(red) / 255.0f * -1.0f;
157 
158       red = GET_PIXEL_RED((x+1) % width, (y-1+height) % height);
159       dx += static_cast<float>(red) / 255.0f *  1.0f;
160 
161       red = GET_PIXEL_RED((x+1) % width, y % height);
162       dx += static_cast<float>(red) / 255.0f *  2.0f;
163 
164       red = GET_PIXEL_RED((x+1) % width, (y+1) % height);
165       dx += static_cast<float>(red) / 255.0f *  1.0f;
166 
167       n[0] = -dx;
168       n[1] = -dy;
169       n[2] = 1.0f;
170       (void) n.normalize();
171 
172       *dstptr++ = static_cast<unsigned char>(SbMin((n[0]+1.0f) * 128.0f, 255.0f));
173       *dstptr++ = static_cast<unsigned char>(SbMin((n[1]+1.0f) * 128.0f, 255.0f));
174       *dstptr++ = static_cast<unsigned char>(SbMin((n[2]+1.0f) * 128.0f, 255.0f));
175     }
176   }
177 #undef GET_PIXEL_RED
178   dst_out.setValue(size, 3, dstarray.get());
179 }
180 
181 void
inputChanged(SoField * which)182 SoHeightMapToNormalMap::inputChanged(SoField * which)
183 {
184   // in case we need to override later
185   inherited::inputChanged(which);
186 }
187 
188 void
evaluate(void)189 SoHeightMapToNormalMap::evaluate(void)
190 {
191   SbVec2s size;
192   int nc;
193   const unsigned char * ptr =
194     static_cast<const unsigned char *>(sourceImage.getValue(size, nc));
195 
196   SbImage targetimg;
197   SoHeightMapToNormalMap::convert(ptr, size, nc, targetimg);
198 
199   ptr = static_cast<const unsigned char *>(targetimg.getValue(size, nc));
200   SO_ENGINE_OUTPUT(image, SoSFImage, setValue(size, nc, ptr));
201 }
202