diff --git a/Source Code/Core/Resources/Headers/ResourceCache.h b/Source Code/Core/Resources/Headers/ResourceCache.h index b79a988952..86a7656d5c 100644 --- a/Source Code/Core/Resources/Headers/ResourceCache.h +++ b/Source Code/Core/Resources/Headers/ResourceCache.h @@ -34,8 +34,8 @@ #define RESOURCE_MANAGER_H_ #include "ResourceLoader.h" -#include "Platform/Threading/Headers/ThreadPool.h" +#include "Core/Headers/Console.h" #include "Utility/Headers/Localization.h" #include "Core/Headers/PlatformContextComponent.h" @@ -94,21 +94,25 @@ class ResourceCache : public PlatformContextComponent { ResourceLoadLock res_lock(loadingHash); /// Check cache first to avoid loading the same resource twice - std::shared_ptr ptr = std::static_pointer_cast(loadResource(loadingHash, descriptor.resourceName())); + std::shared_ptr ptr = std::static_pointer_cast(find(loadingHash)); /// If the cache did not contain our resource ... wasInCache = ptr != nullptr; - if (!ptr) { + if (!wasInCache) { + Console::printfn(Locale::get(_ID("RESOURCE_CACHE_GET_RES")), descriptor.resourceName().c_str(), loadingHash); + /// ...aquire the resource's loader /// and get our resource as the loader creates it ptr = std::static_pointer_cast(ImplResourceLoader(*this, _context, descriptor, loadingHash)()); - if (ptr) { - /// validate it's integrity and add it to the cache - add(ptr); - } - } else { - if (descriptor.onLoadCallback()) { - descriptor.onLoadCallback()(ptr); - } + assert(ptr != nullptr); + add(ptr); + } + + if (descriptor.waitForReady()) { + WAIT_FOR_CONDITION(ptr->getState() == ResourceState::RES_LOADED); + } + + if (wasInCache && descriptor.onLoadCallback()) { + descriptor.onLoadCallback()(ptr); } return ptr; diff --git a/Source Code/Core/Resources/Headers/ResourceDescriptor.h b/Source Code/Core/Resources/Headers/ResourceDescriptor.h index 60e9ec000e..af58b7b1dd 100644 --- a/Source Code/Core/Resources/Headers/ResourceDescriptor.h +++ b/Source Code/Core/Resources/Headers/ResourceDescriptor.h @@ -105,6 +105,7 @@ class ResourceDescriptor : public Hashable { inline U32 getID() const { return _ID; } inline P32 getMask() const { return _mask; } inline const vec3& getData() const { return _data; } + inline bool waitForReady() const { return _waitForReady; } const DELEGATE_CBK& onLoadCallback() const { return _onLoadCallback; @@ -123,6 +124,7 @@ class ResourceDescriptor : public Hashable { inline void setID(U32 ID) { _ID = ID; } inline void setBoolMask(P32 mask) { _mask = mask; } inline void setData(const vec3& data) { _data.set(data); } + inline void waitForReady(bool state) { _waitForReady = state; } inline void setThreadedLoading(const bool threaded) { _threaded = threaded; @@ -149,6 +151,7 @@ class ResourceDescriptor : public Hashable { stringImpl _assetLocation; bool _flag = false; bool _threaded = true; + bool _waitForReady = true; U32 _ID = 0; /// 4 bool values representing ... anything ... P32 _mask; diff --git a/Source Code/Core/Resources/ResourceCache.cpp b/Source Code/Core/Resources/ResourceCache.cpp index 536b682e0e..c73d071185 100644 --- a/Source Code/Core/Resources/ResourceCache.cpp +++ b/Source Code/Core/Resources/ResourceCache.cpp @@ -2,8 +2,6 @@ #include "Headers/ResourceCache.h" -#include "Core/Headers/Console.h" -#include "Utility/Headers/Localization.h" #include "Environment/Terrain/Headers/TerrainLoader.h" #include "Core/Time/Headers/ApplicationTimer.h" @@ -78,17 +76,6 @@ void ResourceCache::add(CachedResource_wptr res) { hashAlg::insert(_resDB, hashAlg::make_pair(hash, res)); } -CachedResource_ptr ResourceCache::loadResource(size_t descriptorHash, const stringImpl& resourceName) { - const CachedResource_ptr& resource = find(descriptorHash); - if (resource) { - WAIT_FOR_CONDITION(resource->getState() == ResourceState::RES_LOADED); - } else { - Console::printfn(Locale::get(_ID("RESOURCE_CACHE_GET_RES")), resourceName.c_str(), descriptorHash); - } - - return resource; -} - CachedResource_ptr ResourceCache::find(size_t descriptorHash) { /// Search in our resource cache SharedLock r_lock(_creationMutex); @@ -97,7 +84,7 @@ CachedResource_ptr ResourceCache::find(size_t descriptorHash) { return it->second.lock(); } - return {}; + return nullptr; } void ResourceCache::remove(CachedResource* resource) { diff --git a/Source Code/Core/Resources/ResourceDescriptor.cpp b/Source Code/Core/Resources/ResourceDescriptor.cpp index cdb7ff6ae3..d7ff68d5bf 100644 --- a/Source Code/Core/Resources/ResourceDescriptor.cpp +++ b/Source Code/Core/Resources/ResourceDescriptor.cpp @@ -16,6 +16,7 @@ ResourceDescriptor::ResourceDescriptor(const stringImpl& resourceName) : _propertyDescriptor(nullptr), _resourceName(resourceName), _assetName(resourceName), + _waitForReady(true), _flag(false), _ID(0), _enumValue(0), diff --git a/Source Code/Environment/Vegetation/Headers/Vegetation.h b/Source Code/Environment/Vegetation/Headers/Vegetation.h index 7ed181a1a6..2eb64d70f6 100644 --- a/Source Code/Environment/Vegetation/Headers/Vegetation.h +++ b/Source Code/Environment/Vegetation/Headers/Vegetation.h @@ -149,6 +149,7 @@ class Vegetation : public SceneNode { static VertexBuffer* s_buffer; static ShaderBuffer* s_treeData; static ShaderBuffer* s_grassData; + static vector s_treeMeshes; static std::unordered_set> s_treePositions; static std::unordered_set> s_grassPositions; diff --git a/Source Code/Environment/Vegetation/Vegetation.cpp b/Source Code/Environment/Vegetation/Vegetation.cpp index 1a400bc4b7..443a67967c 100644 --- a/Source Code/Environment/Vegetation/Vegetation.cpp +++ b/Source Code/Environment/Vegetation/Vegetation.cpp @@ -43,6 +43,8 @@ namespace Divide { namespace { constexpr U32 WORK_GROUP_SIZE = 64; constexpr I16 g_maxRadiusSteps = 512; + + SharedMutex g_treeMeshLock; }; bool Vegetation::s_buffersBound = false; @@ -52,6 +54,7 @@ std::unordered_set> Vegetation::s_grassPositions; ShaderBuffer* Vegetation::s_treeData = nullptr; ShaderBuffer* Vegetation::s_grassData = nullptr; VertexBuffer* Vegetation::s_buffer = nullptr; +vector Vegetation::s_treeMeshes; std::atomic_uint Vegetation::s_bufferUsage = 0; size_t Vegetation::s_maxChunks = 0; size_t Vegetation::s_maxTreeInstancesPerChunk = 0; @@ -86,6 +89,31 @@ Vegetation::Vegetation(GFXDevice& context, _treeMeshNames.insert(eastl::cend(_treeMeshNames), eastl::cbegin(details.treeMeshes), eastl::cend(details.treeMeshes)); + { + UniqueLockShared w_lock(g_treeMeshLock); + for (const stringImpl& meshName : _treeMeshNames) { + if (std::find_if(std::cbegin(s_treeMeshes), std::cend(s_treeMeshes), + [&meshName](const Mesh_ptr& ptr) { + return Util::CompareIgnoreCase(ptr->assetName(), meshName); + }) == std::cend(s_treeMeshes)) + { + ResourceDescriptor model("Tree"); + model.assetLocation(Paths::g_assetsLocation + "models"); + model.setFlag(true); + model.setThreadedLoading(false); //< we need the extents asap! + model.assetName(meshName); + Mesh_ptr meshPtr = CreateResource(_context.parent().resourceCache(), model); + meshPtr->setMaterialTpl(s_treeMaterial); + // CSM last split should probably avoid rendering trees since it would cover most of the scene :/ + meshPtr->renderState().addToDrawExclusionMask(RenderStagePass(RenderStage::SHADOW, RenderPassType::MAIN_PASS, 0, 2)); + for (const SubMesh_ptr& subMesh : meshPtr->subMeshList()) { + subMesh->renderState().addToDrawExclusionMask(RenderStagePass(RenderStage::SHADOW, RenderPassType::MAIN_PASS, 0, 2)); + } + s_treeMeshes.push_back(meshPtr); + } + } + } + ResourceDescriptor instanceCullShaderGrass("instanceCullVegetation.Grass"); instanceCullShaderGrass.setThreadedLoading(true); assert(s_maxGrassInstancesPerChunk != 0u && "Vegetation error: call \"precomputeStaticData\" first!"); @@ -149,6 +177,8 @@ Vegetation::~Vegetation() } assert(getState() != ResourceState::RES_LOADING); if (s_bufferUsage.fetch_sub(1) == 1) { + UniqueLockShared w_lock(g_treeMeshLock); + s_treeMeshes.clear(); s_treeMaterial.reset(); } @@ -393,25 +423,25 @@ void Vegetation::postLoad(SceneGraphNode& sgn) { U32 ID = _terrainChunk.ID(); U32 meshID = to_U32(ID % _treeMeshNames.size()); - - ResourceDescriptor model("Tree"); - model.assetLocation(Paths::g_assetsLocation + "models"); - model.setFlag(true); - model.setThreadedLoading(false); //< we need the extents asap! - model.assetName(_treeMeshNames[meshID]); - Mesh_ptr meshPtr = CreateResource(_context.parent().resourceCache(), model); - meshPtr->setMaterialTpl(s_treeMaterial); - // CSM last split should probably avoid rendering trees since it would cover most of the scene :/ - meshPtr->renderState().addToDrawExclusionMask(RenderStagePass(RenderStage::SHADOW, RenderPassType::MAIN_PASS, 0, 2)); - for (const SubMesh_ptr& subMesh : meshPtr->subMeshList()) { - subMesh->renderState().addToDrawExclusionMask(RenderStagePass(RenderStage::SHADOW, RenderPassType::MAIN_PASS, 0, 2)); + const stringImpl& meshName = _treeMeshNames[meshID]; + + Mesh_ptr crtMesh = nullptr; + { + SharedLock r_lock(g_treeMeshLock); + crtMesh = s_treeMeshes.front(); + for (const Mesh_ptr& mesh : s_treeMeshes) { + if (Util::CompareIgnoreCase(mesh->assetName(), meshName)) { + crtMesh = mesh; + break; + } + } } SceneGraphNodeDescriptor nodeDescriptor = {}; nodeDescriptor._componentMask = normalMask; nodeDescriptor._usageContext = NodeUsageContext::NODE_STATIC; nodeDescriptor._serialize = false; - nodeDescriptor._node = meshPtr; + nodeDescriptor._node = crtMesh; nodeDescriptor._instanceCount = _instanceCountTrees; assert(s_grassData != nullptr); diff --git a/Source Code/Geometry/Material/Headers/ShaderComputeQueue.h b/Source Code/Geometry/Material/Headers/ShaderComputeQueue.h index 8246ebc80f..0a39bcbfe3 100644 --- a/Source Code/Geometry/Material/Headers/ShaderComputeQueue.h +++ b/Source Code/Geometry/Material/Headers/ShaderComputeQueue.h @@ -35,7 +35,6 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "ShaderProgramInfo.h" #include "Core/Resources/Headers/ResourceCache.h" -#include "Managers/Headers/FrameListenerManager.h" namespace Divide { @@ -46,7 +45,7 @@ namespace Time { class ResourceCache; class PlatformContext; -class ShaderComputeQueue : public FrameListener { +class ShaderComputeQueue { public: class ShaderQueueElement { public: @@ -77,9 +76,8 @@ class ShaderComputeQueue : public FrameListener { // Return true if the queue wasn't empty bool stepQueue(); -protected: - bool frameStarted(const FrameEvent& evt) override; - bool frameEnded(const FrameEvent& evt) override; +private: + bool stepQueueLocked(); private: ResourceCache& _cache; @@ -88,8 +86,6 @@ class ShaderComputeQueue : public FrameListener { std::mutex _queueLock; std::deque _shaderComputeQueue; - bool _shadersComputedThisFrame = false; - U32 _totalShaderComputeCountThisFrame = 0; U32 _totalShaderComputeCount = 0; }; diff --git a/Source Code/Geometry/Material/Material.cpp b/Source Code/Geometry/Material/Material.cpp index 94831dea43..38cad9e3b3 100644 --- a/Source Code/Geometry/Material/Material.cpp +++ b/Source Code/Geometry/Material/Material.cpp @@ -495,15 +495,6 @@ bool Material::computeShader(RenderStagePass renderStagePass) { shaderPropertyDescriptor._defines.insert(std::cbegin(shaderPropertyDescriptor._defines), std::cbegin(_extraShaderDefines), std::cend(_extraShaderDefines)); - if (renderStagePass._stage == RenderStage::SHADOW) { - shaderPropertyDescriptor._defines.push_back(std::make_pair("SHADOW_PASS", true)); - } - - if (renderStagePass._passType == RenderPassType::PRE_PASS) { - shaderPropertyDescriptor._defines.push_back(std::make_pair("PRE_PASS", true)); - } else if (renderStagePass._passType == RenderPassType::OIT_PASS) { - shaderPropertyDescriptor._defines.push_back(std::make_pair("OIT_PASS", true)); - } if (_textures[slot1]) { if (!_textures[slot0]) { @@ -515,11 +506,18 @@ bool Material::computeShader(RenderStagePass renderStagePass) { stringImpl shader = _baseShaderName[renderStagePass.isDepthPass() ? 1 : 0]; if (renderStagePass.isDepthPass()) { - shader += renderStagePass._stage == RenderStage::SHADOW ? ".Shadow" : ".PrePass"; + if (renderStagePass._stage == RenderStage::SHADOW) { + shader += ".Shadow"; + shaderPropertyDescriptor._defines.push_back(std::make_pair("SHADOW_PASS", true)); + } else { + shader += ".PrePass"; + shaderPropertyDescriptor._defines.push_back(std::make_pair("PRE_PASS", true)); + } } if (renderStagePass._passType == RenderPassType::OIT_PASS) { shader += ".OIT"; + shaderPropertyDescriptor._defines.push_back(std::make_pair("OIT_PASS", true)); } if (!renderStagePass.isDepthPass()) { @@ -572,43 +570,28 @@ bool Material::computeShader(RenderStagePass renderStagePass) { shaderPropertyDescriptor._defines.push_back(std::make_pair("USE_DOUBLE_SIDED", true)); } - if (isRefractive()) { - shader += ".Refractive"; - shaderPropertyDescriptor._defines.push_back(std::make_pair("IS_REFRACTIVE", true)); - } - - if (isReflective()) { - shader += ".Reflective"; - shaderPropertyDescriptor._defines.push_back(std::make_pair("IS_REFLECTIVE", true)); - } - if (!receivesShadows() || !_context.context().config().rendering.shadowMapping.enabled) { shader += ".NoShadows"; shaderPropertyDescriptor._defines.push_back(std::make_pair("DISABLE_SHADOW_MAPPING", true)); } - // Add the GPU skinning module to the vertex shader? if (_hardwareSkinning) { shaderPropertyDescriptor._defines.push_back(std::make_pair("USE_GPU_SKINNING", true)); shader += ",Skinned"; // 0; + UniqueLock lock(_queueLock); + while (stepQueueLocked()) { + ++_totalShaderComputeCount; } } bool ShaderComputeQueue::stepQueue() { UniqueLock lock(_queueLock); + return stepQueueLocked(); +} + +bool ShaderComputeQueue::stepQueueLocked() { if (_shaderComputeQueue.empty()) { return false; } ShaderQueueElement& currentItem = _shaderComputeQueue.front(); + currentItem._shaderDescriptor.waitForReady(false); + currentItem._shaderRef = CreateResource(_cache, currentItem._shaderDescriptor); _shaderComputeQueue.pop_front(); return true; @@ -55,18 +54,4 @@ void ShaderComputeQueue::addToQueueBack(const ShaderQueueElement& element) { _shaderComputeQueue.push_back(element); } -bool ShaderComputeQueue::frameStarted(const FrameEvent& evt) { - return true; -} - -bool ShaderComputeQueue::frameEnded(const FrameEvent& evt) { - if (_shadersComputedThisFrame) { - _totalShaderComputeCount += _totalShaderComputeCountThisFrame; - _totalShaderComputeCountThisFrame = 0; - _shadersComputedThisFrame = false; - } - - return true; -} - }; //namespace Divide \ No newline at end of file diff --git a/Source Code/Platform/Video/RenderBackend/OpenGL/Shaders/glShaderProgram.cpp b/Source Code/Platform/Video/RenderBackend/OpenGL/Shaders/glShaderProgram.cpp index c270242362..c06085245c 100644 --- a/Source Code/Platform/Video/RenderBackend/OpenGL/Shaders/glShaderProgram.cpp +++ b/Source Code/Platform/Video/RenderBackend/OpenGL/Shaders/glShaderProgram.cpp @@ -938,13 +938,13 @@ void glShaderProgram::shaderFileRead(const stringImpl& filePath, stringImpl& sourceCodeOut) { stringImpl variant = fileName; if (Config::Build::IS_DEBUG_BUILD) { - variant.append(".debug"); + variant.append("debug"); } else if (Config::Build::IS_PROFILE_BUILD) { - variant.append(".profile"); + variant.append("profile"); } else { - variant.append(".release"); + variant.append("release"); } readFile(filePath, variant, sourceCodeOut, FileType::TEXT); } @@ -954,11 +954,11 @@ void glShaderProgram::shaderFileRead(const stringImpl& filePath, void glShaderProgram::shaderFileWrite(const stringImpl& filePath, const stringImpl& fileName, const char* sourceCode) { stringImpl variant = fileName; if (Config::Build::IS_DEBUG_BUILD) { - variant.append(".debug"); + variant.append("debug"); } else if (Config::Build::IS_PROFILE_BUILD) { - variant.append(".profile"); + variant.append("profile"); } else { - variant.append(".release"); + variant.append("release"); } writeFile(filePath, variant, (bufferPtr)sourceCode, strlen(sourceCode), FileType::TEXT); diff --git a/Source Code/Rendering/Lighting/LightPool.cpp b/Source Code/Rendering/Lighting/LightPool.cpp index d8a5d944b8..5e26a2a417 100644 --- a/Source Code/Rendering/Lighting/LightPool.cpp +++ b/Source Code/Rendering/Lighting/LightPool.cpp @@ -74,7 +74,8 @@ void LightPool::init() { bufferDescriptor._ringBufferLength = 6; bufferDescriptor._separateReadWrite = false; bufferDescriptor._flags = to_U32(ShaderBuffer::Flags::ALLOW_THREADED_WRITES) | - to_U32(ShaderBuffer::Flags::AUTO_RANGE_FLUSH); + to_U32(ShaderBuffer::Flags::AUTO_RANGE_FLUSH) | + to_U32(ShaderBuffer::Flags::UNBOUND_STORAGE); bufferDescriptor._updateFrequency = BufferUpdateFrequency::OCASSIONAL; bufferDescriptor._name = "LIGHT_BUFFER"; // NORMAL holds general info about the currently active lights: position, colour, etc. diff --git a/Source Code/Scenes/Scene.cpp b/Source Code/Scenes/Scene.cpp index 7095443ab5..e3f20bf754 100644 --- a/Source Code/Scenes/Scene.cpp +++ b/Source Code/Scenes/Scene.cpp @@ -297,9 +297,9 @@ void Scene::loadAsset(const XML::SceneNode& sceneNode, SceneGraphNode* parent) { to_base(ComponentType::NETWORKING); - auto loadModelComplete = [this](Resource_wptr res) { + auto loadModelComplete = [this](CachedResource_wptr res) { ACKNOWLEDGE_UNUSED(res); - --_loadingTasks; + _loadingTasks.fetch_sub(1); }; boost::property_tree::ptree nodeTree; @@ -316,7 +316,7 @@ void Scene::loadAsset(const XML::SceneNode& sceneNode, SceneGraphNode* parent) { normalMask |= to_base(ComponentType::RENDERING); if (!modelName.empty()) { - ++_loadingTasks; + _loadingTasks.fetch_add(1); ResourceDescriptor item(sceneNode.name); item.setOnLoadCallback(loadModelComplete); item.assetName(modelName); @@ -368,7 +368,7 @@ void Scene::loadAsset(const XML::SceneNode& sceneNode, SceneGraphNode* parent) { // No rendering component for meshes. Only for submeshes //normalMask |= to_base(ComponentType::RENDERING); if (!modelName.empty()) { - ++_loadingTasks; + _loadingTasks.fetch_add(1); ResourceDescriptor model(modelName); model.assetLocation(Paths::g_assetsLocation + "models"); model.assetName(modelName); @@ -585,8 +585,8 @@ void Scene::addTerrain(SceneGraphNode& parentNode, boost::property_tree::ptree p terrainDescriptor.setThreadedLoading(true); terrainDescriptor.setOnLoadCallback(registerTerrain); terrainDescriptor.setFlag(ter->getActive()); - CreateResource(_resCache, terrainDescriptor); _loadingTasks.fetch_add(1); + CreateResource(_resCache, terrainDescriptor); } void Scene::toggleFlashlight(PlayerIndex idx) { diff --git a/assets/shaders/GLSL/common/lightInput.cmn b/assets/shaders/GLSL/common/lightInput.cmn index d128a92166..edf939ac8b 100644 --- a/assets/shaders/GLSL/common/lightInput.cmn +++ b/assets/shaders/GLSL/common/lightInput.cmn @@ -31,7 +31,7 @@ struct Light { ivec4 _options; }; -layout(binding = BUFFER_LIGHT_NORMAL, std140) uniform dvd_LightBlock +layout(binding = BUFFER_LIGHT_NORMAL, std430) coherent ACCESS buffer dvd_LightBlock { // x = directional light count, y = point light count, z = spot light count, w = shadow light count uvec4 dvd_LightData; diff --git a/assets/shaders/GLSL/fragmentAtoms/BRDF.frag b/assets/shaders/GLSL/fragmentAtoms/BRDF.frag index 4cae06cf10..c1018f8fae 100644 --- a/assets/shaders/GLSL/fragmentAtoms/BRDF.frag +++ b/assets/shaders/GLSL/fragmentAtoms/BRDF.frag @@ -112,8 +112,7 @@ vec3 getLitColour(in vec3 albedo, in mat4 colourMatrix, in vec3 normal, in float lightColour.rgb += getEmissive(colourMatrix); -#if defined(IS_REFLECTIVE) - if (dvd_lodLevel < 1) { + if (dvd_lodLevel < 1 && getReflectivity(colourMatrix) > 100) { vec3 reflectDirection = reflect(normalize(VAR._vertexWV.xyz), normal); reflectDirection = vec3(inverse(dvd_ViewMatrix) * vec4(reflectDirection, 0.0f)); /*lightColour.rgb = mix(texture(texEnvironmentCube, vec4(reflectDirection, dvd_reflectionIndex)).rgb, @@ -121,14 +120,17 @@ vec3 getLitColour(in vec3 albedo, in mat4 colourMatrix, in vec3 normal, in float vec3(saturate(lightColour.a))); */ } -#endif //IS_REFLECTIVE return lightColour.rgb * shadowFactor; } #endif //USE_SHADING_FLAT vec4 getPixelColour(in vec4 albedo, in mat4 colourMatrix, in vec3 normal) { +#if defined(OIT_PASS) + const float shadowFactor = 1.0f; +#else const float shadowFactor = getShadowFactor(); +#endif vec4 colour = vec4(getLitColour(albedo.rgb, colourMatrix, normal, shadowFactor), albedo.a); diff --git a/localisation/enGB.ini b/localisation/enGB.ini index d5a84c916a..3ba7a91e01 100644 --- a/localisation/enGB.ini +++ b/localisation/enGB.ini @@ -289,7 +289,7 @@ ERROR_RESOURCE_CACHE_INVALID_NAME = [ResourceCache] Trying to remove resource wi ERROR_RESOURCE_CACHE_UNKNOWN_RESOURCE = [ResourceCache] Trying to remove a resource that wasn't loaded with the ResourceCache! ERROR_RESOURCE_REM = [ResourceCache] Resource [ %s ] [ %d ] not unloaded successfully! ERROR_TEXTURE_LOADER_ARRAY_INIT_COUNT = [TextureLoader] wrong number of texture layers for array texture: [ %s ] -TEXTURE_HAS_TRANSPARENCY_TRANSLUCENCY = [TextureLoader] Texture [ %s ] has transparent pixles [ %s ] / translucent pixels: [ %s ] +TEXTURE_HAS_TRANSPARENCY_TRANSLUCENCY = [TextureLoader] Texture [ %s ] has transparent pixels [ %s ] / translucent pixels: [ %s ] ERROR_TEXTURE_LOADER_CUBMAP_INIT_COUNT = [TextureLoader] wrong number of files for cubemap texture: [ %s ] ERROR_TEXTURE_LOADER_FILE = [TextureLoader] could not load texture file [ (%s)%s ] resource [ %s ] ERROR_TEXTURE_LOAD = [Texture] Unable to load texture [ %s ]