kick
|
00001 // 00002 // project.cpp 00003 // KickCPP 00004 // 00005 // Created by morten on 8/25/13. 00006 // Copyright (c) 2013 Morten Nobel-Joergensen. All rights reserved. 00007 // 00008 00009 #include "kick/core/project.h" 00010 #include "kick/core/engine.h" 00011 #include "kick/core/debug.h" 00012 #include <fstream> 00013 00014 #include "kick/texture/image_format.h" 00015 #include "rapidjson/document.h" 00016 00017 #include <glm/gtc/type_ptr.hpp> 00018 #include "kick/texture/texture_atlas.h" 00019 #include "kick/2d/font.h" 00020 #ifdef EMSCRIPTEN 00021 # include <SDL/SDL_image.h> 00022 #else 00023 # ifdef _WIN32 00024 # include <SDL_image.h> 00025 # else 00026 # include <SDL2_image/SDL_image.h> 00027 # endif 00028 #endif 00029 using namespace std; 00030 00031 namespace kick { 00032 00033 std::map<std::string, std::weak_ptr<TextureAtlas>> Project::textureAtlasRef; 00034 std::map<std::string, std::weak_ptr<Shader>> Project::shaderRef; 00035 std::map<std::string, std::weak_ptr<Texture2D>> Project::texture2DRef; 00036 std::map<std::string, std::weak_ptr<TextureCube>> Project::textureCubeRef; 00037 std::map<std::string, std::weak_ptr<Font>> Project::fontRef; 00038 00039 Project::Project() 00040 { 00041 #ifndef EMSCRIPTEN 00042 SDL_version compile_version; 00043 const SDL_version *link_version=IMG_Linked_Version(); 00044 SDL_IMAGE_VERSION(&compile_version); 00045 if (compile_version.major != link_version->major || 00046 compile_version.minor != link_version->minor || 00047 compile_version.patch != link_version->patch){ 00048 printf("compiled with SDL_image version: %d.%d.%d\n", 00049 compile_version.major, 00050 compile_version.minor, 00051 compile_version.patch); 00052 printf("running with SDL_image version: %d.%d.%d\n", 00053 link_version->major, 00054 link_version->minor, 00055 link_version->patch); 00056 } else { 00057 printf("SDL_image version: %d.%d.%d\n", 00058 compile_version.major, 00059 compile_version.minor, 00060 compile_version.patch); 00061 } 00062 #endif 00063 00064 int flags=IMG_INIT_JPG|IMG_INIT_PNG; 00065 int initted=IMG_Init(flags); 00066 if((initted&flags) != flags) { 00067 printf("IMG_Init: Failed to init required jpg and png support!\n"); 00068 printf("IMG_Init: %s\n", IMG_GetError()); 00069 // handle error 00070 } 00071 } 00072 00073 00074 00075 00076 bool Project::loadTextResource(std::string uri, std::string &res){ 00077 ifstream file(uri); 00078 if(!file.is_open()) { 00079 logError(string{"couldn't open file "} + uri); 00080 return false; 00081 } 00082 00083 string str; 00084 res = ""; 00085 while (std::getline(file, str)) 00086 { 00087 if (res.length()>0){ 00088 res = res + "\n"; 00089 } 00090 res = res + str; 00091 } 00092 return true; 00093 } 00094 00095 bool Project::loadBinaryResource(string uri, vector<char>& fileContents){ 00096 ifstream file(uri, ios::in | ios::binary | ios::ate); 00097 if(!file.is_open()){ 00098 logError(string{"couldn't open "}+uri); 00099 return false; 00100 } 00101 fileContents.resize((unsigned long) file.tellg()); 00102 00103 file.seekg(0, ios::beg); 00104 if(!file.read(&fileContents[ 0 ], fileContents.size())){ 00105 logError(string{"failed to read from "}+uri); 00106 return false; 00107 } 00108 return true; 00109 } 00110 00111 shared_ptr<Texture2D> Project::loadTexture2D(std::string uri, TextureSampler sampler){ 00112 SDL_Surface * surface = IMG_Load(uri.c_str()); 00113 if (surface){ 00114 return shared_ptr<Texture2D>{surfaceToTexture2D(surface, sampler)}; 00115 } else { 00116 return shared_ptr<Texture2D>{}; 00117 } 00118 } 00119 00120 void convertTextureIfNeeded(ImageFormat& imageFormat, SDL_Surface **image){ 00121 SDL_Surface* imgRef = *image; 00122 SDL_PixelFormat * format = imgRef->format; 00123 auto pixelFormat = format->format; 00124 bool isBGR = format->Rshift == 16; 00125 #ifdef GL_ES_VERSION_2_0 00126 GLenum RGB = GL_RGB; 00127 GLenum RGBA = GL_RGBA; 00128 #else 00129 GLenum RGB = isBGR ? GL_BGR : GL_RGB; 00130 GLenum RGBA = isBGR ? GL_BGRA : GL_RGBA; 00131 00132 #endif 00133 const bool alpha = SDL_ISPIXELFORMAT_ALPHA(pixelFormat); 00134 if (alpha){ 00135 imageFormat.format = RGBA; 00136 imageFormat.internalFormat = GL_RGBA; 00137 imageFormat.type = GL_UNSIGNED_BYTE; 00138 } else { 00139 if (format->BytesPerPixel == 4){ 00140 imageFormat.format = RGBA; 00141 imageFormat.internalFormat = GL_RGBA; 00142 } else { 00143 imageFormat.format = RGB; 00144 imageFormat.internalFormat = GL_RGB; 00145 } 00146 imageFormat.type = GL_UNSIGNED_BYTE; 00147 } 00148 } 00149 00150 int invert_image(int width, int height, void* image_pixels) 00151 { 00152 auto temp_row = unique_ptr<char>(new char[width]); 00153 if (temp_row.get() == nullptr) 00154 { 00155 SDL_SetError("Not enough memory for image inversion"); 00156 return -1; 00157 } 00158 //if height is odd, don't need to swap middle row 00159 int height_div_2 = (int) (height * .5f); 00160 for(int index = 0; index < height_div_2; index++) { 00161 //uses string.h 00162 memcpy((Uint8 *)temp_row.get(), 00163 (Uint8 *)(image_pixels) + 00164 width * index, 00165 width); 00166 00167 memcpy( 00168 (Uint8 *)(image_pixels) + 00169 width * index, 00170 (Uint8 *)(image_pixels) + 00171 width * (height - index-1), 00172 width); 00173 memcpy( 00174 (Uint8 *)(image_pixels) + 00175 width * (height - index-1), 00176 temp_row.get(), 00177 width); 00178 } 00179 return 0; 00180 } 00181 00182 std::shared_ptr<Texture2D> Project::loadTexture2DFromMemory(const char *data, int size){ 00183 Texture2D *texturePtr = nullptr; 00184 SDL_RWops* source = SDL_RWFromConstMem(data, size); 00185 if (source){ 00186 // load uri into memory 00187 SDL_Surface *image = nullptr; 00188 image = IMG_Load_RW(source, 1); 00189 00190 if (image) { 00191 texturePtr = surfaceToTexture2D(image); 00192 } else { 00193 SDL_FreeRW(source); 00194 } 00195 } 00196 return std::shared_ptr<Texture2D>(texturePtr); 00197 } 00198 00199 Texture2D *Project::surfaceToTexture2D(SDL_Surface *image, TextureSampler sampler) { 00200 ImageFormat imageFormat; 00201 convertTextureIfNeeded(imageFormat, &image); 00202 #if EMSCRIPTEN 00203 const GLenum GL_UNPACK_FLIP_Y_WEBGL = 37440; 00204 glPixelStorei(GL_UNPACK_FLIP_Y_WEBGL, true); 00205 #else 00206 invert_image(image->pitch, image->h, image->pixels); 00207 #endif 00208 Texture2D *texturePtr = new Texture2D(sampler); 00209 texturePtr->setData(image->w, image->h, static_cast<char*>(image->pixels), imageFormat); 00210 #if EMSCRIPTEN 00211 glPixelStorei(GL_UNPACK_FLIP_Y_WEBGL, false); 00212 #endif 00213 SDL_FreeSurface(image); 00214 return texturePtr; 00215 } 00216 00217 TextureCube* Project::loadTextureCubeFromMemory(const char *data, int size){ 00218 TextureCube *texturePtr = nullptr; 00219 SDL_RWops* source = SDL_RWFromConstMem(data, size); 00220 if (source){ 00221 // load uri into memory 00222 SDL_Surface *image = nullptr; 00223 image = IMG_Load_RW(source, 1); 00224 00225 if (image) { 00226 texturePtr = surfaceToTextureCube(image); 00227 } else { 00228 SDL_FreeRW(source); 00229 } 00230 } 00231 return texturePtr; 00232 } 00233 00234 TextureCube *Project::surfaceToTextureCube( SDL_Surface *image) { 00235 ImageFormat imageFormat; 00236 TextureCube *texturePtr = new TextureCube(); 00237 convertTextureIfNeeded(imageFormat, &image); 00238 assert(image->h == image->w * 6); 00239 for (int i = 0; i < 6; i++) { 00240 texturePtr->setData(image->w, image->h, static_cast<char *>(image->pixels) + image->pitch * image->w * i, i, imageFormat); 00241 } 00242 SDL_FreeSurface(image); 00243 return texturePtr; 00244 } 00245 00246 shared_ptr<TextureCube> Project::loadTextureCube(std::string uri){ 00247 auto iter = textureCubeRef.find(uri); 00248 if (iter != textureCubeRef.end()){ 00249 if (!iter->second.expired()){ 00250 return iter->second.lock(); 00251 } 00252 } 00253 00254 SDL_Surface * surface = IMG_Load(uri.c_str()); 00255 if (surface){ 00256 auto res = shared_ptr<TextureCube>{surfaceToTextureCube(surface)}; 00257 textureCubeRef[uri] = std::weak_ptr<TextureCube>{res}; 00258 return res; 00259 } else { 00260 return shared_ptr<TextureCube>{nullptr}; 00261 } 00262 } 00263 00264 Material* Project::createMaterial(std::string shaderUri){ 00265 auto shader = loadShader(shaderUri); 00266 if (!shader){ 00267 return nullptr; 00268 } 00269 return new Material(shader); 00270 } 00271 00272 00273 shared_ptr<Shader> Project::loadShader(std::string uri){ 00274 auto iter = shaderRef.find(uri); 00275 if (iter != shaderRef.end()){ 00276 if (!iter->second.expired()){ 00277 return iter->second.lock(); 00278 } 00279 } 00280 00281 using namespace rapidjson; 00282 using namespace glm; 00283 string shaderSource; 00284 bool success = loadTextResource(uri, shaderSource); 00285 if (!success){ 00286 return nullptr; 00287 } 00288 00289 Document document; 00290 Shader* shader = nullptr; 00291 if (document.Parse<0>( shaderSource.c_str() ).HasParseError() ) { 00292 logError("Error parsing .shader file"); 00293 } else { 00294 shader = new Shader(); 00295 auto getString = [&](string name, string defaultValue){ 00296 if (!document.HasMember(name.c_str())){ 00297 return defaultValue; 00298 } 00299 return string{document[name.c_str()].GetString()}; 00300 }; 00301 00302 auto getBool = [&](string name, bool defaultValue){ 00303 if (!document.HasMember(name.c_str())){ 00304 return defaultValue; 00305 } 00306 return document[name.c_str()].GetBool(); 00307 }; 00308 00309 auto getFloat = [&](string name, float defaultValue){ 00310 if (!document.HasMember(name.c_str())){ 00311 return defaultValue; 00312 } 00313 return (float)document[name.c_str()].GetDouble(); 00314 }; 00315 00316 auto getInt = [&](string name, int defaultValue){ 00317 if (!document.HasMember(name.c_str())){ 00318 return defaultValue; 00319 } 00320 return document[name.c_str()].GetInt(); 00321 }; 00322 00323 #define createGetX(X) auto get##X = [&](string name, X defaultValue){ \ 00324 if (!document.HasMember(name.c_str())){ \ 00325 return defaultValue; \ 00326 } \ 00327 int val = (int)round(document[name.c_str()].GetDouble()); \ 00328 X res {defaultValue}; \ 00329 toEnum(val, res); \ 00330 return res; \ 00331 } 00332 createGetX(BlendType); 00333 createGetX(FaceCullingType); 00334 createGetX(ZTestType); 00335 00336 string vertexShaderURI = getString("vertexShaderURI", ""); 00337 string geometryShaderURI = getString("geometryShaderURI", ""); 00338 string fragmentShaderURI = getString("fragmentShaderURI", ""); 00339 //string outputAttributeName = getString("outputAttributeName", ""); 00340 bool blend = getBool("blend", false); 00341 BlendType blendDFactorAlpha = getBlendType("blendDFactorAlpha", BlendType::OneMinusSrcAlpha); 00342 BlendType blendDFactorRGB = getBlendType("blendDFactorRGB", BlendType::OneMinusSrcAlpha); 00343 BlendType blendSFactorAlpha = getBlendType("blendSFactorAlpha", BlendType::SrcAlpha); 00344 BlendType blendSFactorRGB = getBlendType("blendSFactorRGB", BlendType::SrcAlpha); 00345 bool depthWrite = getBool("depthWrite", true); 00346 FaceCullingType faceCulling = getFaceCullingType("faceCulling", FaceCullingType::Back); 00347 bool polygonOffsetEnabled = getBool("polygonOffsetEnabled", false); 00348 float polygonOffsetFactor = getFloat("polygonOffsetFactor", 1); 00349 float polygonOffsetUnit = getFloat("polygonOffsetUnit", 0); 00350 ZTestType zTest = getZTestType("zTest", ZTestType::Less); 00351 int renderOrder = getInt("renderOrder", 1000); 00352 string vertexShader; 00353 if (!loadTextResource(vertexShaderURI, vertexShader)){ 00354 return nullptr; 00355 } 00356 string fragmentShader; 00357 if (!loadTextResource(fragmentShaderURI,fragmentShader)){ 00358 return nullptr; 00359 } 00360 00361 00362 shader->setShaderSource(ShaderType::VertexShader, vertexShader); 00363 shader->setShaderSource(ShaderType::FragmentShader, fragmentShader); 00364 00365 if (geometryShaderURI.length()){ 00366 #ifdef GL_ES_VERSION_2_0 00367 assert(false); 00368 #else 00369 string geometryShader; 00370 if (!loadTextResource(geometryShaderURI, geometryShader)){ 00371 return nullptr; 00372 } 00373 shader->setShaderSource(ShaderType::GeometryShader, geometryShader); 00374 #endif 00375 } 00376 00377 shader->setBlend(blend); 00378 shader->setBlendDFactorAlpha(blendDFactorAlpha); 00379 shader->setBlendDFactorRGB(blendDFactorRGB); 00380 shader->setBlendSFactorAlpha(blendSFactorAlpha); 00381 shader->setBlendSFactorRGB(blendSFactorRGB); 00382 shader->setDepthWrite(depthWrite); 00383 shader->setFaceCulling(faceCulling); 00384 shader->setPolygonOffsetEnabled(polygonOffsetEnabled); 00385 shader->setPolygonOffsetFactorAndUnit(vec2{polygonOffsetFactor, polygonOffsetUnit}); 00386 shader->setZTest(zTest); 00387 shader->setRenderOrder(renderOrder); 00388 shader->apply(); 00389 00390 if (document.HasMember("defaultUniform") && document["defaultUniform"].IsObject()){ 00391 auto & defaultUniformArray = document["defaultUniform"]; 00392 for (auto memberIter = defaultUniformArray.MemberBegin();memberIter != defaultUniformArray.MemberEnd();memberIter++){ 00393 string name = memberIter->name.GetString(); 00394 auto& uniformValue = memberIter->value; 00395 const UniformDescriptor * uniform = shader->getShaderUniform(name); 00396 if (uniform) { 00397 switch (uniform->type){ 00398 case GL_INT: 00399 { 00400 int value = uniformValue.GetInt(); 00401 shader->setDefaultUniform(name, value); 00402 } 00403 break; 00404 case GL_FLOAT: 00405 { 00406 float value = (float) uniformValue.GetDouble(); 00407 shader->setDefaultUniform(name, value); 00408 } 00409 break; 00410 case GL_FLOAT_VEC4: 00411 { 00412 vec4 value; 00413 for (SizeType i=0;i<4;i++){ 00414 value[i] = (float)uniformValue[i].GetDouble(); 00415 } 00416 shader->setDefaultUniform(name, value); 00417 } 00418 break; 00419 case GL_FLOAT_MAT3: 00420 { 00421 mat3 value; 00422 float *valuePtr = value_ptr(value); 00423 for (SizeType i=0;i<9;i++){ 00424 valuePtr[i] = (float)uniformValue[i].GetDouble(); 00425 } 00426 shader->setDefaultUniform(name, value); 00427 } 00428 break; 00429 case GL_FLOAT_MAT4: 00430 { 00431 mat4 value; 00432 float *valuePtr = value_ptr(value); 00433 for (SizeType i = 0; i < 16; i++) { 00434 valuePtr[i] = (float) uniformValue[i].GetDouble(); 00435 } 00436 shader->setDefaultUniform(name, value); 00437 } 00438 break; 00439 case GL_SAMPLER_2D: 00440 { 00441 string texName = memberIter->value.GetString(); 00442 auto value = loadTexture2D(texName); 00443 shader->setDefaultUniform(name, value); 00444 } 00445 break; 00446 case GL_SAMPLER_CUBE: 00447 { 00448 string texName = memberIter->value.GetString(); 00449 auto value = loadTextureCube(texName); 00450 shader->setDefaultUniform(name, value); 00451 } 00452 break; 00453 default:{ 00454 cout << "Unhandled case " << uniform->type << endl; 00455 } 00456 break; 00457 } 00458 } 00459 } 00460 } 00461 } 00462 auto ref = std::shared_ptr<Shader>{shader}; 00463 shaderRef[uri] = weak_ptr<Shader>{ref}; 00464 return ref; 00465 } 00466 00467 std::shared_ptr<TextureAtlas> Project::loadTextureAtlas(std::string filename) { 00468 std::string texture = filename.substr(0, filename.size()-4) + ".png"; 00469 00470 auto iter = textureAtlasRef.find(filename); 00471 if (iter != textureAtlasRef.end()){ 00472 if (!iter->second.expired()){ 00473 return iter->second.lock(); 00474 } 00475 } 00476 TextureAtlas* textureAtlas = new TextureAtlas(); 00477 textureAtlas->load(filename, texture); 00478 auto ref = std::shared_ptr<TextureAtlas>{textureAtlas}; 00479 textureAtlasRef[filename] = weak_ptr<TextureAtlas>{ref}; 00480 return ref; 00481 } 00482 00483 std::shared_ptr<Font> Project::loadFont(int fontsize){ 00484 return loadFont(string("assets/font/open_sans_")+std::to_string(fontsize)+".fnt"); 00485 } 00486 00487 std::shared_ptr<Font> Project::loadFont(string fontName) { 00488 auto iter = fontRef.find(fontName); 00489 if (iter != fontRef.end()){ 00490 if (!iter->second.expired()){ 00491 return iter->second.lock(); 00492 } 00493 } 00494 Font* font = new Font(); 00495 font->loadFntFile(fontName); 00496 auto ref = std::shared_ptr<Font>{font}; 00497 fontRef[fontName] = weak_ptr<Font>{ref}; 00498 return ref; 00499 } 00500 }