mirror of
https://github.com/SFML/SFML.git
synced 2024-11-24 20:31:05 +08:00
Simplify island example using vectors and extracting repetitive code
This commit is contained in:
parent
6bcc3414fc
commit
267bbe35b8
@ -24,12 +24,10 @@
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
// Width and height of the application window
|
// Width and height of the application window
|
||||||
const unsigned int windowWidth = 800;
|
const sf::Vector2u windowSize(800, 600);
|
||||||
const unsigned int windowHeight = 600;
|
|
||||||
|
|
||||||
// Resolution of the generated terrain
|
// Resolution of the generated terrain
|
||||||
const unsigned int resolutionX = 800;
|
const sf::Vector2u resolution(800, 600);
|
||||||
const unsigned int resolutionY = 600;
|
|
||||||
|
|
||||||
// Thread pool parameters
|
// Thread pool parameters
|
||||||
const unsigned int threadCount = 4;
|
const unsigned int threadCount = 4;
|
||||||
@ -68,7 +66,7 @@ float edgeDropoffExponent = 1.5f;
|
|||||||
float snowcapHeight = 0.6f;
|
float snowcapHeight = 0.6f;
|
||||||
|
|
||||||
// Terrain lighting parameters
|
// Terrain lighting parameters
|
||||||
float heightFactor = windowHeight / 2.0f;
|
float heightFactor = static_cast<float>(windowSize.y) / 2.0f;
|
||||||
float heightFlatten = 3.0f;
|
float heightFlatten = 3.0f;
|
||||||
float lightFactor = 0.7f;
|
float lightFactor = 0.7f;
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -88,7 +86,7 @@ void generateTerrain(sf::Vertex* vertexBuffer);
|
|||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
// Create the window of the application
|
// Create the window of the application
|
||||||
sf::RenderWindow window(sf::VideoMode({windowWidth, windowHeight}), "SFML Island", sf::Style::Titlebar | sf::Style::Close);
|
sf::RenderWindow window(sf::VideoMode(windowSize), "SFML Island", sf::Style::Titlebar | sf::Style::Close);
|
||||||
window.setVerticalSyncEnabled(true);
|
window.setVerticalSyncEnabled(true);
|
||||||
|
|
||||||
const sf::Font font("resources/tuffy.ttf");
|
const sf::Font font("resources/tuffy.ttf");
|
||||||
@ -133,14 +131,14 @@ int main()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create our VertexBuffer with enough space to hold all the terrain geometry
|
// Create our VertexBuffer with enough space to hold all the terrain geometry
|
||||||
if (!terrain.create(resolutionX * resolutionY * 6))
|
if (!terrain.create(resolution.x * resolution.y * 6))
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to create vertex buffer" << std::endl;
|
std::cerr << "Failed to create vertex buffer" << std::endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize the staging buffer to be able to hold all the terrain geometry
|
// Resize the staging buffer to be able to hold all the terrain geometry
|
||||||
terrainStagingBuffer.resize(resolutionX * resolutionY * 6);
|
terrainStagingBuffer.resize(resolution.x * resolution.y * 6);
|
||||||
|
|
||||||
// Generate the initial terrain
|
// Generate the initial terrain
|
||||||
generateTerrain(terrainStagingBuffer.data());
|
generateTerrain(terrainStagingBuffer.data());
|
||||||
@ -152,7 +150,7 @@ int main()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Center the status text
|
// Center the status text
|
||||||
statusText.setPosition((sf::Vector2f(windowWidth, windowHeight) - statusText.getLocalBounds().size) / 2.f);
|
statusText.setPosition((sf::Vector2f(windowSize) - statusText.getLocalBounds().size) / 2.f);
|
||||||
|
|
||||||
// Set up an array of pointers to our settings for arrow navigation
|
// Set up an array of pointers to our settings for arrow navigation
|
||||||
constexpr std::array<Setting, 9> settings = {
|
constexpr std::array<Setting, 9> settings = {
|
||||||
@ -279,58 +277,45 @@ int main()
|
|||||||
/// Get the terrain elevation at the given coordinates.
|
/// Get the terrain elevation at the given coordinates.
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
float getElevation(float x, float y)
|
float getElevation(sf::Vector2u position)
|
||||||
{
|
{
|
||||||
x = x / resolutionX - 0.5f;
|
const sf::Vector2f normalized = sf::Vector2f(position).componentWiseDiv(sf::Vector2f(resolution)) -
|
||||||
y = y / resolutionY - 0.5f;
|
sf::Vector2f(0.5f, 0.5f);
|
||||||
|
|
||||||
float elevation = 0.0f;
|
float elevation = 0.0f;
|
||||||
|
|
||||||
for (int i = 0; i < perlinOctaves; ++i)
|
for (int i = 0; i < perlinOctaves; ++i)
|
||||||
{
|
{
|
||||||
elevation += stb_perlin_noise3(x * perlinFrequency * static_cast<float>(std::pow(perlinFrequencyBase, i)),
|
const sf::Vector2f scaled = normalized * perlinFrequency * static_cast<float>(std::pow(perlinFrequencyBase, i));
|
||||||
y * perlinFrequency * static_cast<float>(std::pow(perlinFrequencyBase, i)),
|
elevation += stb_perlin_noise3(scaled.x, scaled.y, 0, 0, 0, 0) *
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0) *
|
|
||||||
static_cast<float>(std::pow(perlinFrequencyBase, -i));
|
static_cast<float>(std::pow(perlinFrequencyBase, -i));
|
||||||
}
|
}
|
||||||
|
|
||||||
elevation = (elevation + 1.f) / 2.f;
|
elevation = (elevation + 1.f) / 2.f;
|
||||||
|
|
||||||
const float distance = 2.0f * std::sqrt(x * x + y * y);
|
const float distance = 2.0f * normalized.length();
|
||||||
elevation = (elevation + heightBase) * (1.0f - edgeFactor * std::pow(distance, edgeDropoffExponent));
|
elevation = (elevation + heightBase) * (1.0f - edgeFactor * std::pow(distance, edgeDropoffExponent));
|
||||||
elevation = std::clamp(elevation, 0.0f, 1.0f);
|
elevation = std::clamp(elevation, 0.0f, 1.0f);
|
||||||
|
|
||||||
return elevation;
|
return elevation;
|
||||||
}
|
}
|
||||||
|
|
||||||
float getElevation(unsigned int x, unsigned int y)
|
|
||||||
{
|
|
||||||
return getElevation(static_cast<float>(x), static_cast<float>(y));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// Get the terrain moisture at the given coordinates.
|
/// Get the terrain moisture at the given coordinates.
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
float getMoisture(float x, float y)
|
float getMoisture(sf::Vector2u position)
|
||||||
{
|
{
|
||||||
x = x / resolutionX - 0.5f;
|
const sf::Vector2f normalized = sf::Vector2f(position).componentWiseDiv(sf::Vector2f(resolution)) -
|
||||||
y = y / resolutionY - 0.5f;
|
sf::Vector2f(0.5f, 0.5f);
|
||||||
|
const sf::Vector2f transformed = normalized * 4.f + sf::Vector2f(0.5f, 0.5f);
|
||||||
|
|
||||||
const float moisture = stb_perlin_noise3(x * 4.f + 0.5f, y * 4.f + 0.5f, 0, 0, 0, 0);
|
const float moisture = stb_perlin_noise3(transformed.x, transformed.y, 0, 0, 0, 0);
|
||||||
|
|
||||||
return (moisture + 1.f) / 2.f;
|
return (moisture + 1.f) / 2.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float getMoisture(unsigned int x, unsigned int y)
|
|
||||||
{
|
|
||||||
return getMoisture(static_cast<float>(x), static_cast<float>(y));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// Get the lowlands terrain color for the given moisture.
|
/// Get the lowlands terrain color for the given moisture.
|
||||||
@ -372,18 +357,16 @@ sf::Color getHighlandsTerrainColor(float elevation, float moisture)
|
|||||||
{
|
{
|
||||||
const sf::Color lowlandsColor = getLowlandsTerrainColor(moisture);
|
const sf::Color lowlandsColor = getLowlandsTerrainColor(moisture);
|
||||||
|
|
||||||
sf::Color color = moisture < 0.6f ? sf::Color(112, 128, 144)
|
const sf::Color color = moisture < 0.6f ? sf::Color(112, 128, 144)
|
||||||
: colorFromFloats(112 + (110 * (moisture - 0.6f) / 0.4f),
|
: colorFromFloats(112 + (110 * (moisture - 0.6f) / 0.4f),
|
||||||
128 + (56 * (moisture - 0.6f) / 0.4f),
|
128 + (56 * (moisture - 0.6f) / 0.4f),
|
||||||
144 - (9 * (moisture - 0.6f) / 0.4f));
|
144 - (9 * (moisture - 0.6f) / 0.4f));
|
||||||
|
|
||||||
const float factor = std::min((elevation - 0.4f) / 0.1f, 1.f);
|
const float factor = std::min((elevation - 0.4f) / 0.1f, 1.f);
|
||||||
|
|
||||||
color.r = static_cast<std::uint8_t>(lowlandsColor.r * (1.f - factor) + color.r * factor);
|
return colorFromFloats(lowlandsColor.r * (1.f - factor) + color.r * factor,
|
||||||
color.g = static_cast<std::uint8_t>(lowlandsColor.g * (1.f - factor) + color.g * factor);
|
lowlandsColor.g * (1.f - factor) + color.g * factor,
|
||||||
color.b = static_cast<std::uint8_t>(lowlandsColor.b * (1.f - factor) + color.b * factor);
|
lowlandsColor.b * (1.f - factor) + color.b * factor);
|
||||||
|
|
||||||
return color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -396,15 +379,13 @@ sf::Color getSnowcapTerrainColor(float elevation, float moisture)
|
|||||||
{
|
{
|
||||||
const sf::Color highlandsColor = getHighlandsTerrainColor(elevation, moisture);
|
const sf::Color highlandsColor = getHighlandsTerrainColor(elevation, moisture);
|
||||||
|
|
||||||
sf::Color color = sf::Color::White;
|
const sf::Color color = sf::Color::White;
|
||||||
|
|
||||||
const float factor = std::min((elevation - snowcapHeight) / 0.05f, 1.f);
|
const float factor = std::min((elevation - snowcapHeight) / 0.05f, 1.f);
|
||||||
|
|
||||||
color.r = static_cast<std::uint8_t>(highlandsColor.r * (1.f - factor) + color.r * factor);
|
return colorFromFloats(highlandsColor.r * (1.f - factor) + color.r * factor,
|
||||||
color.g = static_cast<std::uint8_t>(highlandsColor.g * (1.f - factor) + color.g * factor);
|
highlandsColor.g * (1.f - factor) + color.g * factor,
|
||||||
color.b = static_cast<std::uint8_t>(highlandsColor.b * (1.f - factor) + color.b * factor);
|
highlandsColor.b * (1.f - factor) + color.b * factor);
|
||||||
|
|
||||||
return color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -446,9 +427,7 @@ sf::Vector2f computeNormal(float left, float right, float bottom, float top)
|
|||||||
const sf::Vector3f deltaX(1, 0, (std::pow(right, heightFlatten) - std::pow(left, heightFlatten)) * heightFactor);
|
const sf::Vector3f deltaX(1, 0, (std::pow(right, heightFlatten) - std::pow(left, heightFlatten)) * heightFactor);
|
||||||
const sf::Vector3f deltaY(0, 1, (std::pow(top, heightFlatten) - std::pow(bottom, heightFlatten)) * heightFactor);
|
const 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,
|
sf::Vector3f crossProduct = deltaX.cross(deltaY);
|
||||||
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
|
// Scale cross product to make z component 1.0f so we can drop it
|
||||||
crossProduct /= crossProduct.z;
|
crossProduct /= crossProduct.z;
|
||||||
@ -458,6 +437,26 @@ sf::Vector2f computeNormal(float left, float right, float bottom, float top)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// Compute the vertex representing the terrain at the given
|
||||||
|
/// coordinates.
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
const auto scalingFactors = sf::Vector2f(windowSize).componentWiseDiv(sf::Vector2f(resolution));
|
||||||
|
|
||||||
|
sf::Vertex computeVertex(sf::Vector2u position)
|
||||||
|
{
|
||||||
|
sf::Vertex vertex;
|
||||||
|
vertex.position = sf::Vector2f(position).componentWiseMul(scalingFactors);
|
||||||
|
vertex.color = getTerrainColor(getElevation(position), getMoisture(position));
|
||||||
|
vertex.texCoords = computeNormal(getElevation(position - sf::Vector2u(1, 0)),
|
||||||
|
getElevation(position + sf::Vector2u(1, 0)),
|
||||||
|
getElevation(position + sf::Vector2u(0, 1)),
|
||||||
|
getElevation(position - sf::Vector2u(0, 1)));
|
||||||
|
return vertex;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// Process a terrain generation work item. Use the vector
|
/// Process a terrain generation work item. Use the vector
|
||||||
/// of vertices as scratch memory and upload the data to
|
/// of vertices as scratch memory and upload the data to
|
||||||
@ -466,23 +465,20 @@ sf::Vector2f computeNormal(float left, float right, float bottom, float top)
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void processWorkItem(std::vector<sf::Vertex>& vertices, const WorkItem& workItem)
|
void processWorkItem(std::vector<sf::Vertex>& vertices, const WorkItem& workItem)
|
||||||
{
|
{
|
||||||
const unsigned int rowBlockSize = (resolutionY / blockCount) + 1;
|
const unsigned int rowBlockSize = (resolution.y / blockCount) + 1;
|
||||||
const unsigned int rowStart = rowBlockSize * workItem.index;
|
const unsigned int rowStart = rowBlockSize * workItem.index;
|
||||||
|
|
||||||
if (rowStart >= resolutionY)
|
if (rowStart >= resolution.y)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const unsigned int rowEnd = std::min(rowStart + rowBlockSize, resolutionY);
|
const unsigned int rowEnd = std::min(rowStart + rowBlockSize, resolution.y);
|
||||||
const unsigned int rowCount = rowEnd - rowStart;
|
const unsigned int rowCount = rowEnd - rowStart;
|
||||||
|
|
||||||
const float scalingFactorX = float{windowWidth} / float{resolutionX};
|
|
||||||
const float scalingFactorY = float{windowHeight} / float{resolutionY};
|
|
||||||
|
|
||||||
for (unsigned int y = rowStart; y < rowEnd; ++y)
|
for (unsigned int y = rowStart; y < rowEnd; ++y)
|
||||||
{
|
{
|
||||||
for (unsigned int x = 0; x < resolutionX; ++x)
|
for (unsigned int x = 0; x < resolution.x; ++x)
|
||||||
{
|
{
|
||||||
const unsigned int arrayIndexBase = ((y - rowStart) * resolutionX + x) * 6;
|
const unsigned int arrayIndexBase = ((y - rowStart) * resolution.x + x) * 6;
|
||||||
|
|
||||||
// Top left corner (first triangle)
|
// Top left corner (first triangle)
|
||||||
if (x > 0)
|
if (x > 0)
|
||||||
@ -491,17 +487,11 @@ void processWorkItem(std::vector<sf::Vertex>& vertices, const WorkItem& workItem
|
|||||||
}
|
}
|
||||||
else if (y > rowStart)
|
else if (y > rowStart)
|
||||||
{
|
{
|
||||||
vertices[arrayIndexBase + 0] = vertices[arrayIndexBase - resolutionX * 6 + 1];
|
vertices[arrayIndexBase + 0] = vertices[arrayIndexBase - resolution.x * 6 + 1];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
vertices[arrayIndexBase + 0].position = sf::Vector2f(static_cast<float>(x) * scalingFactorX,
|
vertices[arrayIndexBase + 0] = computeVertex({x, y});
|
||||||
static_cast<float>(y) * scalingFactorY);
|
|
||||||
vertices[arrayIndexBase + 0].color = getTerrainColor(getElevation(x, y), getMoisture(x, y));
|
|
||||||
vertices[arrayIndexBase + 0].texCoords = computeNormal(getElevation(x - 1, y),
|
|
||||||
getElevation(x + 1, y),
|
|
||||||
getElevation(x, y + 1),
|
|
||||||
getElevation(x, y - 1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bottom left corner (first triangle)
|
// Bottom left corner (first triangle)
|
||||||
@ -511,23 +501,11 @@ void processWorkItem(std::vector<sf::Vertex>& vertices, const WorkItem& workItem
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
vertices[arrayIndexBase + 1].position = sf::Vector2f(static_cast<float>(x) * scalingFactorX,
|
vertices[arrayIndexBase + 1] = computeVertex({x, y + 1});
|
||||||
static_cast<float>(y + 1) * scalingFactorY);
|
|
||||||
vertices[arrayIndexBase + 1].color = getTerrainColor(getElevation(x, y + 1), getMoisture(x, y + 1));
|
|
||||||
vertices[arrayIndexBase + 1].texCoords = computeNormal(getElevation(x - 1, y + 1),
|
|
||||||
getElevation(x + 1, y + 1),
|
|
||||||
getElevation(x, y + 2),
|
|
||||||
getElevation(x, y));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bottom right corner (first triangle)
|
// Bottom right corner (first triangle)
|
||||||
vertices[arrayIndexBase + 2].position = sf::Vector2f(static_cast<float>(x + 1) * scalingFactorX,
|
vertices[arrayIndexBase + 2] = computeVertex({x + 1, y + 1});
|
||||||
static_cast<float>(y + 1) * scalingFactorY);
|
|
||||||
vertices[arrayIndexBase + 2].color = getTerrainColor(getElevation(x + 1, y + 1), getMoisture(x + 1, y + 1));
|
|
||||||
vertices[arrayIndexBase + 2].texCoords = computeNormal(getElevation(x, y + 1),
|
|
||||||
getElevation(x + 2, y + 1),
|
|
||||||
getElevation(x + 1, y + 2),
|
|
||||||
getElevation(x + 1, y));
|
|
||||||
|
|
||||||
// Top left corner (second triangle)
|
// Top left corner (second triangle)
|
||||||
vertices[arrayIndexBase + 3] = vertices[arrayIndexBase + 0];
|
vertices[arrayIndexBase + 3] = vertices[arrayIndexBase + 0];
|
||||||
@ -538,25 +516,19 @@ void processWorkItem(std::vector<sf::Vertex>& vertices, const WorkItem& workItem
|
|||||||
// Top right corner (second triangle)
|
// Top right corner (second triangle)
|
||||||
if (y > rowStart)
|
if (y > rowStart)
|
||||||
{
|
{
|
||||||
vertices[arrayIndexBase + 5] = vertices[arrayIndexBase - resolutionX * 6 + 2];
|
vertices[arrayIndexBase + 5] = vertices[arrayIndexBase - resolution.x * 6 + 2];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
vertices[arrayIndexBase + 5].position = sf::Vector2f(static_cast<float>(x + 1) * scalingFactorX,
|
vertices[arrayIndexBase + 5] = computeVertex({x + 1, y});
|
||||||
static_cast<float>(y) * scalingFactorY);
|
|
||||||
vertices[arrayIndexBase + 5].color = getTerrainColor(getElevation(x + 1, y), getMoisture(x + 1, y));
|
|
||||||
vertices[arrayIndexBase + 5].texCoords = computeNormal(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
|
// Copy the resulting geometry from our thread-local buffer into the target buffer
|
||||||
std::memcpy(workItem.targetBuffer + (resolutionX * rowStart * 6),
|
std::memcpy(workItem.targetBuffer + (resolution.x * rowStart * 6),
|
||||||
vertices.data(),
|
vertices.data(),
|
||||||
sizeof(sf::Vertex) * resolutionX * rowCount * 6);
|
sizeof(sf::Vertex) * resolution.x * rowCount * 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -568,9 +540,9 @@ void processWorkItem(std::vector<sf::Vertex>& vertices, const WorkItem& workItem
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void threadFunction()
|
void threadFunction()
|
||||||
{
|
{
|
||||||
const unsigned int rowBlockSize = (resolutionY / blockCount) + 1;
|
const unsigned int rowBlockSize = (resolution.y / blockCount) + 1;
|
||||||
|
|
||||||
std::vector<sf::Vertex> vertices(resolutionX * rowBlockSize * 6);
|
std::vector<sf::Vertex> vertices(resolution.x * rowBlockSize * 6);
|
||||||
|
|
||||||
WorkItem workItem = {nullptr, 0};
|
WorkItem workItem = {nullptr, 0};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user