Skip to content

Commit e4c4d01

Browse files
committed
Add experimental surface nets normal smoothing
1 parent d9f688e commit e4c4d01

19 files changed

Lines changed: 174 additions & 16 deletions

gameconfig.lua.default

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ Player = {
1414
Server = {
1515
Port = 29536,
1616
}
17+
Visual = {
18+
ChunkNormalSmoothAngle = 0.0
19+
}

include/ClientLib/ClientChunkEntities.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@ namespace tsom
3232
Nz::Vector3f tangent;
3333
};
3434

35+
class ConfigFile;
36+
3537
class TSOM_CLIENTLIB_API ClientChunkEntities final : public ChunkEntities
3638
{
3739
public:
38-
ClientChunkEntities(Nz::ApplicationBase& app, Nz::EnttWorld& world, ChunkContainer& chunkContainer, const ClientBlockLibrary& blockLibrary, std::size_t layerIndex);
40+
ClientChunkEntities(Nz::ApplicationBase& app, ConfigFile& config, Nz::EnttWorld& world, ChunkContainer& chunkContainer, const ClientBlockLibrary& blockLibrary, std::size_t layerIndex);
3941
ClientChunkEntities(const ClientChunkEntities&) = delete;
4042
ClientChunkEntities(ClientChunkEntities&&) = delete;
4143
~ClientChunkEntities() = default;
@@ -58,6 +60,8 @@ namespace tsom
5860

5961
std::shared_ptr<Nz::MaterialInstance> m_chunkMaterial;
6062
std::shared_ptr<Nz::VertexDeclaration> m_chunkVertexDeclaration;
63+
Nz::Signal<double>::ConnectionGuard m_onVisualChunkNormalSmoothAngleUpdatedSlot;
64+
ConfigFile& m_configFile;
6165
bool m_isCollisionGenerationEnabled;
6266
};
6367
}

include/ClientLib/ClientChunkEntities.inl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ namespace tsom
77
inline void ClientChunkEntities::EnableCollisionGeneration(bool enable)
88
{
99
m_isCollisionGenerationEnabled = enable;
10+
RebuildAllChunks();
1011
}
1112
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (C) 2026 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
2+
// This file is part of the "This Space Of Mine" project
3+
// For conditions of distribution and use, see copyright notice in LICENSE
4+
5+
#pragma once
6+
7+
#ifndef TSOM_CLIENTLIB_CLIENTCONFIGS_HPP
8+
#define TSOM_CLIENTLIB_CLIENTCONFIGS_HPP
9+
10+
#include <CommonLib/ConfigFile.hpp>
11+
12+
namespace tsom::Config
13+
{
14+
static constexpr auto Visual_ChunkNormalSmoothAngle = ConfigFile::FloatOptionName{ "Visual.ChunkNormalSmoothAngle" };
15+
}
16+
17+
#endif // TSOM_CLIENTLIB_CLIENTCONFIGS_HPP

