kick
/Users/morten/Programmering/cpp/kick/src/kick/core/project.cpp
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 }
 All Classes Functions Variables