bgfx/examples/27-terrain/terrain.cpp

530 lines
13 KiB
C++
Raw Normal View History

2015-12-14 04:21:07 +03:00
/*
* Copyright 2015 Andrew Mac. All rights reserved.
2016-01-01 11:11:04 +03:00
* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
2015-12-14 04:21:07 +03:00
*/
2021-10-16 20:22:47 +03:00
#include <bx/allocator.h>
#include <bx/debug.h>
#include <bx/math.h>
2015-12-14 04:21:07 +03:00
#include "common.h"
#include "bgfx_utils.h"
#include "imgui/imgui.h"
#include "camera.h"
2017-06-26 07:44:04 +03:00
namespace
{
2017-03-13 04:25:23 +03:00
static const uint16_t s_terrainSize = 256;
2015-12-14 04:21:07 +03:00
struct PosTexCoord0Vertex
{
2015-12-13 20:48:21 +03:00
float m_x;
float m_y;
float m_z;
float m_u;
float m_v;
static void init()
{
2019-08-17 20:35:21 +03:00
ms_layout
2015-12-13 20:48:21 +03:00
.begin()
.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)
.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float)
.end();
}
2019-08-17 20:35:21 +03:00
static bgfx::VertexLayout ms_layout;
2015-12-14 04:21:07 +03:00
};
2019-08-17 20:35:21 +03:00
bgfx::VertexLayout PosTexCoord0Vertex::ms_layout;
2015-12-14 04:21:07 +03:00
struct TerrainData
{
2017-06-22 03:00:50 +03:00
int32_t m_mode;
2015-12-13 20:48:21 +03:00
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-14 04:21:07 +03:00
};
struct BrushData
{
2015-12-13 20:48:21 +03:00
bool m_raise;
int32_t m_size;
float m_power;
2015-12-14 04:21:07 +03:00
};
2016-03-07 02:29:22 +03:00
class ExampleTerrain : public entry::AppI
2015-12-14 04:21:07 +03:00
{
2017-06-26 07:44:04 +03:00
public:
2019-08-18 00:40:38 +03:00
ExampleTerrain(const char* _name, const char* _description, const char* _url)
: entry::AppI(_name, _description, _url)
2017-06-26 07:44:04 +03:00
{
}
2017-07-15 10:17:29 +03:00
void init(int32_t _argc, const char* const* _argv, uint32_t _width, uint32_t _height) override
2015-12-13 20:48:21 +03:00
{
Args args(_argc, _argv);
2017-06-30 08:23:18 +03:00
m_width = _width;
m_height = _height;
2017-06-26 07:44:04 +03:00
m_debug = BGFX_DEBUG_NONE;
2015-12-13 20:48:21 +03:00
m_reset = BGFX_RESET_VSYNC;
bgfx::Init init;
init.type = args.m_type;
init.vendorId = args.m_pciId;
init.resolution.width = m_width;
init.resolution.height = m_height;
init.resolution.reset = m_reset;
bgfx::init(init);
2015-12-13 20:48:21 +03:00
// Enable m_debug text.
bgfx::setDebug(m_debug);
// Set view 0 clear state.
bgfx::setViewClear(0
2017-06-22 06:06:01 +03:00
, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH
, 0x303030ff
, 1.0f
, 0
);
2015-12-13 20:48:21 +03:00
// 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();
2017-06-10 07:57:08 +03:00
m_vbh.idx = bgfx::kInvalidHandle;
m_ibh.idx = bgfx::kInvalidHandle;
m_dvbh.idx = bgfx::kInvalidHandle;
m_dibh.idx = bgfx::kInvalidHandle;
m_heightTexture.idx = bgfx::kInvalidHandle;
s_heightTexture = bgfx::createUniform("s_heightTexture", bgfx::UniformType::Sampler);
2015-12-13 20:48:21 +03:00
m_oldWidth = 0;
m_oldHeight = 0;
m_oldReset = m_reset;
m_brush.m_power = 0.5f;
m_brush.m_size = 10;
m_brush.m_raise = true;
2015-12-14 04:21:07 +03:00
2015-12-14 08:32:10 +03:00
uint32_t num = s_terrainSize * s_terrainSize;
m_terrain.m_mode = 0;
2015-12-13 20:48:21 +03:00
m_terrain.m_dirty = true;
2015-12-14 08:32:10 +03: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 20:48:21 +03: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);
2017-02-09 06:55:31 +03:00
bx::memSet(m_terrain.m_heightMap, 0, sizeof(uint8_t) * s_terrainSize * s_terrainSize);
2015-12-13 20:48:21 +03:00
cameraCreate();
2018-12-22 08:05:26 +03:00
cameraSetPosition({ s_terrainSize/2.0f, 100.0f, 0.0f });
cameraSetVerticalAngle(-bx::kPiQuarter);
2015-12-13 20:48:21 +03:00
}
2017-07-15 10:17:29 +03:00
virtual int shutdown() override
2015-12-13 20:48:21 +03:00
{
// Cleanup.
cameraDestroy();
imguiDestroy();
if (bgfx::isValid(m_ibh) )
{
bgfx::destroy(m_ibh);
2015-12-13 20:48:21 +03:00
}
if (bgfx::isValid(m_vbh) )
{
bgfx::destroy(m_vbh);
2015-12-13 20:48:21 +03:00
}
if (bgfx::isValid(m_dibh) )
{
bgfx::destroy(m_dibh);
2015-12-13 20:48:21 +03:00
}
if (bgfx::isValid(m_dvbh) )
{
bgfx::destroy(m_dvbh);
2015-12-13 20:48:21 +03:00
}
bgfx::destroy(s_heightTexture);
2015-12-14 08:32:10 +03:00
2015-12-13 20:48:21 +03:00
if (bgfx::isValid(m_heightTexture) )
{
bgfx::destroy(m_heightTexture);
2015-12-13 20:48:21 +03:00
}
bgfx::destroy(m_terrainProgram);
bgfx::destroy(m_terrainHeightTextureProgram);
2015-12-13 20:48:21 +03:00
2016-02-25 08:23:45 +03: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 20:48:21 +03: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 = (x + 0.5f) / s_terrainSize;
vert->m_v = (y + 0.5f) / s_terrainSize;
2015-12-13 20:48:21 +03:00
m_terrain.m_vertexCount++;
}
}
m_terrain.m_indexCount = 0;
2017-03-13 04:25:23 +03:00
for (uint16_t y = 0; y < (s_terrainSize - 1); y++)
2015-12-13 20:48:21 +03:00
{
2017-03-13 04:25:23 +03:00
uint16_t y_offset = (y * s_terrainSize);
for (uint16_t x = 0; x < (s_terrainSize - 1); x++)
2015-12-13 20:48:21 +03:00
{
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-14 06:43:08 +03:00
switch (m_terrain.m_mode)
2015-12-13 20:48:21 +03:00
{
2015-12-14 06:43:08 +03:00
default: // Vertex Buffer : Destroy and recreate a regular vertex buffer to update terrain.
2015-12-13 20:48:21 +03:00
updateTerrainMesh();
if (bgfx::isValid(m_vbh) )
{
bgfx::destroy(m_vbh);
2015-12-13 20:48:21 +03:00
}
mem = bgfx::makeRef(&m_terrain.m_vertices[0], sizeof(PosTexCoord0Vertex) * m_terrain.m_vertexCount);
2019-08-17 20:35:21 +03:00
m_vbh = bgfx::createVertexBuffer(mem, PosTexCoord0Vertex::ms_layout);
2015-12-13 20:48:21 +03:00
if (bgfx::isValid(m_ibh) )
{
bgfx::destroy(m_ibh);
2015-12-13 20:48:21 +03:00
}
mem = bgfx::makeRef(&m_terrain.m_indices[0], sizeof(uint16_t) * m_terrain.m_indexCount);
m_ibh = bgfx::createIndexBuffer(mem);
2015-12-14 06:43:08 +03:00
break;
2015-12-13 20:48:21 +03:00
2015-12-14 06:43:08 +03:00
case 1: // Dynamic Vertex Buffer : Utilize dynamic vertex buffer to update terrain.
2015-12-13 20:48:21 +03:00
updateTerrainMesh();
if (!bgfx::isValid(m_dvbh) )
{
2019-08-17 20:35:21 +03:00
m_dvbh = bgfx::createDynamicVertexBuffer(m_terrain.m_vertexCount, PosTexCoord0Vertex::ms_layout);
2015-12-13 20:48:21 +03:00
}
mem = bgfx::makeRef(&m_terrain.m_vertices[0], sizeof(PosTexCoord0Vertex) * m_terrain.m_vertexCount);
bgfx::update(m_dvbh, 0, mem);
2015-12-13 20:48:21 +03:00
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::update(m_dibh, 0, mem);
2015-12-14 06:43:08 +03:00
break;
2015-12-13 20:48:21 +03:00
2015-12-14 06:43:08 +03:00
case 2: // Height Texture: Update a height texture that is sampled in the terrain vertex shader.
2015-12-13 20:48:21 +03: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);
2019-08-17 20:35:21 +03:00
m_vbh = bgfx::createVertexBuffer(mem, PosTexCoord0Vertex::ms_layout);
2015-12-13 20:48:21 +03:00
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) )
{
2016-08-20 07:05:37 +03:00
m_heightTexture = bgfx::createTexture2D(s_terrainSize, s_terrainSize, false, 1, bgfx::TextureFormat::R8);
2015-12-13 20:48:21 +03:00
}
mem = bgfx::makeRef(&m_terrain.m_heightMap[0], sizeof(uint8_t) * s_terrainSize * s_terrainSize);
2016-08-22 00:03:16 +03:00
bgfx::updateTexture2D(m_heightTexture, 0, 0, 0, 0, s_terrainSize, s_terrainSize, mem);
2015-12-14 06:43:08 +03:00
break;
2015-12-13 20:48:21 +03: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)
2015-12-13 20:48:21 +03:00
{
continue;
}
int32_t brush_y = _y + area_y;
if (brush_y < 0
|| brush_y >= (int32_t)s_terrainSize)
2015-12-13 20:48:21 +03:00
{
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);
2018-01-14 02:33:50 +03:00
float brushAttn = m_brush.m_size - bx::sqrt(a2 + b2);
2015-12-13 20:48:21 +03:00
// Raise/Lower and scale by brush power.
2017-12-03 05:15:31 +03:00
height += 0.0f < bx::clamp(brushAttn*m_brush.m_power, 0.0f, m_brush.m_power) && m_brush.m_raise
2016-01-05 08:48:12 +03:00
? 1.0f
: -1.0f
2015-12-14 07:05:20 +03:00
;
2015-12-13 20:48:21 +03:00
2017-12-03 05:15:31 +03:00
m_terrain.m_heightMap[heightMapPos] = (uint8_t)bx::clamp(height, 0.0f, 255.0f);
2015-12-13 20:48:21 +03: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-05 08:48:12 +03:00
ray_clip[2] = -1.0f;
ray_clip[3] = 1.0f;
2015-12-13 20:48:21 +03: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);
2018-12-22 05:02:39 +03:00
const bx::Vec3 rayDir = bx::mul(bx::normalize(bx::load<bx::Vec3>(ray_world) ), -1.0f);
2018-12-21 08:49:10 +03:00
2018-12-22 08:05:26 +03:00
bx::Vec3 pos = cameraGetPosition();
2015-12-13 20:48:21 +03:00
for (int i = 0; i < 1000; ++i)
{
2018-12-21 08:49:10 +03:00
pos = bx::add(pos, rayDir);
2015-12-13 20:48:21 +03:00
2018-12-21 08:49:10 +03:00
if (pos.x < 0
|| pos.x >= s_terrainSize
|| pos.z < 0
|| pos.z >= s_terrainSize)
2015-12-13 20:48:21 +03:00
{
continue;
}
2018-12-21 08:49:10 +03:00
uint32_t heightMapPos = ( (uint32_t)pos.z * s_terrainSize) + (uint32_t)pos.x;
if (pos.y < m_terrain.m_heightMap[heightMapPos])
2015-12-13 20:48:21 +03:00
{
2018-12-21 08:49:10 +03:00
paintTerrainHeight( (uint32_t)pos.x, (uint32_t)pos.z);
2015-12-13 20:48:21 +03:00
return;
}
}
}
2017-07-15 10:17:29 +03:00
bool update() override
2015-12-13 20:48:21 +03:00
{
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 float deltaTime = float(frameTime/freq);
imguiBeginFrame(m_mouseState.m_mx
2017-06-22 06:06:01 +03:00
, 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
, uint16_t(m_width)
, uint16_t(m_height)
);
2015-12-13 20:48:21 +03:00
2017-06-30 09:19:20 +03:00
showExampleDialog(this);
2017-06-26 07:44:04 +03:00
2017-06-27 08:51:56 +03:00
ImGui::SetNextWindowPos(
ImVec2(m_width - m_width / 5.0f - 10.0f, 10.0f)
2017-12-02 08:04:27 +03:00
, ImGuiCond_FirstUseEver
);
ImGui::SetNextWindowSize(
ImVec2(m_width / 5.0f, m_height / 3.0f)
, ImGuiCond_FirstUseEver
2017-06-27 08:51:56 +03:00
);
2017-06-22 03:00:50 +03:00
ImGui::Begin("Settings"
2017-06-22 06:06:01 +03:00
, NULL
2017-12-02 08:04:27 +03:00
, 0
2017-06-22 06:06:01 +03:00
);
2015-12-13 20:48:21 +03:00
2017-06-22 03:00:50 +03:00
ImGui::Separator();
2015-12-13 20:48:21 +03:00
2017-06-22 03:00:50 +03:00
m_terrain.m_dirty |= ImGui::RadioButton("Vertex Buffer", &m_terrain.m_mode, 0);
m_terrain.m_dirty |= ImGui::RadioButton("Dynamic Vertex Buffer", &m_terrain.m_mode, 1);
m_terrain.m_dirty |= ImGui::RadioButton("Height Texture", &m_terrain.m_mode, 2);
2017-06-22 06:06:01 +03:00
2017-06-22 03:00:50 +03:00
ImGui::Separator();
2015-12-13 20:48:21 +03:00
2017-06-22 03:00:50 +03:00
ImGui::Checkbox("Raise Terrain", &m_brush.m_raise);
2015-12-13 20:48:21 +03:00
2017-06-22 03:00:50 +03:00
ImGui::SliderInt("Brush Size", &m_brush.m_size, 1, 50);
ImGui::SliderFloat("Brush Power", &m_brush.m_power, 0.0f, 1.0f);
2015-12-13 20:48:21 +03:00
2017-06-22 03:00:50 +03:00
ImGui::End();
2015-12-13 20:48:21 +03:00
imguiEndFrame();
// Update camera.
cameraUpdate(deltaTime, m_mouseState, ImGui::MouseOverArea() );
2017-06-24 01:26:28 +03:00
if (!ImGui::MouseOverArea() )
2015-12-13 20:48:21 +03:00
{
2016-02-01 07:38:14 +03:00
if (!!m_mouseState.m_buttons[entry::MouseButton::Left])
{
mousePickTerrain();
}
2015-12-13 20:48:21 +03:00
}
// Update terrain.
if (m_terrain.m_dirty)
{
updateTerrain();
m_terrain.m_dirty = false;
}
// Set view 0 default viewport.
2017-03-13 04:25:23 +03:00
bgfx::setViewRect(0, 0, 0, uint16_t(m_width), uint16_t(m_height) );
2015-12-13 20:48:21 +03:00
cameraGetViewMtx(m_viewMtx);
2017-02-23 09:26:39 +03:00
bx::mtxProj(m_projMtx, 60.0f, float(m_width) / float(m_height), 0.1f, 2000.0f, bgfx::getCaps()->homogeneousDepth);
2015-12-13 20:48:21 +03:00
bgfx::setViewTransform(0, m_viewMtx, m_projMtx);
bgfx::setTransform(m_terrain.m_transform);
2015-12-14 06:43:08 +03:00
switch (m_terrain.m_mode)
2015-12-13 20:48:21 +03:00
{
2015-12-14 06:43:08 +03:00
default:
2017-05-14 21:48:59 +03:00
bgfx::setVertexBuffer(0, m_vbh);
2015-12-13 20:48:21 +03:00
bgfx::setIndexBuffer(m_ibh);
bgfx::submit(0, m_terrainProgram);
2015-12-14 06:43:08 +03:00
break;
case 1:
2017-05-14 21:48:59 +03:00
bgfx::setVertexBuffer(0, m_dvbh);
2015-12-13 20:48:21 +03:00
bgfx::setIndexBuffer(m_dibh);
bgfx::submit(0, m_terrainProgram);
2015-12-14 06:43:08 +03:00
break;
case 2:
2017-05-14 21:48:59 +03:00
bgfx::setVertexBuffer(0, m_vbh);
2015-12-13 20:48:21 +03:00
bgfx::setIndexBuffer(m_ibh);
bgfx::setTexture(0, s_heightTexture, m_heightTexture);
bgfx::submit(0, m_terrainHeightTextureProgram);
2015-12-14 06:43:08 +03:00
break;
2015-12-13 20:48:21 +03:00
}
// Advance to next frame. Rendering thread will be kicked to
// process submitted rendering primitives.
bgfx::frame();
2017-06-30 09:19:20 +03:00
return true;
2015-12-13 20:48:21 +03:00
}
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;
TerrainData m_terrain;
BrushData m_brush;
entry::MouseState m_mouseState;
int64_t m_timeOffset;
2015-12-14 04:21:07 +03:00
};
2017-06-26 07:44:04 +03:00
} // namespace
2019-08-17 23:25:39 +03:00
ENTRY_IMPLEMENT_MAIN(
2019-08-18 00:40:38 +03:00
ExampleTerrain
, "27-terrain"
, "Terrain painting example."
, "https://bkaradzic.github.io/bgfx/examples.html#terrain"
);