Search the Community
Showing results for tags 'decals'.
-
In this tutorial we will add features to real-time tactic/strategy game in Ultra Engine fro prev tutorial: Plan: 1. Add flag model to use when pointing where to go for units 2. Make save/load system with quick save 3. Make blood particle emitter which will be used on hits and use it in Unit::Methood 4. Make blood decals which will be on the ground after death and spawn it in Unit::Kill 5. Download and use sounds for units getting damage. In this tutorial used 2117 Build from Steam Beta branch (Dev in Standalone version). In 0.9.8 version Component::Load/Save are protected. Build number can be found in the Editor Help->About Flag Flag will represent a place that player units will try to reach. Model can be found here: https://sketchfab.com/3d-models/triangle-flag-adadd59fd56d44f7b0354d1caba38f95 Download .glb version and put it to new folder "triangle_flag" in Models. Convert to mdl in the Editor. By default this model is too small. Set Scale 5.0 in Transform tab and in Tools click Reset Transform (works properly atm only for non-animated models) to save new size for good. Also click on Collapse in Tools to unite all meshes so it would be shown like single entity in a scene (otherwise this entity will have some kids which is not critical, but it's better to make it simpler). triangle_flag.zip Flag prefab: 1. Put triangle_flag model to scene 2. In Physics Collision type - None and Pick Mode - None 3. Attach WayPoint component 4. In Appearance tab in add "Save" to tags - it will be needed eventually for game save/load system 5. Save as prefab called FlagWayPoint in Prefabs folder FlagWayPoint.zip Updating classes Let's update Unit class for using getting an entity as target point and save system later. Add new method to Unit.h: void goTo(shared_ptr<Entity> targetPointEntity, bool isForced = false); In Unit.cpp method implementation: void Unit::goTo(shared_ptr<Entity> targetPointEntity, bool isForced) { if (targetPointEntity) { isForcedMovement = isForced; targetPoint = targetPointEntity; goTo(); } } Add to Start() a new tag: entity->AddTag("Save"); And also in Start() after that: if (seq != -1) { //to disable pain state at end of pain animation model->skeleton->AddHook(seq, count - 1, EndPainHook, Self()); } Add this: if (health <= 0) { seq = model->FindAnimation(deathName); int count = model->CountAnimationFrames(seq); model->Animate(deathName, 1.0f, 250, ANIMATION_ONCE, count - 1); } It's needed to make Unit make lie down in death animation if it was killed when game was save. Add a little bit below after healthBar initialization nex tline: healthBar->SetScale((float)health / (float)maxHealth, 1, 1); So health bar would have correct size after a game loading. We also want to save selected state after a loading so add first line to Load and second to Save: if (properties["isSelected"].is_boolean()) isSelected = properties["isSelected"]; properties["isSelected"] = isSelected; After line this line in Kill method: ListenEvent(EVENT_TIMERTICK, removeEntityTimer, RemoveEntityCallback, Self()); Add this: //not saving if supposed to be deleted anyway entity->RemoveTag("Save"); Current Unit: Unit.zip First version of TopDownCamera had an issue that would appear after game load: Update init() method to make it update pivot after game load: if (gameCamera && !targetPivot.lock()) { gameCamera->Listen();//for positional sound gameCamera->SetSweptCollision(true);//for removing pop up effect after quick move/turn gameCamera->SetRotation(CAMERA_PITCH, gameCamera->GetRotation(true).y, gameCamera->GetRotation(true).z); auto targetPivotShared = CreatePivot(gameCamera->GetWorld()); targetPivotShared->SetPickMode(PICK_NONE); sceneWeak.lock()->AddEntity(targetPivotShared); targetPivot = targetPivotShared; } //setting position and rotation here in case of game load gameCamera->SetParent(nullptr); auto targetPivotShared = targetPivot.lock(); auto targetPosition = getCirleCenter(gameCamera->GetPosition(true), gameCamera->GetRotation(true)); targetPosition.y = 0; targetPivotShared->SetPosition(targetPosition); targetPivotShared->SetRotation(0, gameCamera->GetRotation(true).y, gameCamera->GetRotation(true).z); gameCamera->SetParent(targetPivotShared); return true; TopDownCamera.zip In StrategyController.cpp update Start to add tag for Save next to another tag line entity->AddTag("Save"); To Save method add saving selected units: properties["selectedUnits"] = {}; int index = 0; for (auto const& selectedUnitWeak : selectedUnits) { auto selectedUnit = selectedUnitWeak.lock(); if (selectedUnit) { properties["selectedUnits"][index] = selectedUnit->GetUuid(); index++; } } Add their loading to Load: selectedUnits.clear(); if (properties["selectedUnits"].is_array()) { for (int i = 0; i < properties["selectedUnits"].size(); i++) { auto unit = scene->GetEntity(properties["selectedUnits"][i]); if (unit) { selectedUnits.push_back(unit); } } } Find this line for (auto const& entityWeak : selectedUnits) { Replace a code from starting from line above this line and ended 8 lines lower with this snippet: } else if (!selectedUnits.empty()) { auto flag = LoadPrefab(camera->GetWorld(), "Prefabs/FlagWayPoint.pfb"); if (flag) { flag->SetPosition(pick.position); } for (auto const& entityWeak : selectedUnits) { auto entityUnit = entityWeak.lock(); if (entityUnit && entityUnit->GetComponent<Unit>()) { if (flag) { entityUnit->GetComponent<Unit>()->goTo(flag, true); } else { entityUnit->GetComponent<Unit>()->goTo(pick.position, true); } } } } Or just download full class: StrategyController.zip Custom Save/Load system Current official system have a lot of flaws which are listed here: Most of them will be fixed, but something like no loading entities, which were added while run time like flags in this game, may still persist. This one is resolved by loading prefabs. You can also add manual recreation for most types of entities, depends on needs, but prefabs should be enough in most cases. Game class have a lot of changes, which were made to create custom save system. Whole Game.h: #pragma once #include "UltraEngine.h" #include "Components/Player/FPSPlayer.h" using namespace UltraEngine; class Game : public Object { protected: shared_ptr<Camera> uiCamera; shared_ptr<Scene> scene; shared_ptr<FPSPlayer> fpsPlayer; shared_ptr<Widget> menuPanel; shared_ptr<Widget> gameSavedLabel; //hide gameSavedLabel on timer shared_ptr<Timer> gameSavedLabelTimer; Game(); void init(shared_ptr<Framebuffer> framebuffer, WString mapPath); //update basic entity properties - position, rotation, tags void loadEntity(shared_ptr<Entity> entity, table entityTable); void loadGame(table saveTable); void saveGame(WString saveName); static bool QuickSaveGameCallback(const UltraEngine::Event& ev, shared_ptr<UltraEngine::Object> extra); static bool HideGameSavedLabelCallback(const UltraEngine::Event& ev, shared_ptr<UltraEngine::Object> extra); public: //to show/hide game menu on Esc static bool GameMenuButtonCallback(const Event& ev, shared_ptr<Object> extra); static std::shared_ptr<Game> create(shared_ptr<Framebuffer> framebuffer, WString mapPath); //for game loading static std::shared_ptr<Game> create(shared_ptr<Framebuffer> framebuffer, table saveTable); shared_ptr<Interface> ui; shared_ptr<World> world; }; Game.cpp: #include "UltraEngine.h" #include "Game.h" #include "CustomEvents.h" Game::Game() = default; std::shared_ptr<Game> Game::create(shared_ptr<Framebuffer> framebuffer, WString mapPath) { struct Struct : public Game {}; auto instance = std::make_shared<Struct>(); instance->init(framebuffer, mapPath); return instance; } std::shared_ptr<Game> Game::create(shared_ptr<Framebuffer> framebuffer, table saveTable) { struct Struct : public Game {}; auto instance = std::make_shared<Struct>(); std::string mapName = saveTable["MapPath"]; instance->init(framebuffer, WString(mapName)); instance->loadGame(saveTable); return instance; } bool Game::GameMenuButtonCallback(const Event& ev, shared_ptr<Object> extra) { if (KEY_ESCAPE == ev.data && extra) { auto game = extra->As<Game>(); bool isHidden = game->menuPanel->GetHidden(); game->menuPanel->SetHidden(!isHidden); if (game->fpsPlayer) { //we can get a game window anywhere, but take in mind that it will return nullptr, if window is not active ;) auto window = ActiveWindow(); //checking just in case if we actually got a window if (window) { //hiding cursor when hiding a menu and vice versa window->SetCursor(isHidden ? CURSOR_DEFAULT : CURSOR_NONE); } game->fpsPlayer->doResetMousePosition = !isHidden; } //If the callback function returns false no more callbacks will be executed and no event will be added to the event queue. //to avoid double call return false; } return true; } static bool MainMenuButtonCallback(const Event& ev, shared_ptr<Object> extra) { EmitEvent(EVENT_MAIN_MENU); return true; } static bool ExitButtonCallback(const Event& ev, shared_ptr<Object> extra) { exit(0); return true; } void Game::init(shared_ptr<Framebuffer> framebuffer, WString mapPath) { world = CreateWorld(); scene = LoadMap(world, mapPath); for (auto const& entity : scene->entities) { auto foundPlayer = entity->GetComponent<FPSPlayer>(); if (foundPlayer) { fpsPlayer = foundPlayer; break; } } auto font = LoadFont("Fonts/arial.ttf"); //Create user interface for game menu auto frameSize = framebuffer->GetSize(); ui = CreateInterface(world, font, frameSize); ui->SetRenderLayers(2); ui->root->SetColor(0.0f, 0.0f, 0.0f, 0.0f); uiCamera = CreateCamera(world, PROJECTION_ORTHOGRAPHIC); uiCamera->SetPosition(float(frameSize.x) * 0.5f, float(frameSize.y) * 0.5f, 0); uiCamera->SetRenderLayers(2); uiCamera->SetClearMode(CLEAR_DEPTH); //widgets are stays without extra shared pointers because parent widet, ui->root in this case, keep them //to remove widget you should do widget->SetParent(nullptr) menuPanel = CreatePanel(frameSize.width / 2 - 150, frameSize.height / 2 - 300 / 2, 300, 250, ui->root); gameSavedLabel = CreateLabel("GAME SAVED", frameSize.width / 2 - 100, 50, 200, 30, ui->root); gameSavedLabel->SetFontScale(2.0f); gameSavedLabel->SetHidden(true); auto menuButton = CreateButton("Main menu", 50, 50, 200, 50, menuPanel); ListenEvent(EVENT_WIDGETACTION, menuButton, MainMenuButtonCallback); auto exitButton = CreateButton("Exit", 50, 150, 200, 50, menuPanel); ListenEvent(EVENT_WIDGETACTION, exitButton, ExitButtonCallback, nullptr); //we don't need game menu on screen while playing menuPanel->SetHidden(true); //and we will need it once hitting Esc button ListenEvent(EVENT_KEYUP, nullptr, GameMenuButtonCallback, Self()); //take in mind that extra param will be kept as shared_ptr in callback ^ ListenEvent(EVENT_KEYUP, nullptr, QuickSaveGameCallback, Self()); } void Game::loadEntity(shared_ptr<Entity> entity, table entityTable) { if (entityTable["position"].is_array() && entityTable["position"].size() == 3) { entity->SetPosition(entityTable["position"][0], entityTable["position"][1], entityTable["position"][2], true); } if (entityTable["rotation"].is_array() && entityTable["rotation"].size() == 3) { entity->SetRotation(entityTable["rotation"][0], entityTable["rotation"][1], entityTable["rotation"][2], true); } if (entityTable["tags"].is_array()) { for (int i = 0; i < entityTable["tags"].size(); i++) { entity->AddTag(std::string(entityTable["tags"][i])); } } } void Game::loadGame(table saveTable) { //old-new entity id vector<std::pair<String, String>> uuids; std::set<String> newEntities; //entites that was not in scene will be deleted once Components will be loaded and gets needed entities std::set<shared_ptr<Entity>> entitiesToRemoveFromScene; //iterating std::map by key (uuid) and value (entityTable) instead of pair for (auto& [uuid, entityTable] : saveTable["SavedEntities"]) { auto entity = scene->GetEntity(uuid); //load properties for saved entity that was initially on map if (entity) { loadEntity(entity, entityTable); //or if it was not we can recreate prefab at least } else if (entityTable["prefabPath"].is_string()) { //spawn saved entity that was not initially on map auto spawnedEntity = LoadPrefab(world, String(entityTable["prefabPath"])); if (!spawnedEntity) { continue; } scene->AddEntity(spawnedEntity); if (entityTable["isInScene"].is_boolean() && !entityTable["isInScene"]) { entitiesToRemoveFromScene.insert(spawnedEntity); } loadEntity(spawnedEntity, entityTable); uuids.push_back(std::pair(uuid, spawnedEntity->GetUuid())); newEntities.insert(spawnedEntity->GetUuid()); } } //delete not saved entities for (auto const& entity : world->GetTaggedEntities("Save")) { //does newEntities containes curent entity if (newEntities.find(entity->GetUuid()) != newEntities.end()) { //skip new entity continue; } //if supposed to be saved and was not due being removed when save was made then remove it from scene table entityTable = saveTable["SavedEntities"][entity->GetUuid()]; if (entityTable.empty()) { scene->RemoveEntity(entity); } } //saving table++ as a string auto saveString = String(saveTable.to_json()); //replace entities ids so component would use new ones for (auto const& [oldUuid, newUuid] : uuids) { saveString = saveString.Replace(oldUuid, newUuid); } //converting back to table++ from string saveTable = ParseJson(saveString); //Load saved data to components for (auto const& entity : world->GetTaggedEntities("Save")) { auto& entityTable = saveTable["SavedEntities"][entity->GetUuid()]; for (auto const& component : entity->components) { component->Load(entityTable, nullptr, scene, LOAD_DEFAULT, nullptr); } } //starting components now when all data is there for (auto const& entity : world->GetTaggedEntities("Save")) { for (auto const& component : entity->components) { component->Start(); } } //removing from scene entites that scene had not for (auto const& entity : entitiesToRemoveFromScene) { scene->RemoveEntity(entity); } } void Game::saveGame(WString saveName) { table saveTable; //saving map path to use it later to load correct map saveTable["MapPath"] = RelativePath(scene->path).ToUtf8String(); saveTable["SavedEntities"] = {}; for (auto const& entity : world->GetTaggedEntities("Save")) { table entityTable; for (auto const& component : entity->components) { component->Save(entityTable, nullptr, scene, SAVE_DEFAULT, nullptr); } //just to make save file more readable if (!entity->name.empty()) { entityTable["name"] = entity->name.ToUtf8String(); } //saving position and rotation of entity to restore them in Load auto position = entity->GetPosition(true); entityTable["position"] = {}; entityTable["position"][0] = position.x; entityTable["position"][1] = position.y; entityTable["position"][2] = position.z; auto rotation = entity->GetRotation(true); entityTable["rotation"] = {}; entityTable["rotation"][0] = rotation.x; entityTable["rotation"][1] = rotation.y; entityTable["rotation"][2] = rotation.z; entityTable["tags"] = {}; //to remove it from scene later once everything restored inc case if only components supposed to keep this entity entityTable["isInScene"] = scene->GetEntity(entity->GetUuid()) ? true : false; int tagIndex = 0; for (auto& tag : entity->tags) { entityTable["tags"][tagIndex] = tag.ToUtf8String(); tagIndex++; } //save prefab path to be able restore entity if it was added to scene later as prefab auto prefab = entity->GetPrefab(); if (prefab) { entityTable["prefabPath"] = RelativePath(prefab->GetPath()).ToUtf8String(); } //using entity id as key for its properties saveTable["SavedEntities"][entity->GetUuid()] = entityTable; } SaveTable(saveTable, saveName); //showing "Game Saved" labl for 2 seconds gameSavedLabel->SetHidden(false); gameSavedLabelTimer = UltraEngine::CreateTimer(2000); ListenEvent(EVENT_TIMERTICK, gameSavedLabelTimer, HideGameSavedLabelCallback, Self()); } bool Game::QuickSaveGameCallback(const UltraEngine::Event& ev, shared_ptr<UltraEngine::Object> extra) { if (KEY_F5 == ev.data && extra) { auto game = extra->As<Game>(); game->saveGame("QuickSave.save"); } return true; } bool Game::HideGameSavedLabelCallback(const UltraEngine::Event& ev, shared_ptr<UltraEngine::Object> extra) { if (extra && extra->As<Game>()) { auto game = extra->As<Game>(); game->gameSavedLabel->SetHidden(true); game->gameSavedLabelTimer->Stop(); game->gameSavedLabelTimer = nullptr; } return false; } Game.zip Add new functions to main.cpp: void LoadGame(WString savePath) { table saveTable = LoadTable(savePath); if (saveTable == nullptr) { return; } menu.reset(); //to avoid random error in World::Update() game.reset(); loadingWorld->Render(framebuffer); if (currentWorld) currentWorld.reset(); if (currentUi) currentUi.reset(); game = Game::create(framebuffer, saveTable); currentWorld = game->world; currentUi = game->ui; } bool QuickLoadGameCallback(const Event& event, shared_ptr<Object> extra) { if (KEY_F9 == event.data) { LoadGame("QuickSave.save"); } return true; } And add ListenEvent next to other similar lines: ListenEvent(EVENT_KEYUP, nullptr, QuickLoadGameCallback); main.zip Now you can save and load game with F5 and F9 respectively Making Particle Effect Download this blood texture made by TobiasM: https://opengameart.org/content/blood-splat It's not optimal for particle emitter, but good enough for learning purpose. Put into Materials\Particles folder and convert to .dds in the Editor. Generate a material from dds texture. Add to any scene particle emitter: In Appearance tab of Particles choose a recently made material. Now we able to see result when we will change this material properties. Open material: 1. Choose Lambertian shader family - it has better perfomance than PBR and it can make a difference eventually with many particle emitters which produce dozens particles. 2. In Blend tab check Transparent checkbox. Now it particles would look like that: Open Particles tab of Particle Emitter in entity properties: 1. Particle count - how many particles of this emitter exist at same time, left it as 50 2. Emission shape - shape of area where particles will be spawn 3. Emission Area - size of this area (width, height, depth). If 0 then particles spawns at same point. Left it as 0. 4. Burst frequency - how often particles spawns. Will be 800 5. Burst count - how much particles spawns in every burst. Enter 50 6. Colors - how particle looks at spawn and despawn 7. Velocity - direction and speed - make it 0 8. Acceleration - same but it's accel 9. Size - width and height of particle. No depth, because every particle is just a billboard sprite. Makie it 0.2/0.2 10. Radius. First value - relative size (i.e. 1.0 is 100%) for particle at start and second value is size when particles despawns. Value - 1.0/1.0 11. Turbulence - random speed in random direction. With 0 velocity it will make particles move in different directions. Make it 300.0 12. Rotation speed - how fast particle rotate. Make it 0 Download ParticleEffect to ParticleEffect component to Components\Appearance folder. Include into project and add to ComponentSystem as usual. Add this component to an emitter. It's needed to make blood hit effect to dissapear before it would burst again. Temporary checkbox on and Duration 700. Name emitter as BloodHit and save it as prefab "BloodHit.phb" in Prefabs folder Let's use it for units. In Unit.cpp in Damage method add next code somewhere above auto now = world->GetTime(); auto bloodHit = LoadPrefab(world, "Prefabs/BloodHit.pfb"); auto entity = GetEntity(); if (bloodHit) { auto bloodHitPosition = entity->GetPosition(true); //to lift it up to entity center bloodHitPosition.y = bloodHitPosition.y + entity->GetBounds(BOUNDS_GLOBAL).size.height / 2; bloodHit->SetPosition(bloodHitPosition, true); //prefabs component are not started after its load so will do it manually for (auto const& component : bloodHit->components) { component->Start(); } } Making decal Copy blood_splat.dds texture to Materials\Decals folder. Generate a new material from it and name BloodPuddle. Edit this material: 1. Keep PBR as shader family. 2. In Blend tab check Transparent checkbox. 3. In Surface Metalness 0, and Roughness 50. Add new decal to scene - 256x256 size with 16 height (or 250x250 and 20) Choose new material in appearance. Save this decal as prefab wiht BloodPuddle name. We will spawn this decal after unit death. In Unit::Kill() method add above auto model = entity->As<Model>(); line following code: auto scene = sceneWeak.lock(); auto bloodPuddle = LoadPrefab(entity->GetWorld(), "Prefabs/BloodPuddle.pfb"); if (bloodPuddle && scene) { auto bloodHitPosition = entity->GetPosition(true); bloodPuddle->SetPosition(bloodHitPosition, true); //to keep it scene->AddEntity(bloodPuddle); } Pain sounds Download sounds for Warrok: https://opengameart.org/node/132772 And for Paladin: https://opengameart.org/content/pain-sounds-by-emopreben In Sounds folder create Units subfolder and put there bear_02.ogg from 1st pack and "Painsounds v2 - Track 5 - Urggh.ogg" from 2nd Add to properties in Unit.json: { "label": "Pain sound", "name": "painSound", "value": "", "filecategory": "SOUND" }, New member in Unit.h header: shared_ptr<Sound> painSound; In Load method in Unit.cpp will load attached in the Editor sound: if (properties["painSound"].is_string()) painSound = LoadSound(std::string(properties["painSound"])); And play this sound in Damage() above auto now = world->GetTime(); line: if (painSound) { entity->EmitSound(painSound); } Unit class: FinalUnit.zip Now open Paladin prefab and add their pain sound in Unit component. Same for Warror. Delete units from Strategy map and add them again from prefabs. Remember to break prefab after that. strategy.zip Cache In debug mode you could notice micro-freezes when prefabs are loaded every time when it's not at the map. Thats because unload from memory an assets that have no shared_pointer to them. We can fix by making cache in Game class. Add to Game.h new member: vector<shared_ptr<Entity>> prefabCache; And at the end of init method in Game.cpp: //caching prefabCache.push_back(LoadPrefab(world, "Prefabs/BloodPuddle.pfb")); prefabCache.push_back(LoadPrefab(world, "Prefabs/BloodHit.pfb")); prefabCache.push_back(LoadPrefab(world, "Prefabs/FlagWayPoint.pfb")); for (auto const& prefab : prefabCache) { if (prefab) { prefab->SetHidden(true); } } GameCache.zip Now Debug mode will be smoother since assets loaded on game load and not in run time when components uses them. In Release mode it would be also necessary to do for heavy assets. Final version on github: https://github.com/Dreikblack/CppTutorialProject/tree/4-save-load-particles-decals
-
Hello everyone. I'm working on a new demo for Twisted Minds and wanted to use decals for drawing a path on the floor. (See the screenshot below.) However, whenever the decal entity left the screen, the whole decal disappears. Here is a little video showing this problem. 2021-05-15 15-25-32.mp4 As you can see on the video, I set up the view range to infinite just to test and occlusion culling is disabled. I don't know what I'm missing, if I'm missing something, or if I'm not using decals correctly. Could anyone lighten up my lantern, please? Kindly, Ttiki.
-
I'm planning to use LOT'S of decals in my project. I will use them to for signs, labels, dirt etc. My question is, before I go ahead, do they eat lot of processing power? What if I have a prefab with ten decals and then I have that prefab used in 20 different places? Even hundreds? Well, yes best way is to test it But wanted to ask if it is just plain stupid thing to do one should not even try. Another question, can we have emissive decals? I created "Emissive decal" by applying it over emissive surface (needed to reverse the decal though), but although it works, It is not usable in many scenarios. And yet another question, how would you create a piece of paper with text on it? I planned to use decal for that, but it obviously looks like painted to the surface, not separate piece of paper.
-
Running into some difficulties with blurry terrain texture i was trying to solve them using decals. I painted a texture, added an alpa channel, saved it as a tga file. imported into leadwerks, created a normal map, made it a material... same with png format... i copied all the settings from the engine decals... besides the fact the material got all red when Z-sort was unchecked down below is the result... i can run into the cube and see the decal on its walls but not painted onto the terrain. my question now is what did you guys do to get working decals or the other way round what did i do wrong?
-
Not something I need but I mention it as it is something that is supposed to work well with deferred rendering engines.... This is something I came across while reading the docs for Decal Machine (https://machin3.io/DECALmachine/) a low cost Blender Addon that I think is gaining a lot of attention right now. From the Decal Machine docs https://machin3.io/DECALmachine/docs/installation/index.html#instructions Currently I am hoping that I can use Decal Machine to bake out normal maps and but I'm not sure it is supported yet (development is moving quite fast though). I think there is an imminent Unreal exporter but I'm not sure if that uses this geometry rendering for decals or not. Thought I'd mention it in case it is something that Leadwerks is suited to implementing fast (at some point in the future) if it suits the architecture well.
-
Hi Guys, So I have come up with a specific idea in my mind for an event that is going to happen in my game but I need to know a few things before I attempt to try and create it. Basically I want footprints to start walking across my floor in my map (wet footprints to be specific) but I want this to be revealed in front of the player as if there is an invisible person walking in realtime and all they can see is the footprints of the water on the floor. I was wondering at first how to achieve this then I thought the decal system in Leadwerks may just make this work. My questions are: Is there a way to reveal decals in real time (some sort of way to mask them and then move that mask, just a thought). If decals isn't the answer, what tools in Leadwerks would be able to achieve something like this? My first thought would be to get a character and put an invisible material on that character and use decals for the footprints and somehow reveal them that way. Right now all I have is idea's but nothing seems to make me think that it would work haha. Thanks.
-
Using the latest shaders, if place a decal on the floor at the map origin, the decal will appear fine. If you move it around, you'll see it move with the decal placement plus a little bit of distance from the map origin. If you place it too far from the map origin, the decal will not appear. The old shader does not do this.
-
Place a decal (10x10x10, wound,mat) on a terrain slope (08-Terrain.map) and you will get holes in the decal.