Panda3D
Loading...
Searching...
No Matches
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
35NotifyCategoryDef(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 */
50void 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 */
61void 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 */
73void 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 */
89float 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 */
106float 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 */
166float 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 */
214void 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}
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.
void set_candela_values(const PTA_float &candela_values)
Sets the candela values.
float get_candela_value_from_index(size_t vertical_angle_idx, size_t horizontal_angle_idx) const
Internal method to access the candela data.
void set_vertical_angles(const PTA_float &vertical_angles)
Sets the vertical angles of the dataset.
float get_candela_value(float vertical_angle, float horizontal_angle) const
Samples the dataset at the given position.
float get_vertical_candela_value(size_t horizontal_angle_idx, float vertical_angle) const
Fetches a vertical candela value.
void set_horizontal_angles(const PTA_float &horizontal_angles)
Sets the horizontal angles of the dataset.
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
void set_xel(int x, int y, const LRGBColorf &value)
Changes the RGB color at the indicated pixel.
Definition pnmImage.I:579
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition texture.h:72
bool load(const PNMImage &pnmimage, const LoaderOptions &options=LoaderOptions())
Replaces the texture with the indicated image.
Definition texture.I:358
get_y_size
Returns the height of the texture image in texels.
Definition texture.h:347
get_x_size
Returns the width of the texture image in texels.
Definition texture.h:343