include/ClientLib/ClientSessionHandler.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ namespace Nz
3131
namespace tsom
3232
{
3333
class ClientBlockLibrary;
34+
class ConfigFile;
3435
class GravityController;
3536
struct PlayerAnimationAssets;
3637

@@ -39,7 +40,7 @@ namespace tsom
3940
public:
4041
struct PlayerInfo;
4142

42-
ClientSessionHandler(NetworkSession* session, Nz::ApplicationBase& app, Nz::EnttWorld& world, ClientBlockLibrary& blockLibrary);
43+
ClientSessionHandler(NetworkSession* session, Nz::ApplicationBase& app, ConfigFile& config, Nz::EnttWorld& world, ClientBlockLibrary& blockLibrary);
4344
~ClientSessionHandler();
4445

4546
inline entt::handle GetControlledEntity() const;
@@ -142,6 +143,7 @@ namespace tsom
142143
Nz::ApplicationBase& m_app;
143144
Nz::EnttWorld& m_world;
144145
ClientBlockLibrary& m_blockLibrary;
146+
ConfigFile& m_config;
145147
Nz::UInt16 m_lastTickIndex;
146148
Nz::UInt16 m_ownPlayerIndex;
147149
ScriptingContext m_scriptingContext;

include/ClientLib/Entities/ClientChunkClassLibrary.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,19 @@
1313
namespace tsom
1414
{
1515
class ClientBlockLibrary;
16+
class ConfigFile;
1617

1718
class TSOM_CLIENTLIB_API ClientChunkClassLibrary final : public ChunkClassLibrary
1819
{
1920
public:
20-
ClientChunkClassLibrary(Nz::ApplicationBase& app, const ClientBlockLibrary& blockLibrary);
21+
ClientChunkClassLibrary(Nz::ApplicationBase& app, ConfigFile& config, const ClientBlockLibrary& blockLibrary);
2122

2223
private:
2324
void InitializePlanetEntity(entt::handle entity) override;
2425
void InitializeShipEntity(entt::handle entity) override;
2526
std::unique_ptr<ChunkEntities> SetupChunkEntities(Nz::EnttWorld& world, ChunkContainer& chunkContainer, std::size_t layerIndex) override;
27+
28+
ConfigFile& m_config;
2629
};
2730
}
2831

include/ClientLib/Scripting/ClientScriptingLibrary.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ namespace Nz
1818
namespace tsom
1919
{
2020
class ClientSessionHandler;
21+
class ConfigFile;
2122

2223
class TSOM_CLIENTLIB_API ClientScriptingLibrary final : public ScriptingLibrary
2324
{
2425
public:
25-
inline ClientScriptingLibrary(Nz::ApplicationBase& app, ClientSessionHandler& sessionHandler);
26+
inline ClientScriptingLibrary(Nz::ApplicationBase& app, ConfigFile& configFile, ClientSessionHandler& sessionHandler);
2627
ClientScriptingLibrary(const ClientScriptingLibrary&) = delete;
2728
ClientScriptingLibrary(ClientScriptingLibrary&&) = delete;
2829
~ClientScriptingLibrary() = default;
@@ -34,10 +35,12 @@ namespace tsom
3435

3536
private:
3637
void RegisterClientSession(sol::state& state);
38+
void RegisterConfig(sol::state& state);
3739
void RegisterScripts(sol::state& state);
3840

3941
Nz::ApplicationBase& m_app;
4042
ClientSessionHandler& m_sessionHandler;
43+
ConfigFile& m_config;
4144
};
4245
}
4346

include/ClientLib/Scripting/ClientScriptingLibrary.inl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
namespace tsom
66
{
7-
inline ClientScriptingLibrary::ClientScriptingLibrary(Nz::ApplicationBase& app, ClientSessionHandler& sessionHandler) :
7+
inline ClientScriptingLibrary::ClientScriptingLibrary(Nz::ApplicationBase& app, ConfigFile& configFile, ClientSessionHandler& sessionHandler) :
88
m_app(app),
9-
m_sessionHandler(sessionHandler)
9+
m_sessionHandler(sessionHandler),
10+
m_config(configFile)
1011
{
1112
}
1213
}

include/CommonLib/ChunkEntities.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ namespace tsom
5656
void FillChunks();
5757
virtual UpdateJob* ProcessChunkUpdate(const Chunk& chunk, DirectionMask neighborMask);
5858
void OnParentNodeInvalidated(const Nz::Node* node);
59+
void RebuildAllChunks();
5960
inline void UpdateChunkEntity(const ChunkIndices& chunkIndices, DirectionMask neighborMask);
6061

6162
struct UpdateJob

src/ClientLib/ClientChunkEntities.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@
33
// For conditions of distribution and use, see copyright notice in LICENSE
44

55
#include <ClientLib/ClientChunkEntities.hpp>
6+
#include <ClientLib/ClientConfigs.hpp>
67
#include <ClientLib/RenderConstants.hpp>
78
#include <ClientLib/Components/VisualEntityComponent.hpp>
89
#include <CommonLib/ChunkLock.hpp>
10+
#include <CommonLib/ConfigFile.hpp>
911
#include <CommonLib/Components/EntityOwnerComponent.hpp>
1012
#include <Nazara/Core/ApplicationBase.hpp>
1113
#include <Nazara/Core/EnttWorld.hpp>
1214
#include <Nazara/Core/FilesystemAppComponent.hpp>
1315
#include <Nazara/Core/IndexBuffer.hpp>
1416
#include <Nazara/Core/TaskSchedulerAppComponent.hpp>
1517
#include <Nazara/Core/VertexBuffer.hpp>
18+
#include <Nazara/Core/VertexMapper.hpp>
1619
#include <Nazara/Core/Components/NodeComponent.hpp>
1720
#include <Nazara/Graphics/GraphicalMesh.hpp>
1821
#include <Nazara/Graphics/Graphics.hpp>
@@ -26,8 +29,9 @@
2629

2730
namespace tsom
2831
{
29-
ClientChunkEntities::ClientChunkEntities(Nz::ApplicationBase& app, Nz::EnttWorld& world, ChunkContainer& chunkContainer, const ClientBlockLibrary& blockLibrary, std::size_t layerIndex) :
32+
ClientChunkEntities::ClientChunkEntities(Nz::ApplicationBase& app, ConfigFile& config, Nz::EnttWorld& world, ChunkContainer& chunkContainer, const ClientBlockLibrary& blockLibrary, std::size_t layerIndex) :
3033
ChunkEntities(app, world, chunkContainer, blockLibrary, layerIndex, NoInit{}),
34+
m_configFile(config),
3135
m_isCollisionGenerationEnabled(true)
3236
{
3337
auto& filesystem = app.GetComponent<Nz::FilesystemAppComponent>();
@@ -145,6 +149,11 @@ namespace tsom
145149
}
146150
});
147151

