-
Posts
2,600 -
Joined
-
Last visited
Content Type
Blogs
Forums
Store
Gallery
Videos
Downloads
Everything posted by reepblue
-
It doesn't take a Font pointer. Make sure your parameters are a string for the path and a number for the font size.
-
This is what I came up with in the meantime. I use 4 textures (2 diffuse, 2 emission) and the shader swaps what to draw based on the scale of the decal. I'd like to lower the texture budget, but if I can't get something better, this should be fine. #version 400 #define BFN_ENABLED 1 //Uniforms uniform vec2 buffersize; uniform vec4 materialcolorspecular; uniform samplerCube texture15; // Horizontal Textures: uniform sampler2D texture0; uniform sampler2D texture4; // Vertical Textures: uniform sampler2D texture1; uniform sampler2D texture2;// normal map //MSAA textures uniform sampler2DMS texture6;// depth uniform sampler2DMS texture7;// normal uniform bool isbackbuffer; uniform mat4 projectioncameramatrix; uniform vec3 cameraposition; uniform vec4 materialcolordiffuse; uniform int RenderMode; //Inputs in float ex_selectionstate; in vec4 vColor; in mat4 inversemodelmatrix; in vec3 modelposition; in mat3 nmat; in vec2 vTexCoords0; //Outputs out vec4 fragData0; out vec4 fragData1; out vec4 fragData2; out vec4 fragData3; uniform float currenttime; float depthToPosition(in float depth, in vec2 depthrange) { return depthrange.x / (depthrange.y - depth * (depthrange.y - depthrange.x)) * depthrange.y; } vec4 ScreenPositionToWorldPosition(in vec2 texCoord) { float x = (texCoord.s / buffersize.x - 0.5) * 2.0; float y = (texCoord.t / buffersize.y - 0.5) * 2.0; float z; z = texelFetch(texture6, ivec2(texCoord),gl_SampleID).r; z = z / 0.5 - 1.0; vec4 posProj = vec4(x,y,z,1.0); vec4 posView = inverse(projectioncameramatrix) * posProj; posView /= posView.w; return posView; } int getMajorAxis(in vec3 v) { vec3 b = abs(v); if (b.x>b.y) { if (b.x>b.z) { return 0; } else { return 2; } } else { if (b.y>b.z) { return 1; } else { return 2; } } } void main(void) { vec3 normal; vec4 normaldata; float specular; float depth; vec3 screencoord; vec4 worldcoord; vec4 worldpos; vec2 tc; vec4 outcolor = vec4(0,0,0,0); vec4 emission = vec4(0,0,0,0); vec3 ex_normal; vec3 ex_binormal; vec3 ex_tangent; vec3 blendednormal; vec3 screennormal; vec3 worldnormal; float blendedspecular; ivec2 icoord = ivec2(gl_FragCoord.xy); if (isbackbuffer) icoord.y = int(buffersize.y) - icoord.y; depth = texelFetch(texture6, icoord,gl_SampleID).r; worldcoord = vec4(gl_FragCoord.x/buffersize.x,-gl_FragCoord.y/buffersize.y,depth,gl_FragCoord.w); worldcoord = inverse(projectioncameramatrix)*worldcoord; screencoord=worldcoord.xyz; worldpos = ScreenPositionToWorldPosition(gl_FragCoord.xy); screencoord = (inversemodelmatrix * worldpos).xyz; if (screencoord.x<-0.5) discard; if (screencoord.x>0.5) discard; if (screencoord.y<-0.5) discard; if (screencoord.y>0.5) discard; if (screencoord.z<-0.5) discard; if (screencoord.z>0.5) discard; normaldata = texelFetch(texture7, icoord,gl_SampleID); // Get The Entity's Scale vec2 entity_scale; entity_scale.x = length(nmat[0].xyz); entity_scale.y = length(nmat[2].xyz); int materialflags = int(normaldata.a * 255.0 + 0.5); //Filter bool draw = false; if ((1 & RenderMode)!=0)//Brushes { if ((4 & materialflags)!=0) draw=true; } if ((2 & RenderMode)!=0)//Models { if ((8 & materialflags)!=0) draw=true; } if ((4 & RenderMode)!=0)//Terrain { if ((16 & materialflags)!=0) draw=true; } if (!draw) discard; screennormal = normalize(normaldata.xyz*2.0-1.0); worldnormal = inverse(nmat) * screennormal; // Only show up on floors/ceilings. if (getMajorAxis(worldnormal) != 1) discard; tc=vec2(sign(worldnormal.y)*1.0,-1.0)*screencoord.xz-0.5; vec2 scale = entity_scale; bool vertical = false; if (scale.y > scale.x) { // MArk as vertical: vertical = true; // Correct the scale. scale.x = entity_scale.x * (0.78125 * 4); scale.y = entity_scale.y * 0.78125; } else { // Correct the scale. scale.x = entity_scale.x * 0.78125; scale.y = entity_scale.y * (0.78125 * 4); } tc.x = tc.x * scale.x; tc.y = tc.y * scale.y; tc = mod(tc,1.0); if (vertical) { outcolor = texture(texture1,tc); emission = texture(texture2,tc); } else { outcolor = texture(texture0,tc); emission = texture(texture4,tc); } fragData0 = outcolor * materialcolordiffuse; if (ex_selectionstate>0.0) { fragData0.xyz = (fragData0.xyz + vec3(1,0,0)) * 0.5; } emission *= vColor; fragData1 = normaldata; fragData2 = vec4(emission.rgb,fragData0.a); } Result:
-
I ran into this problem myself. I'm trying to prevent making another texture to accommodate this so I decided to try to make a custom shader to handle this. I have rotation code that works well for my effects but it's all distorted and weird if the Z value is greater than X's. I'm hoping to get assistance with this. I was using current time to test the rotation. #version 400 #define BFN_ENABLED 1 //Uniforms uniform vec2 buffersize; uniform vec4 materialcolorspecular; uniform samplerCube texture15; uniform sampler2D texture0; uniform sampler2D texture4;// normal map //MSAA textures uniform sampler2DMS texture6;// depth uniform sampler2DMS texture7;// normal uniform bool isbackbuffer; uniform mat4 projectioncameramatrix; uniform vec3 cameraposition; uniform vec4 materialcolordiffuse; uniform int RenderMode; //Inputs in float ex_selectionstate; in vec4 vColor; in mat4 inversemodelmatrix; in vec3 modelposition; in mat3 nmat; in vec2 vTexCoords0; //Outputs out vec4 fragData0; out vec4 fragData1; out vec4 fragData2; out vec4 fragData3; uniform float currenttime; float depthToPosition(in float depth, in vec2 depthrange) { return depthrange.x / (depthrange.y - depth * (depthrange.y - depthrange.x)) * depthrange.y; } vec4 ScreenPositionToWorldPosition(in vec2 texCoord) { float x = (texCoord.s / buffersize.x - 0.5) * 2.0; float y = (texCoord.t / buffersize.y - 0.5) * 2.0; float z; z = texelFetch(texture6, ivec2(texCoord),gl_SampleID).r; z = z / 0.5 - 1.0; vec4 posProj = vec4(x,y,z,1.0); vec4 posView = inverse(projectioncameramatrix) * posProj; posView /= posView.w; return posView; } int getMajorAxis(in vec3 v) { vec3 b = abs(v); if (b.x>b.y) { if (b.x>b.z) { return 0; } else { return 2; } } else { if (b.y>b.z) { return 1; } else { return 2; } } } void main(void) { vec3 normal; vec4 normaldata; float specular; float depth; vec3 screencoord; vec4 worldcoord; vec4 worldpos; vec2 tc; vec4 emission = vec4(0,0,0,0); vec3 ex_normal; vec3 ex_binormal; vec3 ex_tangent; vec3 blendednormal; vec3 screennormal; vec3 worldnormal; float blendedspecular; ivec2 icoord = ivec2(gl_FragCoord.xy); if (isbackbuffer) icoord.y = int(buffersize.y) - icoord.y; depth = texelFetch(texture6, icoord,gl_SampleID).r; worldcoord = vec4(gl_FragCoord.x/buffersize.x,-gl_FragCoord.y/buffersize.y,depth,gl_FragCoord.w); worldcoord = inverse(projectioncameramatrix)*worldcoord; screencoord=worldcoord.xyz; worldpos = ScreenPositionToWorldPosition(gl_FragCoord.xy); screencoord = (inversemodelmatrix * worldpos).xyz; if (screencoord.x<-0.5) discard; if (screencoord.x>0.5) discard; if (screencoord.y<-0.5) discard; if (screencoord.y>0.5) discard; if (screencoord.z<-0.5) discard; if (screencoord.z>0.5) discard; normaldata = texelFetch(texture7, icoord,gl_SampleID); // Get The Entity's Scale vec2 entity_scale; entity_scale.x = length(nmat[0].xyz); entity_scale.y = length(nmat[2].xyz); // Next, Rotate it if the Z value is greater than X // If Z is greater than X, it's going vertical! if (entity_scale.y > entity_scale.x) { //find center vec2 uv = (vTexCoords0-0.5); //rotate Front float sinx = sin ( -0.002 * currenttime ); float cosx = cos ( -0.002 * currenttime ); float siny = sin ( -0.002 * currenttime ); mat2 rotmatrix = mat2( cosx, -sinx, siny, cosx); //rotuv = rotmatrix*uv+0.5; tc +=rotmatrix*uv+0.5; } int materialflags = int(normaldata.a * 255.0 + 0.5); //Filter bool draw = false; if ((1 & RenderMode)!=0)//Brushes { if ((4 & materialflags)!=0) draw=true; } if ((2 & RenderMode)!=0)//Models { if ((8 & materialflags)!=0) draw=true; } if ((4 & RenderMode)!=0)//Terrain { if ((16 & materialflags)!=0) draw=true; } if (!draw) discard; screennormal = normalize(normaldata.xyz*2.0-1.0); worldnormal = inverse(nmat) * screennormal; // Only show up on floors/ceilings. if (getMajorAxis(worldnormal) != 1) discard; tc+=vec2(sign(worldnormal.y)*1.0,-1.0)*screencoord.xz-0.5; // Correct the scale. vec2 scale; scale.x = entity_scale.x * 0.78125; scale.y = entity_scale.y * (0.78125 * 4); tc.x = tc.x * scale.x; tc.y = tc.y * scale.y; //tc = mod(tc,1.0); fragData0 = texture(texture0,tc) * materialcolordiffuse; if (ex_selectionstate>0.0) { fragData0.xyz = (fragData0.xyz + vec3(1,0,0)) * 0.5; } emission = texture(texture4,tc) * vColor; fragData1 = normaldata; fragData2 = vec4(emission.rgb,fragData0.a); }
-
Thanks for taking a look at this.
-
Does a project generated by the project manager compile via the XCode project? You must be missing a macro or compile flag in your cmake file. I haven't done a test compile with my M1 so the only thing I can recommend is the project manager + XCode for now.
-
I was dumb enough to delete a source file for a texture and today I decided to write an app to decompile the tex file to a TGA. The old Plugin DLL file didn't work, so I pulled the PluginSDK, recompiled the plugin myself and it compiled with no issues after one commented line. Now, I'm getting an exception thrown at on the first MemReader::Read call. Here's a copy of my modded main file. #include "../SDK/MemWriter.h" #include "DLLExports.h" #include "VKFormat.h" #include <algorithm> using namespace GMFSDK; using namespace std; MemWriter* writer = nullptr; std::vector<void*> allocedmem; //DLL entry point function #ifdef _WIN32 BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: writer = nullptr; break; case DLL_PROCESS_DETACH: Cleanup(); break; } return TRUE; } #endif #ifdef __APPLE__ // Initializer. __attribute__((constructor)) static void initializer(void) { FreeImage_Initialise(); } // Finalizer. __attribute__((destructor)) static void finalizer(void) { FreeImage_DeInitialise(); } #endif //Returns all plugin information in a JSON string int GetPluginInfo(unsigned char* cs, int maxsize) { std::string s = "{" "\"plugin\":{" "\"title\":\"FreeImage texture loader.\"," "\"description\":\"Load textures from common image file formats.\"," "\"author\":\"Josh Klint\"," "\"threadSafe\":true," "\"url\":\"www.turboengine.com\"," "\"extension\": [\"bmp\",\"jpg\",\"jpeg\",\"tga\",\"pcx\",\"tga\",\"gif\"]," "\"saveextensions\": [\"bmp\",\"jpg\",\"jpeg\",\"tga\",\"tga\"]," "\"filter\": [\"Portable Network Graphics (*.png):png\",\"Windows Bitmap (*bmp):bmp\"]" "}" "}"; #ifndef _WIN64 if (maxsize > 0) memcpy(cs, s.c_str(), min(maxsize, s.length())); #else if (maxsize > 0) memcpy(cs, s.c_str(), fmin(maxsize, s.length())); #endif return s.length(); } void Cleanup() { delete writer; writer = nullptr; for (auto levelData : allocedmem) { free(levelData); } allocedmem.clear(); } const int LE_TEXTURE_RGBA8 = 1; const int LE_TEXTURE_RGB8 = 7; const int LE_TEXTURE_RGBADXTC1 = 4; const int LE_TEXTURE_RGBDXTC1 = 8; const int LE_TEXTURE_RGBADXTC3 = 5; const int LE_TEXTURE_RGBADXTC5 = 6; const int LE_TEXTURE_RGBADXT5N = 20; const int LE_TEXTURE_1D = 1; const int LE_TEXTURE_2D = 2; const int LE_TEXTURE_3D = 3; const int LE_TEXTURE_CUBEMAP = 4; void* LoadTexture(void* data, uint64_t size, wchar_t* cpath, uint64_t& returnsize) { MemReader reader(data, size); int tag; reader.Read(&tag,4); if (tag != 5784916) return nullptr; // "TEX" int version; reader.Read(&version); if (version != 1) return nullptr; TextureInfo texinfo; int format; reader.Read(&format); switch (format) { case LE_TEXTURE_RGB8: texinfo.format = VK_FORMAT_R8G8B8_UNORM; break; case LE_TEXTURE_RGBA8: texinfo.format = VK_FORMAT_R8G8B8A8_UNORM; break; case LE_TEXTURE_RGBDXTC1: texinfo.format = VK_FORMAT_BC1_RGB_UNORM_BLOCK; break; case LE_TEXTURE_RGBADXTC1: texinfo.format = VK_FORMAT_BC1_RGBA_UNORM_BLOCK; break; case LE_TEXTURE_RGBADXTC3: texinfo.format = VK_FORMAT_BC2_UNORM_BLOCK; break; //case LE_TEXTURE_RGBADXT5N: // texinfo.swizzle_red_alpha = 1; case LE_TEXTURE_RGBADXTC5: texinfo.format = VK_FORMAT_BC3_UNORM_BLOCK; break; default: printf("Unknown texture format."); return nullptr; break; } int target; reader.Read(&target); if (target == LE_TEXTURE_1D) { texinfo.target = 1; } else if (target == LE_TEXTURE_2D) { texinfo.target = 2; } else if (target == LE_TEXTURE_3D) { texinfo.target = 3; } else if (target == LE_TEXTURE_CUBEMAP) { texinfo.target = 4; texinfo.faces = 6; } else { printf("Unknown texture target."); return nullptr; } reader.Read(&texinfo.width); reader.Read(&texinfo.height); reader.Read(&texinfo.depth); reader.Read(&texinfo.filter); reader.Read(&texinfo.clampu); reader.Read(&texinfo.clampv); reader.Read(&texinfo.clampw); reader.Read(&texinfo.frames); reader.Read(&texinfo.mipmaps); writer = new MemWriter(); writer->Write(&texinfo); int mw, mh, sz; for (int i = 0; i < texinfo.mipmaps; i++) { reader.Read(&mw); reader.Read(&mh); reader.Read(&sz); for (int f = 0; f < texinfo.faces; ++f) { void* memblock = malloc(sz); if (memblock == nullptr) { printf("Error: Failed to allocate memory of size %i.\n", sz); return nullptr; } allocedmem.push_back(memblock); reader.Read(memblock, sz); writer->Write(&memblock, sizeof(void*)); } } returnsize = writer->Size(); return writer->data(); }
-
Ensure the model has a shape assigned to it otherwise the collision events will not work.
- 1 reply
-
- 1
-
No, it quietly closed after hanging for 10 seconds.
-
Doesn't run on AMD RX480.
-
Is the light set to Dynamic + Buffered? Static shadows don't update so it doesn't chew up your performance and for it to grab your attention.
-
Back in the summer of 2017, I started experimenting with the idea of a puzzle game by placing launch pads on surfaces. The first build was very sloppy, and there wasn't any wall launching at this time. This build however was the first step of the experimenting process. Only one map was ever made and I just reused old Vectronic assets. I shelved this concept until Winter of 2020 asI developed an itch to work on something and I wanted to get familiar with GIMP. This build again only had one main map along with other sample ideas but it was just another demo. Cyclone Prototype - Work in Progress - Ultra Engine Community - Game Engine for VR Although I really liked the direction of the project, I didn't develop further on that iteration after that March, but I did show it to people to get thoughts which were mostly positive. At this time, I wanted to wait until the Ultra Engine shipped to revisit the concept yet again, but eventually I got bored of waiting and figured I'd have to spend a lot of time learning the new systems Instead of using the almost seven years of working with Leadwerks. The plan was to get a small game out within a year. I set my scope to be a simple Portal clone with 10 maps. Anything else I probably would have got overwhelmed and bailed out like I did many times before with other projects. I first started working on the foundation code base. I came up with the idea of a Stage Class with it handling all the Actors. The Stage class handles time, map loading, transitions, temp decals, and is responsible for attaching actors to the entitles in the editor via a Lua script. It's also is in-charge of ensuring that a Camera is present at all times. After every map load, a camera is created in which something like a Player actor would point to as it's Camera. It all works very nicely. //========= Copyright Reep Softworks, All rights reserved. ============// // // Purpose: // //=====================================================================// #ifndef STAGE_H #define STAGE_H #if defined( _WIN32 ) #pragma once #endif #include "pch.h" enum StageEvent { /// <summary> // NULL; Use this for the main menu. This is the default. /// </summary> STAGE_EVENTNULL = 0, /// <summary> // Intermission; the event(s) between plays. /// </summary> STAGE_EVENTINTERMISSION, /// <summary> // Transition; Intermission between scenes. /// </summary> STAGE_EVENTTRANSITION, /// <summary> // Play; In-Game /// </summary> STAGE_EVENTPLAY }; struct TransitionData { std::string szMapPath; std::string szLandmarkName; Leadwerks::Entity* pLandmarkEntity; Leadwerks::Vec3 vLandmarkDistance; Leadwerks::Vec3 vCamRot; Leadwerks::Quat qCamQuat; Leadwerks::Vec3 vVelo; void Clear() { szMapPath = ""; szLandmarkName = ""; vLandmarkDistance = Leadwerks::Vec3(); vCamRot = Leadwerks::Vec3(); qCamQuat = Leadwerks::Quat(); vVelo = Leadwerks::Vec3(); pLandmarkEntity = NULL; } }; class StageActor; class GameMenu; class Stage : public Leadwerks::Object { protected: Leadwerks::World* m_pWorld; Leadwerks::Camera* m_pCamera; Leadwerks::Context* m_pFramebuffer; GameMenu* m_pMenu; StageEvent m_hEvent; bool m_bVSync; int m_intFrameLimit; std::string m_szCurrentSceneName; std::string m_szPreviousSceneName; //std::string m_szNextSceneName; bool m_bShowStats; Leadwerks::Vec3 m_vGravity; uint8_t m_intPhysSteps; TransitionData m_hTransitionData; std::vector<StageActor*> m_vActors; void ClearScene(const bool bForce = true); bool LoadSceneFile(const std::string& pszFilePath, const bool bForce = true); void SetStageEvent(const StageEvent& hEvent, const bool bFireFunction = true); void PreTransition(); void PostTransition(); GameMenu* GetGameMenu(); public: Stage() {}; Stage(Leadwerks::Window* pWindow); virtual ~Stage(); // Time: void Pause(const bool bFireOutput = true); void Resume(const bool bFireOutput = true); const bool Paused(); uint64_t GetTime(); const bool TogglePause(const bool bHandleMouse = false); void Update(); void Clear(); bool SafeLoadSceneFile(const std::string& pszFilePath); StageEvent GetCurrentEvent(); Leadwerks::Entity* FindEntity(const std::string& pszName); StageActor* FindActor(const std::string& pszName); Leadwerks::Decal* CreateTempDecal(Leadwerks::Material* pMaterial); //std::string GetCurrentScene(); const bool InPlay(); void Reload(); void DrawStats(const bool bMode); void ToggleStats(); const bool ConsoleShowing(); void SetPhysSteps(const uint8_t iSteps); void SetVSync(const bool bState); Leadwerks::World* GetWorld(); Leadwerks::Camera* GetCamera(); Leadwerks::Context* GetFrameBuffer(); Leadwerks::Widget* GetGameMenuHUD(); void Transition(const std::string& pszFilePath, Leadwerks::Entity* pLandmark); void ShowHUD(); void HideHUD(); //Virtual functions: virtual void Start() {}; virtual void OnUpdate() {}; virtual void OnTick() {}; virtual void PostRender(Leadwerks::Context* pFrameBuffer) {}; virtual void OnNoPlay() {}; virtual void OnPostLoad(Leadwerks::Entity* pEntity) {}; virtual void OnIntermission() {}; virtual void OnTransition() {}; virtual void OnPlay() {}; virtual void OnProcessEvent(const Leadwerks::Event iEvent) {}; virtual void OnPaused() {}; virtual void OnResume() {}; virtual void ScheduleRestart() {}; static Stage* Create(Leadwerks::Window* pWindow); friend class StageActor; }; extern Stage* ActiveStage; extern Stage* CreateStage(Leadwerks::Window* pWindow); #endif // STAGE_H The first few months of development of this system still crashed randomly as I was hard loading the map instead of checking if a string was not empty every frame like how it's set up in the MyGame example. I went back to that approach and the crashing stopped. This did cause very hard to find bug which caused random restarts in then Release build. The issue ended up being that the string I was checking wasn't declared as empty. With a great foundation by February 2021 , I went to getting the Cyclone stuff online. I mostly copied from the 2020 build but it was good enough to get started. A quick model later, and I had my item dropper in game. https://cdn.discordapp.com/attachments/226834351982247936/815726095198322698/unknown.png The item dropper was the first item I had to rethink how physics objects would work. The original idea was to have the boxes be an actor that made the impact sounds and other effects when I got to it. However, the entity's actor doesn't get copied when it's instanced. The solution was to have the item dropper create a the box model, and after it instances the model, it also assigns a collision hook to that model. I also apply modifications to the box friction and dampening. //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- uint64_t BoxImpactNoiseLastSoundTime; void PuzzleBoxCollisionHook(Entity* entity, Entity* entity2, const Vec3& position, const Vec3& normal, const float speed) { auto self = entity; auto target = entity2; if (self->GetWorld()->GetWaterMode()) { if (self->GetPosition().y < self->GetWorld()->GetWaterHeight()) { DMsg("Puzzlebox below water level. Deleting."); ReleaseGamePlayObject(self); return; } } float fixedSpeed = __FixSpeed(speed); float flSoftThreshhold = 1.5f; float flHardThreshhold = 4.0f; long intMaxFrequency = 300; if (fixedSpeed > flSoftThreshhold) { int collisiontype = target->GetCollisionType(); if (collisiontype == COLLISION_PROP || collisiontype == COLLISION_SCENE) { long t = Leadwerks::Time::GetCurrent(); if (t - BoxImpactNoiseLastSoundTime > intMaxFrequency) { BoxImpactNoiseLastSoundTime = t; if (fixedSpeed > flHardThreshhold) { // HARD self->EmitSound(GetImpactHardSound(), 20.0f, 0.5f, 1.0f, false); } else { // SOFT self->EmitSound(GetImpactSoftSound(), 20.0f, 0.5f, 1.0f, false); } } } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- Leadwerks::Model* CreatePuzzleBox(const bool bAttactHook) { auto mdl = Model::Load(PATH_PUZZLEBOX_MDL); ApplyBoxSettings(mdl); if (bAttactHook) mdl->AddHook(Entity::CollisionHook, (void*)PuzzleBoxCollisionHook); return mdl; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- Leadwerks::Model* SpawnPuzzleBox(Leadwerks::Model* ModelReference, const Vec3& vPosition) { if (ModelReference == NULL) { ModelReference = CreatePuzzleBox(); } auto newitem = (Model*)ModelReference->Instance(); newitem->SetPosition(vPosition); ApplyBoxSettings(newitem); newitem->AddHook(Entity::CollisionHook, (void*)PuzzleBoxCollisionHook); newitem->Show(); return newitem; } Although adding hooks to entities isn't really considered official, this is only one case out of many that I do this. It's really nice to write a quick function without creating and assigning an actor. The item dropper got improvements down the line to fix some physics issues. (I made the dropper very slippery so there was no friction and it stopped acting weird.) The months rolled on and I kept chipping away at the project. My maps count was increasing every month but there something really bothering me. It was the fly movement. In Cyclone, I wanted a way for the player to launch themselves across rooms and deadly pits. I originally just applied velocity to the player character but the results felt terrible. I noticed that the wall launch was smoother if you held the button of the direction you were flying down. This gave me a nice arch instead of a sharp line. I ended up completely rewriting my player code from the ground up. Doing so allowed me to break apart what my Player actually was. First. there's the base Player class. This class alone will give you a spectator camera with input controls. Everything regarding the camera is defined in this base class. I also spent the time to work how HUD hints would work. The HUD uses the Leadwerks GUI system but for the HUD, I kept it to simple image drawing on the framebuffer. The base class also controls ambient sound, although it's still a work in progress. Base Player Improvements - Work in Progress - Ultra Engine Community - Game Engine for VR //========= Copyright Reep Softworks, All rights reserved. ============// // // Purpose: // //=====================================================================// #ifndef PLAYERACTOR_H #define PLAYERACTOR_H #if defined( _WIN32 ) #pragma once #endif #include "pch.h" #include "Input.h" #include "../Classes/StageActor.h" enum CharacterMoveState { CHARACTERSTATE_IDLE = 0, CHARACTERSTATE_WALKING, CHARACTERSTATE_JUMPING, CHARACTERSTATE_FALLING, CHARACTERSTATE_FLYING }; enum ScreenEffect { SCREENEFFECT_NONE = 0, SCREENEFFECT_FADEIN, SCREENEFFECT_FADEOUT, SCREENEFFECT_FLASH, SCREENEFFECT_BLIND, }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- class CommentaryData { public: std::string devname = ""; uint8_t index = 0; uint8_t maxindex = 0; Source* speaker = NULL; ~CommentaryData() { if (speaker) { speaker->Release(); speaker = NULL; } } }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- class PlayerActor : public StageActor { PickInfo* m_pLookTrace; // HUD: bool m_bShowHint; bool m_bShowCommentary; bool m_bShowGameOver; uint64_t m_u64GameOverTime; CharacterMoveState m_hState; std::weak_ptr<CommentaryData> m_pCommentaryData; protected: Listener* m_pListener; bool m_bIsAlive; bool m_bAllowCrosshair; bool m_bSuspendMovement; bool m_bSuspendLook; bool m_bCrouched; bool m_bCrouching; bool m_bWantsToCrouch; Vec3 m_vPushForce; Vec3 m_vCamRotation; Vec3 m_vCameraOffset; float m_flEyeHeight; bool m_bFreeLook; bool m_bFreeMove; Pivot* m_pRotator; uint64_t m_u64QuickSpinInitTime; bool m_bSpinLockX; double m_dCamTopAngle; double m_dCamBtmAngle; float m_flEyeTraceDistance; float m_flInteractDistance; float m_flPickRadius; Action ACTION_MOVEFORWARD; Action ACTION_MOVEBACKWARD; Action ACTION_MOVERIGHT; Action ACTION_MOVELEFT; Action ACTION_INTERACTION; // Effects: ScreenEffect m_hEffectType; Vec4 m_vCurtianColor; float m_flCurtianRate; bool m_bZoomed; bool m_bZoomedIn; Vec3 m_vCamRotationOffset; Vec3 m_vSmoothedCamRotationOffset; const bool ActionHit(const Action actionname); const bool ActionDown(const Action actionname); void SetVelocity(const Vec3 vVelo); void AddForce(const Vec3 vForce); void ChangeMovementState(const CharacterMoveState hState); public: PlayerActor(); virtual ~PlayerActor(); virtual void Start(); virtual void UpdateWorld(); virtual void UpdateMatrix(); virtual bool IsPlayer() { return true; }; virtual bool IsAlive(); virtual void Kill(); virtual void Respawn() {}; void Kick(); virtual void Spawn() {}; virtual void UpdateKeyBindings(); // Quick functions for locking movement/look: virtual void SuspendMovement(const bool bValue) { m_bSuspendMovement = bValue; }; virtual void SuspendLook(const bool bValue) { m_bSuspendLook = bValue; }; // Camera functions: Camera* GetCamera(); Vec3 GetEyePosition(); Vec3 GetEyeAngles(); Quat GetEyeQAngles(); const float GetEyeHeight(); const float GetCrouchedEyeHeight(); virtual const bool CanUnCrouch() { return true; }; virtual void SetEyeHeight(const float flHeight); virtual void SetCameraAngles(Leadwerks::Vec3 vRot); virtual void Teleport(const Vec3& pNewPos, const Vec3& pNewRot, const Vec3& pNewVelocity); const bool FireUseOnEntity(Entity* pEntity); void SetFreeLookMode(const bool bMode); void SetFreeMoveMode(const bool bMode); void UpdateFreeLookMode(); void UpdateFreeMoveMode(); void QuickSpin(); virtual void UpdateCameraHeight(const bool bCrouchState); const float GetEyeTraceDistance() { return m_flEyeTraceDistance; }; Entity* GetEyeLookingAt(); const Vec3 GetEyeLookPositon(); PickInfo* GetEyeTrace() { return m_pLookTrace; }; const float GetPickRadius(); void ForceLookAtPoint(const Vec3& vPos, const bool bLockX = false); const Line3D GetLine(); // Movement + Physics virtual void SetPhysicsMode(const int iMode); const int GetPhysicsMode(); void SetWalkSpeed(const float iSpeed); const int GetWalkSpeed(); virtual void HandleMovement(); virtual const bool IsCrouched(); const bool IsAirbone(); virtual void HandleInteraction(); virtual void SetNoClipMode(const bool bState) {}; CharacterMoveState GetMovementState() { return m_hState; }; Vec3 GetVelocity(); virtual void Push(const int x, const int y, const int z); void ForceJump(const float fJump); // Interaction virtual void OnSuccessfullUse(Entity* pHitEntity) {}; virtual void OnUnSuccessfullUse() {}; virtual bool ShouldSkipUseTest() { return false; }; // Post drawing: void ShowCrosshair(const bool bShow); virtual void PostRender(Context* context); virtual void DrawHUD(Context* pContext); virtual void DrawCurtian(Context* pContext); Widget* GetHUD(); // Effects: virtual void HandleScreenEffects(); void SetCurtianColor(const Vec3& pColor); void PlayScreenEffect(const ScreenEffect iScreenEffect, const Vec3 vColor = Vec3(0), const float flRate = 0.1f); void PlayQuickFlash(const Vec3 vColor = Vec3(1)); void ClearScreenEffect(); void ZoomIn(const float fNewFov, const float flRate); void ZoomOut(const float flRate); // HUD Hint: void ShowHUDHint(const Action actionname, const std::string& pszMessage); void HideHUDHint(); void ShowCommentaryPanel(std::shared_ptr<CommentaryData> pData); void HideCommentaryPanel(); void ShowGameOverScreen(const std::string& pszMessage); }; extern const bool IsPlayerActor(Leadwerks::Entity* pEntity); extern PlayerActor* ToPlayerActor(Leadwerks::Entity* pEntity); extern PlayerActor* GetPlayerActor(); extern const bool IsPlayerLookingAtEntity(Leadwerks::Entity* pEntity); //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- class PlayerProxy : public StageActor { uint64_t m_u64TriggerTime; uint64_t m_u64DelayTime; protected: PlayerActor* GetPlayer(); const bool IsPlayerLookingAtMe(); public: PlayerProxy(); virtual void Start(); virtual void UpdatePhysics(); virtual void ReceiveSignal(const std::string& inputname, Entity* sender); virtual void Trigger() {}; }; class CommentaryNode : public PlayerProxy { bool m_bActive; std::shared_ptr<CommentaryData> m_hData; public: CommentaryNode(); virtual ~CommentaryNode(); virtual void Start(); virtual void UpdateWorld(); virtual bool Use(StageActor* pActor); virtual void Detach(); friend class CommentaryNode; }; class ProxyEndGame : public PlayerProxy { public: virtual void Trigger(); }; class ProxyCurtain : public PlayerProxy { ScreenEffect m_hType; uint64_t m_u64ShowTime; uint64_t m_u64TimeOut; public: // This needs to be PostStart as the player may be placed/loaded after this actor! virtual void PostStart(); virtual void UpdateWorld(); virtual void Trigger(); }; class ProxyHint : public PlayerProxy { uint64_t m_u64ShowTime; uint64_t m_u64TimeOut; public: virtual void Start(); virtual void UpdateWorld(); virtual void Trigger(); }; class ProxyAmbientSound : public PlayerProxy { Source* m_pSpeaker; float m_flMaxVolume; bool m_bActive; public: virtual void Start(); virtual void UpdatePhysics(); void Activate(); virtual void Detach(); friend class ProxyAmbientSound; }; class ProxyLookAtMe : public PlayerProxy { Model* m_pBox; public: virtual void Start(); Model* GetTargetBox(); }; #endif // PLAYERACTOR_H Next, I made an FPSPlayer which is built on top on the PlayerActor class. This transfroms the base class into a FPS character controller with interaction and physics pickup. It also has base code for a weapon. There is also a CyclonePlayer but it really is just a Start function that controls if it should give the player the weapon or not. //========= Copyright Reep Softworks, All rights reserved. ============// // // Purpose: // //=====================================================================// #ifndef FPSPLAYER_H #define FPSPLAYER_H #if defined( _WIN32 ) #pragma once #endif #include "pch.h" #include "PlayerActor.h" struct InteractPickupStoredData { float mass; int collisiontype; Vec2 dampinging; Vec2 friction; }; class FPSPlayer; class FPSWeapon : public StageActor { protected: Pivot* m_pSwayPivot; bool m_bHolstered; Model* m_pViewModel; Vec3 m_vModelOffset; Vec3 m_vBaseRotation; FPSPlayer* m_pOwner; public: FPSWeapon(); virtual void Holster() {}; virtual void Unholster() {}; virtual void UpdateHolster() {}; virtual void Fire(const int iMode = 0) {}; virtual void TestPickupState() {}; virtual void Reload() {}; virtual void BeginJump() {}; virtual void ReAdjustViewModel(const float flPlayerFOV); friend class FPSPlayer; }; class FPSWeaponPickup : public StageActor { void GiveWeapon(Entity* entity); public: virtual void Collision(Entity* entity, const Vec3& position, const Vec3& normal, float speed); virtual bool Use(StageActor* pActor); }; class FPSPlayer : public PlayerActor { // Movement + Physics bool m_bCrouchedOldState; bool m_bWalking; float m_flUpdateTick; uint64_t m_intLastStepTime; bool m_bLeftStep; std::string m_pszFootStepSounds[2]; Model* m_pCorpse; uint64_t m_u64PushLaunchTime; Vec3 m_flLastImpactSpeed; // Interaction Vec3 m_vCarryPosition; Quat m_vCarryQuat; Vec3 m_flThrowVelocity; float m_flPickDistance; float m_flCarryDistance; float m_flHoldThreshold; Pivot* m_pRotationCorrection; Joint* m_pEffector; Entity* m_pCarryEntity; protected: Action ACTION_JUMP; Action ACTION_CROUCH; Action ACTION_ZOOM; Action ACTION_FIREPRIMARY; Action ACTION_FIRESECONDARY; Action ACTION_RELOAD; // Weapon Pivot* m_pWeaponTag; //FPSWeapon* m_pActiveWeapon; virtual void PickupObject(Entity* pEntity); virtual void UpdateHeldObject(); virtual void ThrowObject(); void SetThrowVelocity(Vec3 vVelocity); public: FPSPlayer(); virtual ~FPSPlayer(); virtual void Spawn(); virtual void Kill(); virtual void Detach(); virtual void UpdateWorld(); virtual void UpdatePhysics(); virtual void Collision(Entity* entity, const Vec3& position, const Vec3& normal, float speed); virtual void UpdateKeyBindings(); virtual void Respawn(); // Movement + Physics virtual const bool IsCrouched(); virtual const bool CanUnCrouch(); virtual void HandleCrouching(); virtual void HandleCharacterController(); virtual void SetNoClipMode(const bool bState); virtual void UpdateMovement(Vec2 vMovement); virtual void OnUpdateMovement() {} virtual void OnPerStep(); virtual void OnStopMovement() {}; virtual void OnJump(); virtual void OnLand(); virtual bool GetCrouched() { return false; }; const bool IsFlying(); virtual void Push(const int x, const int y, const int z); const float GetFallSpeed(); // Interaction virtual void OnSuccessfullUse(Entity* pHitEntity); virtual void OnUnSuccessfullUse(); virtual bool ShouldSkipUseTest(); virtual void ForceDropObject(); const bool HoldingObject(); // Weapon void GiveWeapon(); FPSWeapon* GetWeapon(); virtual void BuildWeapon(FPSWeapon* pWeapon); void AdjustViewModel(); }; extern const bool IsFPSPlayer(Leadwerks::Entity* pEntity); extern FPSPlayer* ToFPSPlayer(Leadwerks::Entity* pEntity); #endif // FPSPLAYER_H The magic of getting the fly code to work correctly is in this bit. I also force the player to crouch in a ball to make it feel way less awkward. It was really tricky, but it all worked out. //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void FPSPlayer::UpdatePhysics() { HandleCharacterController(); UpdateHeldObject(); // This fixes a crash when dying. if (GetEntity()->GetPhysicsMode() == Entity::CharacterPhysics) { // Stop moving me if we're landed and it's been a bit since we were last pushed. if (m_u64PushLaunchTime > 0) { if (GetStage()->GetTime() > m_u64PushLaunchTime + 100) { if (m_vPushForce != Vec3(0) && !IsAirbone()) { DMsg("PlayerActor: Resetting push launch timer."); //GetEntity()->charactercontroller->stepheight = 0.51; m_vPushForce = Vec3(0); SetVelocity(m_vPushForce); m_bWantsToCrouch = false; m_u64PushLaunchTime = 0; } } } } if (GetMovementState() != CHARACTERSTATE_FALLING && GetEntity()->GetVelocity().y < -4.5f) { ChangeMovementState(CHARACTERSTATE_FALLING); } else if (GetMovementState() == CHARACTERSTATE_FALLING && GetEntity()->GetVelocity().y >= 0.0f) { ChangeMovementState(CHARACTERSTATE_IDLE); OnLand(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void FPSPlayer::HandleCharacterController() { if (GetStage()->ConsoleShowing() || m_bSuspendMovement) return; if (GetEntity()->GetPhysicsMode() == Entity::RigidBodyPhysics) return; float jumpinput = m_vPushForce.y; Vec2 movement = Input::GetActionAxis(Input::ToActionAxis(ACTION_MOVEFORWARD, ACTION_MOVEBACKWARD, ACTION_MOVELEFT, ACTION_MOVERIGHT, GAMEPADAXIS_LSTICK)); float speed = g_player_walkspeed; // Handle Crouching HandleCrouching(); if (IsCrouched()) speed = g_player_walkspeed / 2; auto l = movement.Length(); if (l > 0.0) { movement.x = (movement.x / l) * speed; movement.y = (movement.y / l) * speed; } // Handle Jumping if (ActionHit(ACTION_JUMP)) { if (!IsAirbone()) { jumpinput = g_player_jumpforce; ChangeMovementState(CHARACTERSTATE_JUMPING); OnJump(); if (movement.y != 0) movement.y = movement.y * 1.6; if (movement.x == 0 || movement.y == 0) OnPerStep(); } } if (m_u64PushLaunchTime > 0) { if (m_vPushForce.x != 0 || m_vPushForce.z != 0) { movement = Vec2(0); movement += GetEntity()->GetVelocity(true).xz(); //DMsg(movement.ToString()); // Hackory to make the player crouch properly while flying. m_bWantsToCrouch = false; m_bCrouched = true; m_vCameraOffset.y = GetCrouchedEyeHeight(); GetEntity()->SetInput(0, movement.y, movement.x, m_vPushForce.y, m_bCrouched, g_player_maxdecel, g_player_mindecel, true); } } else { UpdateMovement(movement); GetEntity()->SetInput(GetEyeAngles().y, movement.y, movement.x, jumpinput, m_bCrouched, g_player_maxdecel, g_player_mindecel, true); } m_vPushForce.y = 0; } Along with some adjustments to the Cyclone code, Everything was working much better. I continued to make tweaks and changes as I continued along. A nice bonus was this fixed Cyclones on Ramps which didn't work with my old code which helped me to get another map done. Revamped Cyclone Behavior - Work in Progress - Ultra Engine Community - Game Engine for VR After 6 months of development, I'm very happy to say that today I hit my first milestone. I have 10 playable maps with everything feeling acceptable. Most gameplay functions are online AND today I squeezed an Addon System to allow custom maps. The game runs with the Lua Sandbox enabled so custom maps can code new content in Lua! However, the entire game looks like this. I think it's safe to say that it's time to close Visual Studio for a bit and open Blender and Gimp on a daily basis. I'll have to open Visual Studio again to code the indicator strips and other effects but I'm really excited to just zone out and work models and textures. (although I don't see myself as the best in that area.) If you're actually interested in helping, my DM is open. Right now the plan is to get some maps art up for some screenshots and by late September/October set up Cyclone for Steamworks for an Early Access release Q1 2022. I can beat the game within a few minutes but with the project having zero story it can get more content in the future by me or others thanks to the addon system. First thing I need is a logo....
-
There was a template that allowed you to make a console app instead of a Windowed app but it was most likely removed to remain consistent with all platforms. You can change your app type after creating a new project under Project Settings in Visual Studio. This will always pull up the cmd window.
- 1 reply
-
- 1
-
Leadwerks has a similar GUI system that of UAK. You are just limited to loading tex files and it's not DPI aware but does support some form of scaling. For example:
- 1 reply
-
- 1
-
I like to test things on AMD cards due to the humble beginnings of AMD not working at all. Looks like you over came that but I like to keep testing on that card to be sure.
-
-
Submit a pull request on the Github!
- 2 replies
-
- 1
-
- plugins
- ultra app kit
-
(and 1 more)
Tagged with:
-
-
View this video Description I pretty much had to rewrite my FPS player and Cyclone code but it was worth it! Find out more at http://reepsoftworks.com/cyclone/
-
Ultra AppKit - Is it possible to get a DLL version of Ultra AppKit?
reepblue replied to buzzdx's topic in Programming
Yep, that looks like what I have but I used a number index. -
Ultra AppKit - Is it possible to get a DLL version of Ultra AppKit?
reepblue replied to buzzdx's topic in Programming
For the icon, somebody did post somewhere here how to do it the "right way" which is how I did it for my game. However the method is Windows exclusive. I can reshare it via a blog post when I'm infront of my code base again. -
Love these and what you do. Keep it up and I hope you do the same thing for Ultra Engine shaders in the future. ?
-
Controlling Character Controller Character Movement.
reepblue replied to reepblue's topic in Programming
My temp fix that'll probably leave as. if (m_vPushForce.x > 0 || m_vPushForce.z > 0) { movement = Vec2(0); movement += GetEntity()->GetVelocity(false).xz(); //DMsg(movement.ToString()); } -
Controlling Character Controller Character Movement.
reepblue replied to reepblue's topic in Programming
Ok, I think I got something decent. First, I redid my Push function now including a new vector and uint64_t member. //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void FPSPlayer::Push(const int x, const int y, const int z) { m_vPushForce = Vec3(x, y, z); GetEntity()->SetVelocity(m_vPushForce.x, m_vPushForce.y, m_vPushForce.z); m_u64PushLaunchTime = GetStage()->GetTime(); ChangeMovementState(CHARACTERSTATE_FLYING); } Added this to my UpdatePhysics function. // Stop moving me if we're landed and it's been a bit since we were last pushed. if (m_u64PushLaunchTime > 0) { if (GetStage()->GetTime() > m_u64PushLaunchTime + 100) { if (m_vPushForce != Vec3(0) && !IsAirbone()) { m_vPushForce = Vec3(0); m_u64PushLaunchTime = 0; } } } My new move function //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void FPSPlayer::HandleCharacterController() { if (GetStage()->ConsoleShowing() || m_bSuspendMovement) return; if (GetEntity()->GetPhysicsMode() == Entity::RigidBodyPhysics) return; float jumpinput = 0; Vec2 movement = Input::GetActionAxis(Input::ToActionAxis(ACTION_MOVEFORWARD, ACTION_MOVEBACKWARD, ACTION_MOVELEFT, ACTION_MOVERIGHT, GAMEPADAXIS_LSTICK)); float speed = g_player_walkspeed; // Handle Crouching HandleCrouching(); if (IsCrouched()) speed = g_player_walkspeed / 2; auto l = movement.Length(); if (l > 0.0) { movement.x = (movement.x / l) * speed; movement.y = (movement.y / l) * speed; } // Handle Jumping if (ActionHit(ACTION_JUMP)) { if (!IsAirbone()) { jumpinput = g_player_jumpforce; ChangeMovementState(CHARACTERSTATE_JUMPING); OnJump(); if (movement.y != 0) movement.y = movement.y * 1.6; if (movement.x == 0 || movement.y == 0) OnPerStep(); } } else if (m_vPushForce.y != 0) { jumpinput = m_vPushForce.y; m_vPushForce.y = 0; if (movement.y != 0) movement.y = movement.y * 1.6; if (movement.x == 0 || movement.y == 0) OnPerStep(); } UpdateMovement(movement); if (m_vPushForce.x > 0 || m_vPushForce.z > 0) { movement += GetEntity()->GetVelocity(false).xz(); } GetEntity()->SetInput(GetEyeAngles().y, movement.y, movement.x, jumpinput, m_bCrouched, 0.50, 0.25, true); } The player will actually slide a bit but this makes sense and feels more natural than a hard stop. Without any input this is fine but when I add any player input, it's too much. -
For the past 4 months, of my game, I've been simply just applying velocity on my player/forcing the player to Jump. Since like 99% of my game is centered around throwing the player controller around, I decided now would be the best time to perfect the air movement. Before, applying force/velocity to the player caused this motion. The player would reach a velocity limit and then just fall to the floor. I wanted a nice arch. I tried setting up a predefined calculation but my push math was terrible. I noticed that if I held the forward key down I got my arch I wanted. Right now I have something like this: Here's my current code. //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void FPSPlayer::HandleCharacterController() { if (GetStage()->ConsoleShowing() || m_bSuspendMovement) return; if (GetEntity()->GetPhysicsMode() == Entity::RigidBodyPhysics) return; float jumpinput = 0; Vec2 movement = Input::GetActionAxis(Input::ToActionAxis(ACTION_MOVEFORWARD, ACTION_MOVEBACKWARD, ACTION_MOVELEFT, ACTION_MOVERIGHT, GAMEPADAXIS_LSTICK)); float speed = g_player_walkspeed; // Handle Crouching HandleCrouching(); if (IsCrouched()) speed = g_player_walkspeed / 2; auto l = movement.Length(); if (l > 0.0) { movement.x = (movement.x / l) * speed; movement.y = (movement.y / l) * speed; } // Handle Jumping if (ActionHit(ACTION_JUMP)) { if (!GetEntity()->GetAirborne()) { jumpinput = g_player_jumpforce; ChangeMovementState(CHARACTERSTATE_JUMPING); OnJump(); if (movement.y != 0) movement.y = movement.y * 1.6; if (movement.x == 0 || movement.y == 0) OnPerStep(); } } if (GetMovementState() == CHARACTERSTATE_FLYING) { auto force = GetVelocity().xz(); UpdateMovement(force); GetEntity()->SetInput(0, force.y, force.x, 0, false, 0.50, 0.25, true); } else { UpdateMovement(movement); GetEntity()->SetInput(GetEyeAngles().y, movement.y, movement.x, jumpinput, m_bCrouched, 0.50, 0.25, true); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- Vec3 PlayerActor::GetVelocity() { Vec3 r = GetEntity()->GetVelocity(); auto cc = GetEntity()->charactercontroller; if (cc != NULL) { r = cc->GetVelocity(); } return r; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void PlayerActor::Push(const int x, const int y, const int z) { auto v = Vec3(x, y, z); SetVelocity(v); if (y != 0) ChangeMovementState(CHARACTERSTATE_FLYING); } What I have now is more acceptable that my previous code. What I'm asking is for any suggestions to improve this. Should I be using AddForce instead and just multiple the entity's mass? Also any information about if there are any differences between applying/getting the velocity from character controller instead of the entity would be nice. If I can't make anything better, I'm probably going to call it here.
-
Leadwerks doesn't support PBR but rather blinn-phong shading. This is what games used to use before PBR technology was adopted. https://help.poliigon.com/en/articles/1712652-what-are-the-different-texture-maps-for Leadwerks supports diffuse, normal specular and roughness maps with the default shaders. If you do some digging there were some PBR imitation shaders posted here awhile back.