234 lines
9.2 KiB
C++
234 lines
9.2 KiB
C++
/**
|
|
*
|
|
* RenderPipeline
|
|
*
|
|
* Copyright (c) 2014-2016 tobspr <tobias.springer1@gmail.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
|
|
#include "ies_dataset.h"
|
|
|
|
#define _USE_MATH_DEFINES
|
|
#include <math.h>
|
|
|
|
NotifyCategoryDef(iesdataset, "")
|
|
|
|
/**
|
|
* @brief Constructs a new empty dataset.
|
|
* @details This constructs a new IESDataset with no data set.
|
|
*/
|
|
IESDataset::IESDataset() {
|
|
}
|
|
|
|
/**
|
|
* @brief Sets the vertical angles of the dataset.
|
|
* @details This sets the list of vertical angles of the dataset.
|
|
*
|
|
* @param vertical_angles Vector of all vertical angles.
|
|
*/
|
|
void IESDataset::set_vertical_angles(const PTA_float &vertical_angles) {
|
|
nassertv(vertical_angles.size() > 0);
|
|
_vertical_angles = vertical_angles;
|
|
}
|
|
|
|
/**
|
|
* @brief Sets the horizontal angles of the dataset.
|
|
* @details This sets the list of horizontal angles of the dataset.
|
|
*
|
|
* @param horizontal_angles Vector of all horizontal angles.
|
|
*/
|
|
void IESDataset::set_horizontal_angles(const PTA_float &horizontal_angles) {
|
|
nassertv(horizontal_angles.size() > 0);
|
|
_horizontal_angles = horizontal_angles;
|
|
}
|
|
|
|
/**
|
|
* @brief Sets the candela values.
|
|
* @details This sets the candela values of the dataset. They should be an
|
|
* interleaved 2D array with the dimensions vertical_angles x horizontal_angles.
|
|
* They also should be normalized by dividing by the maximum entry.
|
|
* @param candela_values Interleaved 2D-vector of candela values.
|
|
*/
|
|
void IESDataset::set_candela_values(const PTA_float &candela_values) {
|
|
nassertv(candela_values.size() == _horizontal_angles.size() * _vertical_angles.size());
|
|
_candela_values = candela_values;
|
|
}
|
|
|
|
/**
|
|
* @brief Internal method to access the candela data.
|
|
* @details This lookups a candela value in the candela values. It converts a
|
|
* two dimensional index to a onedimensional index and then returns the candela
|
|
* value at that position.
|
|
*
|
|
* @param vertical_angle_idx Index of the vertical angle
|
|
* @param horizontal_angle_idx Index of the horizontal angle
|
|
*
|
|
* @return Candela value between 0 .. 1
|
|
*/
|
|
float IESDataset::get_candela_value_from_index(size_t vertical_angle_idx, size_t horizontal_angle_idx) const {
|
|
size_t index = vertical_angle_idx + horizontal_angle_idx * _vertical_angles.size();
|
|
nassertr(index >= 0 && index < _candela_values.size(), 0.0);
|
|
return _candela_values[index];
|
|
}
|
|
|
|
/**
|
|
* @brief Samples the dataset at the given position
|
|
* @details This looks up a value in the dataset, by specifying a horizontal and
|
|
* vertical angle. This is used for generating the LUT. The vertical and horizontal
|
|
* angle should be inside of the bounds of the vertical and horizontal angle arrays.
|
|
*
|
|
* @param vertical_angle Vertical angle, from 0 .. 90 or 0 .. 180 depending on the dataset
|
|
* @param horizontal_angle Horizontal angle, from 0 .. 180 or 0 .. 360 depending on the dataset.
|
|
*
|
|
* @return Candela value between 0 .. 1
|
|
*/
|
|
float IESDataset::get_candela_value(float vertical_angle, float horizontal_angle) const {
|
|
|
|
// Special case for datasets without horizontal angles
|
|
if (_horizontal_angles.size() == 1) {
|
|
return get_vertical_candela_value(0, vertical_angle);
|
|
}
|
|
|
|
float max_angle = _horizontal_angles[_horizontal_angles.size() - 1];
|
|
|
|
// Wrap angle to fit from 0 .. 360 degree. Most profiles only distribute
|
|
// candela values from 0 .. 180 or even 0 .. 90. We have to mirror the
|
|
// values at those borders (so 2 times for 180 degree and 4 times for 90 degree)
|
|
horizontal_angle = fmod(horizontal_angle, 2.0f * max_angle);
|
|
if (horizontal_angle > max_angle) {
|
|
horizontal_angle = 2.0 * max_angle - horizontal_angle;
|
|
}
|
|
|
|
// Simlar to the vertical step, we now try interpolating a horizontal angle,
|
|
// but we need to evaluate the vertical value for each row instead of fetching
|
|
// the value directly
|
|
for (size_t horizontal_index = 1; horizontal_index < _horizontal_angles.size(); ++horizontal_index) {
|
|
float curr_angle = _horizontal_angles[horizontal_index];
|
|
|
|
if (curr_angle >= horizontal_angle) {
|
|
|
|
// Get previous angle data
|
|
float prev_angle = _horizontal_angles[horizontal_index - 1];
|
|
float prev_value = get_vertical_candela_value(horizontal_index - 1, vertical_angle);
|
|
float curr_value = get_vertical_candela_value(horizontal_index, vertical_angle);
|
|
|
|
// Interpolate lineary
|
|
float lerp = (horizontal_angle - prev_angle) / (curr_angle - prev_angle);
|
|
|
|
// Should never occur, but to be safe:
|
|
if (lerp < 0.0 || lerp > 1.0) {
|
|
iesdataset_cat.error() << "Invalid horizontal lerp: " << lerp
|
|
<< ", requested angle was " << horizontal_angle
|
|
<< ", prev = " << prev_angle << ", cur = " << curr_angle
|
|
<< endl;
|
|
}
|
|
|
|
return curr_value * lerp + prev_value * (1-lerp);
|
|
}
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
/**
|
|
* @brief Fetches a vertical candela value
|
|
* @details Fetches a vertical candela value, using a given horizontal position.
|
|
* This does an 1D interpolation in the candela values array.
|
|
*
|
|
* @param horizontal_angle_idx The index of the horizontal angle in the horizontal
|
|
* angle array.
|
|
* @param vertical_angle The vertical angle. Interpolation will be done if the
|
|
* vertical angle is not in the vertical angles array.
|
|
*
|
|
* @return Candela value between 0 .. 1
|
|
*/
|
|
float IESDataset::get_vertical_candela_value(size_t horizontal_angle_idx, float vertical_angle) const {
|
|
nassertr(horizontal_angle_idx >= 0 && horizontal_angle_idx < _horizontal_angles.size(), 0.0);
|
|
|
|
// Lower bound
|
|
if (vertical_angle < 0.0) return 0.0;
|
|
|
|
// Upper bound
|
|
if (vertical_angle > _vertical_angles[_vertical_angles.size() - 1] ) return 0.0;
|
|
|
|
// Find lowest enclosing angle
|
|
for (size_t vertical_index = 1; vertical_index < _vertical_angles.size(); ++vertical_index) {
|
|
float curr_angle = _vertical_angles[vertical_index];
|
|
|
|
// Found value
|
|
if (curr_angle > vertical_angle) {
|
|
|
|
// Get previous angle data
|
|
float prev_angle = _vertical_angles[vertical_index - 1];
|
|
float prev_value = get_candela_value_from_index(vertical_index - 1, horizontal_angle_idx);
|
|
float curr_value = get_candela_value_from_index(vertical_index, horizontal_angle_idx);
|
|
|
|
// Interpolate lineary
|
|
float lerp = (vertical_angle - prev_angle) / (curr_angle - prev_angle);
|
|
|
|
// Should never occur, but to be safe:
|
|
if (lerp < 0.0 || lerp > 1.0) {
|
|
iesdataset_cat.error() << "ERROR: Invalid vertical lerp: " << lerp
|
|
<< ", requested angle was " << vertical_angle
|
|
<< ", prev = " << prev_angle << ", cur = " << curr_angle
|
|
<< endl;
|
|
}
|
|
|
|
return curr_value * lerp + prev_value * (1-lerp);
|
|
}
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
/**
|
|
* @brief Generates the IES LUT
|
|
* @details This generates the LUT into a given dataset texture. The x-axis
|
|
* referes to the vertical_angle, whereas the y-axis refers to the
|
|
* horizontal angle.
|
|
*
|
|
* @param dest_tex Texture to write the LUT into
|
|
* @param z Layer to write the LUT into, in case the texture is a 3D Texture or
|
|
* 2D Texture Array.
|
|
*/
|
|
void IESDataset::generate_dataset_texture_into(Texture* dest_tex, size_t z) const {
|
|
|
|
size_t resolution_vertical = dest_tex->get_y_size();
|
|
size_t resolution_horizontal = dest_tex->get_x_size();
|
|
|
|
// Candla values are stored flippped - vertical angles in the x - Axis
|
|
// and horizontal angles in the y - Axis
|
|
PNMImage dest = PNMImage(resolution_vertical, resolution_horizontal, 1, 65535);
|
|
|
|
for (size_t vert = 0; vert < resolution_vertical; ++vert) {
|
|
for (size_t horiz = 0; horiz < resolution_horizontal; ++horiz) {
|
|
float vert_angle = (float)vert / (float)(resolution_vertical-1);
|
|
vert_angle = cos(vert_angle * M_PI) * 90.0 + 90.0;
|
|
float horiz_angle = (float)horiz / (float)(resolution_horizontal-1) * 360.0;
|
|
float candela = get_candela_value(vert_angle, horiz_angle);
|
|
dest.set_xel(vert, horiz, candela);
|
|
}
|
|
}
|
|
|
|
|
|
dest_tex->load(dest, z, 0);
|
|
}
|