152+
m_onVisualChunkNormalSmoothAngleUpdatedSlot.Connect(m_configFile.GetFloatUpdateSignal(Config::Visual_ChunkNormalSmoothAngle), [this](double /*newValue*/)
153+
{
154+
RebuildAllChunks();
155+
});
156+
148157
FillChunks();
149158
}
150159

@@ -177,6 +186,47 @@ namespace tsom
177186
std::shared_ptr<Nz::StaticMesh> staticMesh = std::make_shared<Nz::StaticMesh>(std::move(vertexBuffer), std::move(indexBuffer));
178187
staticMesh->GenerateAABB();
179188

189+
Nz::DegreeAnglef smoothLimitAngle = m_configFile.GetFloatValue<float>(Config::Visual_ChunkNormalSmoothAngle);
190+
if (smoothLimitAngle > 0.0f)
191+
{
192+
Nz::VertexMapper mapper(*staticMesh);
193+
Nz::UInt32 vertexCount = mapper.GetVertexCount();
194+
195+
Nz::SparsePtr<Nz::Vector3f> normals = mapper.GetComponentPtr<Nz::Vector3f>(Nz::VertexComponent::Normal);
196+
Nz::SparsePtr<Nz::Vector3f> positions = mapper.GetComponentPtr<Nz::Vector3f>(Nz::VertexComponent::Position);
197+
198+
// TODO: Replace by a vertex finder-like
199+
std::map<Nz::Vector3i, Nz::HybridVector<Nz::UInt32, 6>> posToVerts;
200+
for (Nz::UInt32 i = 0; i < vertexCount; ++i)
201+
{
202+
Nz::Vector3i p = Nz::Vector3i(Nz::Vector3f::Apply(positions[i] * 100.f, std::roundf));
203+
posToVerts[p].push_back(i);
204+
}
205+
206+
float fLimit = smoothLimitAngle.GetCos();
207+
for (Nz::UInt32 i = 0; i < vertexCount; ++i)
208+
{
209+
Nz::Vector3i p = Nz::Vector3i(Nz::Vector3f::Apply(positions[i] * 100.f, std::roundf));
210+
211+
Nz::Vector3f vr = normals[i];
212+
213+
auto& verticesFound = posToVerts[p];
214+
Nz::Vector3f pcNor;
215+
for (Nz::UInt32 j : verticesFound)
216+
{
217+
Nz::Vector3f v = normals[j];
218+
219+
// Check whether the angle between the two normals is not too large.
220+
// Skip the angle check on our own normal to avoid false negatives
221+
// (v*v is not guaranteed to be 1.0 for all unit vectors v)
222+
if ((j == i || (Nz::Vector3f::DotProduct(v, vr) >= fLimit)))
223+
pcNor += v;
224+
}
225+
226+
normals[i] = pcNor.Normalize();
227+
}
228+
}
229+
180230
std::shared_ptr<Nz::Mesh> chunkMesh = std::make_shared<Nz::Mesh>();
181231
chunkMesh->CreateStatic();
182232
chunkMesh->AddSubMesh(std::move(staticMesh));

0 commit comments

Comments
 (0)