Added example demonstrating sf::VertexBuffer, sf::Shader and sf::Thread usage.
This commit is contained in:
parent
fe1407b6b5
commit
d032050ccf
@ -23,6 +23,7 @@ else(SFML_OS_IOS)
|
||||
add_subdirectory(joystick)
|
||||
add_subdirectory(opengl)
|
||||
add_subdirectory(shader)
|
||||
add_subdirectory(island)
|
||||
if(SFML_OS_WINDOWS)
|
||||
add_subdirectory(win32)
|
||||
elseif(SFML_OS_LINUX OR SFML_OS_FREEBSD)
|
||||
|
11
examples/island/CMakeLists.txt
Normal file
11
examples/island/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
set(SRCROOT ${PROJECT_SOURCE_DIR}/examples/island)
|
||||
|
||||
# all source files
|
||||
set(SRC ${SRCROOT}/Island.cpp)
|
||||
|
||||
# define the island target
|
||||
sfml_add_example(island GUI_APP
|
||||
SOURCES ${SRC}
|
||||
DEPENDS sfml-graphics sfml-window sfml-system
|
||||
RESOURCES_DIR resources)
|
590
examples/island/Island.cpp
Normal file
590
examples/island/Island.cpp
Normal file
@ -0,0 +1,590 @@
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Headers
|
||||
////////////////////////////////////////////////////////////
|
||||
#define STB_PERLIN_IMPLEMENTATION
|
||||
#include "stb_perlin.h"
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
// Width and height of the application window
|
||||
const unsigned int windowWidth = 800;
|
||||
const unsigned int windowHeight = 600;
|
||||
|
||||
// Resolution of the generated terrain
|
||||
const unsigned int resolutionX = 800;
|
||||
const unsigned int resolutionY = 600;
|
||||
|
||||
// Thread pool parameters
|
||||
const unsigned int threadCount = 4;
|
||||
const unsigned int blockCount = 32;
|
||||
|
||||
struct WorkItem
|
||||
{
|
||||
sf::Vertex* targetBuffer;
|
||||
unsigned int index;
|
||||
};
|
||||
|
||||
std::deque<WorkItem> workQueue;
|
||||
std::vector<sf::Thread*> threads;
|
||||
int pendingWorkCount = 0;
|
||||
bool workPending = true;
|
||||
bool bufferUploadPending = false;
|
||||
sf::Mutex workQueueMutex;
|
||||
|
||||
struct Setting
|
||||
{
|
||||
const char* name;
|
||||
float* value;
|
||||
};
|
||||
|
||||
// Terrain noise parameters
|
||||
const int perlinOctaves = 3;
|
||||
|
||||
float perlinFrequency = 7.0f;
|
||||
float perlinFrequencyBase = 4.0f;
|
||||
|
||||
// Terrain generation parameters
|
||||
float heightBase = 0.0f;
|
||||
float edgeFactor = 0.9f;
|
||||
float edgeDropoffExponent = 1.5f;
|
||||
|
||||
float snowcapHeight = 0.6f;
|
||||
|
||||
// Terrain lighting parameters
|
||||
float heightFactor = windowHeight / 2.0f;
|
||||
float heightFlatten = 3.0f;
|
||||
float lightFactor = 0.7f;
|
||||
}
|
||||
|
||||
|
||||
// Forward declarations of the functions we define further down
|
||||
void threadFunction();
|
||||
void generateTerrain(sf::Vertex* vertexBuffer);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// Entry point of application
|
||||
///
|
||||
/// \return Application exit code
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
int main()
|
||||
{
|
||||
// Create the window of the application
|
||||
sf::RenderWindow window(sf::VideoMode(windowWidth, windowHeight), "SFML Island",
|
||||
sf::Style::Titlebar | sf::Style::Close);
|
||||
window.setVerticalSyncEnabled(true);
|
||||
|
||||
sf::Font font;
|
||||
if (!font.loadFromFile("resources/sansation.ttf"))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
// Create all of our graphics resources
|
||||
sf::Text hudText;
|
||||
sf::Text statusText;
|
||||
sf::Shader terrainShader;
|
||||
sf::RenderStates terrainStates(&terrainShader);
|
||||
sf::VertexBuffer terrain(sf::Triangles, sf::VertexBuffer::Static);
|
||||
|
||||
// Set up our text drawables
|
||||
statusText.setFont(font);
|
||||
statusText.setCharacterSize(28);
|
||||
statusText.setFillColor(sf::Color::White);
|
||||
statusText.setOutlineColor(sf::Color::Black);
|
||||
statusText.setOutlineThickness(2.0f);
|
||||
|
||||
hudText.setFont(font);
|
||||
hudText.setCharacterSize(14);
|
||||
hudText.setFillColor(sf::Color::White);
|
||||
hudText.setOutlineColor(sf::Color::Black);
|
||||
hudText.setOutlineThickness(2.0f);
|
||||
hudText.setPosition(5.0f, 5.0f);
|
||||
|
||||
// Staging buffer for our terrain data that we will upload to our VertexBuffer
|
||||
std::vector<sf::Vertex> terrainStagingBuffer;
|
||||
|
||||
// Check whether the prerequisites are suppprted
|
||||
bool prerequisitesSupported = sf::VertexBuffer::isAvailable() && sf::Shader::isAvailable();
|
||||
|
||||
// Set up our graphics resources and set the status text accordingly
|
||||
if (!prerequisitesSupported)
|
||||
{
|
||||
statusText.setString("Shaders and/or Vertex Buffers Unsupported");
|
||||
}
|
||||
else if (!terrainShader.loadFromFile("resources/terrain.vert", "resources/terrain.frag"))
|
||||
{
|
||||
prerequisitesSupported = false;
|
||||
|
||||
statusText.setString("Failed to load shader program");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start up our thread pool
|
||||
for (unsigned int i = 0; i < threadCount; i++)
|
||||
{
|
||||
threads.push_back(new sf::Thread(threadFunction));
|
||||
threads.back()->launch();
|
||||
}
|
||||
|
||||
// Create our VertexBuffer with enough space to hold all the terrain geometry
|
||||
terrain.create(resolutionX * resolutionY * 6);
|
||||
|
||||
// Resize the staging buffer to be able to hold all the terrain geometry
|
||||
terrainStagingBuffer.resize(resolutionX * resolutionY * 6);
|
||||
|
||||
// Generate the initial terrain
|
||||
generateTerrain(&terrainStagingBuffer[0]);
|
||||
|
||||
statusText.setString("Generating Terrain...");
|
||||
}
|
||||
|
||||
// Center the status text
|
||||
statusText.setPosition((windowWidth - statusText.getLocalBounds().width) / 2.f, (windowHeight - statusText.getLocalBounds().height) / 2.f);
|
||||
|
||||
// Set up an array of pointers to our settings for arrow navigation
|
||||
Setting settings[] =
|
||||
{
|
||||
{"perlinFrequency", &perlinFrequency},
|
||||
{"perlinFrequencyBase", &perlinFrequencyBase},
|
||||
{"heightBase", &heightBase},
|
||||
{"edgeFactor", &edgeFactor},
|
||||
{"edgeDropoffExponent", &edgeDropoffExponent},
|
||||
{"snowcapHeight", &snowcapHeight},
|
||||
{"heightFactor", &heightFactor},
|
||||
{"heightFlatten", &heightFlatten},
|
||||
{"lightFactor", &lightFactor}
|
||||
};
|
||||
|
||||
const int settingCount = 9;
|
||||
int currentSetting = 0;
|
||||
|
||||
std::ostringstream osstr;
|
||||
sf::Clock clock;
|
||||
|
||||
while (window.isOpen())
|
||||
{
|
||||
// Handle events
|
||||
sf::Event event;
|
||||
while (window.pollEvent(event))
|
||||
{
|
||||
// Window closed or escape key pressed: exit
|
||||
if ((event.type == sf::Event::Closed) ||
|
||||
((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape)))
|
||||
{
|
||||
window.close();
|
||||
break;
|
||||
}
|
||||
|
||||
// Arrow key pressed:
|
||||
if (prerequisitesSupported && (event.type == sf::Event::KeyPressed))
|
||||
{
|
||||
switch (event.key.code)
|
||||
{
|
||||
case sf::Keyboard::Return: generateTerrain(&terrainStagingBuffer[0]); break;
|
||||
case sf::Keyboard::Down: currentSetting = (currentSetting + 1) % settingCount; break;
|
||||
case sf::Keyboard::Up: currentSetting = (currentSetting + settingCount - 1) % settingCount; break;
|
||||
case sf::Keyboard::Left: *(settings[currentSetting].value) -= 0.1f; break;
|
||||
case sf::Keyboard::Right: *(settings[currentSetting].value) += 0.1f; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear, draw graphics objects and display
|
||||
window.clear();
|
||||
|
||||
window.draw(statusText);
|
||||
|
||||
if (prerequisitesSupported)
|
||||
{
|
||||
{
|
||||
sf::Lock lock(workQueueMutex);
|
||||
|
||||
// Don't bother updating/drawing the VertexBuffer while terrain is being regenerated
|
||||
if (!pendingWorkCount)
|
||||
{
|
||||
// If there is new data pending to be uploaded to the VertexBuffer, do it now
|
||||
if (bufferUploadPending)
|
||||
{
|
||||
terrain.update(&terrainStagingBuffer[0]);
|
||||
bufferUploadPending = false;
|
||||
}
|
||||
|
||||
terrainShader.setUniform("lightFactor", lightFactor);
|
||||
window.draw(terrain, terrainStates);
|
||||
}
|
||||
}
|
||||
|
||||
// Update and draw the HUD text
|
||||
osstr.str("");
|
||||
osstr << "Frame: " << clock.restart().asMilliseconds() << "ms\n"
|
||||
<< "perlinOctaves: " << perlinOctaves << "\n\n"
|
||||
<< "Use the arrow keys to change the values.\nUse the return key to regenerate the terrain.\n\n";
|
||||
|
||||
for (int i = 0; i < settingCount; ++i)
|
||||
osstr << ((i == currentSetting) ? ">> " : " ") << settings[i].name << ": " << *(settings[i].value) << "\n";
|
||||
|
||||
hudText.setString(osstr.str());
|
||||
|
||||
window.draw(hudText);
|
||||
}
|
||||
|
||||
// Display things on screen
|
||||
window.display();
|
||||
}
|
||||
|
||||
// Shut down our thread pool
|
||||
{
|
||||
sf::Lock lock(workQueueMutex);
|
||||
workPending = false;
|
||||
}
|
||||
|
||||
while (!threads.empty())
|
||||
{
|
||||
threads.back()->wait();
|
||||
delete threads.back();
|
||||
threads.pop_back();
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// Get the terrain elevation at the given coordinates.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
float getElevation(float x, float y)
|
||||
{
|
||||
x = x / resolutionX - 0.5f;
|
||||
y = y / resolutionY - 0.5f;
|
||||
|
||||
float elevation = 0.0f;
|
||||
|
||||
for (int i = 0; i < perlinOctaves; i++)
|
||||
{
|
||||
elevation += stb_perlin_noise3(
|
||||
x * perlinFrequency * std::pow(perlinFrequencyBase, i),
|
||||
y * perlinFrequency * std::pow(perlinFrequencyBase, i),
|
||||
0, 0, 0, 0
|
||||
) * std::pow(perlinFrequencyBase, -i);
|
||||
}
|
||||
|
||||
elevation = (elevation + 1.f) / 2.f;
|
||||
|
||||
float distance = 2.0f * std::sqrt(x * x + y * y);
|
||||
elevation = (elevation + heightBase) * (1.0f - edgeFactor * std::pow(distance, edgeDropoffExponent));
|
||||
elevation = std::min(std::max(elevation, 0.0f), 1.0f);
|
||||
|
||||
return elevation;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// Get the terrain moisture at the given coordinates.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
float getMoisture(float x, float y)
|
||||
{
|
||||
x = x / resolutionX - 0.5f;
|
||||
y = y / resolutionY - 0.5f;
|
||||
|
||||
float moisture = stb_perlin_noise3(
|
||||
x * 4.f + 0.5f,
|
||||
y * 4.f + 0.5f,
|
||||
0, 0, 0, 0
|
||||
);
|
||||
|
||||
return (moisture + 1.f) / 2.f;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// Get the lowlands terrain color for the given moisture.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
sf::Color getLowlandsTerrainColor(float moisture)
|
||||
{
|
||||
sf::Color color =
|
||||
moisture < 0.27f ? sf::Color(240, 240, 180) :
|
||||
moisture < 0.3f ? sf::Color(240 - 240 * (moisture - 0.27f) / 0.03f, 240 - 40 * (moisture - 0.27f) / 0.03f, 180 - 180 * (moisture - 0.27f) / 0.03f) :
|
||||
moisture < 0.4f ? sf::Color(0, 200, 0) :
|
||||
moisture < 0.48f ? sf::Color(0, 200 - 40 * (moisture - 0.4f) / 0.08f, 0) :
|
||||
moisture < 0.6f ? sf::Color(0, 160, 0) :
|
||||
moisture < 0.7f ? sf::Color(34 * (moisture - 0.6f) / 0.1f, 160 - 60 * (moisture - 0.6f) / 0.1f, 34 * (moisture - 0.6f) / 0.1f) :
|
||||
sf::Color(34, 100, 34);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// Get the highlands terrain color for the given elevation
|
||||
/// and moisture.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
sf::Color getHighlandsTerrainColor(float elevation, float moisture)
|
||||
{
|
||||
sf::Color lowlandsColor = getLowlandsTerrainColor(moisture);
|
||||
|
||||
sf::Color color =
|
||||
moisture < 0.6f ? sf::Color(112, 128, 144) :
|
||||
sf::Color(112 + 110 * (moisture - 0.6f) / 0.4f, 128 + 56 * (moisture - 0.6f) / 0.4f, 144 - 9 * (moisture - 0.6f) / 0.4f);
|
||||
|
||||
float factor = std::min((elevation - 0.4f) / 0.1f, 1.f);
|
||||
|
||||
color.r = lowlandsColor.r * (1.f - factor) + color.r * factor;
|
||||
color.g = lowlandsColor.g * (1.f - factor) + color.g * factor;
|
||||
color.b = lowlandsColor.b * (1.f - factor) + color.b * factor;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// Get the snowcap terrain color for the given elevation
|
||||
/// and moisture.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
sf::Color getSnowcapTerrainColor(float elevation, float moisture)
|
||||
{
|
||||
sf::Color highlandsColor = getHighlandsTerrainColor(elevation, moisture);
|
||||
|
||||
sf::Color color = sf::Color::White;
|
||||
|
||||
float factor = std::min((elevation - snowcapHeight) / 0.05f, 1.f);
|
||||
|
||||
color.r = highlandsColor.r * (1.f - factor) + color.r * factor;
|
||||
color.g = highlandsColor.g * (1.f - factor) + color.g * factor;
|
||||
color.b = highlandsColor.b * (1.f - factor) + color.b * factor;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// Get the terrain color for the given elevation and
|
||||
/// moisture.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
sf::Color getTerrainColor(float elevation, float moisture)
|
||||
{
|
||||
sf::Color color =
|
||||
elevation < 0.11f ? sf::Color(0, 0, elevation / 0.11f * 74.f + 181.0f) :
|
||||
elevation < 0.14f ? sf::Color(std::pow((elevation - 0.11f) / 0.03f, 0.3f) * 48.f, std::pow((elevation - 0.11f) / 0.03f, 0.3f) * 48.f, 255) :
|
||||
elevation < 0.16f ? sf::Color((elevation - 0.14f) * 128.f / 0.02f + 48.f, (elevation - 0.14f) * 128.f / 0.02f + 48.f, 127.0f + (0.16f - elevation) * 128.f / 0.02f) :
|
||||
elevation < 0.17f ? sf::Color(240, 230, 140) :
|
||||
elevation < 0.4f ? getLowlandsTerrainColor(moisture) :
|
||||
elevation < snowcapHeight ? getHighlandsTerrainColor(elevation, moisture) :
|
||||
getSnowcapTerrainColor(elevation, moisture);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// Compute a compressed representation of the surface
|
||||
/// normal based on the given coordinates, and the elevation
|
||||
/// of the 4 adjacent neighbours.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
sf::Vector2f computeNormal(int x, int y, float left, float right, float bottom, float top)
|
||||
{
|
||||
sf::Vector3f deltaX(1, 0, (std::pow(right, heightFlatten) - std::pow(left, heightFlatten)) * heightFactor);
|
||||
sf::Vector3f deltaY(0, 1, (std::pow(top, heightFlatten) - std::pow(bottom, heightFlatten)) * heightFactor);
|
||||
|
||||
sf::Vector3f crossProduct(
|
||||
deltaX.y * deltaY.z - deltaX.z * deltaY.y,
|
||||
deltaX.z * deltaY.x - deltaX.x * deltaY.z,
|
||||
deltaX.x * deltaY.y - deltaX.y * deltaY.x
|
||||
);
|
||||
|
||||
// Scale cross product to make z component 1.0f so we can drop it
|
||||
crossProduct /= crossProduct.z;
|
||||
|
||||
// Return "compressed" normal
|
||||
return sf::Vector2f(crossProduct.x, crossProduct.y);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// Process a terrain generation work item. Use the vector
|
||||
/// of vertices as scratch memory and upload the data to
|
||||
/// the vertex buffer when done.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void processWorkItem(std::vector<sf::Vertex>& vertices, const WorkItem& workItem)
|
||||
{
|
||||
unsigned int rowBlockSize = (resolutionY / blockCount) + 1;
|
||||
unsigned int rowStart = rowBlockSize * workItem.index;
|
||||
|
||||
if (rowStart >= resolutionY)
|
||||
return;
|
||||
|
||||
unsigned int rowEnd = std::min(rowStart + rowBlockSize, resolutionY);
|
||||
unsigned int rowCount = rowEnd - rowStart;
|
||||
|
||||
const float scalingFactorX = static_cast<float>(windowWidth) / static_cast<float>(resolutionX);
|
||||
const float scalingFactorY = static_cast<float>(windowHeight) / static_cast<float>(resolutionY);
|
||||
|
||||
for (unsigned int y = rowStart; y < rowEnd; y++)
|
||||
{
|
||||
for (int x = 0; x < resolutionX; x++)
|
||||
{
|
||||
int arrayIndexBase = ((y - rowStart) * resolutionX + x) * 6;
|
||||
|
||||
// Top left corner (first triangle)
|
||||
if (x > 0)
|
||||
{
|
||||
vertices[arrayIndexBase + 0] = vertices[arrayIndexBase - 6 + 5];
|
||||
}
|
||||
else if (y > rowStart)
|
||||
{
|
||||
vertices[arrayIndexBase + 0] = vertices[arrayIndexBase - resolutionX * 6 + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
vertices[arrayIndexBase + 0].position = sf::Vector2f(x * scalingFactorX, y * scalingFactorY);
|
||||
vertices[arrayIndexBase + 0].color = getTerrainColor(getElevation(x, y), getMoisture(x, y));
|
||||
vertices[arrayIndexBase + 0].texCoords = computeNormal(x, y, getElevation(x - 1, y), getElevation(x + 1, y), getElevation(x, y + 1), getElevation(x, y - 1));
|
||||
}
|
||||
|
||||
// Bottom left corner (first triangle)
|
||||
if (x > 0)
|
||||
{
|
||||
vertices[arrayIndexBase + 1] = vertices[arrayIndexBase - 6 + 2];
|
||||
}
|
||||
else
|
||||
{
|
||||
vertices[arrayIndexBase + 1].position = sf::Vector2f(x * scalingFactorX, (y + 1) * scalingFactorY);
|
||||
vertices[arrayIndexBase + 1].color = getTerrainColor(getElevation(x, y + 1), getMoisture(x, y + 1));
|
||||
vertices[arrayIndexBase + 1].texCoords = computeNormal(x, y + 1, getElevation(x - 1, y + 1), getElevation(x + 1, y + 1), getElevation(x, y + 2), getElevation(x, y));
|
||||
}
|
||||
|
||||
// Bottom right corner (first triangle)
|
||||
vertices[arrayIndexBase + 2].position = sf::Vector2f((x + 1) * scalingFactorX, (y + 1) * scalingFactorY);
|
||||
vertices[arrayIndexBase + 2].color = getTerrainColor(getElevation(x + 1, y + 1), getMoisture(x + 1, y + 1));
|
||||
vertices[arrayIndexBase + 2].texCoords = computeNormal(x + 1, y + 1, getElevation(x, y + 1), getElevation(x + 2, y + 1), getElevation(x + 1, y + 2), getElevation(x + 1, y));
|
||||
|
||||
// Top left corner (second triangle)
|
||||
vertices[arrayIndexBase + 3] = vertices[arrayIndexBase + 0];
|
||||
|
||||
// Bottom right corner (second triangle)
|
||||
vertices[arrayIndexBase + 4] = vertices[arrayIndexBase + 2];
|
||||
|
||||
// Top right corner (second triangle)
|
||||
if (y > rowStart)
|
||||
{
|
||||
vertices[arrayIndexBase + 5] = vertices[arrayIndexBase - resolutionX * 6 + 2];
|
||||
}
|
||||
else
|
||||
{
|
||||
vertices[arrayIndexBase + 5].position = sf::Vector2f((x + 1) * scalingFactorX, y * scalingFactorY);
|
||||
vertices[arrayIndexBase + 5].color = getTerrainColor(getElevation(x + 1, y), getMoisture(x + 1, y));
|
||||
vertices[arrayIndexBase + 5].texCoords = computeNormal(x + 1, y, getElevation(x, y), getElevation(x + 2, y), getElevation(x + 1, y + 1), getElevation(x + 1, y - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the resulting geometry from our thread-local buffer into the target buffer
|
||||
std::memcpy(workItem.targetBuffer + (resolutionX * rowStart * 6), &vertices[0], sizeof(sf::Vertex) * resolutionX * rowCount * 6);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// Worker thread entry point. We use a thread pool to avoid
|
||||
/// the heavy cost of constantly recreating and starting
|
||||
/// new threads whenever we need to regenerate the terrain.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void threadFunction()
|
||||
{
|
||||
unsigned int rowBlockSize = (resolutionY / blockCount) + 1;
|
||||
|
||||
std::vector<sf::Vertex> vertices(resolutionX * rowBlockSize * 6);
|
||||
|
||||
WorkItem workItem = {0, 0};
|
||||
|
||||
// Loop until the application exits
|
||||
for (;;)
|
||||
{
|
||||
workItem.targetBuffer = 0;
|
||||
|
||||
// Check if there are new work items in the queue
|
||||
{
|
||||
sf::Lock lock(workQueueMutex);
|
||||
|
||||
if (!workPending)
|
||||
return;
|
||||
|
||||
if (!workQueue.empty())
|
||||
{
|
||||
workItem = workQueue.front();
|
||||
workQueue.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't receive a new work item, keep looping
|
||||
if (!workItem.targetBuffer)
|
||||
{
|
||||
sf::sleep(sf::milliseconds(10));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
processWorkItem(vertices, workItem);
|
||||
|
||||
{
|
||||
sf::Lock lock(workQueueMutex);
|
||||
|
||||
--pendingWorkCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// Terrain generation entry point. This queues up the
|
||||
/// generation work items which the worker threads dequeue
|
||||
/// and process.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void generateTerrain(sf::Vertex* buffer)
|
||||
{
|
||||
bufferUploadPending = true;
|
||||
|
||||
// Make sure the work queue is empty before queuing new work
|
||||
for (;;)
|
||||
{
|
||||
{
|
||||
sf::Lock lock(workQueueMutex);
|
||||
|
||||
if (workQueue.empty())
|
||||
break;
|
||||
}
|
||||
|
||||
sf::sleep(sf::milliseconds(10));
|
||||
}
|
||||
|
||||
// Queue all the new work items
|
||||
{
|
||||
sf::Lock lock(workQueueMutex);
|
||||
|
||||
for (unsigned int i = 0; i < blockCount; i++)
|
||||
{
|
||||
WorkItem workItem = {buffer, i};
|
||||
workQueue.push_back(workItem);
|
||||
}
|
||||
|
||||
pendingWorkCount = blockCount;
|
||||
}
|
||||
}
|
BIN
examples/island/resources/sansation.ttf
Normal file
BIN
examples/island/resources/sansation.ttf
Normal file
Binary file not shown.
11
examples/island/resources/terrain.frag
Normal file
11
examples/island/resources/terrain.frag
Normal file
@ -0,0 +1,11 @@
|
||||
varying vec3 normal;
|
||||
uniform float lightFactor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 lightPosition = vec3(-1.0, 1.0, 1.0);
|
||||
vec3 eyePosition = vec3(0.0, 0.0, 1.0);
|
||||
vec3 halfVector = normalize(lightPosition + eyePosition);
|
||||
float intensity = lightFactor + (1.0 - lightFactor) * dot(normalize(normal), normalize(halfVector));
|
||||
gl_FragColor = gl_Color * vec4(intensity, intensity, intensity, 1.0);
|
||||
}
|
8
examples/island/resources/terrain.vert
Normal file
8
examples/island/resources/terrain.vert
Normal file
@ -0,0 +1,8 @@
|
||||
varying vec3 normal;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
gl_FrontColor = gl_Color;
|
||||
normal = vec3(gl_MultiTexCoord0.xy, 1.0);
|
||||
}
|
316
examples/island/stb_perlin.h
Normal file
316
examples/island/stb_perlin.h
Normal file
@ -0,0 +1,316 @@
|
||||
// stb_perlin.h - v0.3 - perlin noise
|
||||
// public domain single-file C implementation by Sean Barrett
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file.
|
||||
//
|
||||
//
|
||||
// to create the implementation,
|
||||
// #define STB_PERLIN_IMPLEMENTATION
|
||||
// in *one* C/CPP file that includes this file.
|
||||
//
|
||||
//
|
||||
// Documentation:
|
||||
//
|
||||
// float stb_perlin_noise3( float x,
|
||||
// float y,
|
||||
// float z,
|
||||
// int x_wrap=0,
|
||||
// int y_wrap=0,
|
||||
// int z_wrap=0)
|
||||
//
|
||||
// This function computes a random value at the coordinate (x,y,z).
|
||||
// Adjacent random values are continuous but the noise fluctuates
|
||||
// its randomness with period 1, i.e. takes on wholly unrelated values
|
||||
// at integer points. Specifically, this implements Ken Perlin's
|
||||
// revised noise function from 2002.
|
||||
//
|
||||
// The "wrap" parameters can be used to create wraparound noise that
|
||||
// wraps at powers of two. The numbers MUST be powers of two. Specify
|
||||
// 0 to mean "don't care". (The noise always wraps every 256 due
|
||||
// details of the implementation, even if you ask for larger or no
|
||||
// wrapping.)
|
||||
//
|
||||
// Fractal Noise:
|
||||
//
|
||||
// Three common fractal noise functions are included, which produce
|
||||
// a wide variety of nice effects depending on the parameters
|
||||
// provided. Note that each function will call stb_perlin_noise3
|
||||
// 'octaves' times, so this parameter will affect runtime.
|
||||
//
|
||||
// float stb_perlin_ridge_noise3(float x, float y, float z,
|
||||
// float lacunarity, float gain, float offset, int octaves,
|
||||
// int x_wrap, int y_wrap, int z_wrap);
|
||||
//
|
||||
// float stb_perlin_fbm_noise3(float x, float y, float z,
|
||||
// float lacunarity, float gain, int octaves,
|
||||
// int x_wrap, int y_wrap, int z_wrap);
|
||||
//
|
||||
// float stb_perlin_turbulence_noise3(float x, float y, float z,
|
||||
// float lacunarity, float gain,int octaves,
|
||||
// int x_wrap, int y_wrap, int z_wrap);
|
||||
//
|
||||
// Typical values to start playing with:
|
||||
// octaves = 6 -- number of "octaves" of noise3() to sum
|
||||
// lacunarity = ~ 2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output)
|
||||
// gain = 0.5 -- relative weighting applied to each successive octave
|
||||
// offset = 1.0? -- used to invert the ridges, may need to be larger, not sure
|
||||
//
|
||||
//
|
||||
// Contributors:
|
||||
// Jack Mott - additional noise functions
|
||||
//
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap);
|
||||
extern float stb_perlin_ridge_noise3(float x, float y, float z,float lacunarity, float gain, float offset, int octaves,int x_wrap, int y_wrap, int z_wrap);
|
||||
extern float stb_perlin_fbm_noise3(float x, float y, float z,float lacunarity, float gain, int octaves,int x_wrap, int y_wrap, int z_wrap);
|
||||
extern float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves,int x_wrap, int y_wrap, int z_wrap);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef STB_PERLIN_IMPLEMENTATION
|
||||
|
||||
// not same permutation table as Perlin's reference to avoid copyright issues;
|
||||
// Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/
|
||||
// @OPTIMIZE: should this be unsigned char instead of int for cache?
|
||||
static unsigned char stb__perlin_randtab[512] =
|
||||
{
|
||||
23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123,
|
||||
152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72,
|
||||
175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240,
|
||||
8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57,
|
||||
225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233,
|
||||
94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172,
|
||||
165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243,
|
||||
65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122,
|
||||
26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76,
|
||||
250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246,
|
||||
132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3,
|
||||
91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231,
|
||||
38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221,
|
||||
131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62,
|
||||
27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135,
|
||||
61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5,
|
||||
|
||||
// and a second copy so we don't need an extra mask or static initializer
|
||||
23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123,
|
||||
152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72,
|
||||
175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240,
|
||||
8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57,
|
||||
225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233,
|
||||
94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172,
|
||||
165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243,
|
||||
65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122,
|
||||
26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76,
|
||||
250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246,
|
||||
132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3,
|
||||
91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231,
|
||||
38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221,
|
||||
131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62,
|
||||
27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135,
|
||||
61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5,
|
||||
};
|
||||
|
||||
static float stb__perlin_lerp(float a, float b, float t)
|
||||
{
|
||||
return a + (b-a) * t;
|
||||
}
|
||||
|
||||
static int stb__perlin_fastfloor(float a)
|
||||
{
|
||||
int ai = (int) a;
|
||||
return (a < ai) ? ai-1 : ai;
|
||||
}
|
||||
|
||||
// different grad function from Perlin's, but easy to modify to match reference
|
||||
static float stb__perlin_grad(int hash, float x, float y, float z)
|
||||
{
|
||||
static float basis[12][4] =
|
||||
{
|
||||
{ 1, 1, 0 },
|
||||
{ -1, 1, 0 },
|
||||
{ 1,-1, 0 },
|
||||
{ -1,-1, 0 },
|
||||
{ 1, 0, 1 },
|
||||
{ -1, 0, 1 },
|
||||
{ 1, 0,-1 },
|
||||
{ -1, 0,-1 },
|
||||
{ 0, 1, 1 },
|
||||
{ 0,-1, 1 },
|
||||
{ 0, 1,-1 },
|
||||
{ 0,-1,-1 },
|
||||
};
|
||||
|
||||
// perlin's gradient has 12 cases so some get used 1/16th of the time
|
||||
// and some 2/16ths. We reduce bias by changing those fractions
|
||||
// to 5/64ths and 6/64ths, and the same 4 cases get the extra weight.
|
||||
static unsigned char indices[64] =
|
||||
{
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||
0,9,1,11,
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||
};
|
||||
|
||||
// if you use reference permutation table, change 63 below to 15 to match reference
|
||||
// (this is why the ordering of the table above is funky)
|
||||
float *grad = basis[indices[hash & 63]];
|
||||
return grad[0]*x + grad[1]*y + grad[2]*z;
|
||||
}
|
||||
|
||||
float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap)
|
||||
{
|
||||
float u,v,w;
|
||||
float n000,n001,n010,n011,n100,n101,n110,n111;
|
||||
float n00,n01,n10,n11;
|
||||
float n0,n1;
|
||||
|
||||
unsigned int x_mask = (x_wrap-1) & 255;
|
||||
unsigned int y_mask = (y_wrap-1) & 255;
|
||||
unsigned int z_mask = (z_wrap-1) & 255;
|
||||
int px = stb__perlin_fastfloor(x);
|
||||
int py = stb__perlin_fastfloor(y);
|
||||
int pz = stb__perlin_fastfloor(z);
|
||||
int x0 = px & x_mask, x1 = (px+1) & x_mask;
|
||||
int y0 = py & y_mask, y1 = (py+1) & y_mask;
|
||||
int z0 = pz & z_mask, z1 = (pz+1) & z_mask;
|
||||
int r0,r1, r00,r01,r10,r11;
|
||||
|
||||
#define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a)
|
||||
|
||||
x -= px; u = stb__perlin_ease(x);
|
||||
y -= py; v = stb__perlin_ease(y);
|
||||
z -= pz; w = stb__perlin_ease(z);
|
||||
|
||||
r0 = stb__perlin_randtab[x0];
|
||||
r1 = stb__perlin_randtab[x1];
|
||||
|
||||
r00 = stb__perlin_randtab[r0+y0];
|
||||
r01 = stb__perlin_randtab[r0+y1];
|
||||
r10 = stb__perlin_randtab[r1+y0];
|
||||
r11 = stb__perlin_randtab[r1+y1];
|
||||
|
||||
n000 = stb__perlin_grad(stb__perlin_randtab[r00+z0], x , y , z );
|
||||
n001 = stb__perlin_grad(stb__perlin_randtab[r00+z1], x , y , z-1 );
|
||||
n010 = stb__perlin_grad(stb__perlin_randtab[r01+z0], x , y-1, z );
|
||||
n011 = stb__perlin_grad(stb__perlin_randtab[r01+z1], x , y-1, z-1 );
|
||||
n100 = stb__perlin_grad(stb__perlin_randtab[r10+z0], x-1, y , z );
|
||||
n101 = stb__perlin_grad(stb__perlin_randtab[r10+z1], x-1, y , z-1 );
|
||||
n110 = stb__perlin_grad(stb__perlin_randtab[r11+z0], x-1, y-1, z );
|
||||
n111 = stb__perlin_grad(stb__perlin_randtab[r11+z1], x-1, y-1, z-1 );
|
||||
|
||||
n00 = stb__perlin_lerp(n000,n001,w);
|
||||
n01 = stb__perlin_lerp(n010,n011,w);
|
||||
n10 = stb__perlin_lerp(n100,n101,w);
|
||||
n11 = stb__perlin_lerp(n110,n111,w);
|
||||
|
||||
n0 = stb__perlin_lerp(n00,n01,v);
|
||||
n1 = stb__perlin_lerp(n10,n11,v);
|
||||
|
||||
return stb__perlin_lerp(n0,n1,u);
|
||||
}
|
||||
|
||||
float stb_perlin_ridge_noise3(float x, float y, float z,float lacunarity, float gain, float offset, int octaves,int x_wrap, int y_wrap, int z_wrap)
|
||||
{
|
||||
int i;
|
||||
float frequency = 1.0f;
|
||||
float prev = 1.0f;
|
||||
float amplitude = 0.5f;
|
||||
float sum = 0.0f;
|
||||
|
||||
for (i = 0; i < octaves; i++) {
|
||||
float r = (float)(stb_perlin_noise3(x*frequency,y*frequency,z*frequency,x_wrap,y_wrap,z_wrap));
|
||||
r = r<0 ? -r : r; // fabs()
|
||||
r = offset - r;
|
||||
r = r*r;
|
||||
sum += r*amplitude*prev;
|
||||
prev = r;
|
||||
frequency *= lacunarity;
|
||||
amplitude *= gain;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
float stb_perlin_fbm_noise3(float x, float y, float z,float lacunarity, float gain, int octaves,int x_wrap, int y_wrap, int z_wrap)
|
||||
{
|
||||
int i;
|
||||
float frequency = 1.0f;
|
||||
float amplitude = 1.0f;
|
||||
float sum = 0.0f;
|
||||
|
||||
for (i = 0; i < octaves; i++) {
|
||||
sum += stb_perlin_noise3(x*frequency,y*frequency,z*frequency,x_wrap,y_wrap,z_wrap)*amplitude;
|
||||
frequency *= lacunarity;
|
||||
amplitude *= gain;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves,int x_wrap, int y_wrap, int z_wrap)
|
||||
{
|
||||
int i;
|
||||
float frequency = 1.0f;
|
||||
float amplitude = 1.0f;
|
||||
float sum = 0.0f;
|
||||
|
||||
for (i = 0; i < octaves; i++) {
|
||||
float r = stb_perlin_noise3(x*frequency,y*frequency,z*frequency,x_wrap,y_wrap,z_wrap)*amplitude;
|
||||
r = r<0 ? -r : r; // fabs()
|
||||
sum += r;
|
||||
frequency *= lacunarity;
|
||||
amplitude *= gain;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
#endif // STB_PERLIN_IMPLEMENTATION
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
Loading…
Reference in New Issue
Block a user