Panda3D
iesDataset.cxx
1 /**
2  *
3  * RenderPipeline
4  *
5  * Copyright (c) 2014-2016 tobspr <tobias.springer1@gmail.com>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  *
25  */
26 
27 
28 #include "iesDataset.h"
29 
30 #ifndef _USE_MATH_DEFINES
31 #define _USE_MATH_DEFINES
32 #endif
33 #include <math.h>
34 
35 NotifyCategoryDef(iesdataset, "")
36 
37 /**
38  * @brief Constructs a new empty dataset.
39  * @details This constructs a new IESDataset with no data set.
40  */
42 }
43 
44 /**
45  * @brief Sets the vertical angles of the dataset.
46  * @details This sets the list of vertical angles of the dataset.
47  *
48  * @param vertical_angles Vector of all vertical angles.
49  */
50 void IESDataset::set_vertical_angles(const PTA_float &vertical_angles) {
51  nassertv(vertical_angles.size() > 0);
52  _vertical_angles = vertical_angles;
53 }
54 
55 /**
56  * @brief Sets the horizontal angles of the dataset.
57  * @details This sets the list of horizontal angles of the dataset.
58  *
59  * @param horizontal_angles Vector of all horizontal angles.
60  */
61 void IESDataset::set_horizontal_angles(const PTA_float &horizontal_angles) {
62  nassertv(horizontal_angles.size() > 0);
63  _horizontal_angles = horizontal_angles;
64 }
65 
66 /**
67  * @brief Sets the candela values.
68  * @details This sets the candela values of the dataset. They should be an
69  * interleaved 2D array with the dimensions vertical_angles x horizontal_angles.
70  * They also should be normalized by dividing by the maximum entry.
71  * @param candela_values Interleaved 2D-vector of candela values.
72  */
73 void IESDataset::set_candela_values(const PTA_float &candela_values) {
74  nassertv(candela_values.size() == _horizontal_angles.size() * _vertical_angles.size());
75  _candela_values = candela_values;
76 }
77 
78 /**
79  * @brief Internal method to access the candela data.
80  * @details This lookups a candela value in the candela values. It converts a
81  * two dimensional index to a onedimensional index and then returns the candela
82  * value at that position.
83  *
84  * @param vertical_angle_idx Index of the vertical angle
85  * @param horizontal_angle_idx Index of the horizontal angle
86  *
87  * @return Candela value between 0 .. 1
88  */
89 float IESDataset::get_candela_value_from_index(size_t vertical_angle_idx, size_t horizontal_angle_idx) const {
90  size_t index = vertical_angle_idx + horizontal_angle_idx * _vertical_angles.size();
91  nassertr(index >= 0 && index < _candela_values.size(), 0.0);
92  return _candela_values[index];
93 }
94 
95 /**
96  * @brief Samples the dataset at the given position
97  * @details This looks up a value in the dataset, by specifying a horizontal and
98  * vertical angle. This is used for generating the LUT. The vertical and horizontal
99  * angle should be inside of the bounds of the vertical and horizontal angle arrays.
100  *
101  * @param vertical_angle Vertical angle, from 0 .. 90 or 0 .. 180 depending on the dataset
102  * @param horizontal_angle Horizontal angle, from 0 .. 180 or 0 .. 360 depending on the dataset.
103  *
104  * @return Candela value between 0 .. 1
105  */
106 float IESDataset::get_candela_value(float vertical_angle, float horizontal_angle) const {
107 
108  // Special case for datasets without horizontal angles
109  if (_horizontal_angles.size() == 1) {
110  return get_vertical_candela_value(0, vertical_angle);
111  }
112 
113  float max_angle = _horizontal_angles[_horizontal_angles.size() - 1];
114 
115  // Wrap angle to fit from 0 .. 360 degree. Most profiles only distribute
116  // candela values from 0 .. 180 or even 0 .. 90. We have to mirror the
117  // values at those borders (so 2 times for 180 degree and 4 times for 90 degree)
118  horizontal_angle = fmod(horizontal_angle, 2.0f * max_angle);
119  if (horizontal_angle > max_angle) {
120  horizontal_angle = 2.0 * max_angle - horizontal_angle;
121  }
122 
123  // Simlar to the vertical step, we now try interpolating a horizontal angle,
124  // but we need to evaluate the vertical value for each row instead of fetching
125  // the value directly
126  for (size_t horizontal_index = 1; horizontal_index < _horizontal_angles.size(); ++horizontal_index) {
127  float curr_angle = _horizontal_angles[horizontal_index];
128 
129  if (curr_angle >= horizontal_angle) {
130 
131  // Get previous angle data
132  float prev_angle = _horizontal_angles[horizontal_index - 1];
133  float prev_value = get_vertical_candela_value(horizontal_index - 1, vertical_angle);
134  float curr_value = get_vertical_candela_value(horizontal_index, vertical_angle);
135 
136  // Interpolate lineary
137  float lerp = (horizontal_angle - prev_angle) / (curr_angle - prev_angle);
138 
139  // Should never occur, but to be safe:
140  if (lerp < 0.0 || lerp > 1.0) {
141  iesdataset_cat.error() << "Invalid horizontal lerp: " << lerp
142  << ", requested angle was " << horizontal_angle
143  << ", prev = " << prev_angle << ", cur = " << curr_angle
144  << std::endl;
145  }
146 
147  return curr_value * lerp + prev_value * (1-lerp);
148  }
149  }
150 
151  return 0.0;
152 }
153 
154 /**
155  * @brief Fetches a vertical candela value
156  * @details Fetches a vertical candela value, using a given horizontal position.
157  * This does an 1D interpolation in the candela values array.
158  *
159  * @param horizontal_angle_idx The index of the horizontal angle in the horizontal
160  * angle array.
161  * @param vertical_angle The vertical angle. Interpolation will be done if the
162  * vertical angle is not in the vertical angles array.
163  *
164  * @return Candela value between 0 .. 1
165  */
166 float IESDataset::get_vertical_candela_value(size_t horizontal_angle_idx, float vertical_angle) const {
167  nassertr(horizontal_angle_idx >= 0 && horizontal_angle_idx < _horizontal_angles.size(), 0.0);
168 
169  // Lower bound
170  if (vertical_angle < 0.0) return 0.0;
171 
172  // Upper bound
173  if (vertical_angle > _vertical_angles[_vertical_angles.size() - 1] ) return 0.0;
174 
175  // Find lowest enclosing angle
176  for (size_t vertical_index = 1; vertical_index < _vertical_angles.size(); ++vertical_index) {
177  float curr_angle = _vertical_angles[vertical_index];
178 
179  // Found value
180  if (curr_angle > vertical_angle) {
181 
182  // Get previous angle data
183  float prev_angle = _vertical_angles[vertical_index - 1];
184  float prev_value = get_candela_value_from_index(vertical_index - 1, horizontal_angle_idx);
185  float curr_value = get_candela_value_from_index(vertical_index, horizontal_angle_idx);
186 
187  // Interpolate lineary
188  float lerp = (vertical_angle - prev_angle) / (curr_angle - prev_angle);
189 
190  // Should never occur, but to be safe:
191  if (lerp < 0.0 || lerp > 1.0) {
192  iesdataset_cat.error() << "ERROR: Invalid vertical lerp: " << lerp
193  << ", requested angle was " << vertical_angle
194  << ", prev = " << prev_angle << ", cur = " << curr_angle
195  << std::endl;
196  }
197 
198  return curr_value * lerp + prev_value * (1-lerp);
199  }
200  }
201  return 0.0;
202 }
203 
204 /**
205  * @brief Generates the IES LUT
206  * @details This generates the LUT into a given dataset texture. The x-axis
207  * referes to the vertical_angle, whereas the y-axis refers to the
208  * horizontal angle.
209  *
210  * @param dest_tex Texture to write the LUT into
211  * @param z Layer to write the LUT into, in case the texture is a 3D Texture or
212  * 2D Texture Array.
213  */
214 void IESDataset::generate_dataset_texture_into(Texture* dest_tex, size_t z) const {
215 
216  size_t resolution_vertical = dest_tex->get_y_size();
217  size_t resolution_horizontal = dest_tex->get_x_size();
218 
219  // Candla values are stored flippped - vertical angles in the x - Axis
220  // and horizontal angles in the y - Axis
221  PNMImage dest = PNMImage(resolution_vertical, resolution_horizontal, 1, 65535);
222 
223  for (size_t vert = 0; vert < resolution_vertical; ++vert) {
224  for (size_t horiz = 0; horiz < resolution_horizontal; ++horiz) {
225  float vert_angle = (float)vert / (float)(resolution_vertical-1);
226  vert_angle = cos(vert_angle * M_PI) * 90.0 + 90.0;
227  float horiz_angle = (float)horiz / (float)(resolution_horizontal-1) * 360.0;
228  float candela = get_candela_value(vert_angle, horiz_angle);
229  dest.set_xel(vert, horiz, candela);
230  }
231  }
232 
233 
234  dest_tex->load(dest, z, 0);
235 }
get_y_size
Returns the height of the texture image in texels.
Definition: texture.h:338
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:58
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
void set_vertical_angles(const PTA_float &vertical_angles)
Sets the vertical angles of the dataset.
Definition: iesDataset.cxx:50
void set_horizontal_angles(const PTA_float &horizontal_angles)
Sets the horizontal angles of the dataset.
Definition: iesDataset.cxx:61
This class generates a LUT from IES data.
Definition: iesDataset.h:46
void generate_dataset_texture_into(Texture *dest_tex, size_t z) const
Generates the IES LUT.
Definition: iesDataset.cxx:214
float get_vertical_candela_value(size_t horizontal_angle_idx, float vertical_angle) const
Fetches a vertical candela value.
Definition: iesDataset.cxx:166
bool load(const PNMImage &pnmimage, const LoaderOptions &options=LoaderOptions())
Replaces the texture with the indicated image.
Definition: texture.I:357
float get_candela_value_from_index(size_t vertical_angle_idx, size_t horizontal_angle_idx) const
Internal method to access the candela data.
Definition: iesDataset.cxx:89
float get_candela_value(float vertical_angle, float horizontal_angle) const
Samples the dataset at the given position.
Definition: iesDataset.cxx:106
void set_xel(int x, int y, const LRGBColorf &value)
Changes the RGB color at the indicated pixel.
Definition: pnmImage.I:522
get_x_size
Returns the width of the texture image in texels.
Definition: texture.h:334
void set_candela_values(const PTA_float &candela_values)
Sets the candela values.
Definition: iesDataset.cxx:73