Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
FoliageComponent Source
//
// Created by Victor Holt on 5/1/2017.
//
#include <cor-game/components/FoliageComponent.h>
#include <cor-game/GameEngine.h>
#include <cor-game/terrain/Grid.h>
#include <cor-game/terrain/GridCell.h>
#include <gs-common/helpers/NumberHelper.hpp>
#include <gs-common/helpers/MathHelper.h>
#include <gs-common/core/SenchaThreadManager.h>
#include <gs-common/core/SenchaThread.h>
#include <gs-script/engine/AppSettings.h>
using namespace Urho3D;
using namespace Sencha;
class FoliageThread : public SenchaThread
{
URHO3D_OBJECT(SenchaThread, Object);
public:
FoliageThread(Context* context) : SenchaThread(context) {}
protected:
virtual void OnProcess() override {
FoliageComponent* component = reinterpret_cast<FoliageComponent*>(methodArgs_["FoliageComponent"].GetVoidPtr());
}
};
FoliageComponent::FoliageComponent(Context* context)
: Component(context)
{
SubscribeToEvent(E_UPDATE, URHO3D_HANDLER(FoliageComponent, HandleUpdateEvent));
isProcessing_ = false;
isLoaded_ = false;
isInView_ = false;
}
void FoliageComponent::RegisterObject(Context* context)
{
context->RegisterFactory<FoliageComponent>();
}
void FoliageComponent::HandleUpdateEvent(StringHash eventType, VariantMap& eventData)
{
GameEngine* gameEngine = GetSubsystem<GameEngine>();
if (!gameEngine->GetInit()) return;
if (!isProcessing_) {
Node* cameraNode = gameEngine->GetActiveCameraNode();
Camera* camera = cameraNode->GetComponent<Camera>();
Graphics* graphics = GetSubsystem<Graphics>();
UI* ui = GetSubsystem<UI>();
IntVector2 pos = ui->GetCursorPosition();
Ray cameraRay = camera->GetScreenRay((float) pos.x_ / graphics->GetWidth(),
(float) pos.y_ / graphics->GetHeight());
Vector3 cameraPos = cameraNode->GetWorldPosition();
if (boundingBox_.IsInside(cameraPos) == INSIDE || cameraRay.HitDistance(boundingBox_) < GetSubsystem<Grid>()->GetFoilageDrawDistance()) {
Load();
} else {
Unload();
}
}
}
void FoliageComponent::Initialize(GridCell* gridCell, const String& name, Model* model, uint32_t maxFoliageNodes)
{
gridCell_ = gridCell;
name_ = name;
// Create the mesh.
foliageMesh_ = new CustomMesh(GetContext(), false);
float lengthX = 1.0f;
float lengthY = 1.0f;
float lengthZ = 1.0f;
foliageMesh_->AddFace(
Vector3(0.0f, 0.0f, lengthZ),
Vector3(lengthX, lengthY, lengthZ),
Vector3(0.0f, lengthY, lengthZ),
MeshUVType::UV_XY
);
foliageMesh_->AddFace(
Vector3(0.0f, 0.0f, lengthZ),
Vector3(lengthX, 0.0f, lengthZ),
Vector3(lengthX, lengthY, lengthZ),
MeshUVType::UV_XY
);
foliageMesh_->Commit();
maxFoliageNodes_ = maxFoliageNodes;
Grid* grid = GetSubsystem<Grid>();
float cellSize = grid->GetCellSize();
float cellSizeHalf = cellSize / 2.0f;
Vector3 scale = grid->GetTerrainScale();
Vector3 cellMin = Vector3(-cellSizeHalf * scale.x_, 0.0f, -cellSizeHalf * scale.z_);
Vector3 cellMax = Vector3(cellSize * scale.x_, 0.0f, cellSize * scale.z_);
foliageNode_ = node_->CreateChild("Foilage");
modelGroup_ = foliageNode_->CreateComponent<StaticModelGroup>();
modelGroup_->SetDrawDistance(grid->GetFoilageDrawDistance());
modelGroup_->SetModel(foliageMesh_->GetModel());
modelGroup_->SetCastShadows(false);
modelGroup_->SetOccludee(true);
modelGroup_->SetMaterial(GetSubsystem<ResourceCache>()->GetResource<Material>("CorMod/gfx/materials/Foliage.xml"));
Vector3 worldNodePos = gridCell_->GetTerrainNode()->GetWorldPosition();
boundingBox_ = BoundingBox(Vector3(cellMin.x_, -1000.0f, cellMin.z_) + worldNodePos, Vector3(cellMax.x_, 1000.0f, cellMax.z_) + worldNodePos);
VectorBuffer buffer;
buffer.WriteUInt(maxFoliageNodes);
for (uint32_t i = 0; i < maxFoliageNodes_; i++) {
Vector3 nodePos = Vector3(NumberHelper::RandomFloat(cellMin.x_, cellMax.x_), 0.0f, NumberHelper::RandomFloat(cellMin.z_, cellMax.z_));
nodePos += worldNodePos;
nodePos.y_ = gridCell->GetTerrain()->GetHeight(Vector3(nodePos.x_, 0.0f, nodePos.z_));
Quaternion rot = Quaternion(0.0f, Random(360.0f), 0.0f);
float foilageScale = 0.5f + Random(2.0f);
buffer.WriteVector3(nodePos);
buffer.WriteQuaternion(rot);
buffer.WriteFloat(foilageScale);
}
modelGroup_->SetEnabled(false);
Save(buffer);
// Create our loading thread.
SenchaThreadManager* threadManager = GetSubsystem<SenchaThreadManager>();
loadThread_ = threadManager->CreateThread([&]() {
while (!loadThread_->GetIsAborted()) {
if (!isProcessing_ || !GetSubsystem<GameEngine>()->GetInit() || GetSubsystem<GameEngine>()->GetNumFoliageLoads() > 3) {
loadThread_->Sleep(100);
continue;
}
GetSubsystem<GameEngine>()->IncFoliageLoads();
MutexLock lock(foliageLock_);
if (isInView_) {
if (!modelGroup_->IsEnabled()) {
File file(GetContext(), filePath_);
VectorBuffer buffer(file.ReadBuffer());
uint32_t maxFoliageNodes = buffer.ReadUInt();
file.Close();
for (uint32_t i = 0; i < maxFoliageNodes_; i++) {
Vector3 nodePos = buffer.ReadVector3();
Quaternion rot = buffer.ReadQuaternion();
float foilageScale = buffer.ReadFloat();
Node* node = new Node(GetContext());
node->SetName("Vegetation");
node->SetPosition(nodePos);
node->SetRotation(rot);
node->SetScale(foilageScale);
instancedNodes_.Push(node);
modelGroup_->AddInstanceNode(node);
}
modelGroup_->SetEnabled(true);
isLoaded_ = true;
GetSubsystem<GameEngine>()->DecFoliageLoads();
}
} else {
modelGroup_->RemoveAllInstanceNodes();
for (auto node : instancedNodes_) {
delete node;
}
instancedNodes_.Clear();
isLoaded_ = false;
GetSubsystem<GameEngine>()->DecFoliageLoads();
}
isProcessing_ = false;
loadThread_->Sleep(100);
}
}, true);
}
void FoliageComponent::Save(VectorBuffer& data)
{
AppSettings* settings = GetSubsystem<AppSettings>();
String relFileName;
String cacheFileName = settings->GetCacheDirPath();
cacheFileName.Append("/");
settings->GetRelativeModPath("gfx/map", "", relFileName);
relFileName.Replace('/', '_');
relFileName.Append('_');
filePath_ = cacheFileName;
filePath_.Append(relFileName);
filePath_.Append(name_);
filePath_.Append(".dat");
File file(GetContext(), filePath_, FILE_WRITE);
file.WriteBuffer(data.GetBuffer());
file.Close();
}
void FoliageComponent::Load()
{
if (isLoaded_) return;
isProcessing_ = true;
isInView_ = true;
}
void FoliageComponent::Unload()
{
if (!isLoaded_) return;
isProcessing_ = true;
modelGroup_->SetEnabled(false);
isInView_ = false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment