Add ResourceManager for assets outside of QRC
+ Use qt6_add_big_resources to improve build time + RM::getPath(sting) to build absolute path to assets + Allows easy access to models or other large assets not loaded into QRC
This commit is contained in:
		
							parent
							
								
									82b06c247d
								
							
						
					
					
						commit
						c15d064dce
					
				@ -20,12 +20,16 @@ set(CMAKE_CXX_STANDARD 11)
 | 
				
			|||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
 | 
					set(CMAKE_CXX_STANDARD_REQUIRED ON)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# For CLion builds, point CMAKE_PREFIX_PATH to Qt6 install directory
 | 
					# For CLion builds, point CMAKE_PREFIX_PATH to Qt6 install directory
 | 
				
			||||||
list(APPEND CMAKE_PREFIX_PATH $ENV{HOME}/Code/Clones/Qt6.2/6.2.3/gcc_64/)
 | 
					list(APPEND CMAKE_PREFIX_PATH $ENV{HOME}/Code/Clones/Qt/6.3.1/gcc_64/)
 | 
				
			||||||
find_package(Qt6 COMPONENTS OpenGLWidgets REQUIRED)
 | 
					find_package(Qt6 COMPONENTS OpenGLWidgets)
 | 
				
			||||||
 | 
					if (NOT Qt6_FOUND)
 | 
				
			||||||
 | 
					  message(SEND_ERROR "Unable to find Qt6 at CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}")
 | 
				
			||||||
 | 
					  message(FATAL_ERROR "Specify path to Qt6 with `cmake -DCMAKE_PREFIX_PATH=/path/to/Qt/6.x.x/gcc_64 -S /path/to/qtk -B /path/to/qtk/build && cmake --build /path/to/qtk/build -j $(nprocs)`")
 | 
				
			||||||
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Add our Qt resources.qrc file to our application
 | 
					# Add our Qt resources.qrc file to our application
 | 
				
			||||||
set(SOURCES app/main.cpp)
 | 
					set(SOURCES app/main.cpp)
 | 
				
			||||||
