2015-12-13 21:21:07 -04:00
/*
* Copyright 2015 Andrew Mac . All rights reserved .
2016-01-01 00:11:04 -08:00
* License : https : //github.com/bkaradzic/bgfx#license-bsd-2-clause
2015-12-13 21:21:07 -04:00
*/
# include "common.h"
# include "bgfx_utils.h"
# include "imgui/imgui.h"
# include "camera.h"
# include "bounds.h"
2015-12-13 21:32:10 -08:00
# include <bx/allocator.h>
2015-12-13 21:21:07 -04:00
# include <bx/debug.h>
2015-12-13 21:32:10 -08:00
# include <bx/fpumath.h>
2015-12-13 21:21:07 -04:00
static float s_texelHalf = 0.0f ;
static bool s_originBottomLeft = false ;
static uint32_t s_terrainSize = 256 ;
struct PosTexCoord0Vertex
{
2015-12-13 09:48:21 -08:00
float m_x ;
float m_y ;
float m_z ;
float m_u ;
float m_v ;
static void init ( )
{
ms_decl
. begin ( )
. add ( bgfx : : Attrib : : Position , 3 , bgfx : : AttribType : : Float )
. add ( bgfx : : Attrib : : TexCoord0 , 2 , bgfx : : AttribType : : Float )
. end ( ) ;
}
static bgfx : : VertexDecl ms_decl ;
2015-12-13 21:21:07 -04:00
} ;
bgfx : : VertexDecl PosTexCoord0Vertex : : ms_decl ;
struct TerrainData
{
2015-12-13 09:48:21 -08:00
uint32_t m_mode ;
bool m_dirty ;
float m_transform [ 16 ] ;
uint8_t * m_heightMap ;
PosTexCoord0Vertex * m_vertices ;
uint32_t m_vertexCount ;
uint16_t * m_indices ;
uint32_t m_indexCount ;
2015-12-13 21:21:07 -04:00
} ;
struct BrushData
{
2015-12-13 09:48:21 -08:00
bool m_raise ;
int32_t m_size ;
float m_power ;
2015-12-13 21:21:07 -04:00
} ;
2016-03-06 15:29:22 -08:00
class ExampleTerrain : public entry : : AppI
2015-12-13 21:21:07 -04:00
{
2015-12-13 09:48:21 -08:00
void init ( int _argc , char * * _argv ) BX_OVERRIDE
{
Args args ( _argc , _argv ) ;
m_width = 1280 ;
m_height = 720 ;
m_debug = BGFX_DEBUG_TEXT ;
m_reset = BGFX_RESET_VSYNC ;
2016-02-04 21:46:12 -08:00
bgfx : : init ( args . m_type , args . m_pciId ) ;
2015-12-13 09:48:21 -08:00
bgfx : : reset ( m_width , m_height , m_reset ) ;
// Enable m_debug text.
bgfx : : setDebug ( m_debug ) ;
// Set view 0 clear state.
bgfx : : setViewClear ( 0
, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH
, 0x303030ff
, 1.0f
, 0
) ;
// Create vertex stream declaration.
PosTexCoord0Vertex : : init ( ) ;
// Create program from shaders.
m_terrainProgram = loadProgram ( " vs_terrain " , " fs_terrain " ) ;
m_terrainHeightTextureProgram = loadProgram ( " vs_terrain_height_texture " , " fs_terrain " ) ;
// Imgui.
imguiCreate ( ) ;
m_timeOffset = bx : : getHPCounter ( ) ;
const bgfx : : RendererType : : Enum renderer = bgfx : : getRendererType ( ) ;
s_texelHalf = bgfx : : RendererType : : Direct3D9 = = renderer ? 0.5f : 0.0f ;
s_originBottomLeft = bgfx : : RendererType : : OpenGL = = renderer | | bgfx : : RendererType : : OpenGLES = = renderer ;
m_vbh . idx = bgfx : : invalidHandle ;
m_ibh . idx = bgfx : : invalidHandle ;
m_dvbh . idx = bgfx : : invalidHandle ;
m_dibh . idx = bgfx : : invalidHandle ;
m_heightTexture . idx = bgfx : : invalidHandle ;
s_heightTexture = bgfx : : createUniform ( " s_heightTexture " , bgfx : : UniformType : : Int1 ) ;
m_oldWidth = 0 ;
m_oldHeight = 0 ;
m_oldReset = m_reset ;
m_scrollArea = 0 ;
m_brush . m_power = 0.5f ;
m_brush . m_size = 10 ;
m_brush . m_raise = true ;
2015-12-13 21:21:07 -04:00
2015-12-13 21:32:10 -08:00
uint32_t num = s_terrainSize * s_terrainSize ;
m_terrain . m_mode = 0 ;
2015-12-13 09:48:21 -08:00
m_terrain . m_dirty = true ;
2015-12-13 21:32:10 -08:00
m_terrain . m_vertices = ( PosTexCoord0Vertex * ) BX_ALLOC ( entry : : getAllocator ( ) , num * sizeof ( PosTexCoord0Vertex ) ) ;
m_terrain . m_indices = ( uint16_t * ) BX_ALLOC ( entry : : getAllocator ( ) , num * sizeof ( uint16_t ) * 6 ) ;
m_terrain . m_heightMap = ( uint8_t * ) BX_ALLOC ( entry : : getAllocator ( ) , num ) ;
2015-12-13 09:48:21 -08:00
bx : : mtxSRT ( m_terrain . m_transform , 1.0f , 1.0f , 1.0f , 0.0f , 0.0f , 0.0f , 0.0f , 0.0f , 0.0f ) ;
memset ( m_terrain . m_heightMap , 0 , sizeof ( uint8_t ) * s_terrainSize * s_terrainSize ) ;
cameraCreate ( ) ;
2015-12-13 09:52:57 -08:00
const float initialPos [ 3 ] = { s_terrainSize / 2.0f , 100.0f , 0.0f } ;
2015-12-13 09:48:21 -08:00
cameraSetPosition ( initialPos ) ;
2015-12-13 09:52:57 -08:00
cameraSetVerticalAngle ( - bx : : pi / 4.0f ) ;
2015-12-13 09:48:21 -08:00
}
virtual int shutdown ( ) BX_OVERRIDE
{
// Cleanup.
cameraDestroy ( ) ;
imguiDestroy ( ) ;
if ( bgfx : : isValid ( m_ibh ) )
{
bgfx : : destroyIndexBuffer ( m_ibh ) ;
}
if ( bgfx : : isValid ( m_vbh ) )
{
bgfx : : destroyVertexBuffer ( m_vbh ) ;
}
if ( bgfx : : isValid ( m_dibh ) )
{
bgfx : : destroyDynamicIndexBuffer ( m_dibh ) ;
}
if ( bgfx : : isValid ( m_dvbh ) )
{
bgfx : : destroyDynamicVertexBuffer ( m_dvbh ) ;
}
bgfx : : destroyUniform ( s_heightTexture ) ;
2015-12-13 21:32:10 -08:00
2015-12-13 09:48:21 -08:00
if ( bgfx : : isValid ( m_heightTexture ) )
{
bgfx : : destroyTexture ( m_heightTexture ) ;
}
bgfx : : destroyProgram ( m_terrainProgram ) ;
bgfx : : destroyProgram ( m_terrainHeightTextureProgram ) ;
2016-02-24 21:23:45 -08:00
/// When data is passed to bgfx via makeRef we need to make
/// sure library is done with it before freeing memory blocks.
bgfx : : frame ( ) ;
bx : : AllocatorI * allocator = entry : : getAllocator ( ) ;
BX_FREE ( allocator , m_terrain . m_vertices ) ;
BX_FREE ( allocator , m_terrain . m_indices ) ;
BX_FREE ( allocator , m_terrain . m_heightMap ) ;
2015-12-13 09:48:21 -08:00
// Shutdown bgfx.
bgfx : : shutdown ( ) ;
return 0 ;
}
void updateTerrainMesh ( )
{
m_terrain . m_vertexCount = 0 ;
for ( uint32_t y = 0 ; y < s_terrainSize ; y + + )
{
for ( uint32_t x = 0 ; x < s_terrainSize ; x + + )
{
PosTexCoord0Vertex * vert = & m_terrain . m_vertices [ m_terrain . m_vertexCount ] ;
vert - > m_x = ( float ) x ;
vert - > m_y = m_terrain . m_heightMap [ ( y * s_terrainSize ) + x ] ;
vert - > m_z = ( float ) y ;
vert - > m_u = ( float ) x / ( float ) s_terrainSize ;
vert - > m_v = ( float ) y / ( float ) s_terrainSize ;
m_terrain . m_vertexCount + + ;
}
}
m_terrain . m_indexCount = 0 ;
for ( uint32_t y = 0 ; y < ( s_terrainSize - 1 ) ; y + + )
{
uint32_t y_offset = ( y * s_terrainSize ) ;
for ( uint32_t x = 0 ; x < ( s_terrainSize - 1 ) ; x + + )
{
m_terrain . m_indices [ m_terrain . m_indexCount + 0 ] = y_offset + x + 1 ;
m_terrain . m_indices [ m_terrain . m_indexCount + 1 ] = y_offset + x + s_terrainSize ;
m_terrain . m_indices [ m_terrain . m_indexCount + 2 ] = y_offset + x ;
m_terrain . m_indices [ m_terrain . m_indexCount + 3 ] = y_offset + x + s_terrainSize + 1 ;
m_terrain . m_indices [ m_terrain . m_indexCount + 4 ] = y_offset + x + s_terrainSize ;
m_terrain . m_indices [ m_terrain . m_indexCount + 5 ] = y_offset + x + 1 ;
m_terrain . m_indexCount + = 6 ;
}
}
}
void updateTerrain ( )
{
const bgfx : : Memory * mem ;
2015-12-13 19:43:08 -08:00
switch ( m_terrain . m_mode )
2015-12-13 09:48:21 -08:00
{
2015-12-13 19:43:08 -08:00
default : // Vertex Buffer : Destroy and recreate a regular vertex buffer to update terrain.
2015-12-13 09:48:21 -08:00
updateTerrainMesh ( ) ;
if ( bgfx : : isValid ( m_vbh ) )
{
bgfx : : destroyVertexBuffer ( m_vbh ) ;
}
mem = bgfx : : makeRef ( & m_terrain . m_vertices [ 0 ] , sizeof ( PosTexCoord0Vertex ) * m_terrain . m_vertexCount ) ;
m_vbh = bgfx : : createVertexBuffer ( mem , PosTexCoord0Vertex : : ms_decl ) ;
if ( bgfx : : isValid ( m_ibh ) )
{
bgfx : : destroyIndexBuffer ( m_ibh ) ;
}
mem = bgfx : : makeRef ( & m_terrain . m_indices [ 0 ] , sizeof ( uint16_t ) * m_terrain . m_indexCount ) ;
m_ibh = bgfx : : createIndexBuffer ( mem ) ;
2015-12-13 19:43:08 -08:00
break ;
2015-12-13 09:48:21 -08:00
2015-12-13 19:43:08 -08:00
case 1 : // Dynamic Vertex Buffer : Utilize dynamic vertex buffer to update terrain.
2015-12-13 09:48:21 -08:00
updateTerrainMesh ( ) ;
if ( ! bgfx : : isValid ( m_dvbh ) )
{
m_dvbh = bgfx : : createDynamicVertexBuffer ( m_terrain . m_vertexCount , PosTexCoord0Vertex : : ms_decl ) ;
}
mem = bgfx : : makeRef ( & m_terrain . m_vertices [ 0 ] , sizeof ( PosTexCoord0Vertex ) * m_terrain . m_vertexCount ) ;
bgfx : : updateDynamicVertexBuffer ( m_dvbh , 0 , mem ) ;
if ( ! bgfx : : isValid ( m_dibh ) )
{
m_dibh = bgfx : : createDynamicIndexBuffer ( m_terrain . m_indexCount ) ;
}
mem = bgfx : : makeRef ( & m_terrain . m_indices [ 0 ] , sizeof ( uint16_t ) * m_terrain . m_indexCount ) ;
bgfx : : updateDynamicIndexBuffer ( m_dibh , 0 , mem ) ;
2015-12-13 19:43:08 -08:00
break ;
2015-12-13 09:48:21 -08:00
2015-12-13 19:43:08 -08:00
case 2 : // Height Texture: Update a height texture that is sampled in the terrain vertex shader.
2015-12-13 09:48:21 -08:00
if ( ! bgfx : : isValid ( m_vbh ) | | ! bgfx : : isValid ( m_ibh ) )
{
updateTerrainMesh ( ) ;
mem = bgfx : : makeRef ( & m_terrain . m_vertices [ 0 ] , sizeof ( PosTexCoord0Vertex ) * m_terrain . m_vertexCount ) ;
m_vbh = bgfx : : createVertexBuffer ( mem , PosTexCoord0Vertex : : ms_decl ) ;
mem = bgfx : : makeRef ( & m_terrain . m_indices [ 0 ] , sizeof ( uint16_t ) * m_terrain . m_indexCount ) ;
m_ibh = bgfx : : createIndexBuffer ( mem ) ;
}
if ( ! bgfx : : isValid ( m_heightTexture ) )
{
m_heightTexture = bgfx : : createTexture2D ( s_terrainSize , s_terrainSize , 1 , bgfx : : TextureFormat : : R8 ) ;
}
mem = bgfx : : makeRef ( & m_terrain . m_heightMap [ 0 ] , sizeof ( uint8_t ) * s_terrainSize * s_terrainSize ) ;
bgfx : : updateTexture2D ( m_heightTexture , 0 , 0 , 0 , s_terrainSize , s_terrainSize , mem ) ;
2015-12-13 19:43:08 -08:00
break ;
2015-12-13 09:48:21 -08:00
}
}
void paintTerrainHeight ( uint32_t _x , uint32_t _y )
{
for ( int32_t area_y = - m_brush . m_size ; area_y < m_brush . m_size ; + + area_y )
{
for ( int32_t area_x = - m_brush . m_size ; area_x < m_brush . m_size ; + + area_x )
{
int32_t brush_x = _x + area_x ;
if ( brush_x < 0
| | brush_x > ( int32_t ) s_terrainSize )
{
continue ;
}
int32_t brush_y = _y + area_y ;
if ( brush_y < 0
| | brush_y > ( int32_t ) s_terrainSize )
{
continue ;
}
uint32_t heightMapPos = ( brush_y * s_terrainSize ) + brush_x ;
float height = ( float ) m_terrain . m_heightMap [ heightMapPos ] ;
// Brush attenuation
float a2 = ( float ) ( area_x * area_x ) ;
float b2 = ( float ) ( area_y * area_y ) ;
2015-12-13 20:05:20 -08:00
float brushAttn = m_brush . m_size - bx : : fsqrt ( a2 + b2 ) ;
2015-12-13 09:48:21 -08:00
// Raise/Lower and scale by brush power.
2015-12-13 20:05:20 -08:00
height + = ( bx : : fclamp ( brushAttn * m_brush . m_power , 0.0 , m_brush . m_power ) * m_brush . m_raise )
2016-01-04 21:48:12 -08:00
? 1.0f
: - 1.0f
2015-12-13 20:05:20 -08:00
;
2015-12-13 09:48:21 -08:00
2016-01-04 21:48:12 -08:00
m_terrain . m_heightMap [ heightMapPos ] = ( uint8_t ) bx : : fclamp ( height , 0.0f , 255.0f ) ;
2015-12-13 09:48:21 -08:00
m_terrain . m_dirty = true ;
}
}
}
void mousePickTerrain ( )
{
float ray_clip [ 4 ] ;
ray_clip [ 0 ] = ( ( 2.0f * m_mouseState . m_mx ) / m_width - 1.0f ) * - 1.0f ;
ray_clip [ 1 ] = ( ( 1.0f - ( 2.0f * m_mouseState . m_my ) / m_height ) ) * - 1.0f ;
2016-01-04 21:48:12 -08:00
ray_clip [ 2 ] = - 1.0f ;
ray_clip [ 3 ] = 1.0f ;
2015-12-13 09:48:21 -08:00
float invProjMtx [ 16 ] ;
bx : : mtxInverse ( invProjMtx , m_projMtx ) ;
float ray_eye [ 4 ] ;
bx : : vec4MulMtx ( ray_eye , ray_clip , invProjMtx ) ;
ray_eye [ 2 ] = - 1.0f ;
ray_eye [ 3 ] = 0.0f ;
float invViewMtx [ 16 ] ;
bx : : mtxInverse ( invViewMtx , m_viewMtx ) ;
float ray_world [ 4 ] ;
bx : : vec4MulMtx ( ray_world , ray_eye , invViewMtx ) ;
float ray_dir [ 3 ] ;
bx : : vec3Norm ( ray_dir , ray_world ) ;
ray_dir [ 0 ] * = - 1.0 ;
ray_dir [ 1 ] * = - 1.0 ;
ray_dir [ 2 ] * = - 1.0 ;
float pos [ 3 ] ;
cameraGetPosition ( pos ) ;
for ( int i = 0 ; i < 1000 ; + + i )
{
bx : : vec3Add ( pos , pos , ray_dir ) ;
if ( pos [ 0 ] < 0
| | pos [ 0 ] > s_terrainSize
| | pos [ 2 ] < 0
| | pos [ 2 ] > s_terrainSize )
{
continue ;
}
uint32_t heightMapPos = ( ( uint32_t ) pos [ 2 ] * s_terrainSize ) + ( uint32_t ) pos [ 0 ] ;
if ( pos [ 1 ] < m_terrain . m_heightMap [ heightMapPos ] )
{
paintTerrainHeight ( ( uint32_t ) pos [ 0 ] , ( uint32_t ) pos [ 2 ] ) ;
return ;
}
}
}
bool update ( ) BX_OVERRIDE
{
if ( ! entry : : processEvents ( m_width , m_height , m_debug , m_reset , & m_mouseState ) )
{
int64_t now = bx : : getHPCounter ( ) ;
static int64_t last = now ;
const int64_t frameTime = now - last ;
last = now ;
const double freq = double ( bx : : getHPFrequency ( ) ) ;
const double toMs = 1000.0 / freq ;
const float deltaTime = float ( frameTime / freq ) ;
// Use m_debug font to print information about this example.
bgfx : : dbgTextClear ( ) ;
bgfx : : dbgTextPrintf ( 0 , 1 , 0x4f , " bgfx/examples/27-terrain " ) ;
bgfx : : dbgTextPrintf ( 0 , 2 , 0x6f , " Description: Terrain painting example. " ) ;
bgfx : : dbgTextPrintf ( 0 , 3 , 0x0f , " Frame: % 7.3f[ms] " , double ( frameTime ) * toMs ) ;
imguiBeginFrame ( m_mouseState . m_mx
, m_mouseState . m_my
, ( m_mouseState . m_buttons [ entry : : MouseButton : : Left ] ? IMGUI_MBUT_LEFT : 0 )
| ( m_mouseState . m_buttons [ entry : : MouseButton : : Right ] ? IMGUI_MBUT_RIGHT : 0 )
| ( m_mouseState . m_buttons [ entry : : MouseButton : : Middle ] ? IMGUI_MBUT_MIDDLE : 0 )
, m_mouseState . m_mz
, m_width
, m_height
) ;
imguiBeginScrollArea ( " Settings " , m_width - m_width / 5 - 10 , 10 , m_width / 5 , m_height / 3 , & m_scrollArea ) ;
imguiSeparatorLine ( ) ;
if ( imguiCheck ( " Vertex Buffer " , ( m_terrain . m_mode = = 0 ) ) )
{
m_terrain . m_mode = 0 ;
m_terrain . m_dirty = true ;
}
if ( imguiCheck ( " Dynamic Vertex Buffer " , ( m_terrain . m_mode = = 1 ) ) )
{
m_terrain . m_mode = 1 ;
m_terrain . m_dirty = true ;
}
if ( imguiCheck ( " Height Texture " , ( m_terrain . m_mode = = 2 ) ) )
{
m_terrain . m_mode = 2 ;
m_terrain . m_dirty = true ;
}
imguiSeparatorLine ( ) ;
if ( imguiCheck ( " Raise Terrain " , m_brush . m_raise ) )
{
m_brush . m_raise = ! m_brush . m_raise ;
}
imguiSlider ( " Brush Size " , m_brush . m_size , 1 , 50 ) ;
imguiSlider ( " Brush Power " , m_brush . m_power , 0.0f , 1.0f , 0.01f ) ;
imguiEndScrollArea ( ) ;
imguiEndFrame ( ) ;
2016-01-31 20:38:14 -08:00
if ( ! imguiMouseOverArea ( ) )
2015-12-13 09:48:21 -08:00
{
2016-01-31 20:38:14 -08:00
// Update camera.
cameraUpdate ( deltaTime , m_mouseState ) ;
if ( ! ! m_mouseState . m_buttons [ entry : : MouseButton : : Left ] )
{
mousePickTerrain ( ) ;
}
2015-12-13 09:48:21 -08:00
}
// Update terrain.
if ( m_terrain . m_dirty )
{
updateTerrain ( ) ;
m_terrain . m_dirty = false ;
}
// Set view 0 default viewport.
bgfx : : setViewRect ( 0 , 0 , 0 , m_width , m_height ) ;
cameraGetViewMtx ( m_viewMtx ) ;
bx : : mtxProj ( m_projMtx , 60.0f , float ( m_width ) / float ( m_height ) , 0.1f , 2000.0f , s_originBottomLeft ) ;
bgfx : : setViewTransform ( 0 , m_viewMtx , m_projMtx ) ;
bgfx : : setTransform ( m_terrain . m_transform ) ;
2015-12-13 19:43:08 -08:00
switch ( m_terrain . m_mode )
2015-12-13 09:48:21 -08:00
{
2015-12-13 19:43:08 -08:00
default :
2015-12-13 09:48:21 -08:00
bgfx : : setVertexBuffer ( m_vbh ) ;
bgfx : : setIndexBuffer ( m_ibh ) ;
bgfx : : submit ( 0 , m_terrainProgram ) ;
2015-12-13 19:43:08 -08:00
break ;
case 1 :
2015-12-13 09:48:21 -08:00
bgfx : : setVertexBuffer ( m_dvbh ) ;
bgfx : : setIndexBuffer ( m_dibh ) ;
bgfx : : submit ( 0 , m_terrainProgram ) ;
2015-12-13 19:43:08 -08:00
break ;
case 2 :
2015-12-13 09:48:21 -08:00
bgfx : : setVertexBuffer ( m_vbh ) ;
bgfx : : setIndexBuffer ( m_ibh ) ;
bgfx : : setTexture ( 0 , s_heightTexture , m_heightTexture ) ;
bgfx : : submit ( 0 , m_terrainHeightTextureProgram ) ;
2015-12-13 19:43:08 -08:00
break ;
2015-12-13 09:48:21 -08:00
}
// Advance to next frame. Rendering thread will be kicked to
// process submitted rendering primitives.
bgfx : : frame ( ) ;
return true ;
}
return false ;
}
bgfx : : VertexBufferHandle m_vbh ;
bgfx : : IndexBufferHandle m_ibh ;
bgfx : : DynamicVertexBufferHandle m_dvbh ;
bgfx : : DynamicIndexBufferHandle m_dibh ;
bgfx : : ProgramHandle m_terrainProgram ;
bgfx : : ProgramHandle m_terrainHeightTextureProgram ;
bgfx : : UniformHandle s_heightTexture ;
bgfx : : TextureHandle m_heightTexture ;
float m_viewMtx [ 16 ] ;
float m_projMtx [ 16 ] ;
uint32_t m_width ;
uint32_t m_height ;
uint32_t m_debug ;
uint32_t m_reset ;
uint32_t m_oldWidth ;
uint32_t m_oldHeight ;
uint32_t m_oldReset ;
int32_t m_scrollArea ;
TerrainData m_terrain ;
BrushData m_brush ;
entry : : MouseState m_mouseState ;
int64_t m_timeOffset ;
2015-12-13 21:21:07 -04:00
} ;
2016-03-06 15:29:22 -08:00
ENTRY_IMPLEMENT_MAIN ( ExampleTerrain ) ;