Skip to content

Instantly share code, notes, and snippets.

@victorholt
Created May 2, 2017 14:23
Show Gist options
  • Select an option

  • Save victorholt/cd0977a8243adf712cd2eb184d2bfd7f to your computer and use it in GitHub Desktop.

Select an option

Save victorholt/cd0977a8243adf712cd2eb184d2bfd7f to your computer and use it in GitHub Desktop.

Revisions

  1. victorholt created this gist May 2, 2017.
    214 changes: 214 additions & 0 deletions FoliageComponent.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,214 @@
    //
    // 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;
    }