-
Posts
544 -
Joined
-
Last visited
martyj's Achievements
-
We do loose some performance no doubt as w use Entity objects rather than billboards. We also loose some range as well due to this as well. You're correct that we use the veg system for position and rotation. If someone wanted to, they could extend the radius around this using custom LOD models to provide more of a "billboard" affect. We do "replace" vegetation items around the player only, but you can't mix the two. With Leadwerks you cannot hide a specific vegetation object. You either hide everything or nothing. Benefits: 1. Allows animated models to be placed as easily as vegetation 2. Allows us to hide/show specific vegetation trees as players interact with them Cons: 1. Less distance with vegetation since we don't support billboards 2. Performance hit of trading 1 vegetation layer vs 50 Entity objects. World Factions uses this technique for 2 use cases. 1. Players can cut down any tree in the game. This allows us to place trees with the vegetation system, but provides user interactive content. The tree that the player cuts down is made of two Entities. A top half of tree, and a bottom trunk. When the player cuts the tree, we enable physics on the Tree and provide a random force on the top half to "cut it down" 2. Animals in the game. These are animated models with a small amount of AI added to them. We use the vegetation information for starting point. Players can kill specific animals so we need to hide them.
-
We're not loading the model for tree every vegetation model. We do a model->Instnace. Have a look at VegetatedModel class. It loads VEG_MODEL_ENTITY_COUNT number of models via model->Instance. For your second comment we do an AABB check around the player position like you have suggested. We find all entities in a "boxSize" around the player. AABB scope = AABB(playerPosit.x-boxSizeX, -1000, playerPosit.z-boxSizeZ, playerPosit.x+boxSizeX, 1000, playerPosit.z+boxSizeZ); For your last question, we hide everything on the Veg System and use real Instanced models instead. ==================== Edit: I see what you're saying. This code can be removed. AABB aabb = world->aabb; std::vector<Mat4> instances; layer->GetInstancesInAABB(aabb, instances); System::Print("Loading Layer: " + parentName + " of Size: " + std::to_string(instances.size())); for (unsigned int j = 0; j < instances.size(); j++) { Mat4 instance = instances[j]; Entity* copy = model->Instance(true, false); copy->SetMatrix(instance, true); copy->SetScale(scale); }
-
I had to do this for my game, it's possible, just not with the vegetation system exactly. You have to convert vegetation instances into real Entity objects. Due to performance reasons, you can't convert every vegetation object to an entity, so you have to be smart, and look at the closest objects to the player. I used a pool of about 50 objects and moved them to the closest trees around a player. I would sticky a vegetation entity to a specific tree as well, so if you started cutting one tree down, and you moved a little bit, you could go back to that tree and finish it off. Here is the C++ code. (Headers not included) VegetationToEntity -- Converts a vegetation layer into multiple VegetatedModel classes bool findStringIC(const std::string & strHaystack, const std::string & strNeedle) { auto it = std::search( strHaystack.begin(), strHaystack.end(), strNeedle.begin(), strNeedle.end(), [](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); } ); return (it != strHaystack.end() ); } void VegetationToEntity::LoadVegetationLayer(World* world) { App* app = App::GetApp(); if (app == nullptr) { return; } if (world->terrain == nullptr) { return; } std::map<std::string, bool> limitEntityMap = { { "Antelope", true }, { "Deer", true }, { "Lamb", true }, { "Sheep", true }, { "Wolf", true }, }; std::map<std::string, std::string> vegNameEntMap = { { "oak", "OakTree" }, { "maple", "MapleTree" }, { "birch", "BirchTree" }, { "green", "GreenheartTree" }, { "giantwood", "RedwoodTree" }, { "weep", "WillowTree" }, { "mud", "MudHole" }, { "antelope", "Antelope" }, { "fallowdeer", "Deer" }, { "lamb", "Lamb" }, { "sheep", "Sheep" }, { "wolf", "Wolf" }, }; int vegCount = world->terrain->CountVegetationLayers(); for (int i = 0; i < vegCount; i++) { VegetationLayer* layer = world->terrain->GetVegetationLayer(i); std::string modelPath = layer->modelpath; std::string parentName = ""; System::Print(modelPath); for (auto it = vegNameEntMap.begin(); it != vegNameEntMap.end(); it++) { System::Print(it->first); if(findStringIC(modelPath, it->first)) { parentName = it->second; break; } } if (parentName.empty()) { continue; } Entity* model = world->FindEntity(parentName); if (model == nullptr) { continue; } // Hide Layer layer->viewrange = 0; if (limitEntityMap[parentName]) { models.push_back(new VegetatedModel(layer, model, parentName)); continue; } Vec3 scale = model->GetScale(); AABB aabb = world->aabb; std::vector<Mat4> instances; layer->GetInstancesInAABB(aabb, instances); System::Print("Loading Layer: " + parentName + " of Size: " + std::to_string(instances.size())); for (unsigned int j = 0; j < instances.size(); j++) { Mat4 instance = instances[j]; Entity* copy = model->Instance(true, false); copy->SetMatrix(instance, true); copy->SetScale(scale); } } } // Called manually in App::Loop void VegetationToEntity::UpdateWorld() { for (auto it = models.begin(); it != models.end(); it++) { VegetatedModel* model = *it; model->UpdateWorld(); } } The next class is where the work comes in. VegetatedModel represents a single Vegetation Layer as N number of entities. This will auto-populate the entities around the players positions as the player moves. VegetatedModel::VegetatedModel(VegetationLayer* layer, Entity* model, std::string className) { this->layer = layer; this->cameraPosit = Vec3(0); for (int i = 0; i < VEG_MODEL_ENTITY_COUNT; i++) { Entity* copy = model->Instance(true, false); WFModel* model = Binder::InitCtor(copy, className); this->realEntities[i] = new VegetatedEntity(model, copy); } } void VegetatedModel::UpdateWorld() { Player* p = Player::GetPlayer(); if (p == NULL) { return; } Vec3 playerPosit = p->GetPosition(); if (this->cameraPosit.DistanceToPoint(playerPosit) < 4) { return; } this->cameraPosit = playerPosit; int boxSizeX = 30; int boxSizeZ = 30; AABB scope = AABB(playerPosit.x-boxSizeX, -1000, playerPosit.z-boxSizeZ, playerPosit.x+boxSizeX, 1000, playerPosit.z+boxSizeZ); World* world = World::GetCurrent(); std::vector<Vec3> possiblePoints(0); std::vector<Mat4> instances(0); this->layer->GetInstancesInAABB(world->aabb, instances); for (unsigned int j = 0; j < instances.size(); j++) { Mat4 mat = instances[j]; possiblePoints.push_back(mat.GetTranslation()); } std::sort(possiblePoints.begin(), possiblePoints.end(), VegetatedModel::PointSort); // First VEG_MODEL_ENTITY_COUNT as we want to show only the closest point if (possiblePoints.size() > VEG_MODEL_ENTITY_COUNT) { possiblePoints.erase(possiblePoints.begin() + VEG_MODEL_ENTITY_COUNT, possiblePoints.end()); } std::vector<VegetatedEntity*> entitiesNeedingPoints; std::vector<Vec3> pointsToRemove; int Count = Math::Min(possiblePoints.size(), VEG_MODEL_ENTITY_COUNT); for (int i = 0 ; i < Count; i++) { VegetatedEntity* vegEntity = this->realEntities[i]; Entity* ent = vegEntity->GetEntity(); bool found = false; for (int j = 0; j < Count; j++) { Vec3 posit = possiblePoints[j]; if (vegEntity->GetVegetationPosition() == posit) { found = true; pointsToRemove.push_back(posit); break; } } if (!found) { entitiesNeedingPoints.push_back(vegEntity); } } std::vector<Vec3> newPossiblePoints; for (int i = 0; i < possiblePoints.size(); i++) { Vec3 possiblePoint = possiblePoints[i]; bool found = false; for (auto it = pointsToRemove.begin(); it != pointsToRemove.end(); it++) { Vec3 pointRemove = *it; if (possiblePoint == pointRemove) { found = true; break; } } if (!found) { newPossiblePoints.push_back(possiblePoint); } } possiblePoints = newPossiblePoints; int numEntitiesToSet = Math::Min(possiblePoints.size(), entitiesNeedingPoints.size()); for (int i = 0; i < numEntitiesToSet; i++) { VegetatedEntity* vegEntity = entitiesNeedingPoints[i]; Entity* ent = vegEntity->GetEntity(); WFModel* model = vegEntity->GetWFModel(); Vec3 point = possiblePoints[i]; vegEntity->SetVegetationPosition(point); model->Reset(); } // Remove unused entities for (unsigned int i = numEntitiesToSet; i < entitiesNeedingPoints.size(); i++) { VegetatedEntity* vegEntity = entitiesNeedingPoints[i]; Entity* ent = vegEntity->GetEntity(); //vegEntity->SetVegetationPosition(Vec3(0, -123, 0)); //ent->Hide(); } } bool VegetatedModel::PointSort(Vec3 posit1, Vec3 posit2) { Player* p = Player::GetPlayer(); if (p == NULL) { return 0; } double d1 = p->GetPosition().DistanceToPoint(posit1); double d2 = p->GetPosition().DistanceToPoint(posit2); return d1 < d2; }
-
An game engine is exploitable. It's why valve has VAC software. IMO player movement is a fairly small exploit. Depending on the type of game you are making, there are exploits far more damaging. FPS - suffers from auto-aim bots MMORPG - Duplicate items, auto xp Player movement hacks are less likely as you have to find the memory address of the player positions in the application memory and then modify that memory externally. Auto-aim bots, and auto xp bots are more likely as you don't have to worry about cross application memory. On most modern operating systems application memory is stored in pages. These pages are protected by the operating system to make sure your program doesn't bump into another applications memory. A common way to get past this protection is to do DLL injection. Basically take a commonly used DLL by your game (libcurl for example), modify the library to function exactly how it normally does, plus add in hooks to let you control the game. TLDR: Yes
-
Try running it on an AMD card. Back when I was working on OpenGL I had a bug where a uniform was set to zero by default on Nvidia, yet it was randomized on AMD. If the terrain flashes random colors, you know that's your issue.
-
Wonderful blog post! When can we get a new "beta" for this? Vegetation is the only feature missing to complete everything I need for where I am going with my game. I will be hiring a full time content creator on Oct 1st, so this is exciting.
-
Texture::SetPixels doesn't work in Windows in Leadwerks 4.6
martyj replied to martyj's topic in Leadwerks Engine Bug Reports
Please close this topic. I found the issue. It was my mistake. In Windows, Context->GetWidth() is not the same as Window->GetWidth() This caused my texture to be rendered weird. As far as why this worked in Linux compared to Windows, who knows. -
Texture::SetPixels doesn't work in windows: Linux Screenshot: Windows Screenshot: Code: virtual void RenderBuffer(unsigned char* buffData, int width, int height) { this->texture->SetPixels((const char*)buffData, 0, 0, 0); } I can get Windows to work by manually setting each pixel, but it's hella slow performance. Here is a TGA of what I am trying to render in Linux. (Raw BGRA image format with first 36 bytes being a header) linux_render_21_36.tga TGA for Windows: render_windows2.tga
-
Three improvements I made to Leadwerks Game Engine 5 today
martyj commented on Josh's blog entry in Development Blog
I would like to see Terrain and Vegetation. I'd rather see these before lighting tbh. If I could get a "playable" version of World Factions, that doesn't require a $500 graphics card, it would allow me to hire content developers to develop content until LE5 can deliver the visuals. *Is planning on hiring a full-time content developer in 2 months* From a marketing standpoint, I could see how lighting would be preferred though. -
Very similar to how I did my cutscenes. It would be nice if Leadwerks supported this as well, but allowed the user-interface to add co-routines. For my cutscenes, I have things like Start Animation, Go to position, Go to rotation. You could even have a "Execute Lua Function". I like it!
-
So the one major issue I have at the moment is as follows: mismatch detected for 'RuntimeLibrary': value 'MT_StaticRelease' doesn't match value 'MD_DynamicRelease' For some reason it is passing /MDd to the compiler instead of /MT I will have a look at your CMake project tomorrow to see if I can resolve it based off that.
-
@reepblue That's the goal. To use a single CMake script for Linux and Windows. Right now I have to maintain a VS project for Windows, and a CMake for Linux. There are a lot of caveats to getting CMake for windows to work, such as setting /SUBSYTEM:CONSOLE having the correct MD/MT/MTd flags. Things that normally just work out of the box for visual studio.
-
@Josh How would Upwork work as my task requires the professional C++ license for Leadwerks? My concern for switching to Steamworks library is I already have CEF working well in my game. I'm just trying to not have to maintain a Visual Studio Project for Windows and CMake project for Linux. If I can get CMake to work on Windows I am set.
-
I'm looking for a C++ freelancer to "port" a library and example project into Windows. The library is a wrapper around Chromium Embedded Framework. The example project is just using the library in a Leadwerks sample. There probably isn't any C++ coding required, but more of CMake configurations. (CMake is a requirement. No Visual Studio projects.) PM me if you're interested, include your hourly rate, and a resume/work history.
-
From a Leadwerks Pro perspective. Linux is a hell of a lot easier to deal with. From a Leadwerks perspective, the Leadwerks UI on Linux is no where near the quality of that in Windows. It's this fact alone that's probably hurting Leadwerks Linux. If I need to make a map change. I dual boot into windows to make changes to the map. I have been on Leadwerks Linux for the last year I currently use kubuntu 18.04.