mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
Reorganize source files into logical subdirectories
Co-authored-by: Hirek193 <23196899+Hirek193@users.noreply.github.com>
This commit is contained in:
207
rendering/flip-s3tc.h
Normal file
207
rendering/flip-s3tc.h
Normal file
@@ -0,0 +1,207 @@
|
||||
// https://github.com/inequation/flip-s3tc
|
||||
// Leszek Godlewski places this file in the public domain.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace flip_s3tc
|
||||
{
|
||||
#endif
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct dxt1_block
|
||||
{
|
||||
uint16_t c0, c1;
|
||||
uint8_t dcba,
|
||||
hgfe,
|
||||
lkji,
|
||||
ponm;
|
||||
};
|
||||
|
||||
struct dxt23_block
|
||||
{
|
||||
uint16_t adacabaa,
|
||||
ahagafae,
|
||||
alakajai,
|
||||
apaoanam;
|
||||
uint16_t c0, c1;
|
||||
uint8_t dcba,
|
||||
hgfe,
|
||||
lkji,
|
||||
ponm;
|
||||
};
|
||||
|
||||
struct dxt45_block
|
||||
{
|
||||
uint8_t a0, a1;
|
||||
struct
|
||||
{
|
||||
uint8_t alpha[3];
|
||||
} ahagafaeadacabaa,
|
||||
apaoanamalakajai;
|
||||
uint16_t c0, c1;
|
||||
uint8_t dcba,
|
||||
hgfe,
|
||||
lkji,
|
||||
ponm;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
/** Performs an Y-flip of the given DXT1 block in place. */
|
||||
void flip_dxt1_block(struct dxt1_block *block)
|
||||
{
|
||||
uint8_t temp;
|
||||
|
||||
temp = block->dcba;
|
||||
block->dcba = block->ponm;
|
||||
block->ponm = temp;
|
||||
temp = block->hgfe;
|
||||
block->hgfe = block->lkji;
|
||||
block->lkji = temp;
|
||||
}
|
||||
|
||||
/** Performs an Y-flip of the given DXT2/DXT3 block in place. */
|
||||
void flip_dxt23_block(struct dxt23_block *block)
|
||||
{
|
||||
uint8_t temp8;
|
||||
uint16_t temp16;
|
||||
|
||||
temp16 = block->adacabaa;
|
||||
block->adacabaa = block->apaoanam;
|
||||
block->apaoanam = temp16;
|
||||
temp16 = block->ahagafae;
|
||||
block->ahagafae = block->alakajai;
|
||||
block->alakajai = temp16;
|
||||
|
||||
temp8 = block->dcba;
|
||||
block->dcba = block->ponm;
|
||||
block->ponm = temp8;
|
||||
temp8 = block->hgfe;
|
||||
block->hgfe = block->lkji;
|
||||
block->lkji = temp8;
|
||||
}
|
||||
|
||||
/** Performs an Y-flip of the given DXT4/DXT5 block in place. */
|
||||
void flip_dxt45_block(struct dxt45_block *block)
|
||||
{
|
||||
uint8_t temp8;
|
||||
uint32_t temp32;
|
||||
uint32_t *as_int[2];
|
||||
|
||||
as_int[0] = (uint32_t *)block->ahagafaeadacabaa.alpha;
|
||||
as_int[1] = (uint32_t *)block->apaoanamalakajai.alpha;
|
||||
// swap adacabaa with apaoanam
|
||||
temp32 = *as_int[0] & ((1 << 12) - 1);
|
||||
*as_int[0] &= ~((1 << 12) - 1);
|
||||
*as_int[0] |= (*as_int[1] & (((1 << 12) - 1) << 12)) >> 12;
|
||||
*as_int[1] &= ~(((1 << 12) - 1) << 12);
|
||||
*as_int[1] |= temp32 << 12;
|
||||
// swap ahagafae with alakajai
|
||||
temp32 = *as_int[0] & (((1 << 12) - 1) << 12);
|
||||
*as_int[0] &= ~(((1 << 12) - 1) << 12);
|
||||
*as_int[0] |= (*as_int[1] & ((1 << 12) - 1)) << 12;
|
||||
*as_int[1] &= ~((1 << 12) - 1);
|
||||
*as_int[1] |= temp32 >> 12;
|
||||
|
||||
temp8 = block->dcba;
|
||||
block->dcba = block->ponm;
|
||||
block->ponm = temp8;
|
||||
temp8 = block->hgfe;
|
||||
block->hgfe = block->lkji;
|
||||
block->lkji = temp8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an Y-flip on the given DXT1 image in place.
|
||||
* @param data buffer with image data (S3TC blocks)
|
||||
* @param width image width in pixels
|
||||
* @param height image height in pixels
|
||||
*/
|
||||
void flip_dxt1_image(void *data, int width, int height)
|
||||
{
|
||||
int x, y;
|
||||
struct dxt1_block temp1, temp2;
|
||||
struct dxt1_block *blocks = (struct dxt1_block *)data;
|
||||
|
||||
width = (width + 3) / 4;
|
||||
height = (height + 3) / 4;
|
||||
|
||||
for (y = 0; y < height / 2; ++y)
|
||||
{
|
||||
for (x = 0; x < width; ++x)
|
||||
{
|
||||
temp1 = blocks[y * width + x];
|
||||
temp2 = blocks[(height - y - 1) * width + x];
|
||||
flip_dxt1_block(&temp1);
|
||||
flip_dxt1_block(&temp2);
|
||||
blocks[(height - y - 1) * width + x] = temp1;
|
||||
blocks[y * width + x] = temp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an Y-flip on the given DXT2/DXT3 image in place.
|
||||
* @param data buffer with image data (S3TC blocks)
|
||||
* @param width image width in pixels
|
||||
* @param height image height in pixels
|
||||
*/
|
||||
void flip_dxt23_image(void *data, int width, int height)
|
||||
{
|
||||
int x, y;
|
||||
struct dxt23_block temp1, temp2;
|
||||
struct dxt23_block *blocks = (struct dxt23_block *)data;
|
||||
|
||||
width = (width + 3) / 4;
|
||||
height = (height + 3) / 4;
|
||||
|
||||
for (y = 0; y < height / 2; ++y)
|
||||
{
|
||||
for (x = 0; x < width; ++x)
|
||||
{
|
||||
temp1 = blocks[y * width + x];
|
||||
temp2 = blocks[(height - y - 1) * width + x];
|
||||
flip_dxt23_block(&temp1);
|
||||
flip_dxt23_block(&temp2);
|
||||
blocks[(height - y - 1) * width + x] = temp1;
|
||||
blocks[y * width + x] = temp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an Y-flip on the given DXT1 image in place.
|
||||
* @param data buffer with image data (S3TC blocks)
|
||||
* @param width image width in pixels
|
||||
* @param height image height in pixels
|
||||
*/
|
||||
void flip_dxt45_image(void *data, int width, int height)
|
||||
{
|
||||
int x, y;
|
||||
struct dxt45_block temp1, temp2;
|
||||
struct dxt45_block *blocks = (struct dxt45_block *)data;
|
||||
|
||||
width = (width + 3) / 4;
|
||||
height = (height + 3) / 4;
|
||||
|
||||
for (y = 0; y < height / 2; ++y)
|
||||
{
|
||||
for (x = 0; x < width; ++x)
|
||||
{
|
||||
temp1 = blocks[y * width + x];
|
||||
temp2 = blocks[(height - y - 1) * width + x];
|
||||
flip_dxt45_block(&temp1);
|
||||
flip_dxt45_block(&temp2);
|
||||
blocks[(height - y - 1) * width + x] = temp1;
|
||||
blocks[y * width + x] = temp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if __cplusplus
|
||||
} // namespace flip_s3tc
|
||||
#endif
|
||||
184
rendering/frustum.cpp
Normal file
184
rendering/frustum.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "frustum.h"
|
||||
|
||||
void
|
||||
cFrustum::calculate() {
|
||||
|
||||
auto const &projection = OpenGLMatrices.data( GL_PROJECTION );
|
||||
auto const &modelview = OpenGLMatrices.data( GL_MODELVIEW );
|
||||
|
||||
calculate( projection, modelview );
|
||||
}
|
||||
|
||||
void
|
||||
cFrustum::calculate( glm::mat4 const &Projection, glm::mat4 const &Modelview ) {
|
||||
|
||||
float const *proj = glm::value_ptr( Projection );
|
||||
float const *modl = glm::value_ptr( Modelview );
|
||||
float clip[ 16 ];
|
||||
|
||||
// multiply the matrices to retrieve clipping planes
|
||||
clip[ 0 ] = modl[ 0 ] * proj[ 0 ] + modl[ 1 ] * proj[ 4 ] + modl[ 2 ] * proj[ 8 ] + modl[ 3 ] * proj[ 12 ];
|
||||
clip[ 1 ] = modl[ 0 ] * proj[ 1 ] + modl[ 1 ] * proj[ 5 ] + modl[ 2 ] * proj[ 9 ] + modl[ 3 ] * proj[ 13 ];
|
||||
clip[ 2 ] = modl[ 0 ] * proj[ 2 ] + modl[ 1 ] * proj[ 6 ] + modl[ 2 ] * proj[ 10 ] + modl[ 3 ] * proj[ 14 ];
|
||||
clip[ 3 ] = modl[ 0 ] * proj[ 3 ] + modl[ 1 ] * proj[ 7 ] + modl[ 2 ] * proj[ 11 ] + modl[ 3 ] * proj[ 15 ];
|
||||
|
||||
clip[ 4 ] = modl[ 4 ] * proj[ 0 ] + modl[ 5 ] * proj[ 4 ] + modl[ 6 ] * proj[ 8 ] + modl[ 7 ] * proj[ 12 ];
|
||||
clip[ 5 ] = modl[ 4 ] * proj[ 1 ] + modl[ 5 ] * proj[ 5 ] + modl[ 6 ] * proj[ 9 ] + modl[ 7 ] * proj[ 13 ];
|
||||
clip[ 6 ] = modl[ 4 ] * proj[ 2 ] + modl[ 5 ] * proj[ 6 ] + modl[ 6 ] * proj[ 10 ] + modl[ 7 ] * proj[ 14 ];
|
||||
clip[ 7 ] = modl[ 4 ] * proj[ 3 ] + modl[ 5 ] * proj[ 7 ] + modl[ 6 ] * proj[ 11 ] + modl[ 7 ] * proj[ 15 ];
|
||||
|
||||
clip[ 8 ] = modl[ 8 ] * proj[ 0 ] + modl[ 9 ] * proj[ 4 ] + modl[ 10 ] * proj[ 8 ] + modl[ 11 ] * proj[ 12 ];
|
||||
clip[ 9 ] = modl[ 8 ] * proj[ 1 ] + modl[ 9 ] * proj[ 5 ] + modl[ 10 ] * proj[ 9 ] + modl[ 11 ] * proj[ 13 ];
|
||||
clip[ 10 ] = modl[ 8 ] * proj[ 2 ] + modl[ 9 ] * proj[ 6 ] + modl[ 10 ] * proj[ 10 ] + modl[ 11 ] * proj[ 14 ];
|
||||
clip[ 11 ] = modl[ 8 ] * proj[ 3 ] + modl[ 9 ] * proj[ 7 ] + modl[ 10 ] * proj[ 11 ] + modl[ 11 ] * proj[ 15 ];
|
||||
|
||||
clip[ 12 ] = modl[ 12 ] * proj[ 0 ] + modl[ 13 ] * proj[ 4 ] + modl[ 14 ] * proj[ 8 ] + modl[ 15 ] * proj[ 12 ];
|
||||
clip[ 13 ] = modl[ 12 ] * proj[ 1 ] + modl[ 13 ] * proj[ 5 ] + modl[ 14 ] * proj[ 9 ] + modl[ 15 ] * proj[ 13 ];
|
||||
clip[ 14 ] = modl[ 12 ] * proj[ 2 ] + modl[ 13 ] * proj[ 6 ] + modl[ 14 ] * proj[ 10 ] + modl[ 15 ] * proj[ 14 ];
|
||||
clip[ 15 ] = modl[ 12 ] * proj[ 3 ] + modl[ 13 ] * proj[ 7 ] + modl[ 14 ] * proj[ 11 ] + modl[ 15 ] * proj[ 15 ];
|
||||
|
||||
// get the sides of the frustum.
|
||||
m_frustum[ side_RIGHT ][ plane_A ] = clip[ 3 ] - clip[ 0 ];
|
||||
m_frustum[ side_RIGHT ][ plane_B ] = clip[ 7 ] - clip[ 4 ];
|
||||
m_frustum[ side_RIGHT ][ plane_C ] = clip[ 11 ] - clip[ 8 ];
|
||||
m_frustum[ side_RIGHT ][ plane_D ] = clip[ 15 ] - clip[ 12 ];
|
||||
normalize_plane( side_RIGHT );
|
||||
|
||||
m_frustum[ side_LEFT ][ plane_A ] = clip[ 3 ] + clip[ 0 ];
|
||||
m_frustum[ side_LEFT ][ plane_B ] = clip[ 7 ] + clip[ 4 ];
|
||||
m_frustum[ side_LEFT ][ plane_C ] = clip[ 11 ] + clip[ 8 ];
|
||||
m_frustum[ side_LEFT ][ plane_D ] = clip[ 15 ] + clip[ 12 ];
|
||||
normalize_plane( side_LEFT );
|
||||
|
||||
m_frustum[ side_BOTTOM ][ plane_A ] = clip[ 3 ] + clip[ 1 ];
|
||||
m_frustum[ side_BOTTOM ][ plane_B ] = clip[ 7 ] + clip[ 5 ];
|
||||
m_frustum[ side_BOTTOM ][ plane_C ] = clip[ 11 ] + clip[ 9 ];
|
||||
m_frustum[ side_BOTTOM ][ plane_D ] = clip[ 15 ] + clip[ 13 ];
|
||||
normalize_plane( side_BOTTOM );
|
||||
|
||||
m_frustum[ side_TOP ][ plane_A ] = clip[ 3 ] - clip[ 1 ];
|
||||
m_frustum[ side_TOP ][ plane_B ] = clip[ 7 ] - clip[ 5 ];
|
||||
m_frustum[ side_TOP ][ plane_C ] = clip[ 11 ] - clip[ 9 ];
|
||||
m_frustum[ side_TOP ][ plane_D ] = clip[ 15 ] - clip[ 13 ];
|
||||
normalize_plane( side_TOP );
|
||||
|
||||
m_frustum[ side_BACK ][ plane_A ] = clip[ 3 ] - clip[ 2 ];
|
||||
m_frustum[ side_BACK ][ plane_B ] = clip[ 7 ] - clip[ 6 ];
|
||||
m_frustum[ side_BACK ][ plane_C ] = clip[ 11 ] - clip[ 10 ];
|
||||
m_frustum[ side_BACK ][ plane_D ] = clip[ 15 ] - clip[ 14 ];
|
||||
normalize_plane( side_BACK );
|
||||
|
||||
m_frustum[ side_FRONT ][ plane_A ] = clip[ 3 ] + clip[ 2 ];
|
||||
m_frustum[ side_FRONT ][ plane_B ] = clip[ 7 ] + clip[ 6 ];
|
||||
m_frustum[ side_FRONT ][ plane_C ] = clip[ 11 ] + clip[ 10 ];
|
||||
m_frustum[ side_FRONT ][ plane_D ] = clip[ 15 ] + clip[ 14 ];
|
||||
normalize_plane( side_FRONT );
|
||||
}
|
||||
|
||||
bool
|
||||
cFrustum::point_inside( float const X, float const Y, float const Z ) const {
|
||||
|
||||
// cycle through the sides of the frustum, checking if the point is behind them
|
||||
for( int idx = 0; idx < 6; ++idx ) {
|
||||
if( m_frustum[ idx ][ plane_A ] * X
|
||||
+ m_frustum[ idx ][ plane_B ] * Y
|
||||
+ m_frustum[ idx ][ plane_C ] * Z
|
||||
+ m_frustum[ idx ][ plane_D ] <= 0 )
|
||||
return false;
|
||||
}
|
||||
|
||||
// the point is in front of each frustum plane, i.e. inside of the frustum
|
||||
return true;
|
||||
}
|
||||
|
||||
float
|
||||
cFrustum::sphere_inside( float const X, float const Y, float const Z, float const Radius ) const {
|
||||
|
||||
float distance;
|
||||
// go through all the sides of the frustum. bail out as soon as possible
|
||||
for( int idx = 0; idx < 6; ++idx ) {
|
||||
distance =
|
||||
m_frustum[ idx ][ plane_A ] * X
|
||||
+ m_frustum[ idx ][ plane_B ] * Y
|
||||
+ m_frustum[ idx ][ plane_C ] * Z
|
||||
+ m_frustum[ idx ][ plane_D ];
|
||||
if( distance <= -Radius )
|
||||
return 0.0f;
|
||||
}
|
||||
return distance + Radius;
|
||||
}
|
||||
|
||||
bool
|
||||
cFrustum::cube_inside( float const X, float const Y, float const Z, float const Size ) const {
|
||||
|
||||
for( int idx = 0; idx < 6; ++idx ) {
|
||||
if( m_frustum[ idx ][ plane_A ] * ( X - Size )
|
||||
+ m_frustum[ idx ][ plane_B ] * ( Y - Size )
|
||||
+ m_frustum[ idx ][ plane_C ] * ( Z - Size )
|
||||
+ m_frustum[ idx ][ plane_D ] > 0 )
|
||||
continue;
|
||||
if( m_frustum[ idx ][ plane_A ] * ( X + Size )
|
||||
+ m_frustum[ idx ][ plane_B ] * ( Y - Size )
|
||||
+ m_frustum[ idx ][ plane_C ] * ( Z - Size )
|
||||
+ m_frustum[ idx ][ plane_D ] > 0 )
|
||||
continue;
|
||||
if( m_frustum[ idx ][ plane_A ] * ( X - Size )
|
||||
+ m_frustum[ idx ][ plane_B ] * ( Y + Size )
|
||||
+ m_frustum[ idx ][ plane_C ] * ( Z - Size )
|
||||
+ m_frustum[ idx ][ plane_D ] > 0 )
|
||||
continue;
|
||||
if( m_frustum[ idx ][ plane_A ] * ( X + Size )
|
||||
+ m_frustum[ idx ][ plane_B ] * ( Y + Size )
|
||||
+ m_frustum[ idx ][ plane_C ] * ( Z - Size )
|
||||
+ m_frustum[ idx ][ plane_D ] > 0 )
|
||||
continue;
|
||||
if( m_frustum[ idx ][ plane_A ] * ( X - Size )
|
||||
+ m_frustum[ idx ][ plane_B ] * ( Y - Size )
|
||||
+ m_frustum[ idx ][ plane_C ] * ( Z + Size )
|
||||
+ m_frustum[ idx ][ plane_D ] > 0 )
|
||||
continue;
|
||||
if( m_frustum[ idx ][ plane_A ] * ( X + Size )
|
||||
+ m_frustum[ idx ][ plane_B ] * ( Y - Size )
|
||||
+ m_frustum[ idx ][ plane_C ] * ( Z + Size )
|
||||
+ m_frustum[ idx ][ plane_D ] > 0 )
|
||||
continue;
|
||||
if( m_frustum[ idx ][ plane_A ] * ( X - Size )
|
||||
+ m_frustum[ idx ][ plane_B ] * ( Y + Size )
|
||||
+ m_frustum[ idx ][ plane_C ] * ( Z + Size )
|
||||
+ m_frustum[ idx ][ plane_D ] > 0 )
|
||||
continue;
|
||||
if( m_frustum[ idx ][ plane_A ] * ( X + Size )
|
||||
+ m_frustum[ idx ][ plane_B ] * ( Y + Size )
|
||||
+ m_frustum[ idx ][ plane_C ] * ( Z + Size )
|
||||
+ m_frustum[ idx ][ plane_D ] > 0 )
|
||||
continue;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cFrustum::normalize_plane( cFrustum::side const Side ) {
|
||||
|
||||
float magnitude =
|
||||
std::sqrt(
|
||||
m_frustum[ Side ][ plane_A ] * m_frustum[ Side ][ plane_A ]
|
||||
+ m_frustum[ Side ][ plane_B ] * m_frustum[ Side ][ plane_B ]
|
||||
+ m_frustum[ Side ][ plane_C ] * m_frustum[ Side ][ plane_C ] );
|
||||
|
||||
m_frustum[ Side ][ plane_A ] /= magnitude;
|
||||
m_frustum[ Side ][ plane_B ] /= magnitude;
|
||||
m_frustum[ Side ][ plane_C ] /= magnitude;
|
||||
m_frustum[ Side ][ plane_D ] /= magnitude;
|
||||
}
|
||||
96
rendering/frustum.h
Normal file
96
rendering/frustum.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Float3d.h"
|
||||
#include "dumb3d.h"
|
||||
|
||||
inline std::vector<glm::vec4> const ndcfrustumshapepoints //
|
||||
{
|
||||
{-1, -1, -1, 1}, //
|
||||
{ 1, -1, -1, 1}, //
|
||||
{ 1, 1, -1, 1}, //
|
||||
{-1, 1, -1, 1}, // z-near
|
||||
|
||||
{-1, -1, 1, 1}, //
|
||||
{ 1, -1, 1, 1}, //
|
||||
{ 1, 1, 1, 1}, //
|
||||
{-1, 1, 1, 1}, // z-far
|
||||
};
|
||||
|
||||
inline std::vector<std::size_t> const frustumshapepoinstorder { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 };
|
||||
|
||||
// generic frustum class. used to determine if objects are inside current view area
|
||||
|
||||
class cFrustum {
|
||||
|
||||
public:
|
||||
// constructors:
|
||||
cFrustum() = default;
|
||||
// methods:
|
||||
// update the frustum to match current view orientation
|
||||
void
|
||||
calculate();
|
||||
void
|
||||
calculate(glm::mat4 const &Projection, glm::mat4 const &Modelview);
|
||||
// returns true if specified point is inside of the frustum
|
||||
inline
|
||||
bool
|
||||
point_inside( glm::vec3 const &Point ) const { return point_inside( Point.x, Point.y, Point.z ); }
|
||||
inline
|
||||
bool
|
||||
point_inside( float3 const &Point ) const { return point_inside( Point.x, Point.y, Point.z ); }
|
||||
inline
|
||||
bool
|
||||
point_inside( Math3D::vector3 const &Point ) const { return point_inside( static_cast<float>( Point.x ), static_cast<float>( Point.y ), static_cast<float>( Point.z ) ); }
|
||||
bool
|
||||
point_inside( float const X, float const Y, float const Z ) const;
|
||||
// tests if the sphere is in frustum, returns the distance between origin and sphere centre
|
||||
inline
|
||||
float
|
||||
sphere_inside( glm::dvec3 const &Center, float const Radius ) const { return sphere_inside( static_cast<float>( Center.x ), static_cast<float>( Center.y ), static_cast<float>( Center.z ), Radius ); }
|
||||
inline
|
||||
float
|
||||
sphere_inside( glm::vec3 const &Center, float const Radius ) const { return sphere_inside( Center.x, Center.y, Center.z, Radius ); }
|
||||
inline
|
||||
float
|
||||
sphere_inside( float3 const &Center, float const Radius ) const { return sphere_inside( Center.x, Center.y, Center.z, Radius ); }
|
||||
inline
|
||||
float
|
||||
sphere_inside( Math3D::vector3 const &Center, float const Radius ) const { return sphere_inside( static_cast<float>( Center.x ), static_cast<float>( Center.y ), static_cast<float>( Center.z ), Radius ); }
|
||||
float
|
||||
sphere_inside( float const X, float const Y, float const Z, float const Radius ) const;
|
||||
// returns true if specified cube is inside of the frustum. Size = half of the length
|
||||
inline
|
||||
bool
|
||||
cube_inside( glm::vec3 const &Center, float const Size ) const { return cube_inside( Center.x, Center.y, Center.z, Size ); }
|
||||
inline
|
||||
bool
|
||||
cube_inside( float3 const &Center, float const Size ) const { return cube_inside( Center.x, Center.y, Center.z, Size ); }
|
||||
inline
|
||||
bool
|
||||
cube_inside( Math3D::vector3 const &Center, float const Size ) const { return cube_inside( static_cast<float>( Center.x ), static_cast<float>( Center.y ), static_cast<float>( Center.z ), Size ); }
|
||||
bool
|
||||
cube_inside( float const X, float const Y, float const Z, float const Size ) const;
|
||||
|
||||
private:
|
||||
// types:
|
||||
// planes of the frustum
|
||||
enum side { side_RIGHT = 0, side_LEFT = 1, side_BOTTOM = 2, side_TOP = 3, side_BACK = 4, side_FRONT = 5 };
|
||||
// parameters of the frustum plane: A, B, C define plane normal, D defines distance from origin
|
||||
enum plane { plane_A = 0, plane_B = 1, plane_C = 2, plane_D = 3 };
|
||||
|
||||
// methods:
|
||||
void
|
||||
normalize_plane( cFrustum::side const Side ); // normalizes a plane (A side) from the frustum
|
||||
|
||||
// members:
|
||||
float m_frustum[6][4]; // holds the A B C and D values (normal & distance) for each side of the frustum.
|
||||
};
|
||||
460
rendering/geometrybank.cpp
Normal file
460
rendering/geometrybank.cpp
Normal file
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "geometrybank.h"
|
||||
#include "vertex.h"
|
||||
|
||||
#include "sn_utils.h"
|
||||
#include "Logs.h"
|
||||
#include "Globals.h"
|
||||
|
||||
namespace gfx {
|
||||
|
||||
basic_vertex basic_vertex::convert(world_vertex const &world, glm::dvec3 const& origin)
|
||||
{
|
||||
basic_vertex vertex{};
|
||||
vertex.position = static_cast<glm::vec3>(world.position - origin);
|
||||
vertex.normal = world.normal;
|
||||
vertex.texture = world.texture;
|
||||
return vertex;
|
||||
}
|
||||
|
||||
world_vertex basic_vertex::to_world(const glm::dvec3 &origin) const
|
||||
{
|
||||
world_vertex vertex{};
|
||||
|
||||
vertex.position = static_cast<glm::dvec3>(position) + origin;
|
||||
vertex.normal = normal;
|
||||
vertex.texture = texture;
|
||||
|
||||
return vertex;
|
||||
}
|
||||
|
||||
void
|
||||
basic_vertex::serialize( std::ostream &s, bool const Tangent ) const {
|
||||
|
||||
sn_utils::s_vec3( s, position );
|
||||
sn_utils::s_vec3( s, normal );
|
||||
sn_utils::ls_float32( s, texture.x );
|
||||
sn_utils::ls_float32( s, texture.y );
|
||||
if( Tangent ) {
|
||||
sn_utils::s_vec4( s, tangent );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
basic_vertex::deserialize( std::istream &s, bool const Tangent ) {
|
||||
|
||||
position = sn_utils::d_vec3( s );
|
||||
normal = sn_utils::d_vec3( s );
|
||||
texture.x = sn_utils::ld_float32( s );
|
||||
texture.y = sn_utils::ld_float32( s );
|
||||
if( Tangent ) {
|
||||
tangent = sn_utils::d_vec4( s );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
basic_vertex::serialize_packed( std::ostream &s, bool const Tangent ) const {
|
||||
|
||||
sn_utils::ls_uint64( s, glm::packHalf4x16( { position, 0.f } ) );
|
||||
sn_utils::ls_uint32( s, glm::packSnorm3x10_1x2( { normal, 0.f } ) );
|
||||
sn_utils::ls_uint16( s, glm::packHalf1x16( texture.x ) );
|
||||
sn_utils::ls_uint16( s, glm::packHalf1x16( texture.y ) );
|
||||
if( Tangent ) {
|
||||
sn_utils::ls_uint32( s, glm::packSnorm3x10_1x2( tangent ) );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
basic_vertex::deserialize_packed( std::istream &s, bool const Tangent ) {
|
||||
|
||||
position = glm::unpackHalf4x16( sn_utils::ld_uint64( s ) );
|
||||
normal = glm::unpackSnorm3x10_1x2( sn_utils::ld_uint32( s ) );
|
||||
texture.x = glm::unpackHalf1x16( sn_utils::ld_uint16( s ) );
|
||||
texture.y = glm::unpackHalf1x16( sn_utils::ld_uint16( s ) );
|
||||
if( Tangent ) {
|
||||
tangent = glm::unpackSnorm3x10_1x2( sn_utils::ld_uint32( s ) );
|
||||
}
|
||||
}
|
||||
|
||||
// based on
|
||||
// Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”.
|
||||
// Terathon Software, 2001. http://terathon.com/code/tangent.html
|
||||
void calculate_tangents(vertex_array &vertices, index_array const &indices, int const type)
|
||||
{
|
||||
size_t vertex_count = vertices.size();
|
||||
|
||||
if (!vertex_count || vertices[0].tangent.w != 0.0f)
|
||||
return;
|
||||
|
||||
size_t triangle_count;
|
||||
size_t tri_count_base = indices.empty() ? vertex_count : indices.size();
|
||||
|
||||
if (type == GL_TRIANGLES)
|
||||
triangle_count = tri_count_base / 3;
|
||||
else if (type == GL_TRIANGLE_STRIP)
|
||||
triangle_count = tri_count_base - 2;
|
||||
else if (type == GL_TRIANGLE_FAN)
|
||||
triangle_count = tri_count_base - 2;
|
||||
else
|
||||
return;
|
||||
|
||||
std::vector<glm::vec3> tan(vertex_count * 2);
|
||||
|
||||
for (size_t a = 0; a < triangle_count; a++)
|
||||
{
|
||||
size_t i1, i2, i3;
|
||||
if (type == GL_TRIANGLES)
|
||||
{
|
||||
i1 = a * 3;
|
||||
i2 = a * 3 + 1;
|
||||
i3 = a * 3 + 2;
|
||||
}
|
||||
else if (type == GL_TRIANGLE_STRIP)
|
||||
{
|
||||
if (a % 2 == 0)
|
||||
{
|
||||
i1 = a;
|
||||
i2 = a + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
i1 = a + 1;
|
||||
i2 = a;
|
||||
}
|
||||
i3 = a + 2;
|
||||
}
|
||||
else if (type == GL_TRIANGLE_FAN)
|
||||
{
|
||||
i1 = 0;
|
||||
i2 = a + 1;
|
||||
i3 = a + 2;
|
||||
}
|
||||
|
||||
if (!indices.empty()) {
|
||||
i1 = indices[i1];
|
||||
i2 = indices[i2];
|
||||
i3 = indices[i3];
|
||||
}
|
||||
|
||||
const glm::vec3 &v1 = vertices[i1].position;
|
||||
const glm::vec3 &v2 = vertices[i2].position;
|
||||
const glm::vec3 &v3 = vertices[i3].position;
|
||||
|
||||
const glm::vec2 &w1 = vertices[i1].texture;
|
||||
const glm::vec2 &w2 = vertices[i2].texture;
|
||||
const glm::vec2 &w3 = vertices[i3].texture;
|
||||
|
||||
float x1 = v2.x - v1.x;
|
||||
float x2 = v3.x - v1.x;
|
||||
float y1 = v2.y - v1.y;
|
||||
float y2 = v3.y - v1.y;
|
||||
float z1 = v2.z - v1.z;
|
||||
float z2 = v3.z - v1.z;
|
||||
|
||||
float s1 = w2.x - w1.x;
|
||||
float s2 = w3.x - w1.x;
|
||||
float t1 = w2.y - w1.y;
|
||||
float t2 = w3.y - w1.y;
|
||||
|
||||
float ri = (s1 * t2 - s2 * t1);
|
||||
if (ri == 0.0f) {
|
||||
//ErrorLog("Bad model: failed to generate tangent vectors for vertices: " +
|
||||
// std::to_string(i1) + ", " + std::to_string(i2) + ", " + std::to_string(i3));
|
||||
// useless error, as we don't have name of problematic model here
|
||||
// why does it happen?
|
||||
ri = 1.0f;
|
||||
}
|
||||
|
||||
float r = 1.0f / ri;
|
||||
glm::vec3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
|
||||
(t2 * z1 - t1 * z2) * r);
|
||||
glm::vec3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
|
||||
(s1 * z2 - s2 * z1) * r);
|
||||
|
||||
tan[i1] += sdir;
|
||||
tan[i2] += sdir;
|
||||
tan[i3] += sdir;
|
||||
|
||||
tan[vertex_count + i1] += tdir;
|
||||
tan[vertex_count + i2] += tdir;
|
||||
tan[vertex_count + i3] += tdir;
|
||||
}
|
||||
|
||||
for (size_t a = 0; a < vertex_count; a++)
|
||||
{
|
||||
const glm::vec3 &n = vertices[a].normal;
|
||||
const glm::vec3 &t = tan[a];
|
||||
const glm::vec3 &t2 = tan[vertex_count + a];
|
||||
|
||||
vertices[a].tangent =
|
||||
glm::vec4(
|
||||
glm::normalize((t - n * glm::dot(n, t))),
|
||||
(glm::dot(glm::cross(n, t), t2) < 0.0F) ?
|
||||
-1.0F :
|
||||
1.0F);
|
||||
}
|
||||
}
|
||||
|
||||
void calculate_indices( index_array &Indices, vertex_array &Vertices, userdata_array &Userdata, float tolerancescale ) {
|
||||
|
||||
Indices.resize( Vertices.size() );
|
||||
std::iota( std::begin( Indices ), std::end( Indices ), 0 );
|
||||
// gather instances of used vertices, replace the original vertex bank with it after you're done
|
||||
vertex_array indexedvertices{};
|
||||
userdata_array indexeduserdata{};
|
||||
indexedvertices.reserve( std::max<size_t>( 100, Vertices.size() / 3 ) ); // optimistic guesstimate, but should reduce re-allocation somewhat
|
||||
bool has_userdata = !Userdata.empty();
|
||||
if (has_userdata)
|
||||
indexeduserdata.reserve(std::max<size_t>(100, Userdata.size() / 3));
|
||||
auto const matchtolerance { 1e-5f * tolerancescale };
|
||||
for( auto idx = 0; idx < Indices.size(); ++idx ) {
|
||||
if( Indices[ idx ] < idx ) {
|
||||
// this index is pointing to a vertex out of linear order, i.e. it's an already processed duplicate we can skip
|
||||
continue;
|
||||
}
|
||||
// due to duplicate removal our vertex will likely have different index in the processed set
|
||||
Indices[ idx ] = indexedvertices.size();
|
||||
// see if there's any pointers in the remaining index subrange to similar enough vertices
|
||||
// if found, remap these to use our current vertex instead
|
||||
const auto& vertex { Vertices[ idx ] };
|
||||
const auto* userdata{ has_userdata ? &Userdata[idx] : nullptr };
|
||||
auto matchiter { std::cbegin( Vertices ) + idx };
|
||||
auto matchuserdata = userdata;
|
||||
for( auto matchidx = idx + 1; matchidx < Indices.size() && matchidx < idx + Global.iConvertIndexRange; ++matchidx ) {
|
||||
++matchiter;
|
||||
++matchuserdata;
|
||||
if( ( glm::all( glm::epsilonEqual( vertex.position, matchiter->position, matchtolerance ) ) )
|
||||
&& ( glm::all( glm::epsilonEqual( vertex.normal, matchiter->normal, matchtolerance ) ) )
|
||||
&& ( glm::all( glm::epsilonEqual( vertex.texture, matchiter->texture, matchtolerance ) ) )
|
||||
&& ( !userdata || glm::all( glm::epsilonEqual( userdata->data, matchuserdata->data, matchtolerance ) ) ) ) {
|
||||
Indices[ matchidx ] = Indices[ idx ];
|
||||
}
|
||||
}
|
||||
indexedvertices.emplace_back( vertex );
|
||||
if(userdata)
|
||||
indexeduserdata.emplace_back( *userdata );
|
||||
}
|
||||
// done indexing, swap the source vertex bank with the processed one
|
||||
Vertices.swap( indexedvertices );
|
||||
Userdata.swap( indexeduserdata );
|
||||
}
|
||||
|
||||
// generic geometry bank class, allows storage, update and drawing of geometry chunks
|
||||
|
||||
// creates a new geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL
|
||||
gfx::geometry_handle
|
||||
geometry_bank::create( gfx::vertex_array &Vertices, userdata_array &Userdata, unsigned int const Type ) {
|
||||
|
||||
if(Vertices.empty()) { return { 0, 0 }; }
|
||||
|
||||
m_chunks.emplace_back( Vertices, Userdata, Type );
|
||||
// NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication
|
||||
gfx::geometry_handle chunkhandle { 0, static_cast<std::uint32_t>(m_chunks.size()) };
|
||||
// template method implementation
|
||||
create_( chunkhandle );
|
||||
// all done
|
||||
return chunkhandle;
|
||||
}
|
||||
|
||||
// creates a new indexed geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL
|
||||
gfx::geometry_handle
|
||||
geometry_bank::create( gfx::index_array &Indices, gfx::vertex_array &Vertices, userdata_array &Userdata, unsigned int const Type ) {
|
||||
|
||||
if(Vertices.empty()) { return { 0, 0 }; }
|
||||
|
||||
m_chunks.emplace_back( Indices, Vertices, Userdata, Type );
|
||||
// NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication
|
||||
gfx::geometry_handle chunkhandle { 0, static_cast<std::uint32_t>(m_chunks.size()) };
|
||||
// template method implementation
|
||||
create_( chunkhandle );
|
||||
// all done
|
||||
return chunkhandle;
|
||||
}
|
||||
|
||||
// replaces data of specified chunk with the supplied vertex data, starting from specified offset
|
||||
bool
|
||||
geometry_bank::replace( gfx::vertex_array &Vertices, userdata_array &Userdata, gfx::geometry_handle const &Geometry, std::size_t const Offset ) {
|
||||
|
||||
if( ( Geometry.chunk == 0 ) || ( Geometry.chunk > m_chunks.size() ) ) { return false; }
|
||||
|
||||
auto &chunk = gfx::geometry_bank::chunk( Geometry );
|
||||
|
||||
if( ( Offset == 0 )
|
||||
&& ( Vertices.size() == chunk.vertices.size() ) ) {
|
||||
// check first if we can get away with a simple swap...
|
||||
chunk.vertices.swap( Vertices );
|
||||
chunk.userdata.swap( Userdata );
|
||||
}
|
||||
else {
|
||||
// ...otherwise we need to do some legwork
|
||||
// NOTE: if the offset is larger than existing size of the chunk, it'll bridge the gap with 'blank' vertices
|
||||
// TBD: we could bail out with an error instead if such request occurs
|
||||
chunk.vertices.resize( Offset + Vertices.size(), gfx::basic_vertex() );
|
||||
chunk.userdata.resize( Offset + Userdata.size(), gfx::vertex_userdata() );
|
||||
chunk.vertices.insert( std::end( chunk.vertices ), std::begin( Vertices ), std::end( Vertices ) );
|
||||
chunk.userdata.insert( std::end( chunk.userdata ), std::begin( Userdata ), std::end( Userdata ) );
|
||||
}
|
||||
// template method implementation
|
||||
replace_( Geometry );
|
||||
// all done
|
||||
return true;
|
||||
}
|
||||
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
bool
|
||||
geometry_bank::append( gfx::vertex_array &Vertices, userdata_array &Userdata, gfx::geometry_handle const &Geometry ) {
|
||||
|
||||
if( ( Geometry.chunk == 0 ) || ( Geometry.chunk > m_chunks.size() ) ) { return false; }
|
||||
|
||||
return replace( Vertices, Userdata, Geometry, gfx::geometry_bank::chunk( Geometry ).vertices.size() );
|
||||
}
|
||||
|
||||
// draws geometry stored in specified chunk
|
||||
std::size_t
|
||||
geometry_bank::draw( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams ) {
|
||||
// template method implementation
|
||||
return draw_( Geometry, Units, Streams );
|
||||
}
|
||||
|
||||
// frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time
|
||||
void
|
||||
geometry_bank::release() {
|
||||
// template method implementation
|
||||
release_();
|
||||
}
|
||||
|
||||
// provides direct access to indexdata of specfied chunk
|
||||
index_array const &
|
||||
geometry_bank::indices( gfx::geometry_handle const &Geometry ) const {
|
||||
|
||||
return geometry_bank::chunk( Geometry ).indices;
|
||||
}
|
||||
|
||||
// provides direct access to vertex data of specfied chunk
|
||||
vertex_array const &
|
||||
geometry_bank::vertices( gfx::geometry_handle const &Geometry ) const {
|
||||
|
||||
return geometry_bank::chunk( Geometry ).vertices;
|
||||
}
|
||||
|
||||
// provides direct access to vertex user data of specfied chunk
|
||||
userdata_array const &
|
||||
geometry_bank::userdata( gfx::geometry_handle const &Geometry ) const {
|
||||
|
||||
return geometry_bank::chunk( Geometry ).userdata;
|
||||
}
|
||||
|
||||
// geometry bank manager, holds collection of geometry banks
|
||||
|
||||
// performs a resource sweep
|
||||
void
|
||||
geometrybank_manager::update() {
|
||||
|
||||
m_garbagecollector.sweep();
|
||||
}
|
||||
|
||||
// creates a new geometry bank. returns: handle to the bank or NULL
|
||||
gfx::geometrybank_handle
|
||||
geometrybank_manager::register_bank(std::unique_ptr<geometry_bank> bank) {
|
||||
m_geometrybanks.emplace_back(std::move(bank), std::chrono::steady_clock::time_point() );
|
||||
|
||||
// NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication
|
||||
return { static_cast<std::uint32_t>( m_geometrybanks.size() ), 0 };
|
||||
}
|
||||
|
||||
// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
|
||||
gfx::geometry_handle
|
||||
geometrybank_manager::create_chunk(vertex_array &Vertices, userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type ) {
|
||||
|
||||
auto const newchunkhandle = bank( Geometry ).first->create( Vertices, Userdata, Type );
|
||||
|
||||
if( newchunkhandle.chunk != 0 ) { return { Geometry.bank, newchunkhandle.chunk }; }
|
||||
else { return { 0, 0 }; }
|
||||
}
|
||||
|
||||
// creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
|
||||
gfx::geometry_handle
|
||||
geometrybank_manager::create_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, unsigned int const Type ) {
|
||||
|
||||
auto const newchunkhandle = bank( Geometry ).first->create( Indices, Vertices, Userdata, Type );
|
||||
|
||||
if( newchunkhandle.chunk != 0 ) { return { Geometry.bank, newchunkhandle.chunk }; }
|
||||
else { return { 0, 0 }; }
|
||||
}
|
||||
|
||||
// replaces data of specified chunk with the supplied vertex data, starting from specified offset
|
||||
bool
|
||||
geometrybank_manager::replace( gfx::vertex_array &Vertices, userdata_array &Userdata, gfx::geometry_handle const &Geometry, std::size_t const Offset ) {
|
||||
|
||||
return bank( Geometry ).first->replace( Vertices, Userdata, Geometry, Offset );
|
||||
}
|
||||
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
bool
|
||||
geometrybank_manager::append( gfx::vertex_array &Vertices, userdata_array &Userdata, gfx::geometry_handle const &Geometry ) {
|
||||
|
||||
return bank( Geometry ).first->append( Vertices, Userdata, Geometry );
|
||||
}
|
||||
// draws geometry stored in specified chunk
|
||||
void
|
||||
geometrybank_manager::draw( gfx::geometry_handle const &Geometry, unsigned int const Streams ) {
|
||||
|
||||
if( Geometry == null_handle ) { return; }
|
||||
|
||||
auto &bankrecord = bank( Geometry );
|
||||
|
||||
bankrecord.second = m_garbagecollector.timestamp();
|
||||
m_primitivecount += bankrecord.first->draw( Geometry, m_units, Streams );
|
||||
}
|
||||
|
||||
// provides direct access to index data of specfied chunk
|
||||
gfx::index_array const &
|
||||
geometrybank_manager::indices( gfx::geometry_handle const &Geometry ) const {
|
||||
|
||||
return bank( Geometry ).first->indices( Geometry );
|
||||
}
|
||||
|
||||
// provides direct access to vertex data of specfied chunk
|
||||
gfx::vertex_array const &
|
||||
geometrybank_manager::vertices( gfx::geometry_handle const &Geometry ) const {
|
||||
|
||||
return bank( Geometry ).first->vertices( Geometry );
|
||||
}
|
||||
|
||||
// provides direct access to vertex user data of specfied chunk
|
||||
gfx::userdata_array const &
|
||||
geometrybank_manager::userdata( gfx::geometry_handle const &Geometry ) const {
|
||||
|
||||
return bank( Geometry ).first->userdata( Geometry );
|
||||
}
|
||||
|
||||
void vertex_userdata::serialize(std::ostream &s) const
|
||||
{
|
||||
sn_utils::s_vec4(s, data);
|
||||
}
|
||||
|
||||
void vertex_userdata::deserialize(std::istream &s)
|
||||
{
|
||||
data = sn_utils::d_vec4(s);
|
||||
}
|
||||
|
||||
void vertex_userdata::serialize_packed(std::ostream &s) const
|
||||
{
|
||||
sn_utils::ls_uint64(s, glm::packHalf4x16(data));
|
||||
}
|
||||
|
||||
void vertex_userdata::deserialize_packed(std::istream &s)
|
||||
{
|
||||
data = glm::unpackHalf4x16(sn_utils::ld_uint64(s));
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
254
rendering/geometrybank.h
Normal file
254
rendering/geometrybank.h
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ResourceManager.h"
|
||||
|
||||
struct world_vertex;
|
||||
|
||||
namespace gfx {
|
||||
|
||||
struct basic_vertex {
|
||||
|
||||
glm::vec3 position; // 3d space
|
||||
glm::vec3 normal; // 3d space
|
||||
glm::vec2 texture; // uv space
|
||||
glm::vec4 tangent; // xyz - tangent, w - handedness
|
||||
|
||||
basic_vertex() = default;
|
||||
basic_vertex( glm::vec3 Position, glm::vec3 Normal, glm::vec2 Texture ) :
|
||||
position( Position ), normal( Normal ), texture( Texture )
|
||||
{}
|
||||
static basic_vertex convert(world_vertex const &world, glm::dvec3 const &origin);
|
||||
world_vertex to_world(glm::dvec3 const &origin = glm::dvec3(0.)) const;
|
||||
void serialize( std::ostream&, bool const Tangent = false ) const;
|
||||
void deserialize( std::istream&, bool const Tangent = false );
|
||||
void serialize_packed( std::ostream&, bool const Tangent = false ) const;
|
||||
void deserialize_packed( std::istream&, bool const Tangent = false );
|
||||
};
|
||||
|
||||
struct vertex_userdata{
|
||||
glm::vec4 data; // user data (for color or additional uv channels, not subject to post-processing)
|
||||
void serialize( std::ostream& ) const;
|
||||
void deserialize( std::istream& );
|
||||
void serialize_packed( std::ostream& ) const;
|
||||
void deserialize_packed( std::istream& );
|
||||
};
|
||||
|
||||
// data streams carried in a vertex
|
||||
enum stream {
|
||||
none = 0x0,
|
||||
position = 0x1,
|
||||
normal = 0x2,
|
||||
color = 0x4, // currently normal and colour streams are stored in the same slot, and mutually exclusive
|
||||
texture = 0x8
|
||||
};
|
||||
|
||||
unsigned int const basic_streams { stream::position | stream::normal | stream::texture };
|
||||
unsigned int const color_streams { stream::position | stream::color | stream::texture };
|
||||
|
||||
struct stream_units {
|
||||
|
||||
std::vector<GLint> texture { GL_TEXTURE0 }; // unit associated with main texture data stream. TODO: allow multiple units per stream
|
||||
};
|
||||
|
||||
using basic_index = std::uint32_t;
|
||||
|
||||
using vertex_array = std::vector<basic_vertex>;
|
||||
using userdata_array = std::vector<vertex_userdata>;
|
||||
using index_array = std::vector<basic_index>;
|
||||
|
||||
void calculate_tangents( vertex_array &vertices, index_array const &indices, int const type );
|
||||
void calculate_indices( index_array &Indices, vertex_array &Vertices, userdata_array &Userdata, float tolerancescale = 1.0f );
|
||||
|
||||
// generic geometry bank class, allows storage, update and drawing of geometry chunks
|
||||
|
||||
struct geometry_handle {
|
||||
// constructors
|
||||
geometry_handle() :
|
||||
bank( 0 ), chunk( 0 )
|
||||
{}
|
||||
geometry_handle( std::uint32_t Bank, std::uint32_t Chunk ) :
|
||||
bank( Bank ), chunk( Chunk )
|
||||
{}
|
||||
// methods
|
||||
inline
|
||||
operator std::uint64_t() const {
|
||||
/*
|
||||
return bank << 14 | chunk; }
|
||||
*/
|
||||
return ( std::uint64_t { bank } << 32 | chunk );
|
||||
}
|
||||
|
||||
// members
|
||||
/*
|
||||
std::uint32_t
|
||||
bank : 18, // 250k banks
|
||||
chunk : 14; // 16k chunks per bank
|
||||
*/
|
||||
std::uint32_t bank;
|
||||
std::uint32_t chunk;
|
||||
};
|
||||
|
||||
class geometry_bank {
|
||||
|
||||
public:
|
||||
// types:
|
||||
|
||||
// constructors:
|
||||
|
||||
// destructor:
|
||||
virtual
|
||||
~geometry_bank() {}
|
||||
|
||||
// methods:
|
||||
// creates a new geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL
|
||||
auto create( gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, unsigned int const Type ) -> gfx::geometry_handle;
|
||||
// creates a new indexed geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL
|
||||
auto create( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, unsigned int const Type ) -> gfx::geometry_handle;
|
||||
// replaces vertex data of specified chunk with the supplied data, starting from specified offset
|
||||
auto replace( gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool;
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
auto append( gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, gfx::geometry_handle const &Geometry ) -> bool;
|
||||
// draws geometry stored in specified chunk
|
||||
auto draw( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams = basic_streams ) -> std::size_t;
|
||||
// draws geometry stored in supplied list of chunks
|
||||
template <typename Iterator_>
|
||||
auto draw( Iterator_ First, Iterator_ Last, gfx::stream_units const &Units, unsigned int const Streams = basic_streams ) ->std::size_t {
|
||||
std::size_t count { 0 };
|
||||
while( First != Last ) {
|
||||
count += draw( *First, Units, Streams ); ++First; }
|
||||
return count; }
|
||||
// frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time
|
||||
void release();
|
||||
// provides direct access to index data of specfied chunk
|
||||
auto indices( gfx::geometry_handle const &Geometry ) const -> gfx::index_array const &;
|
||||
// provides direct access to vertex data of specfied chunk
|
||||
auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &;
|
||||
// provides direct access to vertex user data of specfied chunk
|
||||
auto userdata( gfx::geometry_handle const &Geometry ) const -> gfx::userdata_array const &;
|
||||
|
||||
protected:
|
||||
// types:
|
||||
struct geometry_chunk {
|
||||
unsigned int type; // kind of geometry used by the chunk
|
||||
gfx::vertex_array vertices; // geometry data
|
||||
gfx::index_array indices; // index data
|
||||
gfx::userdata_array userdata;
|
||||
// NOTE: constructor doesn't copy provided geometry data, but moves it
|
||||
geometry_chunk( gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, unsigned int Type ) :
|
||||
type( Type )
|
||||
{
|
||||
vertices.swap( Vertices );
|
||||
userdata.swap( Userdata );
|
||||
}
|
||||
// NOTE: constructor doesn't copy provided geometry data, but moves it
|
||||
geometry_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, unsigned int Type ) :
|
||||
type( Type )
|
||||
{
|
||||
vertices.swap( Vertices );
|
||||
userdata.swap( Userdata );
|
||||
indices.swap( Indices );
|
||||
}
|
||||
};
|
||||
|
||||
using geometrychunk_sequence = std::vector<geometry_chunk>;
|
||||
|
||||
// methods
|
||||
inline
|
||||
auto chunk( gfx::geometry_handle const Geometry ) -> geometry_chunk & {
|
||||
return m_chunks[ Geometry.chunk - 1 ]; }
|
||||
inline
|
||||
auto chunk( gfx::geometry_handle const Geometry ) const -> geometry_chunk const & {
|
||||
return m_chunks[ Geometry.chunk - 1 ]; }
|
||||
|
||||
// members:
|
||||
geometrychunk_sequence m_chunks;
|
||||
|
||||
private:
|
||||
// methods:
|
||||
// create() subclass details
|
||||
virtual void create_( gfx::geometry_handle const &Geometry ) = 0;
|
||||
// replace() subclass details
|
||||
virtual void replace_( gfx::geometry_handle const &Geometry ) = 0;
|
||||
// draw() subclass details
|
||||
virtual auto draw_( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams ) -> std::size_t = 0;
|
||||
// resource release subclass details
|
||||
virtual void release_() = 0;
|
||||
};
|
||||
|
||||
// geometry bank manager, holds collection of geometry banks
|
||||
|
||||
using geometrybank_handle = geometry_handle;
|
||||
|
||||
class geometrybank_manager {
|
||||
|
||||
public:
|
||||
// constructors
|
||||
geometrybank_manager() = default;
|
||||
// methods:
|
||||
// performs a resource sweep
|
||||
void update();
|
||||
// registers a new geometry bank. returns: handle to the bank
|
||||
auto register_bank(std::unique_ptr<geometry_bank> bank) -> gfx::geometrybank_handle;
|
||||
// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
|
||||
auto create_chunk( gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type ) -> gfx::geometry_handle;
|
||||
// creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
|
||||
auto create_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, unsigned int const Type ) -> gfx::geometry_handle;
|
||||
// replaces data of specified chunk with the supplied vertex data, starting from specified offset
|
||||
auto replace( gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool;
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
auto append( gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry ) -> bool;
|
||||
// draws geometry stored in specified chunk
|
||||
void draw( gfx::geometry_handle const &Geometry, unsigned int const Streams = basic_streams );
|
||||
template <typename Iterator_>
|
||||
void draw( Iterator_ First, Iterator_ Last, unsigned int const Streams = basic_streams ) {
|
||||
while( First != Last ) {
|
||||
draw( *First, Streams );
|
||||
++First; } }
|
||||
// provides direct access to index data of specfied chunk
|
||||
auto indices( gfx::geometry_handle const &Geometry ) const -> gfx::index_array const &;
|
||||
// provides direct access to vertex data of specfied chunk
|
||||
auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &;
|
||||
// provides direct access to vertex data of specfied chunk
|
||||
auto userdata( gfx::geometry_handle const &Geometry ) const -> gfx::userdata_array const &;
|
||||
// sets target texture unit for the texture data stream
|
||||
auto units() -> gfx::stream_units & { return m_units; }
|
||||
// provides access to primitives count
|
||||
auto primitives_count() const -> std::size_t const & { return m_primitivecount; }
|
||||
auto primitives_count() -> std::size_t & { return m_primitivecount; }
|
||||
|
||||
private:
|
||||
// types:
|
||||
using geometrybanktimepoint_pair = std::pair< std::shared_ptr<geometry_bank>, resource_timestamp >;
|
||||
using geometrybanktimepointpair_sequence = std::deque< geometrybanktimepoint_pair >;
|
||||
|
||||
// members:
|
||||
geometrybanktimepointpair_sequence m_geometrybanks;
|
||||
garbage_collector<geometrybanktimepointpair_sequence> m_garbagecollector { m_geometrybanks, 60, 120, "geometry buffer" };
|
||||
gfx::stream_units m_units;
|
||||
|
||||
// methods
|
||||
inline
|
||||
auto valid( gfx::geometry_handle const &Geometry ) const -> bool {
|
||||
return ( ( Geometry.bank != 0 )
|
||||
&& ( Geometry.bank <= m_geometrybanks.size() ) ); }
|
||||
inline
|
||||
auto bank( gfx::geometry_handle const Geometry ) -> geometrybanktimepointpair_sequence::value_type & {
|
||||
return m_geometrybanks[ Geometry.bank - 1 ]; }
|
||||
inline
|
||||
auto bank( gfx::geometry_handle const Geometry ) const -> geometrybanktimepointpair_sequence::value_type const & {
|
||||
return m_geometrybanks[ Geometry.bank - 1 ]; }
|
||||
|
||||
// members:
|
||||
std::size_t m_primitivecount { 0 }; // number of drawn primitives
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
23
rendering/light.h
Normal file
23
rendering/light.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "color.h"
|
||||
|
||||
// a simple light source, either omni- or directional
|
||||
struct basic_light {
|
||||
|
||||
glm::vec4 ambient { colors::none };
|
||||
glm::vec4 diffuse { colors::white };
|
||||
glm::vec4 specular { colors::white };
|
||||
glm::vec3 position;
|
||||
glm::vec3 direction;
|
||||
bool is_directional { true };
|
||||
};
|
||||
126
rendering/lightarray.cpp
Normal file
126
rendering/lightarray.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/*
|
||||
MaSzyna EU07 locomotive simulator
|
||||
Copyright (C) 2001-2004 Marcin Wozniak and others
|
||||
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "lightarray.h"
|
||||
#include "DynObj.h"
|
||||
|
||||
void
|
||||
light_array::insert( TDynamicObject const *Owner ) {
|
||||
|
||||
// we're only storing lights for locos, which have two sets of lights, front and rear
|
||||
// for a more generic role this function would have to be tweaked to add vehicle type-specific light combinations
|
||||
data.emplace_back( Owner, end::front );
|
||||
data.emplace_back( Owner, end::rear );
|
||||
}
|
||||
|
||||
void
|
||||
light_array::remove( TDynamicObject const *Owner ) {
|
||||
|
||||
data.erase(
|
||||
std::remove_if(
|
||||
data.begin(),
|
||||
data.end(),
|
||||
[=]( light_record const &light ){ return light.owner == Owner; } ),
|
||||
data.end() );
|
||||
}
|
||||
|
||||
// updates records in the collection
|
||||
void
|
||||
light_array::update() {
|
||||
|
||||
for( auto &light : data ) {
|
||||
// update light parameters to match current data of the owner
|
||||
if( light.index == end::front ) {
|
||||
// front light set
|
||||
light.position = light.owner->GetPosition() + ( light.owner->VectorFront() * ( std::max( 0.0, light.owner->GetLength() * 0.5 - 2.0 ) ) );// +( light.owner->VectorUp() * 0.25 );
|
||||
light.direction = glm::make_vec3( light.owner->VectorFront().getArray() );
|
||||
}
|
||||
else {
|
||||
// rear light set
|
||||
light.position = light.owner->GetPosition() - ( light.owner->VectorFront() * ( std::max( 0.0, light.owner->GetLength() * 0.5 - 2.0 ) ) );// +( light.owner->VectorUp() * 0.25 );
|
||||
light.direction = glm::make_vec3( light.owner->VectorFront().getArray() );
|
||||
light.direction.x = -light.direction.x;
|
||||
light.direction.z = -light.direction.z;
|
||||
}
|
||||
// determine intensity of this light set
|
||||
if( ( true == light.owner->MoverParameters->Power24vIsAvailable )
|
||||
|| ( true == light.owner->MoverParameters->Power110vIsAvailable ) ) {
|
||||
// with power on, the intensity depends on the state of activated switches
|
||||
// first we cross-check the list of enabled lights with the lights installed in the vehicle...
|
||||
auto const lights { light.owner->MoverParameters->iLights[ light.index ] & light.owner->LightList( static_cast<end>( light.index ) ) };
|
||||
// ...then check their individual state
|
||||
light.count = 0
|
||||
+ ( ( lights & light::headlight_left ) ? 1 : 0 )
|
||||
+ ( ( lights & light::headlight_right ) ? 1 : 0 )
|
||||
+ ( ( lights & light::headlight_upper ) ? 1 : 0 )
|
||||
+ ( ( lights & light::highbeamlight_left ) ? 1: 0)
|
||||
+ ( ( lights & light::highbeamlight_right ) ? 1 : 0);
|
||||
|
||||
// set intensity
|
||||
if( light.count > 0 ) {
|
||||
bool isEnabled = light.index == end::front ? !light.owner->HeadlightsAoff : !light.owner->HeadlightsBoff;
|
||||
|
||||
light.intensity = std::max(0.0f, std::log((float)light.count + 1.0f));
|
||||
if (light.owner->DimHeadlights && !light.owner->HighBeamLights && isEnabled) // tylko przyciemnione
|
||||
light.intensity *= light.owner->MoverParameters->dimMultiplier;
|
||||
else if (!light.owner->DimHeadlights && !light.owner->HighBeamLights && isEnabled) // normalne
|
||||
light.intensity *= light.owner->MoverParameters->normMultiplier;
|
||||
else if (light.owner->DimHeadlights && light.owner->HighBeamLights && isEnabled) // przyciemnione dlugie
|
||||
light.intensity *= light.owner->MoverParameters->highDimMultiplier;
|
||||
else if (!light.owner->DimHeadlights && light.owner->HighBeamLights && isEnabled) // dlugie zwykle
|
||||
light.intensity *= light.owner->MoverParameters->highMultiplier;
|
||||
else if (!isEnabled)
|
||||
{
|
||||
light.intensity = 0.0f;
|
||||
}
|
||||
|
||||
// TBD, TODO: intensity can be affected further by other factors
|
||||
light.state = {
|
||||
( ( lights & light::headlight_left | light::highbeamlight_left ) ? 1.f : 0.f ),
|
||||
( ( lights & light::headlight_upper ) ? 1.f : 0.f ),
|
||||
( ( lights & light::headlight_right | light::highbeamlight_right) ? 1.f : 0.f ) };
|
||||
|
||||
light.color = {
|
||||
static_cast<float>(light.owner->MoverParameters->refR) / 255.0f,
|
||||
static_cast<float>(light.owner->MoverParameters->refG) / 255.0f,
|
||||
static_cast<float>(light.owner->MoverParameters->refB) / 255.0f
|
||||
};
|
||||
|
||||
if (light.owner->DimHeadlights && !light.owner->HighBeamLights) // tylko przyciemnione
|
||||
light.state *= light.owner->MoverParameters->dimMultiplier;
|
||||
else if (!light.owner->DimHeadlights && !light.owner->HighBeamLights) // normalne
|
||||
light.state *= light.owner->MoverParameters->normMultiplier;
|
||||
else if (light.owner->DimHeadlights && light.owner->HighBeamLights) // przyciemnione dlugie
|
||||
light.state *= light.owner->MoverParameters->highDimMultiplier;
|
||||
else if (!light.owner->DimHeadlights && light.owner->HighBeamLights) // dlugie zwykle
|
||||
light.state *= light.owner->MoverParameters->highMultiplier;
|
||||
else if (!isEnabled)
|
||||
{
|
||||
light.state = glm::vec3{0.f};
|
||||
}
|
||||
}
|
||||
else {
|
||||
light.intensity = 0.0f;
|
||||
light.state = glm::vec3{ 0.f };
|
||||
}
|
||||
}
|
||||
else {
|
||||
// with battery off the lights are off
|
||||
light.intensity = 0.0f;
|
||||
light.count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
43
rendering/lightarray.h
Normal file
43
rendering/lightarray.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "Classes.h"
|
||||
|
||||
// collection of virtual light sources present in the scene
|
||||
// used by the renderer to determine most suitable placement for actual light sources during render
|
||||
struct light_array {
|
||||
|
||||
public:
|
||||
// types
|
||||
struct light_record {
|
||||
|
||||
light_record( TDynamicObject const *Owner, int const Lightindex) :
|
||||
owner(Owner), index(Lightindex)
|
||||
{};
|
||||
|
||||
TDynamicObject const *owner; // the object in world which 'carries' the light
|
||||
int index{ -1 }; // 0: front lights, 1: rear lights
|
||||
glm::dvec3 position; // position of the light in 3d scene
|
||||
glm::vec3 direction; // direction of the light in 3d scene
|
||||
glm::vec3 color{ 255.0f / 255.0f, 241.0f / 255.0f, 224.0f / 255.0f }; // color of the light, default is halogen light
|
||||
float intensity{ 0.0f }; // (combined) intensity of the light(s)
|
||||
int count{ 0 }; // number (or pattern) of active light(s)
|
||||
glm::vec3 state{ 0.f }; // state of individual lights
|
||||
};
|
||||
|
||||
// methods
|
||||
// adds records for lights of specified owner to the collection
|
||||
void
|
||||
insert( TDynamicObject const *Owner );
|
||||
// removes records for lights of specified owner from the collection
|
||||
void
|
||||
remove( TDynamicObject const *Owner );
|
||||
// updates records in the collection
|
||||
void
|
||||
update();
|
||||
|
||||
// types
|
||||
typedef std::vector<light_record> lightrecord_array;
|
||||
|
||||
// members
|
||||
lightrecord_array data;
|
||||
};
|
||||
9
rendering/nullrenderer.cpp
Normal file
9
rendering/nullrenderer.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "stdafx.h"
|
||||
#include "nullrenderer.h"
|
||||
|
||||
std::unique_ptr<gfx_renderer> null_renderer::create_func()
|
||||
{
|
||||
return std::unique_ptr<null_renderer>(new null_renderer());
|
||||
}
|
||||
|
||||
bool null_renderer::renderer_register = gfx_renderer_factory::get_instance()->register_backend("null", null_renderer::create_func);
|
||||
162
rendering/nullrenderer.h
Normal file
162
rendering/nullrenderer.h
Normal file
@@ -0,0 +1,162 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Texture.h"
|
||||
#include "geometrybank.h"
|
||||
#include "material.h"
|
||||
#include "renderer.h"
|
||||
#include "stdafx.h"
|
||||
#include <glm/fwd.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
class null_geometrybank : public gfx::geometry_bank {
|
||||
public:
|
||||
// constructors:
|
||||
null_geometrybank() = default;
|
||||
// destructor
|
||||
~null_geometrybank() {};
|
||||
|
||||
private:
|
||||
// methods:
|
||||
// create() subclass details
|
||||
void
|
||||
create_( gfx::geometry_handle const &Geometry ) override {}
|
||||
// replace() subclass details
|
||||
void
|
||||
replace_( gfx::geometry_handle const &Geometry ) override {}
|
||||
// draw() subclass details
|
||||
auto
|
||||
draw_( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams ) -> std::size_t override { return 0; }
|
||||
// release() subclass details
|
||||
void
|
||||
release_() override {}
|
||||
};
|
||||
|
||||
// bare-bones render controller, in lack of anything better yet
|
||||
class null_renderer : public gfx_renderer {
|
||||
|
||||
public:
|
||||
// types
|
||||
// constructors
|
||||
null_renderer() = default;
|
||||
// destructor
|
||||
~null_renderer() { }
|
||||
// methods
|
||||
bool
|
||||
Init( GLFWwindow *Window ) override { return true; }
|
||||
// main draw call. returns false on error
|
||||
bool
|
||||
Render() override { return true; }
|
||||
void
|
||||
SwapBuffers() override {}
|
||||
inline
|
||||
float
|
||||
Framerate() override { return 10.0f; }
|
||||
|
||||
bool AddViewport(const global_settings::extraviewport_config &conf) override { return false; }
|
||||
bool Debug_Ui_State(std::optional<bool>) override { return false; }
|
||||
void Shutdown() override {}
|
||||
|
||||
// geometry methods
|
||||
// NOTE: hands-on geometry management is exposed as a temporary measure; ultimately all visualization data should be generated/handled automatically by the renderer itself
|
||||
// creates a new geometry bank. returns: handle to the bank or NULL
|
||||
gfx::geometrybank_handle
|
||||
Create_Bank() override { return m_geometry.register_bank(std::make_unique<null_geometrybank>()); }
|
||||
// creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
|
||||
gfx::geometry_handle Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override {
|
||||
return m_geometry.create_chunk( Indices, Vertices, Userdata, Geometry, Type ); }
|
||||
// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
|
||||
gfx::geometry_handle Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override {
|
||||
gfx::calculate_tangents(Vertices, gfx::index_array(), Type);
|
||||
|
||||
return m_geometry.create_chunk(Vertices, Userdata, Geometry, Type);
|
||||
}
|
||||
// replaces data of specified chunk with the supplied vertex data, starting from specified offset
|
||||
bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) override {
|
||||
gfx::calculate_tangents(Vertices, gfx::index_array(), Type);
|
||||
|
||||
return m_geometry.replace(Vertices, Userdata, Geometry, Offset);
|
||||
}
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override {
|
||||
gfx::calculate_tangents(Vertices, gfx::index_array(), Type);
|
||||
|
||||
return m_geometry.append(Vertices, Userdata, Geometry);
|
||||
}
|
||||
// provides direct access to index data of specfied chunk
|
||||
gfx::index_array const &
|
||||
Indices( gfx::geometry_handle const &Geometry ) const override { return m_geometry.indices(Geometry); }
|
||||
// provides direct access to vertex data of specfied chunk
|
||||
gfx::vertex_array const &
|
||||
Vertices( gfx::geometry_handle const &Geometry ) const override { return m_geometry.vertices(Geometry); }
|
||||
// provides direct access to vertex data of specfied chunk
|
||||
gfx::userdata_array const &
|
||||
UserData( gfx::geometry_handle const &Geometry ) const override { return m_geometry.userdata(Geometry); }
|
||||
// material methods
|
||||
material_handle
|
||||
Fetch_Material( std::string const &Filename, bool const Loadnow = true ) override {
|
||||
m_materials.push_back(std::make_shared<opengl_material>());
|
||||
m_materials.back()->name = Filename;
|
||||
return m_materials.size();
|
||||
}
|
||||
void
|
||||
Bind_Material( material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr ) override {}
|
||||
IMaterial const *
|
||||
Material( material_handle const Material ) const override { return m_materials.at(Material - 1).get(); }
|
||||
// shader methods
|
||||
auto Fetch_Shader( std::string const &name ) -> std::shared_ptr<gl::program> override { throw std::runtime_error("not impl"); }
|
||||
// texture methods
|
||||
texture_handle
|
||||
Fetch_Texture( std::string const &Filename, bool const Loadnow = true, GLint format_hint = GL_SRGB_ALPHA ) override { throw std::runtime_error("not impl"); }
|
||||
void
|
||||
Bind_Texture( texture_handle const Texture ) override {}
|
||||
void
|
||||
Bind_Texture( std::size_t const Unit, texture_handle const Texture ) override {}
|
||||
opengl_texture &
|
||||
Texture( texture_handle const Texture ) override { throw std::runtime_error("not impl"); }
|
||||
opengl_texture const &
|
||||
Texture( texture_handle const Texture ) const override { throw std::runtime_error("not impl"); }
|
||||
// utility methods
|
||||
void
|
||||
Pick_Control_Callback( std::function<void( TSubModel const *, const glm::vec2 )> Callback ) override {}
|
||||
void
|
||||
Pick_Node_Callback( std::function<void( scene::basic_node * )> Callback ) override {}
|
||||
TSubModel const *
|
||||
Pick_Control() const override { return nullptr; }
|
||||
scene::basic_node const *
|
||||
Pick_Node() const override { return nullptr; }
|
||||
glm::dvec3
|
||||
Mouse_Position() const override { return glm::dvec3(); }
|
||||
// maintenance methods
|
||||
void
|
||||
Update( double const Deltatime ) override {}
|
||||
void
|
||||
Update_Pick_Control() override {}
|
||||
void
|
||||
Update_Pick_Node() override {}
|
||||
glm::dvec3
|
||||
Update_Mouse_Position() override { return glm::dvec3(); }
|
||||
// debug methods
|
||||
std::string const &
|
||||
info_times() const override { return empty_str; }
|
||||
std::string const &
|
||||
info_stats() const override { return empty_str; }
|
||||
void MakeScreenshot() override {}
|
||||
|
||||
static std::unique_ptr<gfx_renderer> create_func();
|
||||
|
||||
static bool renderer_register;
|
||||
|
||||
virtual imgui_renderer *GetImguiRenderer()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string empty_str;
|
||||
gfx::geometrybank_manager m_geometry;
|
||||
std::vector<std::shared_ptr<opengl_material>> m_materials;
|
||||
};
|
||||
207
rendering/opengl33geometrybank.cpp
Normal file
207
rendering/opengl33geometrybank.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "opengl33geometrybank.h"
|
||||
|
||||
#include "Logs.h"
|
||||
|
||||
namespace gfx {
|
||||
|
||||
// opengl vao/vbo-based variant of the geometry bank
|
||||
|
||||
// create() subclass details
|
||||
void
|
||||
opengl33_vaogeometrybank::create_( gfx::geometry_handle const &Geometry ) {
|
||||
// adding a chunk means we'll be (re)building the buffer, which will fill the chunk records, amongst other things.
|
||||
// thus we don't need to initialize the values here
|
||||
m_chunkrecords.emplace_back( chunk_record() );
|
||||
// kiss the existing buffer goodbye, new overall data size means we'll be making a new one
|
||||
delete_buffer();
|
||||
}
|
||||
|
||||
// replace() subclass details
|
||||
void
|
||||
opengl33_vaogeometrybank::replace_( gfx::geometry_handle const &Geometry ) {
|
||||
|
||||
auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
|
||||
chunkrecord.is_good = false;
|
||||
// if the overall length of the chunk didn't change we can get away with reusing the old buffer...
|
||||
if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.vertex_count ) {
|
||||
// ...but otherwise we'll need to allocate a new one
|
||||
// TBD: we could keep and reuse the old buffer also if the new chunk is smaller than the old one,
|
||||
// but it'd require some extra tracking and work to keep all chunks up to date; also wasting vram; may be not worth it?
|
||||
delete_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
void opengl33_vaogeometrybank::setup_buffer()
|
||||
{
|
||||
if( m_vertexbuffer ) { return; }
|
||||
// if there's no buffer, we'll have to make one
|
||||
// NOTE: this isn't exactly optimal in terms of ensuring the gfx card doesn't stall waiting for the data
|
||||
// may be better to initiate upload earlier (during update phase) and trust this effort won't go to waste
|
||||
if( true == m_chunks.empty() ) { return; }
|
||||
|
||||
std::size_t
|
||||
vertexcount{ 0 },
|
||||
indexcount{ 0 };
|
||||
auto chunkiterator = m_chunks.cbegin();
|
||||
bool has_userdata{ false };
|
||||
for( auto &chunkrecord : m_chunkrecords ) {
|
||||
// fill records for all chunks, based on the chunk data
|
||||
chunkrecord.is_good = false; // if we're re-creating buffer, chunks might've been uploaded in the old one
|
||||
chunkrecord.vertex_offset = vertexcount;
|
||||
chunkrecord.vertex_count = chunkiterator->vertices.size();
|
||||
vertexcount += chunkrecord.vertex_count;
|
||||
chunkrecord.index_offset = indexcount;
|
||||
chunkrecord.index_count = chunkiterator->indices.size();
|
||||
chunkrecord.has_userdata = !chunkiterator->userdata.empty();
|
||||
indexcount += chunkrecord.index_count;
|
||||
has_userdata |= chunkrecord.has_userdata;
|
||||
++chunkiterator;
|
||||
}
|
||||
// the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist
|
||||
if( vertexcount == 0 ) { return; }
|
||||
|
||||
if( !m_vao ) {
|
||||
m_vao.emplace();
|
||||
}
|
||||
m_vao->bind();
|
||||
// try to set up the buffers we need:
|
||||
// optional index buffer...
|
||||
if( indexcount > 0 ) {
|
||||
m_indexbuffer.emplace();
|
||||
m_indexbuffer->allocate( gl::buffer::ELEMENT_ARRAY_BUFFER, indexcount * sizeof( gfx::basic_index ), GL_STATIC_DRAW );
|
||||
if( ::glGetError() == GL_OUT_OF_MEMORY ) {
|
||||
ErrorLog( "openGL error: out of memory; failed to create a geometry index buffer" );
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
m_vao->setup_ebo( *m_indexbuffer );
|
||||
}
|
||||
else {
|
||||
gl::buffer::unbind( gl::buffer::ELEMENT_ARRAY_BUFFER );
|
||||
}
|
||||
// ...and geometry buffer
|
||||
m_vertexbuffer.emplace();
|
||||
// NOTE: we're using static_draw since it's generally true for all we have implemented at the moment
|
||||
// TODO: allow to specify usage hint at the object creation, and pass it here
|
||||
const int vertex_size = has_userdata ? sizeof( gfx::basic_vertex ) + sizeof(gfx::vertex_userdata) : sizeof(gfx::basic_vertex);
|
||||
m_vertexbuffer->allocate( gl::buffer::ARRAY_BUFFER, static_cast<int>(vertexcount * vertex_size), GL_STATIC_DRAW );
|
||||
if( ::glGetError() == GL_OUT_OF_MEMORY ) {
|
||||
ErrorLog( "openGL error: out of memory; failed to create a geometry buffer" );
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
m_vertex_count = static_cast<int>(vertexcount);
|
||||
|
||||
setup_attrib();
|
||||
if(has_userdata)
|
||||
setup_userdata();
|
||||
}
|
||||
|
||||
void
|
||||
opengl33_vaogeometrybank::setup_attrib(size_t offset)
|
||||
{
|
||||
m_vao->setup_attrib( *m_vertexbuffer, 0, 3, GL_FLOAT, sizeof( basic_vertex ), offsetof(basic_vertex, position) + offset * sizeof( basic_vertex ) );
|
||||
// NOTE: normal and color streams share the data
|
||||
m_vao->setup_attrib( *m_vertexbuffer, 1, 3, GL_FLOAT, sizeof( basic_vertex ), offsetof(basic_vertex, normal) + offset * sizeof( basic_vertex ) );
|
||||
m_vao->setup_attrib( *m_vertexbuffer, 2, 2, GL_FLOAT, sizeof( basic_vertex ), offsetof(basic_vertex, texture) + offset * sizeof( basic_vertex ) );
|
||||
m_vao->setup_attrib( *m_vertexbuffer, 3, 4, GL_FLOAT, sizeof( basic_vertex ), offsetof(basic_vertex, tangent) + offset * sizeof( basic_vertex ) );
|
||||
}
|
||||
|
||||
void opengl33_vaogeometrybank::setup_userdata(size_t offset) {
|
||||
const size_t offset_evaluated = m_vertex_count * sizeof(basic_vertex) + offset * sizeof( vertex_userdata );
|
||||
m_vao->setup_attrib( *m_vertexbuffer, 4, 4, GL_FLOAT, sizeof( vertex_userdata ), offsetof(vertex_userdata, data) + offset_evaluated );}
|
||||
|
||||
// draw() subclass details
|
||||
// NOTE: units and stream parameters are unused, but they're part of (legacy) interface
|
||||
// TBD: specialized bank/manager pair without the cruft?
|
||||
std::size_t
|
||||
opengl33_vaogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams )
|
||||
{
|
||||
setup_buffer();
|
||||
|
||||
auto &chunkrecord = m_chunkrecords.at(Geometry.chunk - 1);
|
||||
// sanity check; shouldn't be needed but, eh
|
||||
if( chunkrecord.vertex_count == 0 )
|
||||
return 0;
|
||||
|
||||
auto const &chunk = gfx::geometry_bank::chunk( Geometry );
|
||||
if( !chunkrecord.is_good ) {
|
||||
m_vao->bind();
|
||||
// we may potentially need to upload new buffer data before we can draw it
|
||||
if( chunkrecord.index_count > 0 ) {
|
||||
m_indexbuffer->upload( gl::buffer::ELEMENT_ARRAY_BUFFER, chunk.indices.data(), chunkrecord.index_offset * sizeof( gfx::basic_index ), chunkrecord.index_count * sizeof( gfx::basic_index ) );
|
||||
}
|
||||
m_vertexbuffer->upload( gl::buffer::ARRAY_BUFFER, chunk.vertices.data(), chunkrecord.vertex_offset * sizeof( gfx::basic_vertex ), chunkrecord.vertex_count * sizeof( gfx::basic_vertex ) );
|
||||
if(chunkrecord.has_userdata)
|
||||
m_vertexbuffer->upload(gl::buffer::ARRAY_BUFFER, chunk.userdata.data(),
|
||||
static_cast<int>(m_vertex_count * sizeof(gfx::basic_vertex) + chunkrecord.vertex_offset * sizeof(gfx::vertex_userdata)),
|
||||
static_cast<int>(chunkrecord.vertex_count * sizeof(gfx::vertex_userdata)));
|
||||
chunkrecord.is_good = true;
|
||||
}
|
||||
// render
|
||||
if( chunkrecord.index_count > 0 ) {
|
||||
if (glDrawRangeElementsBaseVertex) {
|
||||
m_vao->bind();
|
||||
::glDrawRangeElementsBaseVertex(
|
||||
chunk.type,
|
||||
0, chunkrecord.vertex_count,
|
||||
chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast<void const *>( chunkrecord.index_offset * sizeof( gfx::basic_index ) ),
|
||||
chunkrecord.vertex_offset );
|
||||
}
|
||||
else if (glDrawElementsBaseVertexOES) {
|
||||
m_vao->bind();
|
||||
::glDrawElementsBaseVertexOES(
|
||||
chunk.type,
|
||||
chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast<void const *>( chunkrecord.index_offset * sizeof( gfx::basic_index ) ),
|
||||
chunkrecord.vertex_offset );
|
||||
}
|
||||
else {
|
||||
setup_attrib(chunkrecord.vertex_offset);
|
||||
m_vao->bind();
|
||||
::glDrawRangeElements(
|
||||
chunk.type,
|
||||
0, chunkrecord.vertex_count,
|
||||
chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast<void const *>( chunkrecord.index_offset * sizeof( gfx::basic_index ) ) );
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_vao->bind();
|
||||
::glDrawArrays( chunk.type, chunkrecord.vertex_offset, chunkrecord.vertex_count );
|
||||
}
|
||||
/*
|
||||
m_vao->unbind();
|
||||
*/
|
||||
auto const vertexcount { ( chunkrecord.index_count > 0 ? chunkrecord.index_count : chunkrecord.vertex_count ) };
|
||||
switch( chunk.type ) {
|
||||
case GL_TRIANGLES: { return vertexcount / 3; }
|
||||
case GL_TRIANGLE_STRIP: { return vertexcount - 2; }
|
||||
default: { return 0; }
|
||||
}
|
||||
}
|
||||
|
||||
// release () subclass details
|
||||
void
|
||||
opengl33_vaogeometrybank::release_() {
|
||||
|
||||
delete_buffer();
|
||||
}
|
||||
|
||||
void
|
||||
opengl33_vaogeometrybank::delete_buffer() {
|
||||
|
||||
m_vao.reset();
|
||||
m_vertexbuffer.reset();
|
||||
m_indexbuffer.reset();
|
||||
// NOTE: since we've deleted the buffer all chunks it held were rendered invalid as well
|
||||
// instead of clearing their state here we're delaying it until new buffer is created to avoid looping through chunk records twice
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
76
rendering/opengl33geometrybank.h
Normal file
76
rendering/opengl33geometrybank.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "geometrybank.h"
|
||||
#include "gl/buffer.h"
|
||||
#include "gl/vao.h"
|
||||
|
||||
namespace gfx {
|
||||
|
||||
// opengl vao/vbo-based variant of the geometry bank
|
||||
|
||||
class opengl33_vaogeometrybank : public geometry_bank {
|
||||
|
||||
public:
|
||||
// constructors:
|
||||
opengl33_vaogeometrybank() = default;
|
||||
// destructor
|
||||
~opengl33_vaogeometrybank() {
|
||||
delete_buffer(); }
|
||||
// methods:
|
||||
static
|
||||
void
|
||||
reset() {;}
|
||||
|
||||
private:
|
||||
// types:
|
||||
struct chunk_record {
|
||||
std::size_t vertex_offset{ 0 }; // beginning of the chunk vertex data as offset from the beginning of the last established buffer
|
||||
std::size_t vertex_count{ 0 }; // size of the chunk in the last established buffer
|
||||
std::size_t index_offset{ 0 };
|
||||
std::size_t index_count{ 0 };
|
||||
bool has_userdata{ false };
|
||||
bool is_good{ false }; // true if local content of the chunk matches the data on the opengl end
|
||||
};
|
||||
|
||||
typedef std::vector<chunk_record> chunkrecord_sequence;
|
||||
|
||||
// methods:
|
||||
// create() subclass details
|
||||
void
|
||||
create_( gfx::geometry_handle const &Geometry ) override;
|
||||
// replace() subclass details
|
||||
void
|
||||
replace_( gfx::geometry_handle const &Geometry ) override;
|
||||
// draw() subclass details
|
||||
auto
|
||||
draw_( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams ) -> std::size_t override;
|
||||
// release() subclass details
|
||||
void
|
||||
release_() override;
|
||||
void
|
||||
setup_buffer();
|
||||
void
|
||||
setup_attrib(size_t offset = 0);
|
||||
void
|
||||
setup_userdata(size_t offset = 0);
|
||||
void
|
||||
delete_buffer();
|
||||
|
||||
// members:
|
||||
std::optional<gl::buffer> m_vertexbuffer; // vertex buffer data on the opengl end
|
||||
std::optional<gl::buffer> m_indexbuffer; // index buffer data on the opengl end
|
||||
std::optional<gl::vao> m_vao;
|
||||
chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order
|
||||
int m_vertex_count;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
22
rendering/opengl33light.cpp
Normal file
22
rendering/opengl33light.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "opengl33light.h"
|
||||
|
||||
void opengl33_light::apply_intensity(float const Factor) {
|
||||
|
||||
factor = Factor;
|
||||
}
|
||||
|
||||
void opengl33_light::apply_angle() {}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
28
rendering/opengl33light.h
Normal file
28
rendering/opengl33light.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "light.h"
|
||||
|
||||
struct opengl33_light : public basic_light {
|
||||
|
||||
GLuint id{(GLuint)-1};
|
||||
|
||||
float factor;
|
||||
|
||||
void apply_intensity(float const Factor = 1.0f);
|
||||
void apply_angle();
|
||||
|
||||
opengl33_light &operator=(basic_light const &Right) {
|
||||
basic_light::operator=(Right);
|
||||
return *this; }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
140
rendering/opengl33particles.cpp
Normal file
140
rendering/opengl33particles.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "opengl33particles.h"
|
||||
|
||||
#include "particles.h"
|
||||
#include "openglcamera.h"
|
||||
#include "simulation.h"
|
||||
#include "simulationenvironment.h"
|
||||
|
||||
std::vector<std::pair<glm::vec3, glm::vec2>> const billboard_vertices {
|
||||
|
||||
{ { -0.5f, -0.5f, 0.f }, { 0.f, 0.f } },
|
||||
{ { 0.5f, -0.5f, 0.f }, { 1.f, 0.f } },
|
||||
{ { 0.5f, 0.5f, 0.f }, { 1.f, 1.f } },
|
||||
{ { -0.5f, -0.5f, 0.f }, { 0.f, 0.f } },
|
||||
{ { 0.5f, 0.5f, 0.f }, { 1.f, 1.f } },
|
||||
{ { -0.5f, 0.5f, 0.f }, { 0.f, 1.f } },
|
||||
};
|
||||
|
||||
void
|
||||
opengl33_particles::update( opengl_camera const &Camera ) {
|
||||
|
||||
if (!Global.Smoke)
|
||||
return;
|
||||
|
||||
m_particlevertices.clear();
|
||||
// build a list of visible smoke sources
|
||||
// NOTE: arranged by distance to camera, if we ever need sorting and/or total amount cap-based culling
|
||||
std::multimap<float, smoke_source const &> sources;
|
||||
|
||||
for( auto const &source : simulation::Particles.sequence() ) {
|
||||
if( false == Camera.visible( source.area() ) ) { continue; }
|
||||
// NOTE: the distance is negative when the camera is inside the source's bounding area
|
||||
sources.emplace(
|
||||
static_cast<float>( glm::length( Camera.position() - source.area().center ) - source.area().radius ),
|
||||
source );
|
||||
}
|
||||
|
||||
if( true == sources.empty() ) { return; }
|
||||
|
||||
// build billboard data for particles from visible sources
|
||||
auto const camerarotation { glm::mat3( Camera.modelview() ) };
|
||||
particle_vertex vertex;
|
||||
for( auto const &source : sources ) {
|
||||
|
||||
auto const particlecolor {
|
||||
glm::clamp(
|
||||
source.second.color()
|
||||
* ( glm::vec3 { Global.DayLight.ambient }
|
||||
+ 0.35f * glm::vec3{ Global.DayLight.diffuse } ) * simulation::Environment.light_intensity(),
|
||||
glm::vec3{ 0.f }, glm::vec3{ 1.f } ) };
|
||||
auto const &particles { source.second.sequence() };
|
||||
// TODO: put sanity cap on the overall amount of particles that can be drawn
|
||||
auto const sizestep { 256.0 * billboard_vertices.size() };
|
||||
m_particlevertices.reserve(
|
||||
sizestep * std::ceil( m_particlevertices.size() + ( particles.size() * billboard_vertices.size() ) / sizestep ) );
|
||||
for( auto const &particle : particles ) {
|
||||
// TODO: particle color support
|
||||
vertex.color[ 0 ] = particlecolor.r;
|
||||
vertex.color[ 1 ] = particlecolor.g;
|
||||
vertex.color[ 2 ] = particlecolor.b;
|
||||
vertex.color.a = clamp(particle.opacity, 0.0f, 1.0f);
|
||||
|
||||
auto const offset { glm::vec3{ particle.position - Camera.position() } };
|
||||
auto const rotation { glm::angleAxis( particle.rotation, glm::vec3{ 0.f, 0.f, 1.f } ) };
|
||||
|
||||
for( auto const &billboardvertex : billboard_vertices ) {
|
||||
vertex.position = offset + ( rotation * billboardvertex.first * particle.size ) * camerarotation;
|
||||
vertex.texture = billboardvertex.second;
|
||||
|
||||
m_particlevertices.emplace_back( vertex );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ship the billboard data to the gpu:
|
||||
// make sure we have enough room...
|
||||
if( m_buffercapacity < m_particlevertices.size() ) {
|
||||
m_buffercapacity = m_particlevertices.size();
|
||||
if (!m_buffer)
|
||||
m_buffer.emplace();
|
||||
|
||||
m_buffer->allocate(gl::buffer::ARRAY_BUFFER,
|
||||
m_buffercapacity * sizeof(particle_vertex), GL_STREAM_DRAW);
|
||||
}
|
||||
|
||||
if (m_buffer) {
|
||||
// ...send the data...
|
||||
m_buffer->upload(gl::buffer::ARRAY_BUFFER,
|
||||
m_particlevertices.data(), 0, m_particlevertices.size() * sizeof(particle_vertex));
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t
|
||||
opengl33_particles::render() {
|
||||
|
||||
if( false == Global.Smoke ) { return 0; }
|
||||
if( m_buffercapacity == 0 ) { return 0; }
|
||||
if( m_particlevertices.empty() ) { return 0; }
|
||||
|
||||
if (!m_vao) {
|
||||
m_vao.emplace();
|
||||
|
||||
m_vao->setup_attrib(*m_buffer, 0, 3, GL_FLOAT, sizeof(particle_vertex), 0);
|
||||
m_vao->setup_attrib(*m_buffer, 1, 4, GL_FLOAT, sizeof(particle_vertex), 12);
|
||||
m_vao->setup_attrib(*m_buffer, 2, 2, GL_FLOAT, sizeof(particle_vertex), 28);
|
||||
|
||||
m_buffer->unbind(gl::buffer::ARRAY_BUFFER);
|
||||
m_vao->unbind();
|
||||
}
|
||||
|
||||
if (!m_shader) {
|
||||
gl::shader vert("smoke.vert");
|
||||
gl::shader frag("smoke.frag");
|
||||
gl::program *prog = new gl::program({vert, frag});
|
||||
m_shader = std::unique_ptr<gl::program>(prog);
|
||||
}
|
||||
|
||||
m_buffer->bind(gl::buffer::ARRAY_BUFFER);
|
||||
m_shader->bind();
|
||||
m_vao->bind();
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, m_particlevertices.size());
|
||||
|
||||
m_shader->unbind();
|
||||
m_vao->unbind();
|
||||
m_buffer->unbind(gl::buffer::ARRAY_BUFFER);
|
||||
|
||||
return m_particlevertices.size() / 6;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
54
rendering/opengl33particles.h
Normal file
54
rendering/opengl33particles.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gl/buffer.h"
|
||||
#include "gl/vao.h"
|
||||
#include "gl/shader.h"
|
||||
|
||||
class opengl_camera;
|
||||
|
||||
// particle data visualizer
|
||||
class opengl33_particles {
|
||||
public:
|
||||
// constructors
|
||||
opengl33_particles() = default;
|
||||
|
||||
// methods
|
||||
void
|
||||
update( opengl_camera const &Camera );
|
||||
std::size_t
|
||||
render( );
|
||||
private:
|
||||
// types
|
||||
struct particle_vertex {
|
||||
glm::vec3 position; // 3d space
|
||||
glm::vec4 color; // rgba, unsigned byte format
|
||||
glm::vec2 texture; // uv space
|
||||
};
|
||||
/*
|
||||
using sourcedistance_pair = std::pair<smoke_source *, float>;
|
||||
using source_sequence = std::vector<sourcedistance_pair>;
|
||||
*/
|
||||
using particlevertex_sequence = std::vector<particle_vertex>;
|
||||
// methods
|
||||
// members
|
||||
/*
|
||||
source_sequence m_sources; // list of particle sources visible in current render pass, with their respective distances to the camera
|
||||
*/
|
||||
particlevertex_sequence m_particlevertices; // geometry data of visible particles, generated on the cpu end
|
||||
std::optional<gl::buffer> m_buffer;
|
||||
std::optional<gl::vao> m_vao;
|
||||
std::unique_ptr<gl::program> m_shader;
|
||||
|
||||
std::size_t m_buffercapacity{ 0 }; // total capacity of the last established buffer
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
159
rendering/opengl33precipitation.cpp
Normal file
159
rendering/opengl33precipitation.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "opengl33precipitation.h"
|
||||
|
||||
#include "Globals.h"
|
||||
#include "renderer.h"
|
||||
#include "simulationenvironment.h"
|
||||
|
||||
opengl33_precipitation::~opengl33_precipitation() {
|
||||
// TODO: release allocated resources
|
||||
}
|
||||
|
||||
void
|
||||
opengl33_precipitation::create( int const Tesselation ) {
|
||||
|
||||
m_vertices.clear();
|
||||
m_uvs.clear();
|
||||
m_indices.clear();
|
||||
|
||||
auto const heightfactor { 10.f }; // height-to-radius factor
|
||||
auto const verticaltexturestretchfactor { 1.5f }; // crude motion blur
|
||||
|
||||
// create geometry chunk
|
||||
auto const latitudes { 3 }; // just a cylinder with end cones
|
||||
auto const longitudes { Tesselation };
|
||||
auto const longitudehalfstep { 0.5f * static_cast<float>( 2.0 * M_PI * 1.f / longitudes ) }; // for crude uv correction
|
||||
|
||||
std::uint16_t index = 0;
|
||||
|
||||
// auto const radius { 25.f }; // cylinder radius
|
||||
std::vector<float> radii { 25.f, 10.f, 5.f, 1.f };
|
||||
for( auto radius : radii ) {
|
||||
|
||||
for( int i = 0; i <= latitudes; ++i ) {
|
||||
|
||||
auto const latitude{ static_cast<float>( M_PI * ( -0.5f + (float)( i ) / latitudes ) ) };
|
||||
auto const z{ std::sin( latitude ) };
|
||||
auto const zr{ std::cos( latitude ) };
|
||||
|
||||
for( int j = 0; j <= longitudes; ++j ) {
|
||||
// NOTE: for the first and last row half of the points we create end up unused but, eh
|
||||
auto const longitude{ static_cast<float>( 2.0 * M_PI * (float)( j ) / longitudes ) };
|
||||
auto const x{ std::cos( longitude ) };
|
||||
auto const y{ std::sin( longitude ) };
|
||||
// NOTE: cartesian to opengl swap would be: -x, -z, -y
|
||||
m_vertices.emplace_back( glm::vec3( -x * zr, -z * heightfactor, -y * zr ) * radius );
|
||||
// uvs
|
||||
// NOTE: first and last row receives modified u values to deal with limitation of mapping onto triangles
|
||||
auto u = (
|
||||
i == 0 ? longitude + longitudehalfstep :
|
||||
i == latitudes ? longitude - longitudehalfstep :
|
||||
longitude );
|
||||
m_uvs.emplace_back(
|
||||
u / ( 2.0 * M_PI ) * radius,
|
||||
1.f - (float)( i ) / latitudes * radius * heightfactor * 0.5f / verticaltexturestretchfactor );
|
||||
|
||||
if( ( i == 0 ) || ( j == 0 ) ) {
|
||||
// initial edge of the dome, don't start indices yet
|
||||
++index;
|
||||
}
|
||||
else {
|
||||
// the end cones are built from one triangle of each quad, the middle rows use both
|
||||
if( i < latitudes ) {
|
||||
m_indices.emplace_back( index - 1 - ( longitudes + 1 ) );
|
||||
m_indices.emplace_back( index - 1 );
|
||||
m_indices.emplace_back( index );
|
||||
}
|
||||
if( i > 1 ) {
|
||||
m_indices.emplace_back( index );
|
||||
m_indices.emplace_back( index - ( longitudes + 1 ) );
|
||||
m_indices.emplace_back( index - 1 - ( longitudes + 1 ) );
|
||||
}
|
||||
++index;
|
||||
}
|
||||
} // longitude
|
||||
} // latitude
|
||||
} // radius
|
||||
}
|
||||
|
||||
void
|
||||
opengl33_precipitation::update() {
|
||||
|
||||
if (!m_shader)
|
||||
{
|
||||
gl::shader vert("precipitation.vert");
|
||||
gl::shader frag("precipitation.frag");
|
||||
m_shader.emplace(std::vector<std::reference_wrapper<const gl::shader>>({vert, frag}));
|
||||
}
|
||||
|
||||
if (!m_vertexbuffer) {
|
||||
m_vao.emplace();
|
||||
m_vao->bind();
|
||||
|
||||
if( m_vertices.empty() ) {
|
||||
// create visualization mesh
|
||||
create( 18 );
|
||||
}
|
||||
// build the buffers
|
||||
m_vertexbuffer.emplace();
|
||||
m_vertexbuffer->allocate(gl::buffer::ARRAY_BUFFER, m_vertices.size() * sizeof( glm::vec3 ), GL_STATIC_DRAW);
|
||||
m_vertexbuffer->upload(gl::buffer::ARRAY_BUFFER, m_vertices.data(), 0, m_vertices.size() * sizeof( glm::vec3 ));
|
||||
|
||||
m_vao->setup_attrib(*m_vertexbuffer, 0, 3, GL_FLOAT, sizeof(glm::vec3), 0);
|
||||
|
||||
m_uvbuffer.emplace();
|
||||
m_uvbuffer->allocate(gl::buffer::ARRAY_BUFFER, m_uvs.size() * sizeof( glm::vec2 ), GL_STATIC_DRAW);
|
||||
m_uvbuffer->upload(gl::buffer::ARRAY_BUFFER, m_uvs.data(), 0, m_uvs.size() * sizeof( glm::vec2 ));
|
||||
|
||||
m_vao->setup_attrib(*m_uvbuffer, 1, 2, GL_FLOAT, sizeof(glm::vec2), 0);
|
||||
|
||||
m_indexbuffer.emplace();
|
||||
m_indexbuffer->allocate(gl::buffer::ELEMENT_ARRAY_BUFFER, m_indices.size() * sizeof( unsigned short ), GL_STATIC_DRAW);
|
||||
m_indexbuffer->upload(gl::buffer::ELEMENT_ARRAY_BUFFER, m_indices.data(), 0, m_indices.size() * sizeof( unsigned short ));
|
||||
m_vao->setup_ebo(*m_indexbuffer);
|
||||
|
||||
m_vao->unbind();
|
||||
// NOTE: vertex and index source data is superfluous past this point, but, eh
|
||||
}
|
||||
|
||||
// TODO: include weather type check in the entry conditions
|
||||
if( m_overcast == Global.Overcast ) { return; }
|
||||
|
||||
m_overcast = Global.Overcast;
|
||||
|
||||
std::string const densitysuffix { (
|
||||
m_overcast < 1.35 ?
|
||||
"_light" :
|
||||
"_medium" ) };
|
||||
if( Global.Weather == "rain:" ) {
|
||||
m_texture = GfxRenderer->Fetch_Texture( "fx/rain" + densitysuffix );
|
||||
}
|
||||
else if( Global.Weather == "snow:" ) {
|
||||
m_texture = GfxRenderer->Fetch_Texture( "fx/snow" + densitysuffix );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
opengl33_precipitation::render() {
|
||||
|
||||
if( !m_shader ) { return; }
|
||||
if( !m_vao ) { return; }
|
||||
|
||||
GfxRenderer->Bind_Texture( 0, m_texture );
|
||||
|
||||
m_shader->bind();
|
||||
m_vao->bind();
|
||||
|
||||
::glDrawElements( GL_TRIANGLES, static_cast<GLsizei>( m_indices.size() ), GL_UNSIGNED_SHORT, reinterpret_cast<void const*>( 0 ) );
|
||||
|
||||
m_vao->unbind();
|
||||
}
|
||||
43
rendering/opengl33precipitation.h
Normal file
43
rendering/opengl33precipitation.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Texture.h"
|
||||
#include "gl/vao.h"
|
||||
#include "gl/shader.h"
|
||||
|
||||
class opengl33_precipitation {
|
||||
|
||||
public:
|
||||
// constructors
|
||||
opengl33_precipitation() = default;
|
||||
// destructor
|
||||
~opengl33_precipitation();
|
||||
// methods
|
||||
void
|
||||
update();
|
||||
void
|
||||
render();
|
||||
|
||||
private:
|
||||
// methods
|
||||
void create( int const Tesselation );
|
||||
// members
|
||||
std::vector<glm::vec3> m_vertices;
|
||||
std::vector<glm::vec2> m_uvs;
|
||||
std::vector<std::uint16_t> m_indices;
|
||||
texture_handle m_texture { -1 };
|
||||
float m_overcast { -1.f }; // cached overcast level, difference from current state triggers texture update
|
||||
std::optional<gl::buffer> m_vertexbuffer;
|
||||
std::optional<gl::buffer> m_uvbuffer;
|
||||
std::optional<gl::buffer> m_indexbuffer;
|
||||
std::optional<gl::program> m_shader;
|
||||
std::optional<gl::vao> m_vao;
|
||||
};
|
||||
4894
rendering/opengl33renderer.cpp
Normal file
4894
rendering/opengl33renderer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
465
rendering/opengl33renderer.h
Normal file
465
rendering/opengl33renderer.h
Normal file
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "renderer.h"
|
||||
#include "openglcamera.h"
|
||||
#include "opengl33light.h"
|
||||
#include "opengl33particles.h"
|
||||
#include "opengl33skydome.h"
|
||||
#include "opengl33precipitation.h"
|
||||
#include "simulationenvironment.h"
|
||||
#include "scene.h"
|
||||
#include "MemCell.h"
|
||||
#include "lightarray.h"
|
||||
#include "vr/vr_interface.h"
|
||||
#include "gl/ubo.h"
|
||||
#include "gl/framebuffer.h"
|
||||
#include "gl/renderbuffer.h"
|
||||
#include "gl/postfx.h"
|
||||
#include "gl/shader.h"
|
||||
#include "gl/cubemap.h"
|
||||
#include "gl/glsl_common.h"
|
||||
#include "gl/pbo.h"
|
||||
#include "gl/query.h"
|
||||
|
||||
// bare-bones render controller, in lack of anything better yet
|
||||
class opengl33_renderer : public gfx_renderer {
|
||||
public:
|
||||
// constructors
|
||||
opengl33_renderer() = default;
|
||||
// destructor
|
||||
~opengl33_renderer() {}
|
||||
// methods
|
||||
bool
|
||||
Init( GLFWwindow *Window ) override;
|
||||
void
|
||||
Shutdown() override;
|
||||
bool
|
||||
AddViewport(const global_settings::extraviewport_config &conf) override;
|
||||
// main draw call. returns false on error
|
||||
bool
|
||||
Render() override;
|
||||
void
|
||||
SwapBuffers() override;
|
||||
inline
|
||||
float
|
||||
Framerate() override { return m_framerate; }
|
||||
// geometry methods
|
||||
// NOTE: hands-on geometry management is exposed as a temporary measure; ultimately all visualization data should be generated/handled automatically by the renderer itself
|
||||
// creates a new geometry bank. returns: handle to the bank or NULL
|
||||
gfx::geometrybank_handle
|
||||
Create_Bank() override;
|
||||
// creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
|
||||
gfx::geometry_handle Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override;
|
||||
// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
|
||||
gfx::geometry_handle Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override;
|
||||
// replaces data of specified chunk with the supplied vertex data, starting from specified offset
|
||||
bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) override;
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override;
|
||||
// provides direct access to index data of specfied chunk
|
||||
gfx::index_array const &
|
||||
Indices( gfx::geometry_handle const &Geometry ) const override;
|
||||
// provides direct access to vertex data of specfied chunk
|
||||
gfx::vertex_array const &
|
||||
Vertices( gfx::geometry_handle const &Geometry ) const override;
|
||||
// provides direct access to vertex data of specfied chunk
|
||||
gfx::userdata_array const &
|
||||
UserData( gfx::geometry_handle const &Geometry ) const override;
|
||||
// material methods
|
||||
material_handle
|
||||
Fetch_Material( std::string const &Filename, bool const Loadnow = true ) override;
|
||||
void
|
||||
Bind_Material( material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr ) override;
|
||||
IMaterial const *
|
||||
Material( material_handle const Material ) const override;
|
||||
// shader methods
|
||||
auto Fetch_Shader( std::string const &name ) -> std::shared_ptr<gl::program> override;
|
||||
// texture methods
|
||||
texture_handle
|
||||
Fetch_Texture( std::string const &Filename, bool const Loadnow = true, GLint format_hint = GL_SRGB_ALPHA ) override;
|
||||
void
|
||||
Bind_Texture( texture_handle const Texture ) override;
|
||||
void
|
||||
Bind_Texture( std::size_t const Unit, texture_handle const Texture ) override;
|
||||
ITexture &
|
||||
Texture( texture_handle const Texture ) override;
|
||||
ITexture const &
|
||||
Texture( texture_handle const Texture ) const override;
|
||||
// utility methods
|
||||
void
|
||||
Pick_Control_Callback( std::function<void( TSubModel const *, const glm::vec2 )> Callback ) override;
|
||||
void
|
||||
Pick_Node_Callback( std::function<void( scene::basic_node * )> Callback ) override;
|
||||
TSubModel const *
|
||||
Pick_Control() const override { return m_pickcontrolitem; }
|
||||
scene::basic_node const *
|
||||
Pick_Node() const override { return m_picksceneryitem; }
|
||||
glm::dvec3
|
||||
Mouse_Position() const override { return m_worldmousecoordinates; }
|
||||
// maintenance methods
|
||||
void
|
||||
Update( double const Deltatime ) override;
|
||||
bool
|
||||
Debug_Ui_State(std::optional<bool>) override;
|
||||
void
|
||||
Update_Pick_Control() override;
|
||||
void
|
||||
Update_Pick_Node() override;
|
||||
glm::dvec3
|
||||
Update_Mouse_Position() override;
|
||||
// debug methods
|
||||
std::string const &
|
||||
info_times() const override;
|
||||
std::string const &
|
||||
info_stats() const override;
|
||||
void MakeScreenshot() override;
|
||||
|
||||
|
||||
|
||||
opengl_material & Material( material_handle const Material );
|
||||
opengl_material const & Material( TSubModel const * Submodel ) const;
|
||||
// draws supplied geometry handles
|
||||
void Draw_Geometry(std::vector<gfx::geometrybank_handle>::iterator begin, std::vector<gfx::geometrybank_handle>::iterator end);
|
||||
void Draw_Geometry(const gfx::geometrybank_handle &handle);
|
||||
// material methods
|
||||
void Bind_Material_Shadow(material_handle const Material);
|
||||
void Update_AnimModel(TAnimModel *model);
|
||||
|
||||
// members
|
||||
GLenum static const sunlight{0};
|
||||
|
||||
static std::unique_ptr<gfx_renderer> create_func();
|
||||
|
||||
private:
|
||||
// types
|
||||
enum class rendermode
|
||||
{
|
||||
none,
|
||||
color,
|
||||
shadows,
|
||||
reflections,
|
||||
pickcontrols,
|
||||
pickscenery
|
||||
};
|
||||
|
||||
struct debug_stats
|
||||
{
|
||||
int dynamics{0};
|
||||
int models{0};
|
||||
int submodels{0};
|
||||
int paths{0};
|
||||
int traction{0};
|
||||
int shapes{0};
|
||||
int lines{0};
|
||||
int particles{0};
|
||||
int drawcalls{0};
|
||||
int triangles{0};
|
||||
|
||||
debug_stats& operator+=( const debug_stats& Right ) {
|
||||
dynamics += Right.dynamics;
|
||||
models += Right.models;
|
||||
submodels += Right.submodels;
|
||||
paths += Right.paths;
|
||||
traction += Right.traction;
|
||||
shapes += Right.shapes;
|
||||
lines += Right.lines;
|
||||
particles += Right.particles;
|
||||
drawcalls += Right.drawcalls;
|
||||
return *this; }
|
||||
};
|
||||
|
||||
using section_sequence = std::vector<scene::basic_section *>;
|
||||
using distancecell_pair = std::pair<double, scene::basic_cell *>;
|
||||
using cell_sequence = std::vector<distancecell_pair>;
|
||||
|
||||
struct renderpass_config
|
||||
{
|
||||
opengl_camera pass_camera;
|
||||
opengl_camera viewport_camera;
|
||||
rendermode draw_mode{rendermode::none};
|
||||
float draw_range{0.0f};
|
||||
debug_stats draw_stats;
|
||||
};
|
||||
|
||||
struct viewport_config {
|
||||
int width;
|
||||
int height;
|
||||
|
||||
float draw_range;
|
||||
|
||||
bool main = false;
|
||||
|
||||
GLFWwindow *window = nullptr; // ogl window context
|
||||
bool real_window = true; // whether we need to blit onto GLFWwindow surface
|
||||
bool custom_backbuffer = false; // whether we want to render to our offscreen LDR backbuffer (pipeline required)
|
||||
bool shadow = false; // include this viewport area in shadowmap
|
||||
|
||||
enum vp_type {
|
||||
normal,
|
||||
custom,
|
||||
vr_left,
|
||||
vr_right
|
||||
} proj_type;
|
||||
viewport_proj_config projection;
|
||||
|
||||
// main msaa render target for pipeline mode
|
||||
std::unique_ptr<gl::framebuffer> msaa_fb;
|
||||
std::unique_ptr<gl::renderbuffer> msaa_rbc;
|
||||
std::unique_ptr<gl::renderbuffer> msaa_rbv;
|
||||
std::unique_ptr<gl::renderbuffer> msaa_rbd;
|
||||
|
||||
// msaa resolve buffer (when using motion blur)
|
||||
std::unique_ptr<gl::framebuffer> main_fb;
|
||||
std::unique_ptr<opengl_texture> main_texv;
|
||||
std::unique_ptr<opengl_texture> main_texd;
|
||||
std::unique_ptr<opengl_texture> main_tex;
|
||||
|
||||
// final HDR buffer (also serving as msaa resolve buffer when not using motion blur)
|
||||
std::unique_ptr<gl::framebuffer> main2_fb;
|
||||
std::unique_ptr<opengl_texture> main2_tex;
|
||||
|
||||
// LDR backbuffer for offscreen rendering
|
||||
std::unique_ptr<gl::framebuffer> backbuffer_fb;
|
||||
std::unique_ptr<opengl_texture> backbuffer_tex;
|
||||
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
viewport_config *m_current_viewport = nullptr;
|
||||
|
||||
typedef std::vector<opengl33_light> opengllight_array;
|
||||
|
||||
// methods
|
||||
std::unique_ptr<gl::program> make_shader(std::string v, std::string f);
|
||||
bool Init_caps();
|
||||
void setup_pass(viewport_config &Viewport, renderpass_config &Config, rendermode const Mode, float const Znear = 0.f, float const Zfar = 1.f, bool const Ignoredebug = false);
|
||||
void setup_matrices();
|
||||
void setup_drawing(bool const Alpha = false);
|
||||
void setup_shadow_unbind_map();
|
||||
void setup_shadow_bind_map();
|
||||
void setup_shadow_color( glm::vec4 const &Shadowcolor );
|
||||
void setup_env_map(gl::cubemap *tex);
|
||||
void setup_environment_light(TEnvironmentType const Environment = e_flat);
|
||||
void setup_sunlight_intensity( float const Factor = 1.f);
|
||||
// runs jobs needed to generate graphics for specified render pass
|
||||
void Render_pass(viewport_config &vp, rendermode const Mode);
|
||||
// creates dynamic environment cubemap
|
||||
bool Render_reflections(viewport_config &vp);
|
||||
bool Render(world_environment *Environment);
|
||||
void Render(scene::basic_region *Region);
|
||||
void Render(section_sequence::iterator First, section_sequence::iterator Last);
|
||||
void Render(cell_sequence::iterator First, cell_sequence::iterator Last);
|
||||
void Render(scene::shape_node const &Shape, bool const Ignorerange);
|
||||
void Render(TAnimModel *Instance);
|
||||
bool Render(TDynamicObject *Dynamic);
|
||||
bool Render(TModel3d *Model, material_data const *Material, float const Squaredistance, Math3D::vector3 const &Position, glm::vec3 const &Angle);
|
||||
bool Render(TModel3d *Model, material_data const *Material, float const Squaredistance);
|
||||
void Render(TSubModel *Submodel);
|
||||
void Render(TTrack *Track);
|
||||
void Render(scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last);
|
||||
bool Render_cab(TDynamicObject const *Dynamic, float const Lightlevel, bool const Alpha = false);
|
||||
bool Render_interior( bool const Alpha = false );
|
||||
bool Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha = false );
|
||||
bool Render_coupler_adapter( TDynamicObject *Dynamic, float const Squaredistance, int const End, bool const Alpha = false );
|
||||
void Render(TMemCell *Memcell);
|
||||
void Render_particles();
|
||||
void Render_precipitation();
|
||||
void Render_vr_models();
|
||||
void Render_Alpha(scene::basic_region *Region);
|
||||
void Render_Alpha(cell_sequence::reverse_iterator First, cell_sequence::reverse_iterator Last);
|
||||
void Render_Alpha(TAnimModel *Instance);
|
||||
void Render_Alpha(TTraction *Traction);
|
||||
void Render_Alpha(scene::lines_node const &Lines);
|
||||
bool Render_Alpha(TDynamicObject *Dynamic);
|
||||
bool Render_Alpha(TModel3d *Model, material_data const *Material, float const Squaredistance, Math3D::vector3 const &Position, glm::vec3 const &Angle);
|
||||
bool Render_Alpha(TModel3d *Model, material_data const *Material, float const Squaredistance);
|
||||
void Render_Alpha(TSubModel *Submodel);
|
||||
void Update_Lights(light_array &Lights);
|
||||
glm::vec3 pick_color(std::size_t const Index);
|
||||
std::size_t pick_index(glm::ivec3 const &Color);
|
||||
|
||||
bool init_viewport(viewport_config &vp);
|
||||
|
||||
void draw(const gfx::geometry_handle &handle);
|
||||
void draw(std::vector<gfx::geometrybank_handle>::iterator begin, std::vector<gfx::geometrybank_handle>::iterator end);
|
||||
|
||||
void draw_debug_ui();
|
||||
|
||||
// members
|
||||
GLFWwindow *m_window{nullptr}; // main window
|
||||
gfx::geometrybank_manager m_geometry;
|
||||
material_manager m_materials;
|
||||
texture_manager m_textures;
|
||||
opengl33_light m_sunlight;
|
||||
opengllight_array m_lights;
|
||||
/*
|
||||
float m_sunandviewangle; // cached dot product of sunlight and camera vectors
|
||||
*/
|
||||
gfx::geometry_handle m_billboardgeometry{0, 0};
|
||||
texture_handle m_glaretexture{-1};
|
||||
texture_handle m_suntexture{-1};
|
||||
texture_handle m_moontexture{-1};
|
||||
texture_handle m_smoketexture{-1};
|
||||
texture_handle m_headlightstexture{-1};
|
||||
|
||||
// main shadowmap resources
|
||||
int m_shadowbuffersize{2048};
|
||||
glm::mat4 m_shadowtexturematrix; // conversion from camera-centric world space to light-centric clip space
|
||||
|
||||
int m_environmentcubetextureface{0}; // helper, currently processed cube map face
|
||||
double m_environmentupdatetime{0}; // time of the most recent environment map update
|
||||
glm::dvec3 m_environmentupdatelocation; // coordinates of most recent environment map update
|
||||
opengl33_skydome m_skydomerenderer;
|
||||
opengl33_precipitation m_precipitationrenderer;
|
||||
opengl33_particles m_particlerenderer; // particle visualization subsystem
|
||||
|
||||
unsigned int m_framestamp; // id of currently rendered gfx frame
|
||||
float m_framerate;
|
||||
double m_updateaccumulator{0.0};
|
||||
std::string m_debugtimestext;
|
||||
std::string m_pickdebuginfo;
|
||||
//debug_stats m_debugstats;
|
||||
std::string m_debugstatstext;
|
||||
struct simulation_state {
|
||||
std::string weather;
|
||||
std::string season;
|
||||
} m_simulationstate;
|
||||
|
||||
glm::vec4 m_baseambient{0.0f, 0.0f, 0.0f, 1.0f};
|
||||
glm::vec4 m_shadowcolor{colors::shadow};
|
||||
// TEnvironmentType m_environment { e_flat };
|
||||
float m_specularopaquescalefactor{1.f};
|
||||
float m_speculartranslucentscalefactor{1.f};
|
||||
|
||||
float m_fogrange = 2000.0f;
|
||||
|
||||
renderpass_config m_renderpass; // parameters for current render pass
|
||||
section_sequence m_sectionqueue; // list of sections in current render pass
|
||||
cell_sequence m_cellqueue;
|
||||
renderpass_config m_colorpass; // parametrs of most recent color pass
|
||||
std::array<renderpass_config, 3> m_shadowpass; // parametrs of most recent shadowmap pass for each of csm stages
|
||||
std::vector<TSubModel const *> m_pickcontrolsitems;
|
||||
std::vector<TSubModel const *> m_picksurfaceitems;
|
||||
TSubModel const *m_pickcontrolitem{nullptr};
|
||||
std::vector<scene::basic_node *> m_picksceneryitems;
|
||||
scene::basic_node *m_picksceneryitem{nullptr};
|
||||
glm::vec3 m_worldmousecoordinates { 0.f };
|
||||
#ifdef EU07_USE_DEBUG_CAMERA
|
||||
renderpass_config m_worldcamera; // debug item
|
||||
#endif
|
||||
|
||||
std::optional<gl::query> m_timequery;
|
||||
GLuint64 m_gllasttime = 0;
|
||||
|
||||
double m_precipitationrotation;
|
||||
|
||||
glm::mat4 perspective_projection_raw(float fovy, float aspect, float znear, float zfar);
|
||||
glm::mat4 perspective_projection(const viewport_proj_config &c, float n, float f, glm::mat4 &frustum);
|
||||
glm::mat4 ortho_projection(float left, float right, float bottom, float top, float z_near, float z_far);
|
||||
glm::mat4 ortho_frustumtest_projection(float left, float right, float bottom, float top, float z_near, float z_far);
|
||||
|
||||
std::vector<std::function<void(TSubModel const *, glm::vec2)>> m_control_pick_requests;
|
||||
std::vector<std::function<void(scene::basic_node *)>> m_node_pick_requests;
|
||||
|
||||
std::unique_ptr<gl::shader> m_vertex_shader;
|
||||
|
||||
std::unique_ptr<gl::ubo> scene_ubo;
|
||||
std::unique_ptr<gl::ubo> model_ubo;
|
||||
std::unique_ptr<gl::ubo> light_ubo;
|
||||
gl::scene_ubs scene_ubs;
|
||||
gl::model_ubs model_ubs;
|
||||
gl::light_ubs light_ubs;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<gl::program>> m_shaders;
|
||||
|
||||
std::unique_ptr<gl::program> m_line_shader;
|
||||
std::unique_ptr<gl::program> m_freespot_shader;
|
||||
std::unique_ptr<gl::program> m_billboard_shader;
|
||||
std::unique_ptr<gl::program> m_celestial_shader;
|
||||
std::unique_ptr<gl::program> m_hiddenarea_shader;
|
||||
std::unique_ptr<gl::program> m_copy_shader;
|
||||
|
||||
std::unique_ptr<gl::vao> m_empty_vao;
|
||||
|
||||
std::vector<std::unique_ptr<viewport_config>> m_viewports;
|
||||
|
||||
std::unique_ptr<gl::postfx> m_pfx_motionblur;
|
||||
std::unique_ptr<gl::postfx> m_pfx_tonemapping;
|
||||
std::unique_ptr<gl::postfx> m_pfx_chromaticaberration;
|
||||
|
||||
std::unique_ptr<gl::program> m_shadow_shader;
|
||||
std::unique_ptr<gl::program> m_alpha_shadow_shader;
|
||||
|
||||
std::unique_ptr<gl::framebuffer> m_pick_fb;
|
||||
std::unique_ptr<opengl_texture> m_pick_tex;
|
||||
std::unique_ptr<gl::renderbuffer> m_pick_rb;
|
||||
std::unique_ptr<gl::program> m_pick_surface_shader;
|
||||
std::unique_ptr<gl::program> m_pick_shader;
|
||||
|
||||
std::unique_ptr<gl::cubemap> m_empty_cubemap;
|
||||
|
||||
std::unique_ptr<gl::framebuffer> m_env_fb;
|
||||
std::unique_ptr<gl::renderbuffer> m_env_rb;
|
||||
std::unique_ptr<gl::cubemap> m_env_tex;
|
||||
|
||||
std::unique_ptr<gl::framebuffer> m_shadow_fb;
|
||||
std::unique_ptr<opengl_texture> m_shadow_tex;
|
||||
|
||||
std::unique_ptr<gl::pbo> m_picking_pbo;
|
||||
std::unique_ptr<gl::pbo> m_picking_node_pbo;
|
||||
|
||||
std::unique_ptr<gl::pbo> m_depth_pointer_pbo;
|
||||
std::unique_ptr<gl::framebuffer> m_depth_pointer_fb;
|
||||
std::unique_ptr<gl::framebuffer> m_depth_pointer_fb2;
|
||||
std::unique_ptr<gl::renderbuffer> m_depth_pointer_rb;
|
||||
std::unique_ptr<opengl_texture> m_depth_pointer_tex;
|
||||
std::unique_ptr<gl::program> m_depth_pointer_shader;
|
||||
|
||||
material_handle m_invalid_material;
|
||||
|
||||
bool m_blendingenabled;
|
||||
|
||||
bool m_widelines_supported;
|
||||
|
||||
struct headlight_config_s
|
||||
{
|
||||
float in_cutoff = 1.005f;
|
||||
float out_cutoff = 0.993f;
|
||||
|
||||
float falloff_linear = 0.15f;
|
||||
float falloff_quadratic = 0.15f;
|
||||
|
||||
float intensity = 1.0f;
|
||||
float ambient = 0.0f;
|
||||
};
|
||||
|
||||
headlight_config_s headlight_config;
|
||||
|
||||
std::unique_ptr<vr_interface> vr;
|
||||
bool debug_ui_active = false;
|
||||
|
||||
static bool renderer_register;
|
||||
|
||||
class opengl33_imgui_renderer : public imgui_renderer
|
||||
{
|
||||
virtual bool Init() override;
|
||||
virtual void Shutdown() override;
|
||||
virtual void BeginFrame() override;
|
||||
virtual void Render() override;
|
||||
} m_imgui_renderer;
|
||||
|
||||
virtual imgui_renderer* GetImguiRenderer() override {
|
||||
return &m_imgui_renderer;
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
69
rendering/opengl33skydome.cpp
Normal file
69
rendering/opengl33skydome.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "opengl33skydome.h"
|
||||
|
||||
#include "simulationenvironment.h"
|
||||
|
||||
void opengl33_skydome::update() {
|
||||
|
||||
if (!m_shader) {
|
||||
|
||||
gl::shader vert("vbocolor.vert");
|
||||
gl::shader frag("color.frag");
|
||||
m_shader.emplace(std::vector<std::reference_wrapper<const gl::shader>>({vert, frag}));
|
||||
}
|
||||
|
||||
auto &skydome { simulation::Environment.skydome() };
|
||||
|
||||
if (!m_vertexbuffer) {
|
||||
m_vao.emplace();
|
||||
m_vao->bind();
|
||||
|
||||
m_vertexbuffer.emplace();
|
||||
m_vertexbuffer->allocate(gl::buffer::ARRAY_BUFFER, skydome.vertices().size() * sizeof( glm::vec3 ), GL_STATIC_DRAW);
|
||||
m_vertexbuffer->upload(gl::buffer::ARRAY_BUFFER, skydome.vertices().data(), 0, skydome.vertices().size() * sizeof( glm::vec3 ));
|
||||
|
||||
m_vao->setup_attrib(*m_vertexbuffer, 0, 3, GL_FLOAT, sizeof(glm::vec3), 0);
|
||||
|
||||
m_coloursbuffer.emplace();
|
||||
m_coloursbuffer->allocate(gl::buffer::ARRAY_BUFFER, skydome.colors().size() * sizeof( glm::vec3 ), GL_STATIC_DRAW);
|
||||
m_coloursbuffer->upload(gl::buffer::ARRAY_BUFFER, skydome.colors().data(), 0, skydome.colors().size() * sizeof( glm::vec3 ));
|
||||
|
||||
m_vao->setup_attrib(*m_coloursbuffer, 1, 3, GL_FLOAT, sizeof(glm::vec3), 0);
|
||||
|
||||
m_indexbuffer.emplace();
|
||||
m_indexbuffer->allocate(gl::buffer::ELEMENT_ARRAY_BUFFER, skydome.indices().size() * sizeof( unsigned short ), GL_STATIC_DRAW);
|
||||
m_indexbuffer->upload(gl::buffer::ELEMENT_ARRAY_BUFFER, skydome.indices().data(), 0, skydome.indices().size() * sizeof( unsigned short ));
|
||||
m_vao->setup_ebo(*m_indexbuffer);
|
||||
|
||||
m_vao->unbind();
|
||||
}
|
||||
// ship the current dynamic data to the gpu
|
||||
if( true == skydome.is_dirty() ) {
|
||||
if( m_coloursbuffer ) {
|
||||
// the colour buffer was already initialized, so on this run we update its content
|
||||
m_coloursbuffer->upload( gl::buffer::ARRAY_BUFFER, skydome.colors().data(), 0, skydome.colors().size() * sizeof( glm::vec3 ) );
|
||||
skydome.is_dirty() = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// render skydome to screen
|
||||
void opengl33_skydome::render() {
|
||||
|
||||
if( ( !m_shader) || ( !m_vao ) ) { return; }
|
||||
|
||||
m_shader->bind();
|
||||
m_vao->bind();
|
||||
|
||||
auto const &skydome { simulation::Environment.skydome() };
|
||||
::glDrawElements( GL_TRIANGLES, static_cast<GLsizei>( skydome.indices().size() ), GL_UNSIGNED_SHORT, reinterpret_cast<void const*>( 0 ) );
|
||||
}
|
||||
36
rendering/opengl33skydome.h
Normal file
36
rendering/opengl33skydome.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gl/vao.h"
|
||||
#include "gl/shader.h"
|
||||
#include "gl/buffer.h"
|
||||
|
||||
class opengl33_skydome {
|
||||
|
||||
public:
|
||||
// constructors
|
||||
opengl33_skydome() = default;
|
||||
// destructor
|
||||
~opengl33_skydome() {;}
|
||||
// methods
|
||||
// updates data stores on the opengl end. NOTE: unbinds buffers
|
||||
void update();
|
||||
// draws the skydome
|
||||
void render();
|
||||
|
||||
private:
|
||||
// members
|
||||
std::optional<gl::buffer> m_vertexbuffer;
|
||||
std::optional<gl::buffer> m_indexbuffer;
|
||||
std::optional<gl::buffer> m_coloursbuffer;
|
||||
std::optional<gl::program> m_shader;
|
||||
std::optional<gl::vao> m_vao;
|
||||
};
|
||||
55
rendering/openglcamera.cpp
Normal file
55
rendering/openglcamera.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openglcamera.h"
|
||||
|
||||
#include "DynObj.h"
|
||||
|
||||
void
|
||||
opengl_camera::update_frustum( glm::mat4 const &Projection, glm::mat4 const &Modelview ) {
|
||||
|
||||
m_frustum.calculate( Projection, Modelview );
|
||||
// cache inverse tranformation matrix
|
||||
// NOTE: transformation is done only to camera-centric space
|
||||
m_inversetransformation = glm::inverse( Projection * glm::mat4{ glm::mat3{ Modelview } } );
|
||||
// calculate frustum corners
|
||||
m_frustumpoints = ndcfrustumshapepoints;
|
||||
transform_to_world(
|
||||
std::begin( m_frustumpoints ),
|
||||
std::end( m_frustumpoints ) );
|
||||
}
|
||||
|
||||
// returns true if specified object is within camera frustum, false otherwise
|
||||
bool
|
||||
opengl_camera::visible( scene::bounding_area const &Area ) const {
|
||||
|
||||
return ( m_frustum.sphere_inside( Area.center, Area.radius ) > 0.f );
|
||||
}
|
||||
|
||||
bool
|
||||
opengl_camera::visible( TDynamicObject const *Dynamic ) const {
|
||||
|
||||
// sphere test is faster than AABB, so we'll use it here
|
||||
// we're giving vehicles some extra padding, to allow for things like shared bogeys extending past the main body
|
||||
return ( m_frustum.sphere_inside( Dynamic->GetPosition(), Dynamic->radius() * 1.25 ) > 0.0f );
|
||||
}
|
||||
|
||||
// debug helper, draws shape of frustum in world space
|
||||
void
|
||||
opengl_camera::draw( glm::vec3 const &Offset ) const {
|
||||
|
||||
::glBegin( GL_LINES );
|
||||
for( auto const pointindex : frustumshapepoinstorder ) {
|
||||
::glVertex3fv( glm::value_ptr( glm::vec3{ m_frustumpoints[ pointindex ] } - Offset ) );
|
||||
}
|
||||
::glEnd();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
81
rendering/openglcamera.h
Normal file
81
rendering/openglcamera.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "frustum.h"
|
||||
#include "scene.h"
|
||||
|
||||
// simple camera object. paired with 'virtual camera' in the scene
|
||||
class opengl_camera {
|
||||
|
||||
public:
|
||||
// constructors
|
||||
opengl_camera() = default;
|
||||
// methods:
|
||||
inline
|
||||
void
|
||||
update_frustum() { update_frustum( m_projection, m_modelview ); }
|
||||
inline
|
||||
void
|
||||
update_frustum(glm::mat4 frustumtest_proj) {
|
||||
update_frustum(frustumtest_proj, m_modelview); }
|
||||
void
|
||||
update_frustum( glm::mat4 const &Projection, glm::mat4 const &Modelview );
|
||||
bool
|
||||
visible( scene::bounding_area const &Area ) const;
|
||||
bool
|
||||
visible( TDynamicObject const *Dynamic ) const;
|
||||
inline
|
||||
glm::dvec3 const &
|
||||
position() const { return m_position; }
|
||||
inline
|
||||
glm::dvec3 &
|
||||
position() { return m_position; }
|
||||
inline
|
||||
glm::mat4 const &
|
||||
projection() const { return m_projection; }
|
||||
inline
|
||||
glm::mat4 &
|
||||
projection() { return m_projection; }
|
||||
inline
|
||||
glm::mat4 const &
|
||||
modelview() const { return m_modelview; }
|
||||
inline
|
||||
glm::mat4 &
|
||||
modelview() { return m_modelview; }
|
||||
inline
|
||||
std::vector<glm::vec4> &
|
||||
frustum_points() { return m_frustumpoints; }
|
||||
// transforms provided set of clip space points to world space
|
||||
template <class Iterator_>
|
||||
void
|
||||
transform_to_world( Iterator_ First, Iterator_ Last ) const {
|
||||
std::for_each(
|
||||
First, Last,
|
||||
[this]( glm::vec4 &point ) {
|
||||
// transform each point using the cached matrix...
|
||||
point = this->m_inversetransformation * point;
|
||||
// ...and scale by transformed w
|
||||
point = glm::vec4{ glm::vec3{ point } / point.w, 1.f }; } ); }
|
||||
// debug helper, draws shape of frustum in world space
|
||||
void
|
||||
draw( glm::vec3 const &Offset ) const;
|
||||
|
||||
private:
|
||||
// members:
|
||||
cFrustum m_frustum;
|
||||
std::vector<glm::vec4> m_frustumpoints; // visualization helper; corners of defined frustum, in world space
|
||||
glm::dvec3 m_position;
|
||||
glm::mat4 m_projection;
|
||||
glm::mat4 m_modelview;
|
||||
glm::mat4 m_inversetransformation; // cached transformation to world space
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
14
rendering/openglcolor.cpp
Normal file
14
rendering/openglcolor.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openglcolor.h"
|
||||
|
||||
opengl_color OpenGLColor;
|
||||
|
||||
77
rendering/openglcolor.h
Normal file
77
rendering/openglcolor.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
// encapsulation of the fixed pipeline opengl color
|
||||
class opengl_color {
|
||||
|
||||
public:
|
||||
// constructors:
|
||||
opengl_color() = default;
|
||||
|
||||
// methods:
|
||||
inline
|
||||
void
|
||||
color3( glm::vec3 const &Color ) {
|
||||
return color4( glm::vec4{ Color, 1.f } ); }
|
||||
inline
|
||||
void
|
||||
color3( float const Red, float const Green, float const Blue ) {
|
||||
return color3( glm::vec3 { Red, Green, Blue } ); }
|
||||
inline
|
||||
void
|
||||
color3( float const *Value ) {
|
||||
return color3( glm::make_vec3( Value ) ); }
|
||||
inline
|
||||
void
|
||||
color4( glm::vec4 const &Color ) {
|
||||
if( ( Color != m_color ) || ( false == Global.bUseVBO ) ) {
|
||||
m_color = Color;
|
||||
::glColor4fv( glm::value_ptr( m_color ) ); } }
|
||||
inline
|
||||
void
|
||||
color4( float const Red, float const Green, float const Blue, float const Alpha ) {
|
||||
return color4( glm::vec4{ Red, Green, Blue, Alpha } ); }
|
||||
inline
|
||||
void
|
||||
color4( float const *Value ) {
|
||||
return color4( glm::make_vec4( Value ) );
|
||||
}
|
||||
inline
|
||||
glm::vec4 const &
|
||||
data() const {
|
||||
return m_color; }
|
||||
inline
|
||||
float const *
|
||||
data_array() const {
|
||||
return glm::value_ptr( m_color ); }
|
||||
|
||||
private:
|
||||
// members:
|
||||
glm::vec4 m_color { -1 };
|
||||
};
|
||||
|
||||
extern opengl_color OpenGLColor;
|
||||
|
||||
// NOTE: standard opengl calls re-definitions
|
||||
#undef glColor3f
|
||||
#undef glColor3fv
|
||||
#undef glColor4f
|
||||
#undef glColor4fv
|
||||
|
||||
#define glColor3f OpenGLColor.color3
|
||||
#define glColor3fv OpenGLColor.color3
|
||||
#define glColor4f OpenGLColor.color4
|
||||
#define glColor4fv OpenGLColor.color4
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
376
rendering/openglgeometrybank.cpp
Normal file
376
rendering/openglgeometrybank.cpp
Normal file
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openglgeometrybank.h"
|
||||
#include "openglcolor.h"
|
||||
|
||||
#include "Logs.h"
|
||||
|
||||
namespace gfx {
|
||||
|
||||
// opengl vbo-based variant of the geometry bank
|
||||
|
||||
GLuint opengl_vbogeometrybank::m_activevertexbuffer { 0 }; // buffer bound currently on the opengl end, if any
|
||||
unsigned int opengl_vbogeometrybank::m_activestreams { gfx::stream::none }; // currently enabled data type pointers
|
||||
std::vector<GLint> opengl_vbogeometrybank::m_activetexturearrays; // currently enabled texture coord arrays
|
||||
|
||||
// create() subclass details
|
||||
void
|
||||
opengl_vbogeometrybank::create_( gfx::geometry_handle const &Geometry ) {
|
||||
// adding a chunk means we'll be (re)building the buffer, which will fill the chunk records, amongst other things.
|
||||
// thus we don't need to initialize the values here
|
||||
m_chunkrecords.emplace_back( chunk_record() );
|
||||
// kiss the existing buffer goodbye, new overall data size means we'll be making a new one
|
||||
delete_buffer();
|
||||
}
|
||||
|
||||
// replace() subclass details
|
||||
void
|
||||
opengl_vbogeometrybank::replace_( gfx::geometry_handle const &Geometry ) {
|
||||
|
||||
auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
|
||||
chunkrecord.is_good = false;
|
||||
// if the overall length of the chunk didn't change we can get away with reusing the old buffer...
|
||||
if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.vertex_count ) {
|
||||
// ...but otherwise we'll need to allocate a new one
|
||||
// TBD: we could keep and reuse the old buffer also if the new chunk is smaller than the old one,
|
||||
// but it'd require some extra tracking and work to keep all chunks up to date; also wasting vram; may be not worth it?
|
||||
delete_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
// draw() subclass details
|
||||
std::size_t
|
||||
opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams ) {
|
||||
|
||||
setup_buffer();
|
||||
|
||||
auto &chunkrecord { m_chunkrecords[ Geometry.chunk - 1 ] };
|
||||
// sanity check; shouldn't be needed but, eh
|
||||
if( chunkrecord.vertex_count == 0 ) { return 0; }
|
||||
// setup...
|
||||
if( m_activevertexbuffer != m_vertexbuffer ) {
|
||||
bind_buffer();
|
||||
}
|
||||
auto const &chunk = gfx::geometry_bank::chunk( Geometry );
|
||||
if( false == chunkrecord.is_good ) {
|
||||
// we may potentially need to upload new buffer data before we can draw it
|
||||
if( chunkrecord.index_count > 0 ) {
|
||||
::glBufferSubData(
|
||||
GL_ELEMENT_ARRAY_BUFFER,
|
||||
chunkrecord.index_offset * sizeof( gfx::basic_index ),
|
||||
chunkrecord.index_count * sizeof( gfx::basic_index ),
|
||||
chunk.indices.data() );
|
||||
}
|
||||
::glBufferSubData(
|
||||
GL_ARRAY_BUFFER,
|
||||
chunkrecord.vertex_offset * sizeof( gfx::basic_vertex ),
|
||||
chunkrecord.vertex_count * sizeof( gfx::basic_vertex ),
|
||||
chunk.vertices.data() );
|
||||
chunkrecord.is_good = true;
|
||||
}
|
||||
// ...render...
|
||||
if( chunkrecord.index_count > 0 ) {
|
||||
/*
|
||||
::glDrawElementsBaseVertex(
|
||||
chunk.type,
|
||||
chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast<void const *>( chunkrecord.index_offset * sizeof( gfx::basic_index ) ),
|
||||
chunkrecord.vertex_offset );
|
||||
*/
|
||||
if (glDrawRangeElementsBaseVertex) {
|
||||
if( m_activestreams != Streams ) {
|
||||
bind_streams( Units, Streams );
|
||||
}
|
||||
::glDrawRangeElementsBaseVertex(
|
||||
chunk.type,
|
||||
0, chunkrecord.vertex_count,
|
||||
chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast<void const *>( chunkrecord.index_offset * sizeof( gfx::basic_index ) ),
|
||||
chunkrecord.vertex_offset );
|
||||
}
|
||||
else {
|
||||
bind_streams( Units, Streams, chunkrecord.vertex_offset );
|
||||
::glDrawRangeElements(
|
||||
chunk.type,
|
||||
0, chunkrecord.vertex_count,
|
||||
chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast<void const *>( chunkrecord.index_offset * sizeof( gfx::basic_index ) ) );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( m_activestreams != Streams ) {
|
||||
bind_streams( Units, Streams );
|
||||
}
|
||||
::glDrawArrays( chunk.type, chunkrecord.vertex_offset, chunkrecord.vertex_count );
|
||||
}
|
||||
// ...post-render cleanup
|
||||
/*
|
||||
::glDisableClientState( GL_VERTEX_ARRAY );
|
||||
::glDisableClientState( GL_NORMAL_ARRAY );
|
||||
::glDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, 0 ); m_activebuffer = 0;
|
||||
*/
|
||||
auto const vertexcount { ( chunkrecord.index_count > 0 ? chunkrecord.index_count : chunkrecord.vertex_count ) };
|
||||
switch( chunk.type ) {
|
||||
case GL_TRIANGLES: { return vertexcount / 3; }
|
||||
case GL_TRIANGLE_STRIP: { return vertexcount - 2; }
|
||||
default: { return 0; }
|
||||
}
|
||||
}
|
||||
|
||||
// release () subclass details
|
||||
void
|
||||
opengl_vbogeometrybank::release_() {
|
||||
|
||||
delete_buffer();
|
||||
}
|
||||
|
||||
void
|
||||
opengl_vbogeometrybank::setup_buffer() {
|
||||
|
||||
if( m_vertexbuffer != 0 ) { return; }
|
||||
// if there's no buffer, we'll have to make one
|
||||
// NOTE: this isn't exactly optimal in terms of ensuring the gfx card doesn't stall waiting for the data
|
||||
// may be better to initiate upload earlier (during update phase) and trust this effort won't go to waste
|
||||
if( true == m_chunks.empty() ) { return; }
|
||||
|
||||
std::size_t
|
||||
vertexcount{ 0 },
|
||||
indexcount{ 0 };
|
||||
auto chunkiterator = m_chunks.cbegin();
|
||||
for( auto &chunkrecord : m_chunkrecords ) {
|
||||
// fill records for all chunks, based on the chunk data
|
||||
chunkrecord.is_good = false; // if we're re-creating buffer, chunks might've been uploaded in the old one
|
||||
chunkrecord.vertex_offset = vertexcount;
|
||||
chunkrecord.vertex_count = chunkiterator->vertices.size();
|
||||
vertexcount += chunkrecord.vertex_count;
|
||||
chunkrecord.index_offset = indexcount;
|
||||
chunkrecord.index_count = chunkiterator->indices.size();
|
||||
indexcount += chunkrecord.index_count;
|
||||
++chunkiterator;
|
||||
}
|
||||
// the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist
|
||||
if( vertexcount == 0 ) { return; }
|
||||
// try to set up the buffers we need
|
||||
if( ( indexcount > 0 ) && ( m_indexbuffer == 0 ) ) {
|
||||
::glGenBuffers( 1, &m_indexbuffer );
|
||||
if( m_indexbuffer == 0 ) { return; } // if we didn't get a buffer we'll try again during the next draw call
|
||||
}
|
||||
if( m_vertexbuffer == 0 ) {
|
||||
::glGenBuffers( 1, &m_vertexbuffer );
|
||||
if( m_vertexbuffer == 0 ) { return; } // if we didn't get a buffer we'll try again during the next draw call
|
||||
}
|
||||
bind_buffer();
|
||||
// NOTE: we're using static_draw since it's generally true for all we have implemented at the moment
|
||||
// TODO: allow to specify usage hint at the object creation, and pass it here
|
||||
if( indexcount > 0 ) {
|
||||
::glBufferData(
|
||||
GL_ELEMENT_ARRAY_BUFFER,
|
||||
indexcount * sizeof( gfx::basic_index ),
|
||||
nullptr,
|
||||
GL_STATIC_DRAW );
|
||||
if( ::glGetError() == GL_OUT_OF_MEMORY ) {
|
||||
// TBD: throw a bad_alloc?
|
||||
ErrorLog( "openGL error: out of memory; failed to create a geometry index buffer" );
|
||||
delete_buffer();
|
||||
return;
|
||||
}
|
||||
}
|
||||
::glBufferData(
|
||||
GL_ARRAY_BUFFER,
|
||||
vertexcount * sizeof( gfx::basic_vertex ),
|
||||
nullptr,
|
||||
GL_STATIC_DRAW );
|
||||
if( ::glGetError() == GL_OUT_OF_MEMORY ) {
|
||||
// TBD: throw a bad_alloc?
|
||||
ErrorLog( "openGL error: out of memory; failed to create a geometry buffer" );
|
||||
delete_buffer();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
opengl_vbogeometrybank::bind_buffer() {
|
||||
|
||||
::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer );
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer );
|
||||
m_activevertexbuffer = m_vertexbuffer;
|
||||
m_activestreams = gfx::stream::none;
|
||||
}
|
||||
|
||||
void
|
||||
opengl_vbogeometrybank::delete_buffer() {
|
||||
|
||||
if( m_indexbuffer != 0 ) {
|
||||
::glDeleteBuffers( 1, &m_indexbuffer );
|
||||
m_indexbuffer = 0;
|
||||
}
|
||||
if( m_vertexbuffer != 0 ) {
|
||||
|
||||
::glDeleteBuffers( 1, &m_vertexbuffer );
|
||||
if( m_activevertexbuffer == m_vertexbuffer ) {
|
||||
m_activevertexbuffer = 0;
|
||||
release_streams();
|
||||
}
|
||||
m_vertexbuffer = 0;
|
||||
// NOTE: since we've deleted the buffer all chunks it held were rendered invalid as well
|
||||
// instead of clearing their state here we're delaying it until new buffer is created to avoid looping through chunk records twice
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
opengl_vbogeometrybank::bind_streams( gfx::stream_units const &Units, unsigned int const Streams, size_t offset ) {
|
||||
|
||||
if( Streams & gfx::stream::position ) {
|
||||
::glVertexPointer( 3, GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast<void const *>( offsetof(gfx::basic_vertex, position) + sizeof( gfx::basic_vertex ) * offset ) );
|
||||
::glEnableClientState( GL_VERTEX_ARRAY );
|
||||
}
|
||||
else {
|
||||
::glDisableClientState( GL_VERTEX_ARRAY );
|
||||
}
|
||||
// NOTE: normal and color streams share the data, making them effectively mutually exclusive
|
||||
if( Streams & gfx::stream::normal ) {
|
||||
::glNormalPointer( GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast<void const *>( offsetof(gfx::basic_vertex, normal) + sizeof( gfx::basic_vertex ) * offset ) );
|
||||
::glEnableClientState( GL_NORMAL_ARRAY );
|
||||
}
|
||||
else {
|
||||
::glDisableClientState( GL_NORMAL_ARRAY );
|
||||
}
|
||||
if( Streams & gfx::stream::color ) {
|
||||
::glColorPointer( 3, GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast<void const *>( offsetof(gfx::basic_vertex, normal) + sizeof( gfx::basic_vertex ) * offset ) );
|
||||
::glEnableClientState( GL_COLOR_ARRAY );
|
||||
}
|
||||
else {
|
||||
::glDisableClientState( GL_COLOR_ARRAY );
|
||||
}
|
||||
if( Streams & gfx::stream::texture ) {
|
||||
for( auto unit : Units.texture ) {
|
||||
::glClientActiveTexture( GL_TEXTURE0 + unit );
|
||||
::glTexCoordPointer( 2, GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast<void const *>( offsetof(gfx::basic_vertex, texture) + sizeof( gfx::basic_vertex ) * offset ) );
|
||||
::glEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
}
|
||||
m_activetexturearrays = Units.texture;
|
||||
}
|
||||
else {
|
||||
for( auto unit : Units.texture ) {
|
||||
::glClientActiveTexture( GL_TEXTURE0 + unit );
|
||||
::glDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
}
|
||||
m_activetexturearrays.clear(); // NOTE: we're simplifying here, since we always toggle the same texture coord sets
|
||||
}
|
||||
|
||||
m_activestreams = Streams;
|
||||
}
|
||||
|
||||
void
|
||||
opengl_vbogeometrybank::release_streams() {
|
||||
|
||||
::glDisableClientState( GL_VERTEX_ARRAY );
|
||||
::glDisableClientState( GL_NORMAL_ARRAY );
|
||||
::glDisableClientState( GL_COLOR_ARRAY );
|
||||
for( auto unit : m_activetexturearrays ) {
|
||||
::glClientActiveTexture( GL_TEXTURE0 + unit );
|
||||
::glDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
}
|
||||
|
||||
m_activestreams = gfx::stream::none;
|
||||
m_activetexturearrays.clear();
|
||||
}
|
||||
|
||||
// opengl display list based variant of the geometry bank
|
||||
|
||||
// create() subclass details
|
||||
void
|
||||
opengl_dlgeometrybank::create_( gfx::geometry_handle const &Geometry ) {
|
||||
|
||||
m_chunkrecords.emplace_back( chunk_record() );
|
||||
}
|
||||
|
||||
// replace() subclass details
|
||||
void
|
||||
opengl_dlgeometrybank::replace_( gfx::geometry_handle const &Geometry ) {
|
||||
|
||||
delete_list( Geometry );
|
||||
}
|
||||
|
||||
// draw() subclass details
|
||||
std::size_t
|
||||
opengl_dlgeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams ) {
|
||||
|
||||
auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
|
||||
if( chunkrecord.streams != Streams ) {
|
||||
delete_list( Geometry );
|
||||
chunkrecord.primitive_count = 0;
|
||||
}
|
||||
if( chunkrecord.list == 0 ) {
|
||||
// we don't have a list ready, so compile one
|
||||
chunkrecord.streams = Streams;
|
||||
chunkrecord.list = ::glGenLists( 1 );
|
||||
auto const &chunk = gfx::geometry_bank::chunk( Geometry );
|
||||
::glNewList( chunkrecord.list, GL_COMPILE );
|
||||
|
||||
::glBegin( chunk.type );
|
||||
if( chunk.indices.size() > 0 ) {
|
||||
// indexed geometry
|
||||
for( auto const &index : chunk.indices ) {
|
||||
auto const &vertex { chunk.vertices[ index ] };
|
||||
if( Streams & gfx::stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); }
|
||||
else if( Streams & gfx::stream::color ) { ::glColor3fv( glm::value_ptr( vertex.normal ) ); }
|
||||
if( Streams & gfx::stream::texture ) { for( auto unit : Units.texture ) { ::glMultiTexCoord2fv( unit, glm::value_ptr( vertex.texture ) ); } }
|
||||
if( Streams & gfx::stream::position ) { ::glVertex3fv( glm::value_ptr( vertex.position ) ); }
|
||||
}
|
||||
}
|
||||
else {
|
||||
// raw geometry
|
||||
for( auto const &vertex : chunk.vertices ) {
|
||||
if( Streams & gfx::stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); }
|
||||
else if( Streams & gfx::stream::color ) { ::glColor3fv( glm::value_ptr( vertex.normal ) ); }
|
||||
if( Streams & gfx::stream::texture ) { for( auto unit : Units.texture ) { ::glMultiTexCoord2fv( unit, glm::value_ptr( vertex.texture ) ); } }
|
||||
if( Streams & gfx::stream::position ) { ::glVertex3fv( glm::value_ptr( vertex.position ) ); }
|
||||
}
|
||||
}
|
||||
::glEnd();
|
||||
::glEndList();
|
||||
|
||||
auto const vertexcount { ( chunk.indices.empty() ? chunk.vertices.size() : chunk.indices.size() ) };
|
||||
switch( chunk.type ) {
|
||||
case GL_TRIANGLES: { chunkrecord.primitive_count += vertexcount / 3; break; }
|
||||
case GL_TRIANGLE_STRIP: { chunkrecord.primitive_count += vertexcount - 2; break; }
|
||||
default: { break; }
|
||||
}
|
||||
}
|
||||
// with the list done we can just play it
|
||||
::glCallList( chunkrecord.list );
|
||||
return chunkrecord.primitive_count;
|
||||
}
|
||||
|
||||
// release () subclass details
|
||||
void
|
||||
opengl_dlgeometrybank::release_() {
|
||||
|
||||
for( auto &chunkrecord : m_chunkrecords ) {
|
||||
if( chunkrecord.list != 0 ) {
|
||||
::glDeleteLists( chunkrecord.list, 1 );
|
||||
chunkrecord.list = 0;
|
||||
}
|
||||
chunkrecord.streams = gfx::stream::none;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
opengl_dlgeometrybank::delete_list( gfx::geometry_handle const &Geometry ) {
|
||||
// NOTE: given it's our own internal method we trust it to be called with valid parameters
|
||||
auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
|
||||
if( chunkrecord.list != 0 ) {
|
||||
::glDeleteLists( chunkrecord.list, 1 );
|
||||
chunkrecord.list = 0;
|
||||
}
|
||||
chunkrecord.streams = gfx::stream::none;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
124
rendering/openglgeometrybank.h
Normal file
124
rendering/openglgeometrybank.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "geometrybank.h"
|
||||
|
||||
namespace gfx {
|
||||
|
||||
// opengl vbo-based variant of the geometry bank
|
||||
|
||||
class opengl_vbogeometrybank : public geometry_bank {
|
||||
|
||||
public:
|
||||
// constructors:
|
||||
opengl_vbogeometrybank() = default;
|
||||
// destructor
|
||||
~opengl_vbogeometrybank() {
|
||||
delete_buffer(); }
|
||||
// methods:
|
||||
static
|
||||
void
|
||||
reset() {
|
||||
m_activevertexbuffer = 0;
|
||||
m_activestreams = gfx::stream::none; }
|
||||
|
||||
private:
|
||||
// types:
|
||||
struct chunk_record {
|
||||
std::size_t vertex_offset{ 0 }; // beginning of the chunk vertex data as offset from the beginning of the last established buffer
|
||||
std::size_t vertex_count{ 0 }; // size of the chunk in the last established buffer
|
||||
std::size_t index_offset{ 0 };
|
||||
std::size_t index_count{ 0 };
|
||||
bool is_good{ false }; // true if local content of the chunk matches the data on the opengl end
|
||||
};
|
||||
|
||||
using chunkrecord_sequence = std::vector<chunk_record>;
|
||||
|
||||
// methods:
|
||||
// create() subclass details
|
||||
void
|
||||
create_( gfx::geometry_handle const &Geometry ) override;
|
||||
// replace() subclass details
|
||||
void
|
||||
replace_( gfx::geometry_handle const &Geometry ) override;
|
||||
// draw() subclass details
|
||||
auto
|
||||
draw_( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams ) -> std::size_t override;
|
||||
// release() subclass details
|
||||
void
|
||||
release_() override;
|
||||
void
|
||||
setup_buffer();
|
||||
void
|
||||
bind_buffer();
|
||||
void
|
||||
delete_buffer();
|
||||
static
|
||||
void
|
||||
bind_streams(gfx::stream_units const &Units, unsigned int const Streams , size_t offset = 0);
|
||||
static
|
||||
void
|
||||
release_streams();
|
||||
|
||||
// members:
|
||||
static GLuint m_activevertexbuffer; // buffer bound currently on the opengl end, if any
|
||||
static unsigned int m_activestreams;
|
||||
static std::vector<GLint> m_activetexturearrays;
|
||||
GLuint m_vertexbuffer { 0 }; // id of the buffer holding vertex data on the opengl end
|
||||
GLuint m_indexbuffer { 0 }; // id of the buffer holding index data on the opengl end
|
||||
chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order
|
||||
|
||||
};
|
||||
|
||||
// opengl display list based variant of the geometry bank
|
||||
|
||||
class opengl_dlgeometrybank : public geometry_bank {
|
||||
|
||||
public:
|
||||
// constructors:
|
||||
opengl_dlgeometrybank() = default;
|
||||
// destructor:
|
||||
~opengl_dlgeometrybank() {
|
||||
for( auto &chunkrecord : m_chunkrecords ) {
|
||||
::glDeleteLists( chunkrecord.list, 1 ); } }
|
||||
|
||||
private:
|
||||
// types:
|
||||
struct chunk_record {
|
||||
GLuint list { 0 }; // display list associated with the chunk
|
||||
unsigned int streams { 0 }; // stream combination used to generate the display list
|
||||
std::size_t primitive_count { 0 };
|
||||
};
|
||||
|
||||
using chunkrecord_sequence = std::vector<chunk_record>;
|
||||
|
||||
// methods:
|
||||
// create() subclass details
|
||||
void
|
||||
create_( gfx::geometry_handle const &Geometry ) override;
|
||||
// replace() subclass details
|
||||
void
|
||||
replace_( gfx::geometry_handle const &Geometry ) override;
|
||||
// draw() subclass details
|
||||
auto
|
||||
draw_( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams ) -> std::size_t override;
|
||||
// release () subclass details
|
||||
void
|
||||
release_() override;
|
||||
void
|
||||
delete_list( gfx::geometry_handle const &Geometry );
|
||||
|
||||
// members:
|
||||
chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order
|
||||
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
45
rendering/opengllight.cpp
Normal file
45
rendering/opengllight.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "opengllight.h"
|
||||
#include "utilities.h"
|
||||
|
||||
void
|
||||
opengl_light::apply_intensity( float const Factor ) {
|
||||
|
||||
if( Factor == 1.f ) {
|
||||
|
||||
::glLightfv( id, GL_AMBIENT, glm::value_ptr( ambient ) );
|
||||
::glLightfv( id, GL_DIFFUSE, glm::value_ptr( diffuse ) );
|
||||
::glLightfv( id, GL_SPECULAR, glm::value_ptr( specular ) );
|
||||
}
|
||||
else {
|
||||
auto const factor{ clamp( Factor, 0.05f, 1.f ) };
|
||||
// temporary light scaling mechanics (ultimately this work will be left to the shaders
|
||||
glm::vec4 scaledambient( ambient.r * factor, ambient.g * factor, ambient.b * factor, ambient.a );
|
||||
glm::vec4 scaleddiffuse( diffuse.r * factor, diffuse.g * factor, diffuse.b * factor, diffuse.a );
|
||||
glm::vec4 scaledspecular( specular.r * factor, specular.g * factor, specular.b * factor, specular.a );
|
||||
glLightfv( id, GL_AMBIENT, glm::value_ptr( scaledambient ) );
|
||||
glLightfv( id, GL_DIFFUSE, glm::value_ptr( scaleddiffuse ) );
|
||||
glLightfv( id, GL_SPECULAR, glm::value_ptr( scaledspecular ) );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
opengl_light::apply_angle() {
|
||||
|
||||
::glLightfv( id, GL_POSITION, glm::value_ptr( glm::vec4{ position, ( is_directional ? 0.f : 1.f ) } ) );
|
||||
if( false == is_directional ) {
|
||||
::glLightfv( id, GL_SPOT_DIRECTION, glm::value_ptr( direction ) );
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
29
rendering/opengllight.h
Normal file
29
rendering/opengllight.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "light.h"
|
||||
|
||||
struct opengl_light : public basic_light {
|
||||
|
||||
GLuint id { (GLuint)-1 };
|
||||
|
||||
void
|
||||
apply_intensity( float const Factor = 1.0f );
|
||||
void
|
||||
apply_angle();
|
||||
|
||||
opengl_light &
|
||||
operator=( basic_light const &Right ) {
|
||||
basic_light::operator=( Right );
|
||||
return *this; }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
12
rendering/openglmatrixstack.cpp
Normal file
12
rendering/openglmatrixstack.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
opengl_matrices OpenGLMatrices;
|
||||
249
rendering/openglmatrixstack.h
Normal file
249
rendering/openglmatrixstack.h
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
// encapsulation of the fixed pipeline opengl matrix stack
|
||||
class opengl_matrices {
|
||||
|
||||
// types:
|
||||
class opengl_stack {
|
||||
|
||||
public:
|
||||
// constructors:
|
||||
opengl_stack() { m_stack.emplace(1.f); }
|
||||
|
||||
// methods:
|
||||
glm::mat4 const &
|
||||
data() const {
|
||||
return m_stack.top(); }
|
||||
void
|
||||
push_matrix() {
|
||||
m_stack.emplace( m_stack.top() ); }
|
||||
void
|
||||
pop_matrix( bool const Upload = true ) {
|
||||
if( m_stack.size() > 1 ) {
|
||||
m_stack.pop();
|
||||
if( Upload ) { upload(); } } }
|
||||
void
|
||||
load_identity( bool const Upload = true ) {
|
||||
m_stack.top() = glm::mat4( 1.f );
|
||||
if( Upload ) { upload(); } }
|
||||
void
|
||||
load_matrix( glm::mat4 const &Matrix, bool const Upload = true ) {
|
||||
m_stack.top() = Matrix;
|
||||
if( Upload ) { upload(); } }
|
||||
void
|
||||
rotate( float const Angle, glm::vec3 const &Axis, bool const Upload = true ) {
|
||||
m_stack.top() = glm::rotate( m_stack.top(), Angle, Axis );
|
||||
if( Upload ) { upload(); } }
|
||||
void
|
||||
translate( glm::vec3 const &Translation, bool const Upload = true ) {
|
||||
m_stack.top() = glm::translate( m_stack.top(), Translation );
|
||||
if( Upload ) { upload(); } }
|
||||
void
|
||||
scale( glm::vec3 const &Scale, bool const Upload = true ) {
|
||||
m_stack.top() = glm::scale( m_stack.top(), Scale );
|
||||
if( Upload ) { upload(); } }
|
||||
void
|
||||
multiply( glm::mat4 const &Matrix, bool const Upload = true ) {
|
||||
m_stack.top() *= Matrix;
|
||||
if( Upload ) { upload(); } }
|
||||
void
|
||||
ortho( float const Left, float const Right, float const Bottom, float const Top, float const Znear, float const Zfar, bool const Upload = true ) {
|
||||
m_stack.top() *= glm::ortho( Left, Right, Bottom, Top, Znear, Zfar );
|
||||
if( Upload ) { upload(); } }
|
||||
void
|
||||
perspective( float const Fovy, float const Aspect, float const Znear, float const Zfar, bool const Upload = true ) {
|
||||
m_stack.top() *= glm::perspective( Fovy, Aspect, Znear, Zfar );
|
||||
if( Upload ) { upload(); } }
|
||||
void
|
||||
look_at( glm::vec3 const &Eye, glm::vec3 const &Center, glm::vec3 const &Up, bool const Upload = true ) {
|
||||
m_stack.top() *= glm::lookAt( Eye, Center, Up );
|
||||
if( Upload ) { upload(); } }
|
||||
|
||||
private:
|
||||
// types:
|
||||
typedef std::stack<glm::mat4> mat4_stack;
|
||||
|
||||
// methods:
|
||||
void
|
||||
upload() { ::glLoadMatrixf( glm::value_ptr( m_stack.top() ) ); }
|
||||
|
||||
// members:
|
||||
mat4_stack m_stack;
|
||||
};
|
||||
|
||||
enum stack_mode { gl_modelview = 0, gl_projection = 1, gl_texture = 2 };
|
||||
typedef std::vector<opengl_stack> openglstack_array;
|
||||
|
||||
public:
|
||||
// constructors:
|
||||
opengl_matrices() {
|
||||
m_stacks.emplace_back(); // modelview
|
||||
m_stacks.emplace_back(); // projection
|
||||
m_stacks.emplace_back(); // texture
|
||||
}
|
||||
|
||||
// methods:
|
||||
bool &
|
||||
upload() {
|
||||
return m_upload; }
|
||||
void
|
||||
mode( GLuint const Mode ) {
|
||||
switch( Mode ) {
|
||||
case GL_MODELVIEW: { m_mode = stack_mode::gl_modelview; break; }
|
||||
case GL_PROJECTION: { m_mode = stack_mode::gl_projection; break; }
|
||||
case GL_TEXTURE: { m_mode = stack_mode::gl_texture; break; }
|
||||
default: { break; } }
|
||||
if( m_upload ) {::glMatrixMode( Mode ); } }
|
||||
glm::mat4 const &
|
||||
data( GLuint const Mode = -1 ) const {
|
||||
switch( Mode ) {
|
||||
case GL_MODELVIEW: { return m_stacks[ stack_mode::gl_modelview ].data(); }
|
||||
case GL_PROJECTION: { return m_stacks[ stack_mode::gl_projection ].data(); }
|
||||
case GL_TEXTURE: { return m_stacks[ stack_mode::gl_texture ].data(); }
|
||||
default: { return m_stacks[ m_mode ].data(); } } }
|
||||
float const *
|
||||
data_array( GLuint const Mode = -1 ) const {
|
||||
return glm::value_ptr( data( Mode ) ); }
|
||||
void
|
||||
push_matrix() { m_stacks[ m_mode ].push_matrix(); }
|
||||
void
|
||||
pop_matrix() { m_stacks[ m_mode ].pop_matrix( m_upload ); }
|
||||
void
|
||||
load_identity() { m_stacks[ m_mode ].load_identity( m_upload ); }
|
||||
void
|
||||
load_matrix( glm::mat4 const &Matrix ) { m_stacks[ m_mode ].load_matrix( Matrix, m_upload ); }
|
||||
template <typename Type_>
|
||||
void
|
||||
load_matrix( Type_ const *Matrix ) { load_matrix( glm::make_mat4( Matrix ) ); }
|
||||
template <typename Type_>
|
||||
void
|
||||
rotate( Type_ const Angle, Type_ const X, Type_ const Y, Type_ const Z ) {
|
||||
m_stacks[ m_mode ].rotate(
|
||||
static_cast<float>(glm::radians(Angle)),
|
||||
glm::vec3(
|
||||
static_cast<float>( X ),
|
||||
static_cast<float>( Y ),
|
||||
static_cast<float>( Z ) ),
|
||||
m_upload ); }
|
||||
template <typename Type_>
|
||||
void
|
||||
translate( Type_ const X, Type_ const Y, Type_ const Z ) {
|
||||
m_stacks[ m_mode ].translate(
|
||||
glm::vec3(
|
||||
static_cast<float>( X ),
|
||||
static_cast<float>( Y ),
|
||||
static_cast<float>( Z ) ),
|
||||
m_upload ); }
|
||||
template <typename Type_>
|
||||
void
|
||||
scale( Type_ const X, Type_ const Y, Type_ const Z ) {
|
||||
m_stacks[ m_mode ].scale(
|
||||
glm::vec3(
|
||||
static_cast<float>( X ),
|
||||
static_cast<float>( Y ),
|
||||
static_cast<float>( Z ) ),
|
||||
m_upload ); }
|
||||
template <typename Type_>
|
||||
void
|
||||
multiply( Type_ const *Matrix ) {
|
||||
m_stacks[ m_mode ].multiply(
|
||||
glm::make_mat4( Matrix ),
|
||||
m_upload ); }
|
||||
template <typename Type_>
|
||||
void
|
||||
ortho( Type_ const Left, Type_ const Right, Type_ const Bottom, Type_ const Top, Type_ const Znear, Type_ const Zfar ) {
|
||||
m_stacks[ m_mode ].ortho(
|
||||
static_cast<float>( Left ),
|
||||
static_cast<float>( Right ),
|
||||
static_cast<float>( Bottom ),
|
||||
static_cast<float>( Top ),
|
||||
static_cast<float>( Znear ),
|
||||
static_cast<float>( Zfar ),
|
||||
m_upload ); }
|
||||
template <typename Type_>
|
||||
void
|
||||
perspective( Type_ const Fovy, Type_ const Aspect, Type_ const Znear, Type_ const Zfar ) {
|
||||
m_stacks[ m_mode ].perspective(
|
||||
static_cast<float>( glm::radians( Fovy ) ),
|
||||
static_cast<float>( Aspect ),
|
||||
static_cast<float>( Znear ),
|
||||
static_cast<float>( Zfar ),
|
||||
m_upload ); }
|
||||
template <typename Type_>
|
||||
void
|
||||
look_at( Type_ const Eyex, Type_ const Eyey, Type_ const Eyez, Type_ const Centerx, Type_ const Centery, Type_ const Centerz, Type_ const Upx, Type_ const Upy, Type_ const Upz ) {
|
||||
m_stacks[ m_mode ].look_at(
|
||||
glm::vec3(
|
||||
static_cast<float>( Eyex ),
|
||||
static_cast<float>( Eyey ),
|
||||
static_cast<float>( Eyez ) ),
|
||||
glm::vec3(
|
||||
static_cast<float>( Centerx ),
|
||||
static_cast<float>( Centery ),
|
||||
static_cast<float>( Centerz ) ),
|
||||
glm::vec3(
|
||||
static_cast<float>( Upx ),
|
||||
static_cast<float>( Upy ),
|
||||
static_cast<float>( Upz ) ),
|
||||
m_upload ); }
|
||||
|
||||
private:
|
||||
// members:
|
||||
stack_mode m_mode{ stack_mode::gl_projection };
|
||||
openglstack_array m_stacks;
|
||||
bool m_upload { true };
|
||||
};
|
||||
|
||||
extern opengl_matrices OpenGLMatrices;
|
||||
|
||||
// NOTE: standard opengl calls re-definitions
|
||||
|
||||
#undef glMatrixMode
|
||||
#undef glPushMatrix
|
||||
#undef glPopMatrix
|
||||
#undef glLoadIdentity
|
||||
#undef glLoadMatrixf
|
||||
#undef glLoadMatrixd
|
||||
#undef glRotated
|
||||
#undef glRotatef
|
||||
#undef glTranslated
|
||||
#undef glTranslatef
|
||||
#undef glScaled
|
||||
#undef glScalef
|
||||
#undef glMultMatrixd
|
||||
#undef glMultMatrixf
|
||||
#undef glOrtho
|
||||
#undef gluPerspective
|
||||
#undef gluLookAt
|
||||
|
||||
#define glMatrixMode OpenGLMatrices.mode
|
||||
#define glPushMatrix OpenGLMatrices.push_matrix
|
||||
#define glPopMatrix OpenGLMatrices.pop_matrix
|
||||
#define glLoadIdentity OpenGLMatrices.load_identity
|
||||
#define glLoadMatrixf OpenGLMatrices.load_matrix
|
||||
#define glLoadMatrixd OpenGLMatrices.load_matrix
|
||||
#define glRotated OpenGLMatrices.rotate
|
||||
#define glRotatef OpenGLMatrices.rotate
|
||||
#define glTranslated OpenGLMatrices.translate
|
||||
#define glTranslatef OpenGLMatrices.translate
|
||||
#define glScaled OpenGLMatrices.scale
|
||||
#define glScalef OpenGLMatrices.scale
|
||||
#define glMultMatrixd OpenGLMatrices.multiply
|
||||
#define glMultMatrixf OpenGLMatrices.multiply
|
||||
#define glOrtho OpenGLMatrices.ortho
|
||||
#define gluPerspective OpenGLMatrices.perspective
|
||||
#define gluLookAt OpenGLMatrices.look_at
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
158
rendering/openglparticles.cpp
Normal file
158
rendering/openglparticles.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openglparticles.h"
|
||||
|
||||
#include "particles.h"
|
||||
#include "openglcamera.h"
|
||||
#include "simulation.h"
|
||||
#include "Logs.h"
|
||||
|
||||
std::vector<std::pair<glm::vec3, glm::vec2>> const billboard_vertices {
|
||||
|
||||
{ { -0.5f, -0.5f, 0.f }, { 0.f, 0.f } },
|
||||
{ { 0.5f, -0.5f, 0.f }, { 1.f, 0.f } },
|
||||
{ { 0.5f, 0.5f, 0.f }, { 1.f, 1.f } },
|
||||
{ { -0.5f, 0.5f, 0.f }, { 0.f, 1.f } }
|
||||
};
|
||||
|
||||
void
|
||||
opengl_particles::update( opengl_camera const &Camera ) {
|
||||
|
||||
m_particlevertices.clear();
|
||||
|
||||
if( false == Global.Smoke ) { return; }
|
||||
|
||||
// build a list of visible smoke sources
|
||||
// NOTE: arranged by distance to camera, if we ever need sorting and/or total amount cap-based culling
|
||||
std::multimap<float, smoke_source const &> sources;
|
||||
|
||||
for( auto const &source : simulation::Particles.sequence() ) {
|
||||
if( false == Camera.visible( source.area() ) ) { continue; }
|
||||
// NOTE: the distance is negative when the camera is inside the source's bounding area
|
||||
sources.emplace(
|
||||
static_cast<float>( glm::length( Camera.position() - source.area().center ) - source.area().radius ),
|
||||
source );
|
||||
}
|
||||
|
||||
if( true == sources.empty() ) { return; }
|
||||
|
||||
// build billboard data for particles from visible sources
|
||||
auto const camerarotation { glm::mat3( Camera.modelview() ) };
|
||||
particle_vertex vertex;
|
||||
for( auto const &source : sources ) {
|
||||
|
||||
auto const particlecolor {
|
||||
glm::clamp(
|
||||
source.second.color()
|
||||
* ( glm::vec3 { Global.DayLight.ambient } + 0.35f * glm::vec3{ Global.DayLight.diffuse } )
|
||||
* 255.f,
|
||||
glm::vec3{ 0.f }, glm::vec3{ 255.f } ) };
|
||||
auto const &particles { source.second.sequence() };
|
||||
// TODO: put sanity cap on the overall amount of particles that can be drawn
|
||||
auto const sizestep { 256.0 * billboard_vertices.size() };
|
||||
m_particlevertices.reserve(
|
||||
sizestep * std::ceil( m_particlevertices.size() + ( particles.size() * billboard_vertices.size() ) / sizestep ) );
|
||||
for( auto const &particle : particles ) {
|
||||
// TODO: particle color support
|
||||
vertex.color[ 0 ] = static_cast<std::uint_fast8_t>( particlecolor.r );
|
||||
vertex.color[ 1 ] = static_cast<std::uint_fast8_t>( particlecolor.g );
|
||||
vertex.color[ 2 ] = static_cast<std::uint_fast8_t>( particlecolor.b );
|
||||
vertex.color[ 3 ] = clamp<std::uint8_t>( particle.opacity * 255, 0, 255 );
|
||||
|
||||
auto const offset { glm::vec3{ particle.position - Camera.position() } };
|
||||
auto const rotation { glm::angleAxis( particle.rotation, glm::vec3{ 0.f, 0.f, 1.f } ) };
|
||||
|
||||
for( auto const &billboardvertex : billboard_vertices ) {
|
||||
vertex.position = offset + ( rotation * billboardvertex.first * particle.size ) * camerarotation;
|
||||
vertex.texture = billboardvertex.second;
|
||||
|
||||
m_particlevertices.emplace_back( vertex );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ship the billboard data to the gpu:
|
||||
// setup...
|
||||
::glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT );
|
||||
// ...make sure we have enough room...
|
||||
if( m_buffercapacity < m_particlevertices.size() ) {
|
||||
// allocate gpu side buffer big enough to hold the data
|
||||
m_buffercapacity = 0;
|
||||
if( m_buffer != (GLuint)-1 ) {
|
||||
// get rid of the old buffer
|
||||
::glDeleteBuffers( 1, &m_buffer );
|
||||
m_buffer = (GLuint)-1;
|
||||
}
|
||||
::glGenBuffers( 1, &m_buffer );
|
||||
if( ( m_buffer > 0 ) && ( m_buffer != (GLuint)-1 ) ) {
|
||||
// if we didn't get a buffer we'll try again during the next draw call
|
||||
// NOTE: we match capacity instead of current size to reduce number of re-allocations
|
||||
auto const particlecount { m_particlevertices.capacity() };
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_buffer );
|
||||
::glBufferData(
|
||||
GL_ARRAY_BUFFER,
|
||||
particlecount * sizeof( particle_vertex ),
|
||||
nullptr,
|
||||
GL_DYNAMIC_DRAW );
|
||||
if( ::glGetError() == GL_OUT_OF_MEMORY ) {
|
||||
// TBD: throw a bad_alloc?
|
||||
ErrorLog( "openGL error: out of memory; failed to create a geometry buffer" );
|
||||
::glDeleteBuffers( 1, &m_buffer );
|
||||
m_buffer = (GLuint)-1;
|
||||
}
|
||||
else {
|
||||
m_buffercapacity = particlecount;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ...send the data...
|
||||
if( ( m_buffer > 0 ) && ( m_buffer != (GLuint)-1 ) ) {
|
||||
// if the buffer exists at this point it's guaranteed to be big enough to hold our data
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_buffer );
|
||||
::glBufferSubData(
|
||||
GL_ARRAY_BUFFER,
|
||||
0,
|
||||
m_particlevertices.size() * sizeof( particle_vertex ),
|
||||
m_particlevertices.data() );
|
||||
}
|
||||
// ...and cleanup
|
||||
::glPopClientAttrib();
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, 0 );
|
||||
}
|
||||
|
||||
std::size_t
|
||||
opengl_particles::render( GLint const Textureunit ) {
|
||||
|
||||
if( false == Global.Smoke ) { return 0; }
|
||||
if( m_buffercapacity == 0 ) { return 0; }
|
||||
if( m_particlevertices.empty() ) { return 0; }
|
||||
if( ( m_buffer == 0 ) || ( m_buffer == (GLuint)-1 ) ) { return 0; }
|
||||
|
||||
// setup...
|
||||
::glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT );
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_buffer );
|
||||
::glVertexPointer( 3, GL_FLOAT, sizeof( particle_vertex ), static_cast<char *>( nullptr ) );
|
||||
::glEnableClientState( GL_VERTEX_ARRAY );
|
||||
::glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( particle_vertex ), reinterpret_cast<void const *>( sizeof( float ) * 3 ) );
|
||||
::glEnableClientState( GL_COLOR_ARRAY );
|
||||
::glClientActiveTexture( GL_TEXTURE0 + Textureunit );
|
||||
::glTexCoordPointer( 2, GL_FLOAT, sizeof( particle_vertex ), reinterpret_cast<void const *>( sizeof( float ) * 3 + sizeof( std::uint8_t ) * 4 ) );
|
||||
::glEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
// ...draw...
|
||||
::glDrawArrays( GL_QUADS, 0, m_particlevertices.size() );
|
||||
// ...and cleanup
|
||||
::glPopClientAttrib();
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, 0 );
|
||||
|
||||
return m_particlevertices.size() / 4;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
51
rendering/openglparticles.h
Normal file
51
rendering/openglparticles.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class opengl_camera;
|
||||
|
||||
// particle data visualizer
|
||||
class opengl_particles {
|
||||
public:
|
||||
// constructors
|
||||
opengl_particles() = default;
|
||||
// destructor
|
||||
~opengl_particles() {
|
||||
if( m_buffer != -1 ) {
|
||||
::glDeleteBuffers( 1, &m_buffer ); } }
|
||||
// methods
|
||||
void
|
||||
update( opengl_camera const &Camera );
|
||||
std::size_t
|
||||
render( GLint const Textureunit );
|
||||
private:
|
||||
// types
|
||||
struct particle_vertex {
|
||||
glm::vec3 position; // 3d space
|
||||
std::uint8_t color[ 4 ]; // rgba, unsigned byte format
|
||||
glm::vec2 texture; // uv space
|
||||
float padding[ 2 ]; // experimental, some gfx hardware allegedly works better with 32-bit aligned data blocks
|
||||
};
|
||||
/*
|
||||
using sourcedistance_pair = std::pair<smoke_source *, float>;
|
||||
using source_sequence = std::vector<sourcedistance_pair>;
|
||||
*/
|
||||
using particlevertex_sequence = std::vector<particle_vertex>;
|
||||
// methods
|
||||
// members
|
||||
/*
|
||||
source_sequence m_sources; // list of particle sources visible in current render pass, with their respective distances to the camera
|
||||
*/
|
||||
particlevertex_sequence m_particlevertices; // geometry data of visible particles, generated on the cpu end
|
||||
GLuint m_buffer{ (GLuint)-1 }; // id of the buffer holding geometry data on the opengl end
|
||||
std::size_t m_buffercapacity{ 0 }; // total capacity of the last established buffer
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
173
rendering/openglprecipitation.cpp
Normal file
173
rendering/openglprecipitation.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openglprecipitation.h"
|
||||
|
||||
#include "Globals.h"
|
||||
#include "renderer.h"
|
||||
#include "simulationenvironment.h"
|
||||
|
||||
opengl_precipitation::~opengl_precipitation() {
|
||||
|
||||
if( m_vertexbuffer != -1 ) { ::glDeleteBuffers( 1, &m_vertexbuffer ); }
|
||||
if( m_indexbuffer != -1 ) { ::glDeleteBuffers( 1, &m_indexbuffer ); }
|
||||
if( m_uvbuffer != -1 ) { ::glDeleteBuffers( 1, &m_uvbuffer ); }
|
||||
}
|
||||
|
||||
void
|
||||
opengl_precipitation::create( int const Tesselation ) {
|
||||
|
||||
m_vertices.clear();
|
||||
m_uvs.clear();
|
||||
m_indices.clear();
|
||||
|
||||
auto const heightfactor { 10.f }; // height-to-radius factor
|
||||
auto const verticaltexturestretchfactor { 1.5f }; // crude motion blur
|
||||
|
||||
// create geometry chunk
|
||||
auto const latitudes { 3 }; // just a cylinder with end cones
|
||||
auto const longitudes { Tesselation };
|
||||
auto const longitudehalfstep { 0.5f * static_cast<float>( 2.0 * M_PI * 1.f / longitudes ) }; // for crude uv correction
|
||||
|
||||
std::uint16_t index = 0;
|
||||
|
||||
// auto const radius { 25.f }; // cylinder radius
|
||||
std::vector<float> radii { 25.f, 10.f, 5.f, 1.f };
|
||||
for( auto radius : radii ) {
|
||||
|
||||
for( int i = 0; i <= latitudes; ++i ) {
|
||||
|
||||
auto const latitude{ static_cast<float>( M_PI * ( -0.5f + (float)( i ) / latitudes ) ) };
|
||||
auto const z{ std::sin( latitude ) };
|
||||
auto const zr{ std::cos( latitude ) };
|
||||
|
||||
for( int j = 0; j <= longitudes; ++j ) {
|
||||
// NOTE: for the first and last row half of the points we create end up unused but, eh
|
||||
auto const longitude{ static_cast<float>( 2.0 * M_PI * (float)( j ) / longitudes ) };
|
||||
auto const x{ std::cos( longitude ) };
|
||||
auto const y{ std::sin( longitude ) };
|
||||
// NOTE: cartesian to opengl swap would be: -x, -z, -y
|
||||
m_vertices.emplace_back( glm::vec3( -x * zr, -z * heightfactor, -y * zr ) * radius );
|
||||
// uvs
|
||||
// NOTE: first and last row receives modified u values to deal with limitation of mapping onto triangles
|
||||
auto u = (
|
||||
i == 0 ? longitude + longitudehalfstep :
|
||||
i == latitudes ? longitude - longitudehalfstep :
|
||||
longitude );
|
||||
m_uvs.emplace_back(
|
||||
u / ( 2.0 * M_PI ) * radius,
|
||||
1.f - (float)( i ) / latitudes * radius * heightfactor * 0.5f / verticaltexturestretchfactor );
|
||||
|
||||
if( ( i == 0 ) || ( j == 0 ) ) {
|
||||
// initial edge of the dome, don't start indices yet
|
||||
++index;
|
||||
}
|
||||
else {
|
||||
// the end cones are built from one triangle of each quad, the middle rows use both
|
||||
if( i < latitudes ) {
|
||||
m_indices.emplace_back( index - 1 - ( longitudes + 1 ) );
|
||||
m_indices.emplace_back( index - 1 );
|
||||
m_indices.emplace_back( index );
|
||||
}
|
||||
if( i > 1 ) {
|
||||
m_indices.emplace_back( index );
|
||||
m_indices.emplace_back( index - ( longitudes + 1 ) );
|
||||
m_indices.emplace_back( index - 1 - ( longitudes + 1 ) );
|
||||
}
|
||||
++index;
|
||||
}
|
||||
} // longitude
|
||||
} // latitude
|
||||
} // radius
|
||||
}
|
||||
|
||||
void
|
||||
opengl_precipitation::update() {
|
||||
|
||||
// NOTE: we should really be checking state of each buffer as theoretically allocation could go wrong mid-way, but, eh
|
||||
if( m_vertexbuffer == (GLuint)-1 ) {
|
||||
|
||||
if( m_vertices.empty() ) {
|
||||
// create visualization mesh
|
||||
create( 18 );
|
||||
}
|
||||
// cache entry state
|
||||
::glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT );
|
||||
|
||||
// build the buffers
|
||||
::glGenBuffers( 1, &m_vertexbuffer );
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer );
|
||||
::glBufferData( GL_ARRAY_BUFFER, m_vertices.size() * sizeof( glm::vec3 ), m_vertices.data(), GL_STATIC_DRAW );
|
||||
|
||||
::glGenBuffers( 1, &m_uvbuffer );
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_uvbuffer );
|
||||
::glBufferData( GL_ARRAY_BUFFER, m_uvs.size() * sizeof( glm::vec2 ), m_uvs.data(), GL_STATIC_DRAW );
|
||||
|
||||
::glGenBuffers( 1, &m_indexbuffer );
|
||||
::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer );
|
||||
::glBufferData( GL_ELEMENT_ARRAY_BUFFER, m_indices.size() * sizeof( unsigned short ), m_indices.data(), GL_STATIC_DRAW );
|
||||
// NOTE: vertex and index source data is superfluous past this point, but, eh
|
||||
|
||||
// cleanup
|
||||
::glPopClientAttrib();
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, 0 );
|
||||
::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
|
||||
}
|
||||
|
||||
// TODO: include weather type check in the entry conditions
|
||||
if( m_overcast == Global.Overcast ) { return; }
|
||||
|
||||
m_overcast = Global.Overcast;
|
||||
|
||||
std::string const densitysuffix { (
|
||||
m_overcast < 1.35 ?
|
||||
"_light" :
|
||||
"_medium" ) };
|
||||
if( Global.Weather == "rain:" ) {
|
||||
m_texture = GfxRenderer->Fetch_Texture( "fx/rain" + densitysuffix );
|
||||
}
|
||||
else if( Global.Weather == "snow:" ) {
|
||||
m_texture = GfxRenderer->Fetch_Texture( "fx/snow" + densitysuffix );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
opengl_precipitation::render( GLint const Textureunit ) {
|
||||
|
||||
if( m_texture == null_handle ) { return; }
|
||||
|
||||
GfxRenderer->Bind_Texture( m_texture );
|
||||
|
||||
// cache entry state
|
||||
::glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT );
|
||||
|
||||
// positions
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer );
|
||||
::glVertexPointer( 3, GL_FLOAT, sizeof( glm::vec3 ), reinterpret_cast<void const*>( 0 ) );
|
||||
::glEnableClientState( GL_VERTEX_ARRAY );
|
||||
// uvs
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_uvbuffer );
|
||||
::glClientActiveTexture( GL_TEXTURE0 + Textureunit );
|
||||
::glTexCoordPointer( 2, GL_FLOAT, sizeof( glm::vec2 ), reinterpret_cast<void const*>( 0 ) );
|
||||
::glEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
// uv transformation matrix
|
||||
::glMatrixMode( GL_TEXTURE );
|
||||
::glLoadIdentity();
|
||||
::glTranslatef( 0.f, simulation::Environment.precipitation().get_textureoffset(), 0.f );
|
||||
// indices
|
||||
::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer );
|
||||
::glDrawElements( GL_TRIANGLES, static_cast<GLsizei>( m_indices.size() ), GL_UNSIGNED_SHORT, reinterpret_cast<void const*>( 0 ) );
|
||||
// cleanup
|
||||
::glLoadIdentity();
|
||||
::glMatrixMode( GL_MODELVIEW );
|
||||
::glPopClientAttrib();
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, 0 );
|
||||
::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
|
||||
}
|
||||
39
rendering/openglprecipitation.h
Normal file
39
rendering/openglprecipitation.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Texture.h"
|
||||
|
||||
class opengl_precipitation {
|
||||
|
||||
public:
|
||||
// constructors
|
||||
opengl_precipitation() = default;
|
||||
// destructor
|
||||
~opengl_precipitation();
|
||||
// methods
|
||||
void
|
||||
update();
|
||||
void
|
||||
render( GLint const Textureunit );
|
||||
|
||||
private:
|
||||
// methods
|
||||
void create( int const Tesselation );
|
||||
// members
|
||||
std::vector<glm::vec3> m_vertices;
|
||||
std::vector<glm::vec2> m_uvs;
|
||||
std::vector<std::uint16_t> m_indices;
|
||||
GLuint m_vertexbuffer { (GLuint)-1 };
|
||||
GLuint m_uvbuffer { (GLuint)-1 };
|
||||
GLuint m_indexbuffer { (GLuint)-1 };
|
||||
texture_handle m_texture { null_handle };
|
||||
float m_overcast { -1.f }; // cached overcast level, difference from current state triggers texture update
|
||||
};
|
||||
4548
rendering/openglrenderer.cpp
Normal file
4548
rendering/openglrenderer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
387
rendering/openglrenderer.h
Normal file
387
rendering/openglrenderer.h
Normal file
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "renderer.h"
|
||||
#include "opengllight.h"
|
||||
#include "openglcamera.h"
|
||||
#include "openglparticles.h"
|
||||
#include "openglskydome.h"
|
||||
#include "openglprecipitation.h"
|
||||
#include "lightarray.h"
|
||||
#include "scene.h"
|
||||
#include "simulationenvironment.h"
|
||||
#include "MemCell.h"
|
||||
|
||||
#define EU07_USE_PICKING_FRAMEBUFFER
|
||||
//#define EU07_USE_DEBUG_SHADOWMAP
|
||||
//#define EU07_USE_DEBUG_CABSHADOWMAP
|
||||
//#define EU07_USE_DEBUG_CAMERA
|
||||
//#define EU07_USE_DEBUG_SOUNDEMITTERS
|
||||
//#define EU07_DISABLECABREFLECTIONS
|
||||
|
||||
// bare-bones render controller, in lack of anything better yet
|
||||
class opengl_renderer : public gfx_renderer {
|
||||
|
||||
public:
|
||||
// types
|
||||
// constructors
|
||||
opengl_renderer() = default;
|
||||
// destructor
|
||||
~opengl_renderer() { }
|
||||
// methods
|
||||
bool
|
||||
Init( GLFWwindow *Window ) override;
|
||||
// main draw call. returns false on error
|
||||
bool
|
||||
Render() override;
|
||||
void
|
||||
SwapBuffers() override;
|
||||
inline
|
||||
float
|
||||
Framerate() override { return m_framerate; }
|
||||
|
||||
bool AddViewport(const global_settings::extraviewport_config &conf) override { return false; }
|
||||
bool Debug_Ui_State(std::optional<bool>) override { return false; }
|
||||
void Shutdown() override {}
|
||||
|
||||
// geometry methods
|
||||
// NOTE: hands-on geometry management is exposed as a temporary measure; ultimately all visualization data should be generated/handled automatically by the renderer itself
|
||||
// creates a new geometry bank. returns: handle to the bank or NULL
|
||||
gfx::geometrybank_handle
|
||||
Create_Bank() override;
|
||||
// creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
|
||||
gfx::geometry_handle Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override;
|
||||
// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
|
||||
gfx::geometry_handle Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override;
|
||||
// replaces data of specified chunk with the supplied vertex data, starting from specified offset
|
||||
bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) override;
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override;
|
||||
// provides direct access to index data of specfied chunk
|
||||
gfx::index_array const &
|
||||
Indices( gfx::geometry_handle const &Geometry ) const override;
|
||||
// provides direct access to vertex data of specfied chunk
|
||||
gfx::vertex_array const &
|
||||
Vertices( gfx::geometry_handle const &Geometry ) const override;
|
||||
// provides direct access to vertex data of specfied chunk
|
||||
gfx::userdata_array const &
|
||||
UserData( gfx::geometry_handle const &Geometry ) const override;
|
||||
// material methods
|
||||
material_handle
|
||||
Fetch_Material( std::string const &Filename, bool const Loadnow = true ) override;
|
||||
void
|
||||
Bind_Material( material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr ) override;
|
||||
IMaterial const *
|
||||
Material( material_handle const Material ) const override;
|
||||
// shader methods
|
||||
auto Fetch_Shader( std::string const &name ) -> std::shared_ptr<gl::program> override;
|
||||
// texture methods
|
||||
texture_handle
|
||||
Fetch_Texture( std::string const &Filename, bool const Loadnow = true, GLint format_hint = GL_SRGB_ALPHA ) override;
|
||||
void
|
||||
Bind_Texture( texture_handle const Texture ) override;
|
||||
void
|
||||
Bind_Texture( std::size_t const Unit, texture_handle const Texture ) override;
|
||||
ITexture &
|
||||
Texture( texture_handle const Texture ) override;
|
||||
ITexture const &
|
||||
Texture( texture_handle const Texture ) const override;
|
||||
// utility methods
|
||||
void
|
||||
Pick_Control_Callback( std::function<void( TSubModel const *, const glm::vec2 )> Callback ) override;
|
||||
void
|
||||
Pick_Node_Callback( std::function<void( scene::basic_node * )> Callback ) override;
|
||||
TSubModel const *
|
||||
Pick_Control() const override { return m_pickcontrolitem; }
|
||||
scene::basic_node const *
|
||||
Pick_Node() const override { return m_picksceneryitem; }
|
||||
glm::dvec3
|
||||
Mouse_Position() const override { return m_worldmousecoordinates; }
|
||||
// maintenance methods
|
||||
void
|
||||
Update( double const Deltatime ) override;
|
||||
void
|
||||
Update_Pick_Control() override;
|
||||
void
|
||||
Update_Pick_Node() override;
|
||||
glm::dvec3
|
||||
Update_Mouse_Position() override;
|
||||
// debug methods
|
||||
std::string const &
|
||||
info_times() const override;
|
||||
std::string const &
|
||||
info_stats() const override;
|
||||
void MakeScreenshot() override;
|
||||
|
||||
opengl_material const & Material( TSubModel const * Submodel ) const;
|
||||
|
||||
// members
|
||||
GLenum static const sunlight { GL_LIGHT0 };
|
||||
|
||||
static std::unique_ptr<gfx_renderer> create_func();
|
||||
|
||||
private:
|
||||
// types
|
||||
enum class rendermode {
|
||||
none,
|
||||
color,
|
||||
shadows,
|
||||
cabshadows,
|
||||
reflections,
|
||||
pickcontrols,
|
||||
pickscenery
|
||||
};
|
||||
|
||||
enum textureunit {
|
||||
helper = 0,
|
||||
shadows,
|
||||
normals,
|
||||
diffuse
|
||||
};
|
||||
|
||||
struct debug_stats {
|
||||
int dynamics { 0 };
|
||||
int models { 0 };
|
||||
int submodels { 0 };
|
||||
int paths { 0 };
|
||||
int traction { 0 };
|
||||
int shapes { 0 };
|
||||
int lines { 0 };
|
||||
int particles { 0 };
|
||||
int drawcalls { 0 };
|
||||
};
|
||||
|
||||
using section_sequence = std::vector<scene::basic_section *>;
|
||||
using distancecell_pair = std::pair<double, scene::basic_cell *>;
|
||||
using cell_sequence = std::vector<distancecell_pair>;
|
||||
|
||||
struct renderpass_config {
|
||||
|
||||
opengl_camera camera;
|
||||
rendermode draw_mode { rendermode::none };
|
||||
float draw_range { 0.0f };
|
||||
debug_stats draw_stats;
|
||||
};
|
||||
|
||||
struct units_state {
|
||||
|
||||
bool diffuse { false };
|
||||
bool shadows { false };
|
||||
bool reflections { false };
|
||||
};
|
||||
|
||||
typedef std::vector<opengl_light> opengllight_array;
|
||||
|
||||
// methods
|
||||
void
|
||||
Disable_Lights();
|
||||
void
|
||||
setup_pass( renderpass_config &Config, rendermode const Mode, float const Znear = 0.f, float const Zfar = 1.f, bool const Ignoredebug = false );
|
||||
void
|
||||
setup_matrices();
|
||||
void
|
||||
setup_drawing( bool const Alpha = false );
|
||||
void
|
||||
setup_units( bool const Diffuse, bool const Shadows, bool const Reflections );
|
||||
void
|
||||
setup_shadow_map( GLuint const Texture, glm::mat4 const &Transformation );
|
||||
void
|
||||
setup_shadow_color( glm::vec4 const &Shadowcolor );
|
||||
void
|
||||
setup_environment_light( TEnvironmentType const Environment = e_flat );
|
||||
void
|
||||
switch_units( bool const Diffuse, bool const Shadows, bool const Reflections );
|
||||
// helper, texture manager method; activates specified texture unit
|
||||
void
|
||||
select_unit( GLint const Textureunit );
|
||||
// runs jobs needed to generate graphics for specified render pass
|
||||
void
|
||||
Render_pass( rendermode const Mode );
|
||||
// creates dynamic environment cubemap
|
||||
bool
|
||||
Render_reflections();
|
||||
bool
|
||||
Render( world_environment *Environment );
|
||||
void
|
||||
Render( scene::basic_region *Region );
|
||||
void
|
||||
Render( section_sequence::iterator First, section_sequence::iterator Last );
|
||||
void
|
||||
Render( cell_sequence::iterator First, cell_sequence::iterator Last );
|
||||
void
|
||||
Render( scene::shape_node const &Shape, bool const Ignorerange );
|
||||
void
|
||||
Render( TAnimModel *Instance );
|
||||
bool
|
||||
Render( TDynamicObject *Dynamic );
|
||||
bool
|
||||
Render( TModel3d *Model, material_data const *Material, float const Squaredistance, Math3D::vector3 const &Position, glm::vec3 const &Angle );
|
||||
bool
|
||||
Render( TModel3d *Model, material_data const *Material, float const Squaredistance );
|
||||
void
|
||||
Render( TSubModel *Submodel );
|
||||
void
|
||||
Render( TTrack *Track );
|
||||
void
|
||||
Render( scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last );
|
||||
bool
|
||||
Render_cab( TDynamicObject const *Dynamic, float const Lightlevel, bool const Alpha = false );
|
||||
bool
|
||||
Render_interior( bool const Alpha = false );
|
||||
bool
|
||||
Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha = false );
|
||||
bool
|
||||
Render_coupler_adapter( TDynamicObject *Dynamic, float const Squaredistance, int const End, bool const Alpha = false );
|
||||
void
|
||||
Render( TMemCell *Memcell );
|
||||
void
|
||||
Render_particles();
|
||||
void
|
||||
Render_precipitation();
|
||||
void
|
||||
Render_Alpha( scene::basic_region *Region );
|
||||
void
|
||||
Render_Alpha( cell_sequence::reverse_iterator First, cell_sequence::reverse_iterator Last );
|
||||
void
|
||||
Render_Alpha( TAnimModel *Instance );
|
||||
void
|
||||
Render_Alpha( TTraction *Traction );
|
||||
void
|
||||
Render_Alpha( scene::lines_node const &Lines );
|
||||
bool
|
||||
Render_Alpha( TDynamicObject *Dynamic );
|
||||
bool
|
||||
Render_Alpha( TModel3d *Model, material_data const *Material, float const Squaredistance, Math3D::vector3 const &Position, glm::vec3 const &Angle );
|
||||
bool
|
||||
Render_Alpha( TModel3d *Model, material_data const *Material, float const Squaredistance );
|
||||
void
|
||||
Render_Alpha( TSubModel *Submodel );
|
||||
void
|
||||
Update_Lights( light_array &Lights );
|
||||
bool
|
||||
Init_caps();
|
||||
glm::vec3
|
||||
pick_color( std::size_t const Index );
|
||||
std::size_t
|
||||
pick_index( glm::ivec3 const &Color );
|
||||
|
||||
// members
|
||||
GLFWwindow *m_window { nullptr };
|
||||
gfx::geometrybank_manager m_geometry;
|
||||
material_manager m_materials;
|
||||
texture_manager m_textures;
|
||||
opengl_light m_sunlight;
|
||||
opengllight_array m_lights;
|
||||
opengl_skydome m_skydomerenderer;
|
||||
opengl_precipitation m_precipitationrenderer;
|
||||
opengl_particles m_particlerenderer; // particle visualization subsystem
|
||||
/*
|
||||
float m_sunandviewangle; // cached dot product of sunlight and camera vectors
|
||||
*/
|
||||
gfx::geometry_handle m_billboardgeometry { 0, 0 };
|
||||
texture_handle m_glaretexture { -1 };
|
||||
texture_handle m_suntexture { -1 };
|
||||
texture_handle m_moontexture { -1 };
|
||||
texture_handle m_reflectiontexture { -1 };
|
||||
texture_handle m_smoketexture { -1 };
|
||||
material_handle m_invalid_material;
|
||||
// TODO: refactor framebuffer stuff into an object
|
||||
bool m_framebuffersupport { false };
|
||||
#ifdef EU07_USE_PICKING_FRAMEBUFFER
|
||||
GLuint m_pickframebuffer { 0 };
|
||||
GLuint m_picktexture { 0 };
|
||||
GLuint m_pickdepthbuffer { 0 };
|
||||
#endif
|
||||
// main shadowmap resources
|
||||
int m_shadowbuffersize { 2048 };
|
||||
GLuint m_shadowframebuffer { 0 };
|
||||
GLuint m_shadowtexture { 0 };
|
||||
#ifdef EU07_USE_DEBUG_SHADOWMAP
|
||||
GLuint m_shadowdebugtexture{ 0 };
|
||||
#endif
|
||||
#ifdef EU07_USE_DEBUG_CABSHADOWMAP
|
||||
GLuint m_cabshadowdebugtexture{ 0 };
|
||||
#endif
|
||||
glm::mat4 m_shadowtexturematrix; // conversion from camera-centric world space to light-centric clip space
|
||||
// cab shadowmap resources
|
||||
GLuint m_cabshadowframebuffer { 0 };
|
||||
GLuint m_cabshadowtexture { 0 };
|
||||
glm::mat4 m_cabshadowtexturematrix; // conversion from cab-centric world space to light-centric clip space
|
||||
// environment map resources
|
||||
GLuint m_environmentframebuffer { 0 };
|
||||
GLuint m_environmentcubetexture { 0 };
|
||||
GLuint m_environmentdepthbuffer { 0 };
|
||||
bool m_environmentcubetexturesupport { false }; // indicates whether we can use the dynamic environment cube map
|
||||
int m_environmentcubetextureface { 0 }; // helper, currently processed cube map face
|
||||
double m_environmentupdatetime { 0.0 }; // time of the most recent environment map update
|
||||
glm::dvec3 m_environmentupdatelocation; // coordinates of most recent environment map update
|
||||
|
||||
int m_helpertextureunit { 0 };
|
||||
int m_shadowtextureunit { 1 };
|
||||
int m_normaltextureunit { 2 };
|
||||
int m_diffusetextureunit{ 3 };
|
||||
units_state m_unitstate;
|
||||
|
||||
unsigned int m_framestamp; // id of currently rendered gfx frame
|
||||
float m_framerate;
|
||||
double m_updateaccumulator { 0.0 };
|
||||
// double m_pickupdateaccumulator { 0.0 };
|
||||
std::string m_debugtimestext;
|
||||
std::string m_pickdebuginfo;
|
||||
std::string m_debugstatstext;
|
||||
struct simulation_state {
|
||||
std::string weather;
|
||||
std::string season;
|
||||
} m_simulationstate;
|
||||
|
||||
glm::vec4 m_baseambient { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
glm::vec4 m_shadowcolor { colors::shadow };
|
||||
float m_fogrange { 2000.f };
|
||||
// TEnvironmentType m_environment { e_flat };
|
||||
float m_specularopaquescalefactor { 1.f };
|
||||
float m_speculartranslucentscalefactor { 1.f };
|
||||
bool m_renderspecular{ false }; // controls whether to include specular component in the calculations
|
||||
|
||||
renderpass_config m_renderpass; // parameters for current render pass
|
||||
section_sequence m_sectionqueue; // list of sections in current render pass
|
||||
cell_sequence m_cellqueue;
|
||||
renderpass_config m_colorpass; // parametrs of most recent color pass
|
||||
renderpass_config m_shadowpass; // parametrs of most recent shadowmap pass
|
||||
renderpass_config m_cabshadowpass; // parameters of most recent cab shadowmap pass
|
||||
std::vector<TSubModel *> m_pickcontrolsitems;
|
||||
TSubModel *m_pickcontrolitem { nullptr };
|
||||
std::vector<scene::basic_node *> m_picksceneryitems;
|
||||
scene::basic_node *m_picksceneryitem { nullptr };
|
||||
glm::vec3 m_worldmousecoordinates { 0.f };
|
||||
std::vector<std::function<void( TSubModel const *, const glm::vec2 )>> m_control_pick_requests;
|
||||
std::vector<std::function<void( scene::basic_node * )>> m_node_pick_requests;
|
||||
#ifdef EU07_USE_DEBUG_CAMERA
|
||||
renderpass_config m_worldcamera; // debug item
|
||||
#endif
|
||||
bool m_isATI;
|
||||
|
||||
static bool renderer_register;
|
||||
|
||||
class opengl_imgui_renderer : public imgui_renderer
|
||||
{
|
||||
virtual bool Init() override;
|
||||
virtual void Shutdown() override;
|
||||
virtual void BeginFrame() override;
|
||||
virtual void Render() override;
|
||||
} m_imgui_renderer;
|
||||
|
||||
virtual imgui_renderer *GetImguiRenderer() override
|
||||
{
|
||||
return &m_imgui_renderer;
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
105
rendering/openglskydome.cpp
Normal file
105
rendering/openglskydome.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "openglskydome.h"
|
||||
|
||||
#include "simulationenvironment.h"
|
||||
|
||||
opengl_skydome::~opengl_skydome() {
|
||||
|
||||
if( m_vertexbuffer != -1 ) { ::glDeleteBuffers( 1, &m_vertexbuffer ); }
|
||||
if( m_indexbuffer != -1 ) { ::glDeleteBuffers( 1, &m_indexbuffer ); }
|
||||
if( m_coloursbuffer != -1 ) { ::glDeleteBuffers( 1, &m_coloursbuffer ); }
|
||||
}
|
||||
|
||||
void opengl_skydome::update() {
|
||||
|
||||
auto &skydome { simulation::Environment.skydome() };
|
||||
// cache entry state
|
||||
::glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT );
|
||||
// setup gpu data buffers:
|
||||
// ...static data...
|
||||
if( m_indexbuffer == (GLuint)-1 ) {
|
||||
::glGenBuffers( 1, &m_indexbuffer );
|
||||
if( ( m_indexbuffer > 0 ) && ( m_indexbuffer != (GLuint)-1 ) ) {
|
||||
::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer );
|
||||
auto const &indices { skydome.indices() };
|
||||
::glBufferData( GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof( unsigned short ), indices.data(), GL_STATIC_DRAW );
|
||||
m_indexcount = indices.size();
|
||||
}
|
||||
}
|
||||
if( m_vertexbuffer == (GLuint)-1 ) {
|
||||
::glGenBuffers( 1, &m_vertexbuffer );
|
||||
if( ( m_vertexbuffer > 0 ) && ( m_vertexbuffer != (GLuint)-1 ) ) {
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer );
|
||||
auto const &vertices { skydome.vertices() };
|
||||
::glBufferData( GL_ARRAY_BUFFER, vertices.size() * sizeof( glm::vec3 ), vertices.data(), GL_STATIC_DRAW );
|
||||
}
|
||||
}
|
||||
// ...and dynamic data
|
||||
if( m_coloursbuffer == (GLuint)-1 ) {
|
||||
::glGenBuffers( 1, &m_coloursbuffer );
|
||||
if( ( m_coloursbuffer > 0 ) && ( m_coloursbuffer != (GLuint)-1 ) ) {
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_coloursbuffer );
|
||||
auto const &colors { skydome.colors() };
|
||||
::glBufferData( GL_ARRAY_BUFFER, colors.size() * sizeof( glm::vec3 ), colors.data(), GL_DYNAMIC_DRAW );
|
||||
}
|
||||
}
|
||||
// ship the current dynamic data to the gpu
|
||||
// TODO: ship the data if it changed since the last update
|
||||
if( true == skydome.is_dirty() ) {
|
||||
if( ( m_coloursbuffer > 0 ) && ( m_coloursbuffer != (GLuint)-1 ) ) {
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_coloursbuffer );
|
||||
auto &colors{ skydome.colors() };
|
||||
/*
|
||||
float twilightfactor = clamp( -simulation::Environment.sun().getAngle(), 0.0f, 18.0f ) / 18.0f;
|
||||
auto gamma = interpolate( glm::vec3( 0.45f ), glm::vec3( 1.0f ), twilightfactor );
|
||||
for( auto & color : colors ) {
|
||||
color = glm::pow( color, gamma );
|
||||
}
|
||||
*/
|
||||
::glBufferSubData( GL_ARRAY_BUFFER, 0, colors.size() * sizeof( glm::vec3 ), colors.data() );
|
||||
skydome.is_dirty() = false;
|
||||
}
|
||||
}
|
||||
// cleanup
|
||||
::glPopClientAttrib();
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, 0 );
|
||||
::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
|
||||
}
|
||||
|
||||
// render skydome to screen
|
||||
void opengl_skydome::render() {
|
||||
|
||||
if( ( m_indexbuffer == (GLuint)-1 )
|
||||
|| ( m_vertexbuffer == (GLuint)-1 )
|
||||
|| ( m_coloursbuffer == (GLuint)-1 ) ) {
|
||||
return;
|
||||
}
|
||||
// setup:
|
||||
::glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT );
|
||||
// ...positions...
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer );
|
||||
::glVertexPointer( 3, GL_FLOAT, sizeof( glm::vec3 ), reinterpret_cast<void const*>( 0 ) );
|
||||
::glEnableClientState( GL_VERTEX_ARRAY );
|
||||
// ...colours...
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_coloursbuffer );
|
||||
::glColorPointer( 3, GL_FLOAT, sizeof( glm::vec3 ), reinterpret_cast<void const*>( 0 ) );
|
||||
::glEnableClientState( GL_COLOR_ARRAY );
|
||||
// ...indices
|
||||
::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer );
|
||||
// draw
|
||||
::glDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
::glDrawElements( GL_TRIANGLES, static_cast<GLsizei>( m_indexcount ), GL_UNSIGNED_SHORT, reinterpret_cast<void const*>( 0 ) );
|
||||
// cleanup
|
||||
::glPopClientAttrib();
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, 0 );
|
||||
::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
|
||||
}
|
||||
31
rendering/openglskydome.h
Normal file
31
rendering/openglskydome.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class opengl_skydome {
|
||||
|
||||
public:
|
||||
// constructors
|
||||
opengl_skydome() = default;
|
||||
// destructor
|
||||
~opengl_skydome();
|
||||
// methods
|
||||
// updates data stores on the opengl end. NOTE: unbinds buffers
|
||||
void update();
|
||||
// draws the skydome
|
||||
void render();
|
||||
|
||||
private:
|
||||
// members
|
||||
GLuint m_indexbuffer{ (GLuint)-1 }; // id of the buffer holding index data on the opengl end
|
||||
std::size_t m_indexcount { 0 };
|
||||
GLuint m_vertexbuffer{ (GLuint)-1 }; // id of the buffer holding vertex data on the opengl end
|
||||
GLuint m_coloursbuffer{ (GLuint)-1 }; // id of the buffer holding colour data on the opengl end
|
||||
};
|
||||
493
rendering/particles.cpp
Normal file
493
rendering/particles.cpp
Normal file
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "particles.h"
|
||||
|
||||
#include "Timer.h"
|
||||
#include "Globals.h"
|
||||
#include "AnimModel.h"
|
||||
#include "simulationenvironment.h"
|
||||
#include "Logs.h"
|
||||
|
||||
|
||||
void
|
||||
smoke_source::particle_emitter::deserialize( cParser &Input ) {
|
||||
|
||||
if( Input.getToken<std::string>() != "{" ) { return; }
|
||||
|
||||
std::unordered_map<std::string, float &> const variablemap {
|
||||
{ "min_inclination:", inclination[ value_limit::min ] },
|
||||
{ "max_inclination:", inclination[ value_limit::max ] },
|
||||
{ "min_velocity:", velocity[ value_limit::min ] },
|
||||
{ "max_velocity:", velocity[ value_limit::max ] },
|
||||
{ "min_size:", size[ value_limit::min ] },
|
||||
{ "max_size:", size[ value_limit::max ] },
|
||||
{ "min_opacity:", opacity[ value_limit::min ] },
|
||||
{ "max_opacity:", opacity[ value_limit::max ] } };
|
||||
std::string key;
|
||||
|
||||
while( ( false == ( ( key = Input.getToken<std::string>( true, "\n\r\t ,;[]" ) ).empty() ) )
|
||||
&& ( key != "}" ) ) {
|
||||
|
||||
if( key == "color:" ) {
|
||||
// special case, vec3 attribute type
|
||||
// TODO: variable table, if amount of vector attributes increases
|
||||
color = Input.getToken<glm::vec3>( true, "\n\r\t ,;[]" );
|
||||
color =
|
||||
glm::clamp(
|
||||
color / 255.f,
|
||||
glm::vec3{ 0.f }, glm::vec3{ 1.f } );
|
||||
}
|
||||
else {
|
||||
// float type attributes
|
||||
auto const lookup { variablemap.find( key ) };
|
||||
if( lookup != variablemap.end() ) {
|
||||
lookup->second = Input.getToken<float>( true, "\n\r\t ,;[]" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
smoke_source::particle_emitter::initialize( smoke_particle &Particle ) {
|
||||
|
||||
auto const polarangle { glm::radians( LocalRandom( inclination[ value_limit::min ], inclination[ value_limit::max ] ) ) }; // theta
|
||||
auto const azimuthalangle { glm::radians( LocalRandom( -180, 180 ) ) }; // phi
|
||||
// convert spherical coordinates to opengl coordinates
|
||||
auto const launchvector { glm::vec3(
|
||||
std::sin( polarangle ) * std::sin( azimuthalangle ) * -1,
|
||||
std::cos( polarangle ),
|
||||
std::sin( polarangle ) * std::cos( azimuthalangle ) ) };
|
||||
auto const launchvelocity { static_cast<float>( LocalRandom( velocity[ value_limit::min ], velocity[ value_limit::max ] ) ) };
|
||||
|
||||
Particle.velocity = launchvector * launchvelocity;
|
||||
|
||||
Particle.rotation = glm::radians( LocalRandom( 0, 360 ) );
|
||||
Particle.size = LocalRandom( size[ value_limit::min ], size[ value_limit::max ] );
|
||||
Particle.opacity = LocalRandom( opacity[ value_limit::min ], opacity[ value_limit::max ] ) / Global.SmokeFidelity;
|
||||
Particle.age = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
smoke_source::deserialize( cParser &Input ) {
|
||||
|
||||
if( false == Input.ok() ) { return false; }
|
||||
|
||||
while( true == deserialize_mapping( Input ) ) {
|
||||
; // all work done by while()
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// imports member data pair from the config file
|
||||
bool
|
||||
smoke_source::deserialize_mapping( cParser &Input ) {
|
||||
|
||||
// token can be a key or block end
|
||||
std::string const key { Input.getToken<std::string>( true, "\n\r\t ,;[]" ) };
|
||||
|
||||
if( ( true == key.empty() ) || ( key == "}" ) ) { return false; }
|
||||
|
||||
// if not block end then the key is followed by assigned value or sub-block
|
||||
if( key == "spawn_rate:" ) {
|
||||
Input.getTokens();
|
||||
Input >> m_spawnrate;
|
||||
}
|
||||
else if( key == "initializer:" ) {
|
||||
m_emitter.deserialize( Input );
|
||||
}
|
||||
/*
|
||||
else if( key == "velocity_change:" ) {
|
||||
m_velocitymodifier.deserialize( Input );
|
||||
}
|
||||
*/
|
||||
else if( key == "size_change:" ) {
|
||||
m_sizemodifier.deserialize( Input );
|
||||
}
|
||||
else if( key == "opacity_change:" ) {
|
||||
m_opacitymodifier.deserialize( Input );
|
||||
}
|
||||
|
||||
return true; // return value marks a [ key: value ] pair was extracted, nothing about whether it's recognized
|
||||
}
|
||||
|
||||
void
|
||||
smoke_source::initialize() {
|
||||
|
||||
m_max_particles =
|
||||
// put a cap on number of particles in a single source. TBD, TODO: make it part of he source configuration?
|
||||
std::min(
|
||||
static_cast<int>( 500 * Global.SmokeFidelity ),
|
||||
// NOTE: given nature of the smoke we're presuming opacity decreases over time and the particle is killed when it reaches 0
|
||||
// this gives us estimate of longest potential lifespan of single particle, and how many particles total can there be at any given time
|
||||
// TBD, TODO: explicit lifespan variable as part of the source configuration?
|
||||
static_cast<int>( m_spawnrate / std::abs( m_opacitymodifier.value_change() ) ) );
|
||||
}
|
||||
|
||||
void
|
||||
smoke_source::bind( TDynamicObject const *Vehicle ) {
|
||||
|
||||
m_owner.vehicle = Vehicle;
|
||||
m_ownertype = (
|
||||
m_owner.vehicle != nullptr ?
|
||||
owner_type::vehicle :
|
||||
owner_type::none );
|
||||
}
|
||||
|
||||
void
|
||||
smoke_source::bind( TAnimModel const *Node ) {
|
||||
|
||||
m_owner.node = Node;
|
||||
m_ownertype = (
|
||||
m_owner.node != nullptr ?
|
||||
owner_type::node :
|
||||
owner_type::none );
|
||||
}
|
||||
|
||||
// updates state of owned particles
|
||||
void
|
||||
smoke_source::update( double const Timedelta, bool const Onlydespawn ) {
|
||||
|
||||
// prepare bounding box for new pass
|
||||
// TODO: include bounding box in the bounding_area class
|
||||
bounding_box boundingbox {
|
||||
glm::dvec3{ std::numeric_limits<double>::max() },
|
||||
glm::dvec3{ std::numeric_limits<double>::lowest() } };
|
||||
|
||||
m_spawncount = (
|
||||
( ( false == Global.Smoke ) || ( true == Onlydespawn ) ) ?
|
||||
0.f :
|
||||
std::min<float>(
|
||||
m_spawncount + ( m_spawnrate * Timedelta * Global.SmokeFidelity ),
|
||||
m_max_particles ) );
|
||||
// consider special spawn rate cases
|
||||
if( m_ownertype == owner_type::vehicle ) {
|
||||
|
||||
if (Global.AirTemperature <= 5.f || m_owner.vehicle->MoverParameters->dizel_heat.Ts < 45.f)
|
||||
{
|
||||
m_emitter.color == glm::vec3{128, 128, 128};
|
||||
}
|
||||
|
||||
if (m_owner.vehicle->MoverParameters->dizel_spinup == true)
|
||||
{
|
||||
m_spawncount =
|
||||
((false == Global.Smoke) || (true == Onlydespawn)) ?
|
||||
0.f :
|
||||
std::min<float>(
|
||||
// m_spawncount + ( m_spawnrate * Timedelta * Global.SmokeFidelity ),
|
||||
m_spawncount +(m_spawnrate * Timedelta * Global.SmokeFidelity * (((m_owner.vehicle->MoverParameters->enrot) / 4 ) * 0.01)), m_max_particles);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_owner.vehicle->MoverParameters->DirAbsolute == 0 ||
|
||||
m_owner.vehicle->MoverParameters->Im == 0)
|
||||
{
|
||||
m_spawncount =
|
||||
((false == Global.Smoke) || (true == Onlydespawn)) ?
|
||||
0.f :
|
||||
std::min<float>(
|
||||
// m_spawncount + ( m_spawnrate * Timedelta * Global.SmokeFidelity ),
|
||||
m_spawncount + (m_spawnrate * Timedelta * Global.SmokeFidelity * ((((m_owner.vehicle->MoverParameters->DElist[m_owner.vehicle->MoverParameters->MainCtrlPosNo].RPM -m_owner.vehicle->MoverParameters->enrot) /60) *0.02) *(m_owner.vehicle->MoverParameters->EnginePower * 0.005))), m_max_particles);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
m_spawncount =
|
||||
((false == Global.Smoke) || (true == Onlydespawn)) ?
|
||||
0.f :
|
||||
std::min<float>(
|
||||
// m_spawncount + ( m_spawnrate * Timedelta * Global.SmokeFidelity ),
|
||||
m_spawncount + (m_spawnrate * Timedelta * Global.SmokeFidelity * ((((m_owner.vehicle->MoverParameters->DElist[m_owner.vehicle->MoverParameters->MainCtrlPosNo].RPM -m_owner.vehicle->MoverParameters->enrot) /60) * (sqrt(m_owner.vehicle->MoverParameters->Im) * 0.01) * 0.02) *(m_owner.vehicle->MoverParameters->EnginePower * 0.005))), m_max_particles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update spawned particles
|
||||
for( auto particleiterator { std::begin( m_particles ) }; particleiterator != std::end( m_particles ); ++particleiterator ) {
|
||||
|
||||
auto &particle { *particleiterator };
|
||||
bool particleisalive;
|
||||
|
||||
while( ( false == ( particleisalive = update( particle, boundingbox, Timedelta ) ) )
|
||||
&& ( m_spawncount >= 1.f ) ) {
|
||||
// replace dead particle with a new one
|
||||
m_spawncount -= 1.f;
|
||||
initialize( particle );
|
||||
}
|
||||
if( false == particleisalive ) {
|
||||
// we have a dead particle and no pending spawn requests, (try to) move the last particle here
|
||||
do {
|
||||
if( std::next( particleiterator ) == std::end( m_particles ) ) { break; } // already at last particle
|
||||
particle = m_particles.back();
|
||||
m_particles.pop_back();
|
||||
} while( false == ( particleisalive = update( particle, boundingbox, Timedelta ) ) );
|
||||
}
|
||||
if( false == particleisalive ) {
|
||||
// NOTE: if we're here it means the iterator is at last container slot which holds a dead particle about to be eliminated...
|
||||
m_particles.pop_back();
|
||||
// ...since this effectively makes the iterator now point at end() and the advancement at the end of the loop will move it past end()
|
||||
// we have to break the loop manually (could use < comparison but with both ways being ugly, this is
|
||||
break;
|
||||
}
|
||||
}
|
||||
// spawn pending particles in remaining container slots
|
||||
while( ( m_spawncount >= 1.f )
|
||||
&& ( m_particles.size() < m_max_particles ) ) {
|
||||
|
||||
m_spawncount -= 1.f;
|
||||
// work with a temporary copy in case initial update renders the particle dead
|
||||
smoke_particle newparticle;
|
||||
initialize( newparticle );
|
||||
if( true == update( newparticle, boundingbox, Timedelta ) ) {
|
||||
// if the new particle didn't die immediately place it in the container...
|
||||
m_particles.emplace_back( newparticle );
|
||||
}
|
||||
}
|
||||
// if we still have pending requests after filling entire container replace older particles
|
||||
if( m_spawncount >= 1.f ) {
|
||||
// sort all particles from most to least transparent, oldest to youngest if it's a tie
|
||||
std::sort(
|
||||
std::begin( m_particles ),
|
||||
std::end( m_particles ),
|
||||
[]( smoke_particle const &Left, smoke_particle const &Right ) {
|
||||
return ( Left.opacity != Right.opacity ?
|
||||
Left.opacity < Right.opacity :
|
||||
Left.age > Right.age ); } );
|
||||
// replace old particles with new ones until we run out of either requests or room
|
||||
for( auto &particle : m_particles ) {
|
||||
|
||||
while( m_spawncount >= 1.f ) {
|
||||
m_spawncount -= 1.f;
|
||||
// work with a temporary copy so we don't wind up with replacing a good particle with a dead on arrival one
|
||||
smoke_particle newparticle;
|
||||
initialize( newparticle );
|
||||
if( true == update( newparticle, boundingbox, Timedelta ) ) {
|
||||
// if the new particle didn't die immediately place it in the container...
|
||||
particle = newparticle;
|
||||
// ...and move on to the next slot
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// discard pending spawn requests our container couldn't fit
|
||||
m_spawncount -= std::floor( m_spawncount );
|
||||
}
|
||||
// determine bounding area from calculated bounding box
|
||||
if( false == m_particles.empty() ) {
|
||||
m_area.center = interpolate( boundingbox[ value_limit::min ], boundingbox[ value_limit::max ], 0.5 );
|
||||
m_area.radius = 0.5 * ( glm::length( boundingbox[ value_limit::max ] - boundingbox[ value_limit::min ] ) );
|
||||
}
|
||||
else {
|
||||
m_area.center = location();
|
||||
m_area.radius = 0;
|
||||
}
|
||||
}
|
||||
|
||||
glm::dvec3
|
||||
smoke_source::location() const {
|
||||
|
||||
glm::dvec3 location;
|
||||
|
||||
switch( m_ownertype ) {
|
||||
case owner_type::vehicle: {
|
||||
location = glm::dvec3 {
|
||||
m_offset.x * m_owner.vehicle->VectorLeft()
|
||||
+ m_offset.y * m_owner.vehicle->VectorUp()
|
||||
+ m_offset.z * m_owner.vehicle->VectorFront() };
|
||||
location += glm::dvec3{ m_owner.vehicle->GetPosition() };
|
||||
break;
|
||||
}
|
||||
case owner_type::node: {
|
||||
auto const rotationx { glm::angleAxis( glm::radians( m_owner.node->Angles().x ), glm::vec3{ 1.f, 0.f, 0.f } ) };
|
||||
auto const rotationy { glm::angleAxis( glm::radians( m_owner.node->Angles().y ), glm::vec3{ 0.f, 1.f, 0.f } ) };
|
||||
auto const rotationz { glm::angleAxis( glm::radians( m_owner.node->Angles().z ), glm::vec3{ 0.f, 0.f, 1.f } ) };
|
||||
location = rotationy * rotationx * rotationz * glm::vec3{ m_offset };
|
||||
location += m_owner.node->location();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
location = m_offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
// sets particle state to fresh values
|
||||
void
|
||||
smoke_source::initialize( smoke_particle &Particle ) {
|
||||
|
||||
m_emitter.initialize( Particle );
|
||||
|
||||
Particle.position = location();
|
||||
|
||||
if( m_ownertype == owner_type::vehicle ) {
|
||||
Particle.opacity *= m_owner.vehicle->MoverParameters->dizel_fill;
|
||||
auto const enginerevolutionsfactor { 1.5f }; // high engine revolutions increase initial particle velocity
|
||||
switch( m_owner.vehicle->MoverParameters->EngineType ) {
|
||||
case TEngineType::DieselElectric: {
|
||||
if (m_owner.vehicle->MoverParameters->dizel_spinup == true)
|
||||
{
|
||||
Particle.velocity *= 0.38*(((m_owner.vehicle->MoverParameters->enrot)/2)*0.5); // / m_owner.vehicle->MoverParameters->dizel_fill *0.01)) ;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Particle.velocity *= ((((m_owner.vehicle->MoverParameters->enrot)/60)*0.6) * (m_owner.vehicle->MoverParameters->EnginePower *0.03)); // / m_owner.vehicle->MoverParameters->dizel_fill *0.01)) ;
|
||||
//Particle.velocity *= m_owner.vehicle->GetVelocity(); // / m_owner.vehicle->MoverParameters->dizel_fill *0.01)) ;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// updates state of provided particle and bounding box. returns: true if particle is still alive afterwards, false otherwise
|
||||
bool
|
||||
smoke_source::update( smoke_particle &Particle, bounding_box &Boundingbox, double const Timedelta ) {
|
||||
|
||||
m_opacitymodifier.update( Particle.opacity, Timedelta );
|
||||
// if the particle is dead we can bail out early...
|
||||
if( Particle.opacity <= 0.f ) { return false; }
|
||||
// ... otherwise proceed with full update
|
||||
m_sizemodifier.update( Particle.size, Timedelta );
|
||||
|
||||
// crude smoke dispersion simulation
|
||||
// http://www.auburn.edu/academic/forestry_wildlife/fire/smoke_guide/smoke_dispersion.htm
|
||||
switch (m_ownertype)
|
||||
{
|
||||
case owner_type::vehicle:
|
||||
{
|
||||
Particle.velocity.y += ( 0.025 * Particle.velocity.y ) * std::min( 0.f, Global.AirTemperature - 90 ) * Timedelta; // decelerate faster in cold weather
|
||||
Particle.velocity.y -= ( (0.05 * (pow(m_owner.vehicle->GetVelocity()*1,0.4))) * Particle.velocity.y ) * Global.Overcast * Timedelta; // decelerate faster with high air humidity and/or precipitation
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Particle.velocity.y += ( 0.005 * Particle.velocity.y ) * std::min( 0.f, Global.AirTemperature - 10 ) * Timedelta; // decelerate faster in cold weather
|
||||
Particle.velocity.y -= ( 0.050 * Particle.velocity.y ) * Global.Overcast * Timedelta; // decelerate faster with high air humidity and/or precipitation
|
||||
Particle.velocity.y = std::max<float>( 0.25 * ( 2.f - Global.Overcast ), Particle.velocity.y ); // put a cap on deceleration
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Particle.position += Particle.velocity * static_cast<float>( Timedelta );
|
||||
Particle.position += 0.1f * Particle.age * simulation::Environment.wind() * static_cast<float>( Timedelta );
|
||||
// m_velocitymodifier.update( Particle.velocity, Timedelta );
|
||||
|
||||
Particle.age += Timedelta;
|
||||
|
||||
// update bounding box
|
||||
Boundingbox[ value_limit::min ] = glm::min( Boundingbox[ value_limit::min ], Particle.position - glm::dvec3{ Particle.size } );
|
||||
Boundingbox[ value_limit::max ] = glm::max( Boundingbox[ value_limit::max ], Particle.position + glm::dvec3{ Particle.size } );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// adds a new particle source of specified type, placing it in specified world location
|
||||
// returns: true on success, false if the specified type definition couldn't be located
|
||||
bool
|
||||
particle_manager::insert( std::string const &Sourcetemplate, glm::dvec3 const Location ) {
|
||||
|
||||
auto const *sourcetemplate { find( Sourcetemplate ) };
|
||||
|
||||
if( sourcetemplate == nullptr ) { return false; }
|
||||
|
||||
// ...if template lookup didn't fail put template clone on the source list and initialize it
|
||||
m_sources.emplace_back( *sourcetemplate );
|
||||
auto &source { m_sources.back() };
|
||||
source.initialize();
|
||||
source.m_offset = Location;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
particle_manager::insert( std::string const &Sourcetemplate, TDynamicObject const *Vehicle, glm::dvec3 const Location ) {
|
||||
|
||||
if( false == insert( Sourcetemplate, Location ) ) { return false; }
|
||||
|
||||
// attach the source to specified vehicle
|
||||
auto &source { m_sources.back() };
|
||||
source.bind( Vehicle );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
particle_manager::insert( std::string const &Sourcetemplate, TAnimModel const *Node, glm::dvec3 const Location ) {
|
||||
|
||||
if( false == insert( Sourcetemplate, Location ) ) { return false; }
|
||||
|
||||
// attach the source to specified node
|
||||
auto &source { m_sources.back() };
|
||||
source.bind( Node );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// updates state of all owned emitters
|
||||
void
|
||||
particle_manager::update() {
|
||||
|
||||
auto const timedelta { Timer::GetDeltaTime() };
|
||||
|
||||
if( timedelta == 0.0 ) { return; }
|
||||
|
||||
auto const distancethreshold { 2 * Global.BaseDrawRange * Global.fDistanceFactor }; // to reduce workload distant enough sources won't spawn new particles
|
||||
|
||||
for( auto &source : m_sources ) {
|
||||
|
||||
auto const viewerdistance { glm::length( source.area().center - glm::dvec3{ Global.pCamera.Pos } ) - source.area().radius };
|
||||
|
||||
source.update( timedelta, viewerdistance > distancethreshold );
|
||||
}
|
||||
}
|
||||
|
||||
smoke_source *
|
||||
particle_manager::find( std::string const &Template ) {
|
||||
|
||||
auto const templatepath { "data/" };
|
||||
auto const templatename { ToLower( Template ) };
|
||||
|
||||
// try to locate specified rail profile...
|
||||
auto const lookup { m_sourcetemplates.find( templatename ) };
|
||||
if( lookup != m_sourcetemplates.end() ) {
|
||||
// ...if it works, we're done...
|
||||
return &(lookup->second);
|
||||
}
|
||||
// ... and if it fails try to add the template to the database from a data file
|
||||
smoke_source source;
|
||||
cParser parser(templatepath + templatename + ".txt", cParser::buffer_FILE);
|
||||
|
||||
if (source.deserialize(parser))
|
||||
{
|
||||
// if deserialization didn't fail finish source setup...
|
||||
source.m_opacitymodifier.bind( &Global.SmokeFidelity );
|
||||
// ...then cache the source as template for future instances
|
||||
m_sourcetemplates.emplace( templatename, source );
|
||||
// should be 'safe enough' to return lookup result directly afterwards
|
||||
return &( m_sourcetemplates.find( templatename )->second );
|
||||
}
|
||||
else {
|
||||
ErrorLog( "Bad file: failed to locate particle source configuration file \"" + std::string( templatepath + templatename + ".txt" ) + "\"", logtype::file );
|
||||
}
|
||||
// if fetching data from the file fails too, give up
|
||||
return nullptr;
|
||||
}
|
||||
243
rendering/particles.h
Normal file
243
rendering/particles.h
Normal file
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Classes.h"
|
||||
#include "scene.h"
|
||||
|
||||
// particle specialized for drawing smoke
|
||||
// given smoke features we can take certain shortcuts
|
||||
// -- there's no need to sort the particles, can be drawn in any order with depth write turned off
|
||||
// -- the colour remains consistent throughout, only opacity changes
|
||||
// -- randomized particle rotation
|
||||
// -- initial velocity reduced over time to slow drift upwards (drift speed depends on particle and air temperature difference)
|
||||
// -- size increased over time
|
||||
struct smoke_particle {
|
||||
|
||||
glm::dvec3 position; // meters, 3d space;
|
||||
float rotation; // radians; local z axis angle
|
||||
glm::vec3 velocity; // meters per second, 3d space; current velocity
|
||||
float size; // multiplier, billboard size
|
||||
// glm::vec4 color; // 0-1 range, rgba; geometry color and opacity
|
||||
float opacity; // 0-1 range
|
||||
// glm::vec2 uv_offset; // 0-1 range, uv space; for texture animation
|
||||
float age; // seconds; time elapsed since creation
|
||||
// double distance; // meters; distance between particle and camera
|
||||
};
|
||||
|
||||
enum value_limit {
|
||||
min = 0,
|
||||
max = 1
|
||||
};
|
||||
|
||||
// helper, adjusts provided variable by fixed amount, keeping resulting value between limits
|
||||
template <typename Type_>
|
||||
class fixedstep_modifier {
|
||||
|
||||
public:
|
||||
// methods
|
||||
void
|
||||
deserialize( cParser &Input );
|
||||
// updates state of provided variable
|
||||
void
|
||||
update( Type_ &Variable, double const Timedelta ) const;
|
||||
void
|
||||
bind( Type_ const *Modifier ) {
|
||||
m_valuechangemodifier = Modifier; }
|
||||
Type_
|
||||
value_change() const {
|
||||
return (
|
||||
m_valuechangemodifier == nullptr ?
|
||||
m_valuechange :
|
||||
m_valuechange / *( m_valuechangemodifier ) ); }
|
||||
|
||||
private:
|
||||
//types
|
||||
// methods
|
||||
// members
|
||||
// Type_ m_intialvalue { Type_( 0 ) }; // meters per second; velocity applied to freshly spawned particles
|
||||
Type_ m_valuechange { Type_( 0 ) }; // meters per second; change applied to initial velocity
|
||||
Type_ m_valuelimits[ 2 ] { Type_( std::numeric_limits<Type_>::lowest() ), Type_( std::numeric_limits<Type_>::max() ) };
|
||||
Type_ const *m_valuechangemodifier{ nullptr }; // optional modifier applied to value change
|
||||
};
|
||||
|
||||
|
||||
// particle emitter
|
||||
class smoke_source {
|
||||
// located in scenery
|
||||
// new particles emitted if distance of source < double view range
|
||||
// existing particles are updated until dead no matter the range (presumed to have certain lifespan)
|
||||
// during update pass dead particle slots are filled with new instances, if there's no particles queued the slot is swapped with the last particle in the list
|
||||
// bounding box/sphere calculated based on position of all owned particles, used by the renderer to include/discard data in a draw pass
|
||||
friend class particle_manager;
|
||||
|
||||
public:
|
||||
// types
|
||||
using particle_sequence = std::vector<smoke_particle>;
|
||||
// methods
|
||||
bool
|
||||
deserialize( cParser &Input );
|
||||
void
|
||||
initialize();
|
||||
void
|
||||
bind( TDynamicObject const *Vehicle );
|
||||
void
|
||||
bind( TAnimModel const *Node );
|
||||
// updates state of owned particles
|
||||
void
|
||||
update( double const Timedelta, bool const Onlydespawn );
|
||||
glm::vec3 const &
|
||||
color() const {
|
||||
return m_emitter.color; }
|
||||
glm::dvec3
|
||||
location() const;
|
||||
// provides access to bounding area data
|
||||
scene::bounding_area const &
|
||||
area() const {
|
||||
return m_area; }
|
||||
particle_sequence const &
|
||||
sequence() const {
|
||||
return m_particles; }
|
||||
|
||||
private:
|
||||
// types
|
||||
enum class owner_type {
|
||||
none = 0,
|
||||
vehicle,
|
||||
node
|
||||
};
|
||||
|
||||
struct particle_emitter {
|
||||
float inclination[ 2 ] { 0.f, 0.f };
|
||||
float velocity[ 2 ] { 1.f, 1.f };
|
||||
float size[ 2 ] { 1.f, 1.f };
|
||||
float opacity[ 2 ] { 1.f, 1.f };
|
||||
glm::vec3 color { 16.f / 255.f };
|
||||
|
||||
void deserialize( cParser &Input );
|
||||
void initialize( smoke_particle &Particle );
|
||||
};
|
||||
|
||||
using bounding_box = glm::dvec3[ 2 ]; // bounding box of owned particles
|
||||
|
||||
// methods
|
||||
// imports member data pair from the config file
|
||||
bool
|
||||
deserialize_mapping( cParser &Input );
|
||||
void
|
||||
initialize( smoke_particle &Particle );
|
||||
// updates state of provided particle and bounding box. returns: true if particle is still alive afterwards, false otherwise
|
||||
bool
|
||||
update( smoke_particle &Particle, bounding_box &Boundingbox, double const Timedelta );
|
||||
// members
|
||||
// config/inputs
|
||||
// TBD: union and indicator, or just plain owner variables?
|
||||
owner_type m_ownertype { owner_type::none };
|
||||
union {
|
||||
TDynamicObject const * vehicle;
|
||||
TAnimModel const * node;
|
||||
} m_owner { nullptr }; // optional, scene item carrying this source
|
||||
glm::dvec3 m_offset; // meters, 3d space; relative position of the source, either from the owner or the region centre
|
||||
float m_spawnrate { 0.f }; // number of particles to spawn per second
|
||||
particle_emitter m_emitter;
|
||||
// bool m_inheritvelocity { false }; // whether spawned particle should receive velocity of its owner
|
||||
// TODO: replace modifiers with configurable interpolator item allowing keyframe-based changes over time
|
||||
fixedstep_modifier<float> m_sizemodifier; // particle billboard size
|
||||
// fixedstep_modifier<glm::vec3> m_colormodifier; // particle billboard color and opacity
|
||||
fixedstep_modifier<float> m_opacitymodifier;
|
||||
// texture_handle m_texture { -1 }; // texture assigned to particle billboards
|
||||
// current state
|
||||
float m_spawncount { 0.f }; // number of particles to spawn during next update
|
||||
particle_sequence m_particles; // collection of spawned particles
|
||||
std::size_t m_max_particles; // maximum number of particles existing
|
||||
scene::bounding_area m_area; // bounding sphere of owned particles
|
||||
};
|
||||
|
||||
|
||||
// holds all particle emitters defined in the scene and updates their state
|
||||
class particle_manager {
|
||||
|
||||
friend opengl_renderer;
|
||||
|
||||
public:
|
||||
// types
|
||||
using source_sequence = std::vector<smoke_source>;
|
||||
// constructors
|
||||
particle_manager() = default;
|
||||
// destructor
|
||||
// ~particle_manager();
|
||||
// methods
|
||||
// adds a new particle source of specified type, placing it in specified world location. returns: true on success, false if the specified type definition couldn't be located
|
||||
bool
|
||||
insert( std::string const &Sourcetemplate, glm::dvec3 const Location );
|
||||
bool
|
||||
insert( std::string const &Sourcetemplate, TDynamicObject const *Vehicle, glm::dvec3 const Location );
|
||||
bool
|
||||
insert( std::string const &Sourcetemplate, TAnimModel const *Node, glm::dvec3 const Location );
|
||||
// updates state of all owned emitters
|
||||
void
|
||||
update();
|
||||
// data access
|
||||
source_sequence &
|
||||
sequence() {
|
||||
return m_sources; }
|
||||
|
||||
// members
|
||||
|
||||
private:
|
||||
// types
|
||||
using source_map = std::unordered_map<std::string, smoke_source>;
|
||||
// methods
|
||||
smoke_source *
|
||||
find( std::string const &Template );
|
||||
// members
|
||||
source_map m_sourcetemplates; // cached particle emitter configurations
|
||||
source_sequence m_sources; // all owned particle emitters
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <typename Type_>
|
||||
void
|
||||
fixedstep_modifier<Type_>::update( Type_ &Variable, double const Timedelta ) const {
|
||||
// HACK: float cast to avoid vec3 and double mismatch
|
||||
// TBD, TODO: replace with vector types specialization
|
||||
auto const valuechange { (
|
||||
m_valuechangemodifier == nullptr ?
|
||||
m_valuechange :
|
||||
m_valuechange / *( m_valuechangemodifier ) ) };
|
||||
Variable += ( valuechange * static_cast<float>( Timedelta ) );
|
||||
// clamp down to allowed value range
|
||||
Variable = glm::max( Variable, m_valuelimits[ value_limit::min ] );
|
||||
Variable = glm::min( Variable, m_valuelimits[ value_limit::max ] );
|
||||
}
|
||||
|
||||
template <typename Type_>
|
||||
void
|
||||
fixedstep_modifier<Type_>::deserialize( cParser &Input ) {
|
||||
|
||||
if( Input.getToken<std::string>() != "{" ) { return; }
|
||||
|
||||
std::unordered_map<std::string, Type_ &> const variablemap {
|
||||
{ "step:", m_valuechange },
|
||||
{ "min:", m_valuelimits[ value_limit::min ] },
|
||||
{ "max:", m_valuelimits[ value_limit::max ] } };
|
||||
|
||||
std::string key;
|
||||
|
||||
while( ( false == ( ( key = Input.getToken<std::string>( true, "\n\r\t ,;[]" ) ).empty() ) )
|
||||
&& ( key != "}" ) ) {
|
||||
|
||||
auto const lookup { variablemap.find( key ) };
|
||||
if( lookup == variablemap.end() ) { continue; }
|
||||
|
||||
lookup->second = Input.getToken<Type_>( true, "\n\r\t ,;[]" );
|
||||
}
|
||||
}
|
||||
87
rendering/precipitation.cpp
Normal file
87
rendering/precipitation.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "precipitation.h"
|
||||
|
||||
#include "Globals.h"
|
||||
#include "Timer.h"
|
||||
#include "simulation.h"
|
||||
#include "Train.h"
|
||||
|
||||
basic_precipitation::~basic_precipitation() {
|
||||
// TODO: release allocated resources
|
||||
}
|
||||
|
||||
bool
|
||||
basic_precipitation::init() {
|
||||
|
||||
auto const heightfactor { 10.f }; // height-to-radius factor
|
||||
m_moverate *= heightfactor;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
basic_precipitation::update() {
|
||||
|
||||
auto const timedelta { static_cast<float>( ( DebugModeFlag ? Timer::GetDeltaTime() : Timer::GetDeltaTime() ) ) };
|
||||
|
||||
if( timedelta == 0.0 ) { return; }
|
||||
|
||||
m_textureoffset += m_moverate * m_moverateweathertypefactor * timedelta;
|
||||
m_textureoffset = clamp_circular( m_textureoffset, 10.f );
|
||||
|
||||
auto cameramove { glm::dvec3{ Global.pCamera.Pos - m_camerapos} };
|
||||
cameramove.y = 0.0; // vertical movement messes up vector calculation
|
||||
|
||||
m_camerapos = Global.pCamera.Pos;
|
||||
|
||||
// intercept sudden user-induced camera jumps...
|
||||
// ...from free fly mode change
|
||||
if( m_freeflymode != FreeFlyModeFlag ) {
|
||||
m_freeflymode = FreeFlyModeFlag;
|
||||
if( true == m_freeflymode ) {
|
||||
// cache last precipitation vector in the cab
|
||||
m_cabcameramove = m_cameramove;
|
||||
// don't carry previous precipitation vector to a new unrelated location
|
||||
m_cameramove = glm::dvec3{ 0.0 };
|
||||
}
|
||||
else {
|
||||
// restore last cached precipitation vector
|
||||
m_cameramove = m_cabcameramove;
|
||||
}
|
||||
cameramove = glm::dvec3{ 0.0 };
|
||||
}
|
||||
// ...from jump between cab and window/mirror view
|
||||
if( m_windowopen != Global.CabWindowOpen ) {
|
||||
m_windowopen = Global.CabWindowOpen;
|
||||
cameramove = glm::dvec3{ 0.0 };
|
||||
}
|
||||
// ... from cab change
|
||||
if( ( simulation::Train != nullptr ) && ( simulation::Train->iCabn != m_activecab ) ) {
|
||||
m_activecab = simulation::Train->iCabn;
|
||||
cameramove = glm::dvec3{ 0.0 };
|
||||
}
|
||||
// ... from camera jump to another location
|
||||
if( glm::length( cameramove ) > 100.0 ) {
|
||||
cameramove = glm::dvec3{ 0.0 };
|
||||
}
|
||||
|
||||
m_cameramove = m_cameramove * std::max( 0.0, 1.0 - 5.0 * timedelta ) + cameramove * ( 30.0 * timedelta );
|
||||
if( std::abs( m_cameramove.x ) < 0.001 ) { m_cameramove.x = 0.0; }
|
||||
if( std::abs( m_cameramove.y ) < 0.001 ) { m_cameramove.y = 0.0; }
|
||||
if( std::abs( m_cameramove.z ) < 0.001 ) { m_cameramove.z = 0.0; }
|
||||
}
|
||||
|
||||
float
|
||||
basic_precipitation::get_textureoffset() const {
|
||||
|
||||
return m_textureoffset;
|
||||
}
|
||||
42
rendering/precipitation.h
Normal file
42
rendering/precipitation.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// based on "Rendering Falling Rain and Snow"
|
||||
// by Niniane Wang, Bretton Wade
|
||||
|
||||
class basic_precipitation {
|
||||
|
||||
public:
|
||||
// constructors
|
||||
basic_precipitation() = default;
|
||||
// destructor
|
||||
~basic_precipitation();
|
||||
// methods
|
||||
bool
|
||||
init();
|
||||
void
|
||||
update();
|
||||
float
|
||||
get_textureoffset() const;
|
||||
|
||||
glm::dvec3 m_cameramove{ 0.0 };
|
||||
|
||||
private:
|
||||
// members
|
||||
float m_textureoffset { 0.f };
|
||||
float m_moverate { 30 * 0.001f };
|
||||
float m_moverateweathertypefactor { 1.f }; // medium-dependent; 1.0 for snow, faster for rain
|
||||
glm::dvec3 m_camerapos { 0.0 };
|
||||
bool m_freeflymode { true };
|
||||
bool m_windowopen { true };
|
||||
int m_activecab{ 0 };
|
||||
glm::dvec3 m_cabcameramove{ 0.0 };
|
||||
};
|
||||
43
rendering/renderer.cpp
Normal file
43
rendering/renderer.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "renderer.h"
|
||||
#include "Logs.h"
|
||||
|
||||
std::unique_ptr<gfx_renderer> GfxRenderer;
|
||||
|
||||
bool gfx_renderer_factory::register_backend(const std::string &backend, gfx_renderer_factory::create_method func)
|
||||
{
|
||||
backends[backend] = func;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<gfx_renderer> gfx_renderer_factory::create(const std::string &backend)
|
||||
{
|
||||
auto it = backends.find(backend);
|
||||
if (it != backends.end())
|
||||
return it->second();
|
||||
|
||||
ErrorLog("renderer \"" + backend + "\" not found!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gfx_renderer_factory *gfx_renderer_factory::get_instance()
|
||||
{
|
||||
if (!instance)
|
||||
instance = new gfx_renderer_factory();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
gfx_renderer_factory *gfx_renderer_factory::instance;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
116
rendering/renderer.h
Normal file
116
rendering/renderer.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "geometrybank.h"
|
||||
#include "interfaces/IMaterial.h"
|
||||
#include "interfaces/ITexture.h"
|
||||
#include "Globals.h"
|
||||
|
||||
struct lighting_data;
|
||||
|
||||
namespace gl
|
||||
{
|
||||
class program;
|
||||
}
|
||||
|
||||
class gfx_renderer {
|
||||
|
||||
public:
|
||||
// types
|
||||
// constructors
|
||||
// destructor
|
||||
virtual ~gfx_renderer() {}
|
||||
// methods
|
||||
virtual auto Init( GLFWwindow *Window ) -> bool = 0;
|
||||
virtual bool AddViewport(const global_settings::extraviewport_config &conf) = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
// main draw call. returns false on error
|
||||
virtual auto Render() -> bool = 0;
|
||||
virtual void SwapBuffers() = 0;
|
||||
virtual auto Framerate() -> float = 0;
|
||||
// geometry methods
|
||||
// NOTE: hands-on geometry management is exposed as a temporary measure; ultimately all visualization data should be generated/handled automatically by the renderer itself
|
||||
// creates a new geometry bank. returns: handle to the bank or NULL
|
||||
virtual auto Create_Bank() -> gfx::geometrybank_handle = 0;
|
||||
// creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
|
||||
virtual auto Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) -> gfx::geometry_handle = 0;
|
||||
// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
|
||||
virtual auto Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) -> gfx::geometry_handle = 0;
|
||||
// replaces data of specified chunk with the supplied vertex data, starting from specified offset
|
||||
virtual auto Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) -> bool = 0;
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
virtual auto Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) -> bool = 0;
|
||||
// provides direct access to index data of specfied chunk
|
||||
virtual auto Indices( gfx::geometry_handle const &Geometry ) const->gfx::index_array const & = 0;
|
||||
// provides direct access to vertex data of specfied chunk
|
||||
virtual auto Vertices( gfx::geometry_handle const &Geometry ) const ->gfx::vertex_array const & = 0;
|
||||
// provides direct access to vertex user data of specfied chunk
|
||||
virtual auto UserData( gfx::geometry_handle const &Geometry ) const ->gfx::userdata_array const & = 0;
|
||||
// material methods
|
||||
virtual auto Fetch_Material( std::string const &Filename, bool const Loadnow = true ) -> material_handle = 0;
|
||||
virtual void Bind_Material( material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr ) = 0;
|
||||
virtual auto Material( material_handle const Material ) const -> IMaterial const * = 0;
|
||||
// shader methods
|
||||
virtual auto Fetch_Shader( std::string const &name ) -> std::shared_ptr<gl::program> = 0;
|
||||
// texture methods
|
||||
virtual auto Fetch_Texture( std::string const &Filename, bool const Loadnow = true, GLint format_hint = GL_SRGB_ALPHA ) -> texture_handle = 0;
|
||||
virtual void Bind_Texture( texture_handle const Texture ) = 0;
|
||||
virtual void Bind_Texture( std::size_t const Unit, texture_handle const Texture ) = 0;
|
||||
virtual auto Texture( texture_handle const Texture ) -> ITexture & = 0;
|
||||
virtual auto Texture( texture_handle const Texture ) const -> ITexture const & = 0;
|
||||
// utility methods
|
||||
virtual void Pick_Control_Callback( std::function<void( TSubModel const *, const glm::vec2 )> Callback ) = 0;
|
||||
virtual void Pick_Node_Callback( std::function<void( scene::basic_node * )> Callback ) = 0;
|
||||
virtual auto Pick_Control() const -> TSubModel const * = 0;
|
||||
virtual auto Pick_Node() const -> scene::basic_node const * = 0;
|
||||
|
||||
virtual auto Mouse_Position() const -> glm::dvec3 = 0;
|
||||
// maintenance methods
|
||||
virtual void Update( double const Deltatime ) = 0;
|
||||
virtual void Update_Pick_Control() = 0;
|
||||
virtual void Update_Pick_Node() = 0;
|
||||
virtual auto Update_Mouse_Position() -> glm::dvec3 = 0;
|
||||
virtual bool Debug_Ui_State(std::optional<bool>) = 0;
|
||||
// debug methods
|
||||
virtual auto info_times() const -> std::string const & = 0;
|
||||
virtual auto info_stats() const -> std::string const & = 0;
|
||||
// imgui renderer
|
||||
virtual class imgui_renderer *GetImguiRenderer() = 0;
|
||||
virtual void MakeScreenshot() = 0;
|
||||
};
|
||||
|
||||
class gfx_renderer_factory
|
||||
{
|
||||
public:
|
||||
using create_method = std::unique_ptr<gfx_renderer>(*)();
|
||||
|
||||
bool register_backend(const std::string &backend, create_method func);
|
||||
std::unique_ptr<gfx_renderer> create(const std::string &name);
|
||||
|
||||
static gfx_renderer_factory* get_instance();
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, create_method> backends;
|
||||
static gfx_renderer_factory *instance;
|
||||
};
|
||||
|
||||
class imgui_renderer
|
||||
{
|
||||
public:
|
||||
virtual bool Init() = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
virtual void BeginFrame() = 0;
|
||||
virtual void Render() = 0;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<gfx_renderer> GfxRenderer;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
67
rendering/screenshot.cpp
Normal file
67
rendering/screenshot.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "stdafx.h"
|
||||
#include "screenshot.h"
|
||||
#include "Globals.h"
|
||||
#include "Logs.h"
|
||||
#include <png.h>
|
||||
|
||||
void screenshot_manager::screenshot_save_thread( char *img, int w, int h )
|
||||
{
|
||||
png_image png;
|
||||
memset(&png, 0, sizeof(png_image));
|
||||
png.version = PNG_IMAGE_VERSION;
|
||||
png.width = w;
|
||||
png.height = h;
|
||||
|
||||
int stride;
|
||||
if (Global.gfx_usegles)
|
||||
{
|
||||
png.format = PNG_FORMAT_RGBA;
|
||||
stride = -w * 4;
|
||||
|
||||
for (int y = 0; y < h; y++)
|
||||
for (int x = 0; x < w; x++)
|
||||
img[(y * w + x) * 4 + 3] = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
png.format = PNG_FORMAT_RGB;
|
||||
stride = -w * 3;
|
||||
}
|
||||
|
||||
char datetime[64];
|
||||
time_t timer;
|
||||
struct tm* tm_info;
|
||||
time(&timer);
|
||||
tm_info = localtime(&timer);
|
||||
strftime(datetime, 64, "%Y-%m-%d_%H-%M-%S", tm_info);
|
||||
|
||||
uint64_t perf;
|
||||
#ifdef _WIN32
|
||||
QueryPerformanceCounter((LARGE_INTEGER*)&perf);
|
||||
#elif __unix__
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
perf = ts.tv_nsec;
|
||||
#endif
|
||||
|
||||
std::string filename = Global.screenshot_dir + "/" + std::string(datetime) +
|
||||
"_" + std::to_string(perf) + ".png";
|
||||
|
||||
if (png_image_write_to_file(&png, filename.c_str(), 0, img, stride, nullptr) == 1)
|
||||
WriteLog("saved " + filename);
|
||||
else
|
||||
WriteLog("failed to save " + filename);
|
||||
|
||||
delete[] img;
|
||||
}
|
||||
|
||||
void screenshot_manager::make_screenshot()
|
||||
{
|
||||
char *img = new char[Global.fb_size.x * Global.fb_size.y * 4];
|
||||
glReadPixels(0, 0, Global.fb_size.x, Global.fb_size.y, Global.gfx_usegles ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)img);
|
||||
//m7t: use pbo
|
||||
|
||||
std::thread t(screenshot_save_thread, img, Global.fb_size.x, Global.fb_size.y);
|
||||
t.detach();
|
||||
}
|
||||
|
||||
7
rendering/screenshot.h
Normal file
7
rendering/screenshot.h
Normal file
@@ -0,0 +1,7 @@
|
||||
class screenshot_manager
|
||||
{
|
||||
static void screenshot_save_thread(char *img , int w, int h);
|
||||
|
||||
public:
|
||||
static void make_screenshot();
|
||||
};
|
||||
Reference in New Issue
Block a user