qt6_add_resources(SOURCES resources.qrc)
 | 
					qt6_add_big_resources(SOURCES resources.qrc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_executable(
 | 
					add_executable(
 | 
				
			||||||
    qtk             # Executable name
 | 
					    qtk             # Executable name
 | 
				
			||||||
@ -59,6 +63,7 @@ add_library(main-widget SHARED
 | 
				
			|||||||
    src/transform3D.cpp src/transform3D.h
 | 
					    src/transform3D.cpp src/transform3D.h
 | 
				
			||||||
    src/model.cpp src/model.h
 | 
					    src/model.cpp src/model.h
 | 
				
			||||||
    src/scene.cpp src/scene.h
 | 
					    src/scene.cpp src/scene.h
 | 
				
			||||||
 | 
					    src/resourcemanager.cpp src/resourcemanager.h
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
target_include_directories(main-widget PUBLIC src/)
 | 
					target_include_directories(main-widget PUBLIC src/)
 | 
				
			||||||
 | 
				
			|||||||
@ -23,10 +23,9 @@ Be sure to take note of the Qt6 installation directory, as we will need it to co
 | 
				
			|||||||
Once Qt6 is installed, to build and run `qtk` on Ubuntu -
 | 
					Once Qt6 is installed, to build and run `qtk` on Ubuntu -
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
sudo apt update -y && sudo apt install freeglut3-dev libassimp-dev cmake build-essential git
 | 
					sudo apt update -y && sudo apt install freeglut3-dev libassimp-dev cmake build-essential git
 | 
				
			||||||
git clone https://gitlab.com/shaunrd0/qtk && cd qtk
 | 
					git clone https://gitlab.com/shaunrd0/qtk
 | 
				
			||||||
mkdir build && cd build
 | 
					cmake -DCMAKE_PREFIX_PATH=$HOME/Qt/6.3.1/gcc_64 -S qtk/ -B qtk/build/ && cmake --build qtk/build/ -j $(nprocs)
 | 
				
			||||||
cmake .. -DCMAKE_PREFIX_PATH=$HOME/Qt6/6.2.3/gcc_64 && cmake --build .
 | 
					./qtk/build/qtk
 | 
				
			||||||
./qtk
 | 
					 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You can fly around the scene if you hold the right mouse button and use WASD.
 | 
					You can fly around the scene if you hold the right mouse button and use WASD.
 | 
				
			||||||
 | 
				
			|||||||
@ -37,7 +37,8 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // Shader settings
 | 
					  // Shader settings
 | 
				
			||||||
  inline void setShaderVertex(const std::string & vert) { mVertexShader = vert;}
 | 
					  inline void setShaderVertex(const std::string & vert) { mVertexShader = vert;}
 | 
				
			||||||
  inline void setShaderFragment(const std::string & frag) { mFragmentShader = frag;}
 | 
					  inline void setShaderFragment(const std::string & frag)
 | 
				
			||||||
 | 
					  { mFragmentShader = frag;}
 | 
				
			||||||
  void setShaders(const std::string & vert, const std::string & frag);
 | 
					  void setShaders(const std::string & vert, const std::string & frag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  template <typename T>
 | 
					  template <typename T>
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <scene.h>
 | 
					#include <scene.h>
 | 
				
			||||||
#include <texture.h>
 | 
					#include <texture.h>
 | 
				
			||||||
 | 
					#include <resourcemanager.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <model.h>
 | 
					#include <model.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -188,6 +189,21 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY)
 | 
				
			|||||||
 * Model Private Member Functions
 | 
					 * Model Private Member Functions
 | 
				
			||||||
 ******************************************************************************/
 | 
					 ******************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Loads a model in .obj, .fbx, .gltf, and other formats
 | 
				
			||||||
 | 
					 * For a full list of formats see assimp documentation:
 | 
				
			||||||
 | 
					 *  https://github.com/assimp/assimp/blob/master/doc/Fileformats.md
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Models should not be loaded into Qt resource system
 | 
				
			||||||
 | 
					 * Instead pass an *absolute* path to this function
 | 
				
			||||||
 | 
					 * Relative paths will break if Qtk is executed from different locations
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Models can also be loaded from the `qtk/resource` directory using qrc format
 | 
				
			||||||
 | 
					 *  loadModel(":/models/backpack/backpack.obj")
 | 
				
			||||||
 | 
					 * See resourcemanager.h for more information
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param path Absolute path to a model .obj or other format accepted by assimp
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
void Model::loadModel(const std::string & path)
 | 
					void Model::loadModel(const std::string & path)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  Assimp::Importer import;
 | 
					  Assimp::Importer import;
 | 
				
			||||||
@ -195,13 +211,14 @@ void Model::loadModel(const std::string & path)
 | 
				
			|||||||
  // JIC a relative path was used, get the absolute file path
 | 
					  // JIC a relative path was used, get the absolute file path
 | 
				
			||||||
  QFileInfo info(path.c_str());
 | 
					  QFileInfo info(path.c_str());
 | 
				
			||||||
  info.makeAbsolute();
 | 
					  info.makeAbsolute();
 | 
				
			||||||
  std::string temp = info.absoluteFilePath().toStdString();
 | 
					  mDirectory = path[0] == ':' ? RM::getPath(path)
 | 
				
			||||||
 | 
					      : info.absoluteFilePath().toStdString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Import the model, converting non-triangular geometry to triangles
 | 
					  // Import the model, converting non-triangular geometry to triangles
 | 
				
			||||||
  // + And flipping texture UVs, etc..
 | 
					  // + And flipping texture UVs, etc..
 | 
				
			||||||
  // Assimp options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html
 | 
					  // Assimp options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html
 | 
				
			||||||
  const aiScene * scene =
 | 
					  const aiScene * scene =
 | 
				
			||||||
      import.ReadFile(temp, aiProcess_Triangulate
 | 
					      import.ReadFile(mDirectory, aiProcess_Triangulate
 | 
				
			||||||
                            | aiProcess_FlipUVs
 | 
					                            | aiProcess_FlipUVs
 | 
				
			||||||
                            | aiProcess_GenSmoothNormals
 | 
					                            | aiProcess_GenSmoothNormals
 | 
				
			||||||
                            | aiProcess_CalcTangentSpace
 | 
					                            | aiProcess_CalcTangentSpace
 | 
				
			||||||
@ -216,7 +233,7 @@ void Model::loadModel(const std::string & path)
 | 
				
			|||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // If there were no errors, find the directory that contains this model
 | 
					  // If there were no errors, find the directory that contains this model
 | 
				
			||||||
  mDirectory = path.substr(0, path.find_last_of('/'));
 | 
					  mDirectory = mDirectory.substr(0, mDirectory.find_last_of('/'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Pass the pointers to the root node and the scene to recursive function
 | 
					  // Pass the pointers to the root node and the scene to recursive function
 | 
				
			||||||
  // + Base case breaks when no nodes left to process on model
 | 
					  // + Base case breaks when no nodes left to process on model
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										13
									
								
								src/resourcemanager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/resourcemanager.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					/*##############################################################################
 | 
				
			||||||
 | 
					## Author: Shaun Reed                                                         ##
 | 
				
			||||||
 | 
					## Legal: All Content (c) 2022 Shaun Reed, all rights reserved                ##
 | 
				
			||||||
 | 
					## About: Manage files and resources used by qtk                              ##
 | 
				
			||||||
 | 
					##                                                                            ##
 | 
				
			||||||
 | 
					## Contact: shaunrd0@gmail.com  | URL: www.shaunreed.com | GitHub: shaunrd0   ##
 | 
				
			||||||
 | 
					##############################################################################*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "resourcemanager.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string RM::resourcesDir =
 | 
				
			||||||
 | 
					    std::string(__FILE__).substr(0, std::string(__FILE__).find("src/"))
 | 
				
			||||||
 | 
					    + "resources/";
 | 
				
			||||||
							
								
								
									
										35
									
								
								src/resourcemanager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/resourcemanager.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					/*##############################################################################
 | 
				
			||||||
 | 
					## Author: Shaun Reed                                                         ##
 | 
				
			||||||
 | 
					## Legal: All Content (c) 2022 Shaun Reed, all rights reserved                ##
 | 
				
			||||||
 | 
					## About: Manage files and resources used by qtk                              ##
 | 
				
			||||||
 | 
					##                                                                            ##
 | 
				
			||||||
 | 
					## Contact: shaunrd0@gmail.com  | URL: www.shaunreed.com | GitHub: shaunrd0   ##
 | 
				
			||||||
 | 
					##############################################################################*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef QTK_RESOURCEMANAGER_H
 | 
				
			||||||
 | 
					#define QTK_RESOURCEMANAGER_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef class ResourceManager {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Takes a path using qrc format and constructs full system path to qtk assets
 | 
				
			||||||
 | 
					   * Qrc format prefix ':/' is trimmed from the path for the caller
 | 
				
			||||||
 | 
					   * Assets used with RM may (or may not) appear in qtk/resources.qrc
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param path Path relative to qtk/resources/; ie) ':/models/backpack/backpack.obj'
 | 
				
			||||||
 | 
					   *    An asset at location qtk/resources/path/to/asset.obj
 | 
				
			||||||
 | 
					   *    Should be given in qrc format: ':/path/to/asset.obj'
 | 
				
			||||||
 | 
					   * @return Absoulte system path to a qtk asset
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  static std::string getPath(const std::string & path) {
 | 
				
			||||||
 | 
					    // Only construct qtk resource path if in qrc format; else return it as-is
 | 
				
			||||||
 | 
					    return path[0] == ':' ? resourcesDir + path.substr(2) : path;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static std::string resourcesDir;
 | 
				
			||||||
 | 
					} RM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif //QTK_RESOURCEMANAGER_H
 | 
				
			||||||
@ -10,6 +10,7 @@
 | 
				
			|||||||
#include <texture.h>
 | 
					#include <texture.h>
 | 
				
			||||||
#include <meshrenderer.h>
 | 
					#include <meshrenderer.h>
 | 
				
			||||||
#include <model.h>
 | 
					#include <model.h>
 | 
				
			||||||
 | 
					#include <resourcemanager.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <scene.h>
 | 
					#include <scene.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -151,42 +152,31 @@ void Scene::init()
 | 
				
			|||||||
  //
 | 
					  //
 | 
				
			||||||
  // Model loading
 | 
					  // Model loading
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mModels.push_back(new Model("backpack",
 | 
					  mModels.push_back(new Model("backpack", ":/models/backpack/backpack.obj"));
 | 
				
			||||||
                              "../resources/models/backpack/backpack.obj"));
 | 
					 | 
				
			||||||
  // Sometimes model textures need flipped in certain directions
 | 
					  // Sometimes model textures need flipped in certain directions
 | 
				
			||||||
  mModels.back()->flipTexture("diffuse.jpg", false, true);
 | 
					  mModels.back()->flipTexture("diffuse.jpg", false, true);
 | 
				
			||||||
  mModels.back()->mTransform.setTranslation(0.0f, 0.0f, -10.0f);
 | 
					  mModels.back()->mTransform.setTranslation(0.0f, 0.0f, -10.0f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mModels.push_back(
 | 
					  mModels.push_back(new Model("bird", ":/models/bird/bird.obj"));
 | 
				
			||||||
      new Model("bird",
 | 
					 | 
				
			||||||
                "../resources/models/bird/bird.obj"));
 | 
					 | 
				
			||||||
  mModels.back()->mTransform.setTranslation(2.0f, 2.0f, -10.0f);
 | 
					  mModels.back()->mTransform.setTranslation(2.0f, 2.0f, -10.0f);
 | 
				
			||||||
  // Sometimes the models are very large
 | 
					  // Sometimes the models are very large
 | 
				
			||||||
  mModels.back()->mTransform.scale(0.0025f);
 | 
					  mModels.back()->mTransform.scale(0.0025f);
 | 
				
			||||||
  mModels.back()->mTransform.rotate(-110.0f, 0.0f, 1.0f, 0.0f);
 | 
					  mModels.back()->mTransform.rotate(-110.0f, 0.0f, 1.0f, 0.0f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mModels.push_back(new Model("lion",
 | 
					  mModels.push_back(new Model("lion", ":/models/lion/lion.obj"));
 | 
				
			||||||
                              "../resources/models/lion/lion.obj"));
 | 
					 | 
				
			||||||
  mModels.back()->mTransform.setTranslation(-3.0f, -1.0f, -10.0f);
 | 
					  mModels.back()->mTransform.setTranslation(-3.0f, -1.0f, -10.0f);
 | 
				
			||||||
  mModels.back()->mTransform.scale(0.15f);
 | 
					  mModels.back()->mTransform.scale(0.15f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mModels.push_back(
 | 
					  mModels.push_back(new Model("alien", ":/models/alien-hominid/alien.obj"));
 | 
				
			||||||
      new Model("alien",
 | 
					 | 
				
			||||||
                "../resources/models/alien-hominid/alien.obj"));
 | 
					 | 
				
			||||||
  mModels.back()->mTransform.setTranslation(2.0f, -1.0f, -5.0f);
 | 
					  mModels.back()->mTransform.setTranslation(2.0f, -1.0f, -5.0f);
 | 
				
			||||||
  mModels.back()->mTransform.scale(0.15f);
 | 
					  mModels.back()->mTransform.scale(0.15f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mModels.push_back(
 | 
					  mModels.push_back(new Model("scythe", ":/models/scythe/scythe.obj"));
 | 
				
			||||||
      new Model("scythe",
 | 
					 | 
				
			||||||
                "../resources/models/scythe/scythe.obj")
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
  mModels.back()->mTransform.setTranslation(-6.0f, 0.0f, -10.0f);
 | 
					  mModels.back()->mTransform.setTranslation(-6.0f, 0.0f, -10.0f);
 | 
				
			||||||
  mModels.back()->mTransform.rotate(-90.0f, 1.0f, 0.0f, 0.0f);
 | 
					  mModels.back()->mTransform.rotate(-90.0f, 1.0f, 0.0f, 0.0f);
 | 
				
			||||||
  mModels.back()->mTransform.rotate(90.0f, 0.0f, 1.0f, 0.0f);
 | 
					  mModels.back()->mTransform.rotate(90.0f, 0.0f, 1.0f, 0.0f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mModels.push_back(
 | 
					  mModels.push_back(new Model("masterChief", ":/models/spartan/spartan.obj"));
 | 
				
			||||||
      new Model("masterChief",
 | 
					 | 
				
			||||||
                "../resources/models/spartan/spartan.obj"));
 | 
					 | 
				
			||||||
  mModels.back()->mTransform.setTranslation(-1.5f, 0.5f, -2.0f);
 | 
					  mModels.back()->mTransform.setTranslation(-1.5f, 0.5f, -2.0f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -204,9 +194,9 @@ void Scene::init()
 | 
				
			|||||||
  mMeshes.back()->init();
 | 
					  mMeshes.back()->init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mModels.push_back(
 | 
					  mModels.push_back(
 | 
				
			||||||
      new Model("alienTest",
 | 
					      new Model("alienTest", ":/models/alien-hominid/alien.obj",
 | 
				
			||||||
                "../resources/models/alien-hominid/alien.obj",
 | 
					                ":/model-specular.vert", ":/model-specular.frag")
 | 
				
			||||||
                ":/model-specular.vert", ":/model-specular.frag"));
 | 
					  );
 | 
				
			||||||
  mModels.back()->mTransform.setTranslation(3.0f, -1.0f, 10.0f);
 | 
					  mModels.back()->mTransform.setTranslation(3.0f, -1.0f, 10.0f);
 | 
				
			||||||
  mModels.back()->mTransform.scale(0.15f);
 | 
					  mModels.back()->mTransform.scale(0.15f);
 | 
				
			||||||
  mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f));
 | 
					  mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f));
 | 
				
			||||||
@ -224,7 +214,8 @@ void Scene::init()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // Test spartan Model with phong lighting, specular and normal mapping
 | 
					  // Test spartan Model with phong lighting, specular and normal mapping
 | 
				
			||||||
  mMeshes.push_back(
 | 
					  mMeshes.push_back(
 | 
				
			||||||
      new MeshRenderer("spartanTestLight", Triangle(QTK_DRAW_ELEMENTS)));
 | 
					      new MeshRenderer("spartanTestLight", Triangle(QTK_DRAW_ELEMENTS))
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
  mMeshes.back()->mTransform.setTranslation(1.0f, 1.5f, 10.0f);
 | 
					  mMeshes.back()->mTransform.setTranslation(1.0f, 1.5f, 10.0f);
 | 
				
			||||||
  mMeshes.back()->mTransform.scale(0.25f);
 | 
					  mMeshes.back()->mTransform.scale(0.25f);
 | 
				
			||||||
  // This function changes values we have allocated in a buffer, so init() after
 | 
					  // This function changes values we have allocated in a buffer, so init() after
 | 
				
			||||||
@ -232,9 +223,9 @@ void Scene::init()
 | 
				
			|||||||
  mMeshes.back()->init();
 | 
					  mMeshes.back()->init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mModels.push_back(
 | 
					  mModels.push_back(
 | 
				
			||||||
      new Model("spartanTest",
 | 
					      new Model("spartanTest", ":/models/spartan/spartan.obj",
 | 
				
			||||||
                "../resources/models/spartan/spartan.obj",
 | 
					                ":/model-normals.vert", ":/model-normals.frag")
 | 
				
			||||||
                ":/model-normals.vert", ":/model-normals.frag"));
 | 
					  );
 | 
				
			||||||
  mModels.back()->mTransform.setTranslation(0.0f, -1.0f, 10.0f);
 | 
					  mModels.back()->mTransform.setTranslation(0.0f, -1.0f, 10.0f);
 | 
				
			||||||
  mModels.back()->mTransform.scale(2.0f);
 | 
					  mModels.back()->mTransform.scale(2.0f);
 | 
				
			||||||
  mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f));
 | 
					  mModels.back()->setUniform("uMaterial.ambient", QVector3D(1.0f, 1.0f, 1.0f));
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user