The current code formatting guide isn’t followed consistently. One thing that could improve this (but doesn’t fix naming and other issue like that) formating issue is using an automatic formating tool.
I’ve started using clang format with my engine and I think we could also use it for thrive to have the code formatting be consistent in the c++ files (it doesn’t work perfectly for angelscript). Here’s what I suggest the format configuration is:
Language: Cpp
AccessModifierOffset: '-4'
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: 'false'
AlignConsecutiveDeclarations: 'false'
AlignEscapedNewlinesLeft: Left
AlignTrailingComments: 'false'
AllowAllParametersOfDeclarationOnNextLine: 'false'
AllowShortBlocksOnASingleLine: 'false'
AllowShortCaseLabelsOnASingleLine: 'true'
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: 'false'
AllowShortLoopsOnASingleLine: 'false'
AlwaysBreakAfterReturnType: All
AlwaysBreakBeforeMultilineStrings: 'false'
AlwaysBreakTemplateDeclarations: 'false'
BinPackArguments: 'true'
BinPackParameters: 'false'
BreakBeforeBinaryOperators: None
# BreakBeforeBraces: Attach
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterStruct: false
AfterUnion: false
# AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: false
SplitEmptyNamespace: true
BreakBeforeInheritanceComma: 'false'
BreakBeforeTernaryOperators: 'false'
BreakConstructorInitializers: AfterColon
# Works fine even without this
# CommentPragmas: '^! \\'
CompactNamespaces: 'true'
ConstructorInitializerAllOnOneLineOrOnePerLine: 'false'
Cpp11BracedListStyle: 'true'
DerivePointerAlignment: 'false'
IndentCaseLabels: 'false'
IndentWidth: '4'
IndentWrappedFunctionNames: 'true'
KeepEmptyLinesAtTheStartOfBlocks: 'true'
MaxEmptyLinesToKeep: '3'
NamespaceIndentation: None
PointerAlignment: Left
ReflowComments: 'true'
SortIncludes: 'true'
SortUsingDeclarations: 'true'
SpaceAfterCStyleCast: 'false'
SpaceAfterTemplateKeyword: 'false'
SpaceBeforeAssignmentOperators: 'true'
SpaceBeforeParens: Never
# This is also a new setting not yet available
# SpaceBeforeRangeBasedForLoopColon: 'true'
SpaceInEmptyParentheses: 'false'
SpacesBeforeTrailingComments: '1'
SpacesInAngles: 'false'
SpacesInCStyleCastParentheses: 'false'
SpacesInParentheses: 'false'
SpacesInContainerLiterals: 'false'
SpacesInParentheses: 'false'
SpacesInSquareBrackets: 'false'
Standard: Cpp11
TabWidth: '4'
UseTab: Never
Here’s an explanation of the options: https://clang.llvm.org/docs/ClangFormatStyleOptions.html
Thoughts on these settings? I’m not sure what the BraceWrapping
and SpaceBeforeParens
options should be to match the old style guide as well as possible.
Here’s the membrane system formatted like that:
#include "membrane_system.h"
// #include "bullet/collision_system.h"
// #include "bullet/rigid_body_system.h"
// #include "engine/component_factory.h"
// #include "engine/engine.h"
// #include "engine/entity_filter.h"
// #include "engine/game_state.h"
// #include "engine/serialization.h"
// #include "game.h"
// #include "ogre/scene_node_system.h"
// #include "scripting/luajit.h"
// #include "util/make_unique.h"
#include <OgreMaterialManager.h>
#include <OgreMesh2.h>
#include <OgreMeshManager2.h>
// #include <OgreMaterial.h>
// #include <OgreTechnique.h>
// #include <OgreEntity.h>
#include <OgreRoot.h>
#include <OgreSceneManager.h>
#include <OgreSubMesh2.h>
// #include <stdexcept>
#include <atomic>
using namespace thrive;
////////////////////////////////////////////////////////////////////////////////
// Membrane Component
////////////////////////////////////////////////////////////////////////////////
static std::atomic<int> MembraneMeshNumber = {0};
MembraneComponent::MembraneComponent() : Leviathan::Component(TYPE)
{
// Create the mesh for rendering us
m_mesh = Ogre::MeshManager::getSingleton().createManual(
"MembraneMesh_" + std::to_string(++MembraneMeshNumber),
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
m_subMesh = m_mesh->createSubMesh();
}
MembraneComponent::~MembraneComponent()
{
LEVIATHAN_ASSERT(!m_item, "MembraneComponent not released");
Ogre::MeshManager::getSingleton().remove(m_mesh);
m_mesh.reset();
m_subMesh = nullptr;
}
void
MembraneComponent::Release(Ogre::SceneManager* scene)
{
if(m_item) {
scene->destroyItem(m_item);
m_item = nullptr;
}
}
// ------------------------------------ //
Ogre::Vector3
MembraneComponent::FindClosestOrganelles(Ogre::Vector3 target)
{
// The distance we want the membrane to be from the organelles squared.
double closestSoFar = 4;
int closestIndex = -1;
for(size_t i = 0, end = organellePositions.size(); i < end; i++) {
double lenToObject = target.squaredDistance(organellePositions[i]);
if(lenToObject < 4 && lenToObject < closestSoFar) {
closestSoFar = lenToObject;
closestIndex = i;
}
}
if(closestIndex != -1)
return (organellePositions[closestIndex]);
else
return Ogre::Vector3(0, 0, -1);
}
Ogre::Vector3
MembraneComponent::GetMovement(Ogre::Vector3 target,
Ogre::Vector3 closestOrganelle)
{
double power = pow(2.7, (-target.distance(closestOrganelle)) / 10) / 50;
return (Ogre::Vector3(closestOrganelle) - Ogre::Vector3(target)) * power;
}
Ogre::Vector3
MembraneComponent::GetExternalOrganelle(double x, double y)
{
float organelleAngle = Ogre::Math::ATan2(y, x).valueRadians();
Ogre::Vector3 closestSoFar(0, 0, 0);
float angleToClosest = Ogre::Math::TWO_PI;
for(size_t i = 0, end = vertices2D.size(); i < end; i++) {
if(Ogre::Math::Abs(Ogre::Math::ATan2(vertices2D[i].y, vertices2D[i].x)
.valueRadians() -
organelleAngle) < angleToClosest) {
closestSoFar = Ogre::Vector3(vertices2D[i].x, vertices2D[i].y, 0);
angleToClosest = Ogre::Math::Abs(
Ogre::Math::ATan2(vertices2D[i].y, vertices2D[i].x)
.valueRadians() -
organelleAngle);
}
}
return closestSoFar;
}
bool
MembraneComponent::contains(float x, float y)
{
// if (x < -cellDimensions/2 || x > cellDimensions/2 || y <
// -cellDimensions/2 || y > cellDimensions/2) return false;
bool crosses = false;
int n = vertices2D.size();
for(int i = 0; i < n - 1; i++) {
if((vertices2D[i].y <= y && y < vertices2D[i + 1].y) ||
(vertices2D[i + 1].y <= y && y < vertices2D[i].y)) {
if(x < (vertices2D[i + 1].x - vertices2D[i].x) *
(y - vertices2D[i].y) /
(vertices2D[i + 1].y - vertices2D[i].y) +
vertices2D[i].x) {
crosses = !crosses;
}
}
}
return crosses;
}
// ------------------------------------ //
//! Should set the colour of the membrane once working
void
MembraneComponent::setColour(const Float4& value)
{
colour = value;
// TODO: apply
DEBUG_BREAK;
}
// ------------------------------------ //
void
MembraneComponent::Update(Ogre::SceneManager* scene,
Ogre::SceneNode* parentcomponentpos)
{
// Skip if the mesh is already created //
if(isInitialized)
return;
if(!isInitialized)
Initialize();
DrawMembrane();
// 12 vertices added per index of vertices2D
const auto bufferSize = vertices2D.size() * 12;
if(!m_vertexBuffer) {
Ogre::RenderSystem* renderSystem =
Ogre::Root::getSingleton().getRenderSystem();
Ogre::VaoManager* vaoManager = renderSystem->getVaoManager();
Ogre::VertexElement2Vec vertexElements;
vertexElements.push_back(
Ogre::VertexElement2(Ogre::VET_FLOAT3, Ogre::VES_POSITION));
vertexElements.push_back(Ogre::VertexElement2(
Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES));
// vertexElements.push_back(Ogre::VertexElement2(Ogre::VET_FLOAT3,
// Ogre::VES_NORMAL));
m_vertexBuffer = vaoManager->createVertexBuffer(vertexElements,
bufferSize, Ogre::BT_DYNAMIC_PERSISTENT, nullptr, false);
Ogre::VertexBufferPackedVec vertexBuffers;
vertexBuffers.push_back(m_vertexBuffer);
// 1 to 1 index buffer mapping
Ogre::uint16* indices =
reinterpret_cast<Ogre::uint16*>(OGRE_MALLOC_SIMD(
sizeof(Ogre::uint16) * bufferSize, Ogre::MEMCATEGORY_GEOMETRY));
for(size_t i = 0; i < bufferSize; ++i) {
indices[i] = static_cast<Ogre::uint16>(i);
}
// TODO: check if this is needed (when a 1 to 1 vertex and index mapping
// is used)
Ogre::IndexBufferPacked* indexBuffer = nullptr;
try {
indexBuffer =
vaoManager->createIndexBuffer(Ogre::IndexBufferPacked::IT_16BIT,
bufferSize, Ogre::BT_IMMUTABLE,
// Could this be false like the vertex buffer to not keep a
// shadow buffer
indices, true);
} catch(const Ogre::Exception& e) {
// Avoid memory leak
OGRE_FREE_SIMD(indices, Ogre::MEMCATEGORY_GEOMETRY);
indexBuffer = nullptr;
throw e;
}
Ogre::VertexArrayObject* vao = vaoManager->createVertexArrayObject(
vertexBuffers, indexBuffer, Ogre::OT_TRIANGLE_LIST);
m_subMesh->mVao[Ogre::VpNormal].push_back(vao);
// This might be needed because we use a v2 mesh
// Use the same geometry for shadow casting.
m_subMesh->mVao[Ogre::VpShadow].push_back(vao);
// Set the bounds to get frustum culling and LOD to work correctly.
// TODO: make this more accurate
m_mesh->_setBounds(
Ogre::Aabb(Ogre::Vector3::ZERO, Ogre::Vector3::UNIT_SCALE * 50)
/*, false*/);
m_mesh->_setBoundingSphereRadius(50);
// Set the membrane material //
// Ogre::MaterialPtr baseMaterial =
// Ogre::MaterialManager::getSingleton().getByName(
// "Membrane");
// If this is uncommented material destruction should be added
// to the destructor (and this probably moved to the
// constructor)
// Ogre::MaterialPtr materialPtr =
// baseMaterial->clone(m_mesh->getName()); materialPtr->compile();
// // This doesn't work anymore
// Ogre::TextureUnitState* ptus = materialPtr->getTechnique(0)->
// getPass(0)->getTextureUnitState(0);
// ptus->setColourOperationEx(Ogre::LBX_MODULATE, Ogre::LBS_MANUAL,
// Ogre::LBS_TEXTURE,
// colour);
// m_mesh->setMaterial(materialPtr);
m_subMesh->setMaterialName("Membrane");
// Use different material for testing to see the mesh better
// m_subMesh->setMaterialName("Background");
}
// Map the buffer for writing //
// DO NOT READ FROM THE MAPPED BUFFER
MembraneVertex* RESTRICT_ALIAS meshVertices =
reinterpret_cast<MembraneVertex * RESTRICT_ALIAS>(
m_vertexBuffer->map(0, m_vertexBuffer->getNumElements()));
// Update mesh data //
// Creates a 3D prism from the 2D vertices.
// All of these floats were originally doubles. But to have more
// performance they are now floats
float height = .1;
size_t writeIndex = 0;
for(size_t i = 0, end = vertices2D.size(); i < end; i++) {
// Finds the UV coordinates be projecting onto a plane and stretching to
// fit a circle.
const float x = vertices2D[i].x;
const float y = vertices2D[i].y;
const float z = vertices2D[i].z;
const float ray = x * x + y * y + z * z;
const float t = Ogre::Math::Sqrt(ray) / (2.0 * ray);
const float a = t * x;
const float b = t * y;
// const float c = t*z;
const Ogre::Vector2 uv(a + 0.5, b + 0.5);
const Ogre::Vector2 center(0.5, 0.5);
const double currentRadians = 2.0 * 3.1416 * i / end;
const double nextRadians = 2.0 * 3.1416 * (i + 1) / end;
// y and z coordinates are swapped to match the Ogre up direction
// Bottom (or top?) half first triangle
meshVertices[writeIndex++] = {Ogre::Vector3(0, 0, 0), uv};
meshVertices[writeIndex++] = {
Ogre::Vector3(vertices2D[(i + 1) % end].x,
vertices2D[(i + 1) % end].z - height / 2,
vertices2D[(i + 1) % end].y),
uv};
meshVertices[writeIndex++] = {
Ogre::Vector3(vertices2D[i % end].x,
vertices2D[i % end].z - height / 2, vertices2D[i % end].y),
uv};
// Second triangle
meshVertices[writeIndex++] = {
Ogre::Vector3(vertices2D[i % end].x,
vertices2D[i % end].z + height / 2, vertices2D[i % end].y),
};
meshVertices[writeIndex++] = {
Ogre::Vector3(vertices2D[(i + 1) % end].x,
vertices2D[(i + 1) % end].z + height / 2,
vertices2D[(i + 1) % end].y),
uv};
meshVertices[writeIndex++] = {
Ogre::Vector3(vertices2D[(i + 1) % end].x,
vertices2D[(i + 1) % end].z - height / 2,
vertices2D[(i + 1) % end].y),
uv};
// This was originally a second loop
// Top half first triangle
// This seems to be the only one that is actually drawn to the screen,
// at least with the current test membrane.
meshVertices[writeIndex++] = {
Ogre::Vector3(vertices2D[i % end].x,
vertices2D[i % end].z + height / 2, vertices2D[i % end].y),
center +
Ogre::Vector2(cos(currentRadians), sin(currentRadians)) / 2};
meshVertices[writeIndex++] = {Ogre::Vector3(0, height / 2, 0), center};
meshVertices[writeIndex++] = {
Ogre::Vector3(vertices2D[(i + 1) % end].x,
vertices2D[(i + 1) % end].z + height / 2,
vertices2D[(i + 1) % end].y),
center + Ogre::Vector2(cos(nextRadians), sin(nextRadians)) / 2};
// Second triangle
meshVertices[writeIndex++] = {
Ogre::Vector3(vertices2D[i % end].x,
vertices2D[i % end].z - height / 2, vertices2D[i % end].y),
uv};
meshVertices[writeIndex++] = {
Ogre::Vector3(vertices2D[(i + 1) % end].x,
vertices2D[(i + 1) % end].z - height / 2,
vertices2D[(i + 1) % end].y),
uv};
meshVertices[writeIndex++] = {Ogre::Vector3(0, -height / 2, 0), uv};
}
// LOG_INFO("Write index is: " + std::to_string(writeIndex) + ", buffer
// size: " +
// std::to_string(bufferSize));
// This can be commented out when this works correctly, or maybe a
// different macro for debug builds to include this check could
// work, but it has to also work on linux
LEVIATHAN_ASSERT(writeIndex == bufferSize, "Invalid array element math in "
"fill vertex buffer");
// Upload finished data to the gpu (unmap all needs to be used to
// suppress warnings about destroying mapped buffers)
m_vertexBuffer->unmap(Ogre::UO_UNMAP_ALL);
// TODO: apply the current colour to the material instance
if(!m_item) {
// This needs the v2 mesh to contain data to work
m_item = scene->createItem(m_mesh, Ogre::SCENE_DYNAMIC);
parentcomponentpos->attachObject(m_item);
}
}
void
MembraneComponent::Initialize()
{
for(Ogre::Vector3 pos : organellePositions) {
if(abs(pos.x) + 1 > cellDimensions) {
cellDimensions = abs(pos.x) + 1;
}
if(abs(pos.y) + 1 > cellDimensions) {
cellDimensions = abs(pos.y) + 1;
}
}
for(int i = 0; i < membraneResolution; i++) {
vertices2D.emplace_back(
-cellDimensions + 2 * cellDimensions / membraneResolution * i,
-cellDimensions, 0);
}
for(int i = 0; i < membraneResolution; i++) {
vertices2D.emplace_back(cellDimensions,
-cellDimensions + 2 * cellDimensions / membraneResolution * i, 0);
}
for(int i = 0; i < membraneResolution; i++) {
vertices2D.emplace_back(
cellDimensions - 2 * cellDimensions / membraneResolution * i,
cellDimensions, 0);
}
for(int i = 0; i < membraneResolution; i++) {
vertices2D.emplace_back(-cellDimensions,
cellDimensions - 2 * cellDimensions / membraneResolution * i, 0);
}
// Does this need to run 50*cellDimensions times. That seems to be
// really high and probably causes some of the lag
for(int i = 0; i < 50 * cellDimensions; i++) {
DrawMembrane();
}
// Subdivide();
isInitialized = true;
}
// ------------------------------------ //
void
MembraneComponent::DrawMembrane()
{
// Stores the temporary positions of the membrane.
auto newPositions = vertices2D;
// Loops through all the points in the membrane and relocates them as
// necessary.
for(size_t i = 0, end = newPositions.size(); i < end; i++) {
Ogre::Vector3 closestOrganelle = FindClosestOrganelles(vertices2D[i]);
if(closestOrganelle == Ogre::Vector3(0, 0, -1)) {
newPositions[i] =
(vertices2D[(end + i - 1) % end] + vertices2D[(i + 1) % end]) /
2;
} else {
Ogre::Vector3 movementDirection =
GetMovement(vertices2D[i], closestOrganelle);
newPositions[i].x -= movementDirection.x;
newPositions[i].y -= movementDirection.y;
}
}
// Allows for the addition and deletion of points in the membrane.
for(size_t i = 0; i < newPositions.size() - 1; i++) {
// Check to see if the gap between two points in the membrane is too
// big.
if(newPositions[i].distance(
newPositions[(i + 1) % newPositions.size()]) >
cellDimensions / membraneResolution) {
// Add an element after the ith term that is the average of the i
// and i+1 term.
auto it = newPositions.begin();
Ogre::Vector3 tempPoint =
(newPositions[(i + 1) % newPositions.size()] +
newPositions[i]) /
2;
newPositions.insert(it + i + 1, tempPoint);
i++;
}
// Check to see if the gap between two points in the membrane is too
// small.
if(newPositions[(i + 1) % newPositions.size()].distance(
newPositions[(i - 1) % newPositions.size()]) <
cellDimensions / membraneResolution) {
// Delete the ith term.
auto it = newPositions.begin();
newPositions.erase(it + i);
}
}
vertices2D = newPositions;
}
void
MembraneComponent::sendOrganelles(double x, double y)
{
organellePositions.emplace_back(x, y, 0);
}
void
MembraneComponent::clear()
{
isInitialized = false;
vertices2D.clear();
if(m_item)
m_item->detachFromParent();
}